@zaip-ai/cli 1.3.0 → 1.3.1

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 (258) hide show
  1. package/README.md +1 -1
  2. package/dist/services/agent-adapters.js +1 -1
  3. package/dist/services/agent-adapters.js.map +1 -1
  4. package/dist/services/agent-adapters.test.js +2 -1
  5. package/dist/services/agent-adapters.test.js.map +1 -1
  6. package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
  7. package/dist/services/codex-pre-tool-hook-template.js +89 -7
  8. package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
  9. package/dist/services/codex-rules.d.ts +1 -1
  10. package/dist/services/codex-rules.d.ts.map +1 -1
  11. package/dist/services/codex-rules.js +7 -0
  12. package/dist/services/codex-rules.js.map +1 -1
  13. package/dist/services/install-agents-template.d.ts.map +1 -1
  14. package/dist/services/install-agents-template.js +1 -0
  15. package/dist/services/install-agents-template.js.map +1 -1
  16. package/dist/services/runtime-template.test.js +31 -7
  17. package/dist/services/runtime-template.test.js.map +1 -1
  18. package/dist/services/template-manifest.d.ts.map +1 -1
  19. package/dist/services/template-manifest.js +7 -3
  20. package/dist/services/template-manifest.js.map +1 -1
  21. package/package.json +1 -1
  22. package/templates/.agents/config/agents/catalog.yaml +7 -7
  23. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +2 -2
  24. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +2 -2
  25. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +8 -8
  26. package/templates/.agents/dashboard/.zaip-managed.json +373 -205
  27. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-DxR_pgSV.js → CostCalculationGuidePage-ovxurkIk.js} +1 -1
  28. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-4jZLTWE_.js → DashboardHelpDialog-BgVgrfhQ.js} +240 -86
  29. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CKcsqb-r.js → PricingSettingsDialog-vSvvOHQp.js} +1 -1
  30. package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-DmmJD2Th.js → ProjectSettingsPage-zH758vfs.js} +1 -1
  31. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-DLn-Mz2M.js +1 -0
  32. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CaKwQP43.css → TraceabilityWorkspace-DvvavDLY.css} +1 -1
  33. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DKwP1stV.js +68 -0
  34. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-PoJA2PI1.css +1 -0
  35. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-C0fj2uU9.js → icons-vendor-B_lDDHn1.js} +1 -1
  36. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-B31GMG5O.css +1 -0
  37. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-CZQlsLV9.js +2 -0
  38. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-BEsJ-FYH.js → react-vendor-B3TJrD4O.js} +1 -1
  39. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-B6y6Gw12.js → traceability-7f1j5wx3.js} +1 -1
  40. package/templates/.agents/dashboard/apps/frontend/dist/index.html +7 -7
  41. package/templates/.agents/dashboard/apps/frontend/playwright.dom.config.ts +32 -0
  42. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +132 -245
  43. package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +0 -21
  44. package/templates/.agents/dashboard/apps/frontend/src/DashboardAlternateContent.tsx +9 -0
  45. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.dom.e2e.ts +56 -0
  46. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.test.ts +18 -1
  47. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.tsx +75 -8
  48. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +25 -1
  49. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +7 -9
  50. package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +4 -4
  51. package/templates/.agents/dashboard/apps/frontend/src/overview-status-page.test.ts +1 -1
  52. package/templates/.agents/dashboard/apps/frontend/src/snapshot-banner-semantics.test.ts +7 -0
  53. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +20 -8
  54. package/templates/.agents/dashboard/apps/frontend/src/sqlite-page-export.test.ts +32 -0
  55. package/templates/.agents/dashboard/apps/frontend/src/sqlite-page-export.ts +21 -0
  56. package/templates/.agents/dashboard/apps/frontend/src/styles.css +19 -0
  57. package/templates/.agents/dashboard/apps/frontend/src/traceability-task-pagination.test.ts +32 -0
  58. package/templates/.agents/dashboard/apps/frontend/src/traceability-task-pagination.ts +36 -0
  59. package/templates/.agents/dashboard/apps/frontend/src/traceability-workspace.css +6 -2
  60. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +48 -0
  61. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +31 -27
  62. package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.ts +7 -1
  63. package/templates/.agents/dashboard/apps/frontend/src/use-app-work-list-page.test.ts +163 -0
  64. package/templates/.agents/dashboard/apps/frontend/src/use-app-work-list-page.ts +136 -0
  65. package/templates/.agents/dashboard/apps/frontend/src/use-operations-detail-export.ts +76 -0
  66. package/templates/.agents/dashboard/apps/frontend/src/work-list-load-dedup.test.ts +23 -0
  67. package/templates/.agents/dashboard/apps/frontend/src/work-list-load-dedup.ts +27 -0
  68. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.test.ts +38 -14
  69. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionPanel.tsx +60 -0
  70. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionTable.tsx +191 -0
  71. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.loading.test.ts +14 -0
  72. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.run-page.test.ts +18 -0
  73. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.tsx +18 -3
  74. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +8 -2
  75. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorLoadErrors.tsx +35 -0
  76. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +47 -1
  77. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +50 -58
  78. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableLoading.test.ts +132 -0
  79. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableLoading.tsx +70 -0
  80. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +128 -138
  81. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +17 -67
  82. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-copy.ts +2 -0
  83. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-views.ts +3 -1
  84. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-page-range.ts +35 -0
  85. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +16 -4
  86. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.test.ts +37 -2
  87. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +44 -8
  88. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +6 -1
  89. package/templates/.agents/dashboard/modules/operations/src/contracts.ts +20 -3
  90. package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.test.ts +54 -0
  91. package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.tsx +247 -0
  92. package/templates/.agents/dashboard/modules/operations/src/react/OperationsMainContent.tsx +39 -13
  93. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +107 -2
  94. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.macro-metrics.test.ts +55 -0
  95. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +20 -28
  96. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +162 -165
  97. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewTopPanel.tsx +127 -0
  98. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +13 -46
  99. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +3 -0
  100. package/templates/.agents/dashboard/modules/operations/src/react/PagedWorkList.test.tsx +43 -0
  101. package/templates/.agents/dashboard/modules/operations/src/react/PagedWorkList.tsx +181 -0
  102. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.test.ts +4 -2
  103. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.tsx +13 -0
  104. package/templates/.agents/dashboard/modules/operations/src/react/developer-identities.test.ts +18 -0
  105. package/templates/.agents/dashboard/modules/operations/src/react/developer-identities.ts +30 -0
  106. package/templates/.agents/dashboard/modules/operations/src/react/execution-metrics.ts +1 -0
  107. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +8 -0
  108. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +3 -0
  109. package/templates/.agents/dashboard/modules/operations/src/react/export/export-layout.test.ts +14 -5
  110. package/templates/.agents/dashboard/modules/operations/src/react/index.ts +1 -0
  111. package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +4 -3
  112. package/templates/.agents/dashboard/modules/operations/src/react/operations-date-filters.css +1 -1
  113. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +21 -11
  114. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.css +4 -0
  115. package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +8 -1
  116. package/templates/.agents/dashboard/modules/operations/src/react/operations-view-order.test.ts +1 -0
  117. package/templates/.agents/dashboard/modules/operations/src/react/period-summary.ts +1 -0
  118. package/templates/.agents/dashboard/modules/operations/src/server/metric-coverage.ts +1 -0
  119. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +527 -335
  120. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +501 -309
  121. package/templates/.agents/dashboard/packages/local-runtime/gitignore.template +2 -0
  122. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +3 -1
  123. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-read-model.mjs +3 -3
  124. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +25 -15
  125. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +527 -335
  126. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.test.ts +34 -0
  127. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.ts +15 -8
  128. package/templates/.agents/dashboard/packages/local-runtime/src/server-classification-page.test.ts +87 -0
  129. package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.test.ts +44 -0
  130. package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.ts +125 -0
  131. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +66 -0
  132. package/templates/.agents/dashboard/packages/local-runtime/src/server-request-authority.test.ts +61 -0
  133. package/templates/.agents/dashboard/packages/local-runtime/src/server-request-authority.ts +18 -0
  134. package/templates/.agents/dashboard/packages/local-runtime/src/server-run-projections.test.ts +42 -0
  135. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +97 -0
  136. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +3 -1
  137. package/templates/.agents/dashboard/packages/local-runtime/src/server-sqlite-task-trace.ts +91 -0
  138. package/templates/.agents/dashboard/packages/local-runtime/src/server.test-fixtures.ts +36 -0
  139. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +98 -123
  140. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +111 -135
  141. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-artifact-counts.ts +28 -0
  142. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +63 -0
  143. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +109 -0
  144. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +497 -0
  145. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-fast-page-parity.test-support.ts +65 -0
  146. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-page.test.ts +176 -0
  147. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-page.ts +119 -0
  148. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +41 -0
  149. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.test.ts +16 -0
  150. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.ts +29 -0
  151. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-cache.ts +355 -0
  152. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +121 -0
  153. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-timing.ts +27 -0
  154. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +15 -0
  155. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-work-list.test.ts +223 -0
  156. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-work-list.ts +304 -0
  157. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +23 -0
  158. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-first-fold-types.ts +89 -0
  159. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +227 -0
  160. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +691 -0
  161. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +81 -0
  162. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +440 -863
  163. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1120 -1086
  164. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +129 -0
  165. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-install.test.ts +26 -0
  166. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-install.ts +28 -0
  167. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-seed.test.ts +116 -0
  168. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +146 -0
  169. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.test.mjs +165 -0
  170. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +140 -0
  171. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +188 -299
  172. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +51 -70
  173. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +5 -2
  174. package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +4 -1
  175. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-loading.spec.ts +142 -0
  176. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-pagination.spec.ts +232 -0
  177. package/templates/.agents/dashboard/tests/e2e/specs/dashboard-performance-matrix.spec.ts +743 -0
  178. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +183 -210
  179. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  180. package/templates/.agents/requirements/autonomous-assurance.md +6 -6
  181. package/templates/.agents/requirements/comparative-audit.md +1 -1
  182. package/templates/.agents/requirements/context-engine.md +1 -1
  183. package/templates/.agents/requirements/cost-observability.md +1 -1
  184. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  185. package/templates/.agents/requirements/data-export.md +1 -1
  186. package/templates/.agents/requirements/design-system.md +1 -1
  187. package/templates/.agents/requirements/execution-engine.md +3 -3
  188. package/templates/.agents/requirements/kanban-dashboard.md +10 -2
  189. package/templates/.agents/requirements/model-orchestration.md +1 -1
  190. package/templates/.agents/requirements/public-distribution.md +1 -1
  191. package/templates/.agents/requirements/software-architecture.md +1 -1
  192. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  193. package/templates/.agents/requirements/work-lifecycle.md +4 -4
  194. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  195. package/templates/.agents/rules/dashboard-ui-consistency.md +14 -0
  196. package/templates/.agents/rules/frontend-lite.md +4 -4
  197. package/templates/.agents/rules/run-lifecycle-closure.md +4 -4
  198. package/templates/.agents/runtime/bin/zaip.mjs +1137 -1037
  199. package/templates/.agents/runtime/checksums.sha256 +2 -2
  200. package/templates/.agents/runtime/provenance.json +2 -2
  201. package/templates/.agents/skills/criar-pasta-obsidian/scripts/generate-obsidian-vault.test.mjs +20 -16
  202. package/templates/.agents/skills/zaip-skill-agent-catalog/SKILL.md +22 -0
  203. package/templates/.agents/skills/zaip-skill-agent-catalog/agents/openai.yaml +4 -0
  204. package/templates/.agents/skills/zaip-skill-author-skills/SKILL.md +3 -2
  205. package/templates/.agents/skills/zaip-skill-author-skills/scripts/audit-skill-catalog.mjs +49 -0
  206. package/templates/.agents/skills/zaip-skill-author-skills/scripts/author-skill-scripts.test.mjs +27 -0
  207. package/templates/.agents/skills/zaip-skill-author-skills/scripts/run-skill-tests.mjs +37 -0
  208. package/templates/.agents/skills/zaip-skill-backend/SKILL.md +1 -0
  209. package/templates/.agents/skills/zaip-skill-change/SKILL.md +1 -1
  210. package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +83 -0
  211. package/templates/.agents/skills/zaip-skill-dashboard-diagnostics/SKILL.md +22 -0
  212. package/templates/.agents/skills/zaip-skill-dashboard-diagnostics/agents/openai.yaml +4 -0
  213. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +1 -1
  214. package/templates/.agents/skills/zaip-skill-frontend/SKILL.md +4 -3
  215. package/templates/.agents/skills/zaip-skill-frontend/agents/openai.yaml +2 -2
  216. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +10 -10
  217. package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +27 -0
  218. package/templates/.agents/skills/zaip-skill-lifecycle-closure/agents/openai.yaml +4 -0
  219. package/templates/.agents/skills/zaip-skill-prd/SKILL.md +4 -0
  220. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +72 -22
  221. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-explicit-recovery.mjs +223 -0
  222. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +42 -1
  223. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +101 -0
  224. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +53 -0
  225. package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +20 -0
  226. package/templates/.agents/skills/zaip-skill-release-qualification/agents/openai.yaml +4 -0
  227. package/templates/.agents/skills/zaip-skill-review/SKILL.md +5 -0
  228. package/templates/.agents/skills/zaip-skill-rules/SKILL.md +1 -1
  229. package/templates/.agents/skills/zaip-skill-spec/SKILL.md +4 -0
  230. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +22 -90
  231. package/templates/.agents/zaip-aios/INDEX.md +57 -32
  232. package/templates/.agents/zaip-aios/README.md +22 -12
  233. package/templates/.agents/zaip-aios/guides/02-requirements.md +5 -0
  234. package/templates/.agents/zaip-aios/guides/06-execucao.md +13 -4
  235. package/templates/.agents/zaip-aios/guides/07-revisao-e-validacao.md +7 -0
  236. package/templates/.agents/zaip-aios/guides/10-seguranca.md +23 -0
  237. package/templates/.agents/zaip-aios/guides/11-observabilidade-e-custos.md +6 -6
  238. package/templates/.agents/zaip-aios/guides/13-boas-praticas.md +3 -0
  239. package/templates/.agents/zaip-aios/guides/16-lifecycle-e-reconciliacao.md +12 -3
  240. package/templates/.agents/zaip-aios/guides/17-como-o-custo-e-calculado.md +5 -5
  241. package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +23 -0
  242. package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +44 -8
  243. package/templates/.agents/zaip-aios/guides/21-telemetria-monitor-custos.md +8 -14
  244. package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +5 -0
  245. package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +6 -6
  246. package/templates/.agents/zaip-aios/guides/24-calculo-de-custos-openai.md +8 -8
  247. package/templates/.agents/zaip-aios/guides/25-fontes-de-precos-openai.md +3 -2
  248. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +77 -10
  249. package/templates/.agents/zaip-aios/guides/27-telemetria-git-native.md +8 -3
  250. package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +5 -0
  251. package/templates/template-manifest.json +555 -170
  252. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BwtitWa_.js +0 -1
  253. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-BZ5WdaMN.css +0 -1
  254. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-N5Csr0RP.js +0 -68
  255. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-C4aIB1PV.js +0 -2
  256. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DK_enB5Z.css +0 -1
  257. package/templates/.agents/dashboard/apps/frontend/src/use-idle-cost-prefetch.ts +0 -45
  258. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs.metafile.json +0 -7333
@@ -1,17 +1,11 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { queryInvocationManifestPeriod } from "@zaip-ai/costs";
3
3
  import { describe, expect, it } from "vitest";
4
- import {
5
- activeCostPeriodData,
6
- costPeriodDateRange,
7
- defaultCostPeriod,
8
- emptyCostFilters,
9
- loadCostMonitorPeriod,
10
- shouldFetchCostPeriod,
11
- } from "./CostFilters.js";
12
4
  import { displayModelName, isModelAuditRow, sumModelDurationMs } from "./CostModelsAuditTable.js";
13
5
  import {
14
6
  agentRunModelPresentation,
7
+ type CostDeveloperAttributionRow,
8
+ CostDeveloperAttributionTable,
15
9
  CostGroupsTable,
16
10
  CostRunsTable,
17
11
  contextUpliftUsd,
@@ -29,7 +23,6 @@ import {
29
23
  formatCostPeriodLabel,
30
24
  formatDateTime,
31
25
  formatDurationCoverage,
32
- formatMultiplierPair,
33
26
  formatRate,
34
27
  formatReasoningEffort,
35
28
  formatServiceTier,
@@ -207,6 +200,112 @@ describe("runtime availability", () => {
207
200
  });
208
201
  });
209
202
  describe("grouped cost availability", () => {
203
+ it("uses the requested Cost Monitor developer metric columns without Operations source or coverage", () => {
204
+ const table = CostDeveloperAttributionTable({
205
+ locale: "pt-BR",
206
+ rows: [
207
+ {
208
+ account_id: "42",
209
+ login_snapshot: "dev-42",
210
+ prd_count: 1,
211
+ spec_count: 2,
212
+ task_count: 3,
213
+ run_count: 4,
214
+ test_count: null,
215
+ evidence_count: null,
216
+ total_duration_ms: 60_000,
217
+ total_tokens: 50,
218
+ total_cost_brl: 1.5,
219
+ duration_covered_runs: 4,
220
+ token_covered_runs: 4,
221
+ brl_covered_runs: 4,
222
+ },
223
+ ],
224
+ });
225
+ const columns = table.props.columns as Array<{
226
+ key: string;
227
+ label: string;
228
+ render: (row: CostDeveloperAttributionRow) => unknown;
229
+ }>;
230
+ const unavailable = {
231
+ account_id: "unknown",
232
+ login_snapshot: null,
233
+ prd_count: 0,
234
+ spec_count: 0,
235
+ task_count: 0,
236
+ run_count: 0,
237
+ test_count: null,
238
+ evidence_count: null,
239
+ total_duration_ms: null,
240
+ total_tokens: null,
241
+ total_cost_brl: null,
242
+ duration_covered_runs: 0,
243
+ token_covered_runs: 0,
244
+ brl_covered_runs: 0,
245
+ } satisfies CostDeveloperAttributionRow;
246
+ expect(columns.map((column) => [column.key, column.label])).toEqual([
247
+ ["developer", "Desenvolvedor"],
248
+ ["prds", "PRDs"],
249
+ ["specs", "SPECs"],
250
+ ["tasks", "Tarefas"],
251
+ ["runs", "Runs"],
252
+ ["tests", "Testes"],
253
+ ["evidences", "Evidências"],
254
+ ["duration", "Tempo"],
255
+ ["tokens", "Tokens"],
256
+ ["cost", "Custos"],
257
+ ]);
258
+ expect(columns.find((column) => column.key === "tests")?.render(unavailable)).toBe("—");
259
+ expect(columns.find((column) => column.key === "evidences")?.render(unavailable)).toBe("—");
260
+ expect(columns.find((column) => column.key === "duration")?.render(unavailable)).toBe(
261
+ "Indisponível",
262
+ );
263
+ });
264
+ it("marks incomplete aggregates in place without adding source or coverage columns", () => {
265
+ const partial = {
266
+ account_id: "42",
267
+ login_snapshot: "dev-42",
268
+ prd_count: 1,
269
+ spec_count: 2,
270
+ task_count: 3,
271
+ run_count: 4,
272
+ test_count: null,
273
+ evidence_count: null,
274
+ total_duration_ms: 60_000,
275
+ total_tokens: 50,
276
+ total_cost_brl: 1.5,
277
+ duration_covered_runs: 3,
278
+ token_covered_runs: 2,
279
+ brl_covered_runs: 2,
280
+ } satisfies CostDeveloperAttributionRow;
281
+ const table = CostDeveloperAttributionTable({ locale: "pt-BR", rows: [partial] });
282
+ const columns = table.props.columns as Array<{
283
+ key: string;
284
+ render: (
285
+ row: CostDeveloperAttributionRow,
286
+ ) => { props?: { children?: unknown[]; title?: string } } | string;
287
+ }>;
288
+ for (const key of ["duration", "tokens", "cost"]) {
289
+ const rendered = columns.find((column) => column.key === key)?.render(partial);
290
+ expect(rendered).toMatchObject({
291
+ props: {
292
+ title: expect.stringContaining("Valor parcial"),
293
+ },
294
+ });
295
+ const children = (rendered as { props: { children: unknown[] } }).props.children;
296
+ expect(children[0]).toMatchObject({
297
+ props: { "aria-hidden": "true", children: expect.any(String) },
298
+ });
299
+ expect(children[1]).toMatchObject({
300
+ props: { children: expect.stringContaining("Valor parcial") },
301
+ });
302
+ expect(
303
+ (rendered as { props: { "aria-label"?: string } }).props["aria-label"],
304
+ ).toBeUndefined();
305
+ }
306
+ expect(columns.map((column) => column.key)).not.toContain("coverage");
307
+ expect(columns.map((column) => column.key)).not.toContain("source");
308
+ });
210
309
  it("uses deduplicated root and child invocation effort in the Run drilldown footer", () => {
211
310
  const root = {
212
311
  run_id: "RUN-ROOT",
@@ -290,22 +389,28 @@ describe("grouped cost availability", () => {
290
389
  expect(column("tokens")?.sortValue?.(unavailableGroup)).toBeNull();
291
390
  expect(column("cost")?.sortValue?.(unavailableGroup)).toBeNull();
292
391
  const totalSummary = "Total geral — Tempo: Indisponível; Tokens: 0; BRL: R$ 0,00";
293
- expect(
294
- (column("latestStartedAt")!.footer as { props: { "aria-label": string } }).props[
295
- "aria-label"
296
- ],
297
- ).toBe("Total geral");
298
- expect(
299
- (column("duration")!.footer as { props: { "aria-label": string } }).props["aria-label"],
300
- ).toBe(totalSummary);
301
- expect(
302
- (column("tokens")!.footer as { props: { "aria-label": string } }).props["aria-label"],
303
- ).toBe(totalSummary);
304
- expect(
305
- (column("cost")!.footer as { props: { "aria-label": string } }).props["aria-label"],
306
- ).toBe(totalSummary);
392
+ const footerLabel = (key: string) => {
393
+ const footer = column(key)?.footer as { props?: { "aria-label"?: string } } | undefined;
394
+ return footer?.props?.["aria-label"];
395
+ };
396
+ expect(footerLabel("latestStartedAt")).toBe("Total geral");
397
+ expect(footerLabel("duration")).toBe(totalSummary);
398
+ expect(footerLabel("tokens")).toBe(totalSummary);
399
+ expect(footerLabel("cost")).toBe(totalSummary);
307
400
  expect(column("coverage")).toBeUndefined();
308
401
  });
402
+ it("keeps the empty state until loading settles for local cost-group pages", () => {
403
+ const loadingTable = CostGroupsTable({
404
+ rows: [],
405
+ currency: "BRL",
406
+ locale: "pt-BR",
407
+ loading: true,
408
+ });
409
+ const emptyTable = CostGroupsTable({ rows: [], currency: "BRL", locale: "pt-BR" });
410
+
411
+ expect(loadingTable.props.emptyMessage).toBe("Carregando…");
412
+ expect(emptyTable.props.emptyMessage).toBe("Nenhum dado disponível.");
413
+ });
309
414
  it("uses supplied pre-pagination totals for daily footer metrics", () => {
310
415
  const table = CostGroupsTable({
311
416
  rows: [{ ...unavailableGroup, total_tokens: 5, estimated_brl: 2, estimated_usd: 1 }],
@@ -622,121 +727,6 @@ describe("grouped cost availability", () => {
622
727
  expect(pager.props.pagination.onRetry).toEqual(expect.any(Function));
623
728
  });
624
729
  });
625
- describe("period-driven Cost Monitor loading", () => {
626
- it("drops a stale in-memory Cost period response when refresh replaces its base snapshot", () => {
627
- const stale = { runs: [{ run_id: "RUN-OBSERVED", duration_ms: 31 * 3_600_000 }] } as never;
628
- const refreshed = { runs: [{ run_id: "RUN-PROJECTED", duration_ms: 413_659_026 }] } as never;
629
- expect(activeCostPeriodData(refreshed, stale, stale)).toBe(refreshed);
630
- expect(activeCostPeriodData(refreshed, refreshed, refreshed)).toBe(refreshed);
631
- });
632
- it("does not issue a second Cost request after an embedded default atomic snapshot", () => {
633
- const filters = {
634
- query: "",
635
- provider: "",
636
- phase: "",
637
- origin: "",
638
- workType: "",
639
- agent: "",
640
- model: "",
641
- status: "",
642
- priority: "",
643
- risk: "",
644
- };
645
- expect(shouldFetchCostPeriod({ filters, period: "month", snapshotBacked: true })).toBe(false);
646
- expect(shouldFetchCostPeriod({ filters, period: "all", snapshotBacked: true })).toBe(true);
647
- expect(shouldFetchCostPeriod({ filters, period: "7", snapshotBacked: true })).toBe(true);
648
- expect(
649
- shouldFetchCostPeriod({
650
- filters: { ...filters, phase: "execution" },
651
- period: "month",
652
- snapshotBacked: true,
653
- }),
654
- ).toBe(true);
655
- });
656
- it("uses the current month for the default period and keeps All Time explicit", async () => {
657
- expect(defaultCostPeriod).toBe("month");
658
- expect(costPeriodDateRange("all", new Date("2026-08-01T03:30:00.000Z"))).toBeUndefined();
659
- expect(costPeriodDateRange("month", new Date("2026-08-01T03:30:00.000Z"))).toEqual({
660
- from: "2026-08-01",
661
- to: "2026-08-01",
662
- });
663
- // Selecting standalone All Time still removes the prior month bounds.
664
- expect(costPeriodDateRange("all", new Date("2026-08-01T03:30:00.000Z"))).toBeUndefined();
665
- const calls: string[] = [];
666
- await loadCostMonitorPeriod({
667
- currency: "BRL",
668
- fetcher: async (url) => {
669
- calls.push(String(url));
670
- return new Response(JSON.stringify({ runs: [] }), { status: 200 });
671
- },
672
- });
673
- expect(calls).toEqual(["/api/v2/cost-monitor?currency=BRL&scope=summary"]);
674
- });
675
- it("derives an inclusive current-month range without inspecting materialized Runs", () => {
676
- expect(costPeriodDateRange("month", new Date("2026-07-26T15:00:00.000Z"))).toEqual({
677
- from: "2026-07-01",
678
- to: "2026-07-26",
679
- });
680
- expect(costPeriodDateRange("7", new Date("2026-07-26T15:00:00.000Z"))).toEqual({
681
- from: "2026-07-20",
682
- to: "2026-07-26",
683
- });
684
- });
685
- it("requests the selected range from the local runtime and forwards cancellation", async () => {
686
- const calls: Array<{ url: string; signal: AbortSignal | null | undefined }> = [];
687
- const controller = new AbortController();
688
- const projection = { runs: [] };
689
- const loaded = await loadCostMonitorPeriod({
690
- currency: "BRL",
691
- from: "2026-07-01",
692
- to: "2026-07-26",
693
- filters: {
694
- query: "TASK-1",
695
- provider: "openai",
696
- phase: "execution",
697
- origin: "spec",
698
- workType: "rework",
699
- agent: "zaip-agent-builder",
700
- model: "gpt-5.6-terra",
701
- status: "completed",
702
- priority: "high",
703
- risk: "standard",
704
- },
705
- signal: controller.signal,
706
- fetcher: async (url, init) => {
707
- calls.push({ url: String(url), signal: init?.signal });
708
- return new Response(JSON.stringify(projection), {
709
- headers: { "content-type": "application/json" },
710
- status: 200,
711
- });
712
- },
713
- });
714
- expect(loaded).toEqual(projection);
715
- expect(calls).toEqual([
716
- {
717
- url:
718
- "/api/v2/cost-monitor?currency=BRL&scope=summary&from=2026-07-01&to=2026-07-26" +
719
- "&provider=openai&phase=execution&agent=zaip-agent-builder" +
720
- "&model=gpt-5.6-terra&status=completed",
721
- signal: controller.signal,
722
- },
723
- ]);
724
- });
725
- it("never leaves period/filter hydration scope-less or on detail", async () => {
726
- const calls: string[] = [];
727
- await loadCostMonitorPeriod({
728
- currency: "USD",
729
- filters: { ...emptyCostFilters, provider: "openai", model: "gpt-5.6-terra" },
730
- fetcher: async (url) => {
731
- calls.push(String(url));
732
- return new Response(JSON.stringify({ runs: [] }), { status: 200 });
733
- },
734
- });
735
- const request = new URL(calls[0]!, "http://dashboard.local");
736
- expect(request.searchParams.get("scope")).toBe("summary");
737
- expect(request.searchParams.get("scope")).not.toBe("detail");
738
- });
739
- });
740
730
  describe("manifest-first invocation query", () => {
741
731
  it("opens only intersecting monthly manifests and candidate chunks", async () => {
742
732
  const inRange = signedSummary({
@@ -6,6 +6,7 @@ import {
6
6
  } from "@zaip-ai/dashboard-ui";
7
7
  import type { CostGroupRow, CostRunRow, DurationCoverage } from "../contracts.js";
8
8
  import { MoreVerticalIcon } from "./CostTableIcons.js";
9
+ import { type CostRunPagination, CostRunServerPagination } from "./CostTableLoading.js";
9
10
  import type { CostCurrency, CostLocale } from "./cost-monitor-copy.js";
10
11
  import {
11
12
  agentRunModelPresentation,
@@ -30,6 +31,10 @@ import {
30
31
  } from "./cost-table-presentation.js";
31
32
  import type { CostGroupTotals } from "./use-cost-group-page.js";
32
33
 
34
+ export {
35
+ type CostDeveloperAttributionRow,
36
+ CostDeveloperAttributionTable,
37
+ } from "./CostDeveloperAttributionTable.js";
33
38
  export { CostModelsAuditTable } from "./CostModelsAuditTable.js";
34
39
 
35
40
  export {
@@ -269,16 +274,7 @@ export function CostRunsTable({
269
274
  currency: CostCurrency;
270
275
  locale: CostLocale;
271
276
  compact?: boolean;
272
- pagination?: {
273
- offset: number;
274
- limit: number;
275
- total: number | null;
276
- has_more: boolean;
277
- loading: boolean;
278
- error: string | null;
279
- onChange: (offset: number) => void;
280
- onRetry: () => void;
281
- };
277
+ pagination?: CostRunPagination;
282
278
  }) {
283
279
  const footerLabel = costRunsFooterLabel(locale, pagination !== undefined);
284
280
  const total = (key: keyof CostRunRow) => sumNumbers(rows.map((row) => row[key]));
@@ -599,63 +595,6 @@ function inclusiveTokenTotal(
599
595
  : row.total_tokens;
600
596
  }
601
597
 
602
- function CostRunServerPagination({
603
- locale,
604
- pagination,
605
- }: {
606
- locale: CostLocale;
607
- pagination: NonNullable<Parameters<typeof CostRunsTable>[0]["pagination"]>;
608
- }) {
609
- const first = pagination.total === 0 ? 0 : pagination.offset + 1;
610
- const last =
611
- pagination.total === null
612
- ? pagination.offset + pagination.limit
613
- : Math.min(pagination.offset + pagination.limit, pagination.total);
614
- const previous = Math.max(0, pagination.offset - pagination.limit);
615
- const next = pagination.offset + pagination.limit;
616
- return (
617
- <nav
618
- aria-label={locale === "pt-BR" ? "Paginação das invocações" : "Invocation pagination"}
619
- className="ui-table-pagination"
620
- >
621
- <button
622
- disabled={pagination.loading || pagination.offset === 0}
623
- onClick={() => pagination.onChange(previous)}
624
- type="button"
625
- >
626
- {locale === "pt-BR" ? "Anterior" : "Previous"}
627
- </button>
628
- <span aria-live="polite">
629
- {pagination.loading
630
- ? locale === "pt-BR"
631
- ? "Carregando invocações…"
632
- : "Loading invocations…"
633
- : pagination.total === null
634
- ? `${first}–${last}${pagination.has_more ? "+" : ""}`
635
- : `${first}–${last} ${locale === "pt-BR" ? "de" : "of"} ${pagination.total}`}
636
- </span>
637
- <button
638
- disabled={
639
- pagination.loading ||
640
- (!pagination.has_more && (pagination.total === null || next >= pagination.total))
641
- }
642
- onClick={() => pagination.onChange(next)}
643
- type="button"
644
- >
645
- {locale === "pt-BR" ? "Próxima" : "Next"}
646
- </button>
647
- {pagination.error ? (
648
- <span role="status">
649
- {pagination.error}{" "}
650
- <button onClick={pagination.onRetry} type="button">
651
- {locale === "pt-BR" ? "Tentar novamente" : "Retry"}
652
- </button>
653
- </span>
654
- ) : null}
655
- </nav>
656
- );
657
- }
658
-
659
598
  function costPeriodHeading(period: "daily" | "monthly" | "annual", locale: CostLocale): string {
660
599
  if (locale === "en-US") {
661
600
  return period === "daily" ? "Day" : period === "monthly" ? "Month" : "Year";
@@ -675,6 +614,7 @@ export function CostGroupsTable({
675
614
  dateHeading,
676
615
  labelHeading,
677
616
  totals: suppliedTotals,
617
+ loading = false,
678
618
  }: {
679
619
  rows: readonly CostGroupRow[];
680
620
  currency: CostCurrency;
@@ -687,6 +627,7 @@ export function CostGroupsTable({
687
627
  dateHeading?: string;
688
628
  labelHeading?: string;
689
629
  totals?: CostGroupTotals;
630
+ loading?: boolean;
690
631
  }) {
691
632
  const pageTotals = {
692
633
  runs: rows.reduce((sum, row) => sum + row.run_count, 0),
@@ -926,6 +867,15 @@ export function CostGroupsTable({
926
867
  : { key: "label", direction: "desc" }
927
868
  }
928
869
  locale={locale}
870
+ emptyMessage={
871
+ loading
872
+ ? locale === "pt-BR"
873
+ ? "Carregando…"
874
+ : "Loading…"
875
+ : locale === "pt-BR"
876
+ ? "Nenhum dado disponível."
877
+ : "No data available."
878
+ }
929
879
  pageSize={100}
930
880
  rowKey={(row) => row.key}
931
881
  onRowClick={onOpenTrace === undefined ? undefined : (row) => onOpenTrace(row.key)}
@@ -37,6 +37,7 @@ export const costCopy = {
37
37
  daily: "Diário",
38
38
  monthly: "Mensal",
39
39
  annual: "Anual",
40
+ developers: "Desenvolvedores",
40
41
  },
41
42
  },
42
43
  "en-US": {
@@ -73,6 +74,7 @@ export const costCopy = {
73
74
  daily: "Daily",
74
75
  monthly: "Monthly",
75
76
  annual: "Annual",
77
+ developers: "Developers",
76
78
  },
77
79
  },
78
80
  } as const;
@@ -11,7 +11,8 @@ export type CostView =
11
11
  | "models"
12
12
  | "daily"
13
13
  | "monthly"
14
- | "annual";
14
+ | "annual"
15
+ | "developers";
15
16
 
16
17
  export const DEFAULT_COST_VIEW: CostView = "daily";
17
18
 
@@ -19,6 +20,7 @@ export const COST_VIEW_ORDER: readonly CostView[] = [
19
20
  "daily",
20
21
  "monthly",
21
22
  "annual",
23
+ "developers",
22
24
  "overview",
23
25
  "executions",
24
26
  "agentCalls",
@@ -0,0 +1,35 @@
1
+ import { type CostPeriod, defaultCostPeriod } from "./CostFilters.js";
2
+
3
+ export type CostPageDateRange = { from: string; to: string };
4
+
5
+ /**
6
+ * Embedded pages use the host's all-time atomic snapshot until an explicit
7
+ * period, date range, or local Cost filter narrows the population.
8
+ */
9
+ export function resolveCostGroupPageRange({
10
+ embedded,
11
+ globalDateRange,
12
+ hasLocalCostFilters,
13
+ period,
14
+ runPageRange,
15
+ }: {
16
+ embedded: boolean;
17
+ globalDateRange: { start: string; end: string } | undefined;
18
+ hasLocalCostFilters: boolean;
19
+ period: CostPeriod;
20
+ runPageRange: CostPageDateRange | undefined;
21
+ }): CostPageDateRange | undefined {
22
+ if (
23
+ embedded &&
24
+ globalDateRange === undefined &&
25
+ period === defaultCostPeriod &&
26
+ !hasLocalCostFilters
27
+ )
28
+ return undefined;
29
+ return runPageRange;
30
+ }
31
+
32
+ /** Compact lifecycle tables share the same embedded snapshot contract. */
33
+ export function resolveCompactCostPageRange(args: Parameters<typeof resolveCostGroupPageRange>[0]) {
34
+ return resolveCostGroupPageRange(args);
35
+ }
@@ -28,15 +28,26 @@ export function compactReadModelKind(view: CostView): CompactReadModelKind | nul
28
28
  : null;
29
29
  }
30
30
 
31
+ /** A page fallback is valid only for the same compact table surface. */
32
+ export function isSameCompactCostSurface(resolvedKey: string | null, requestKey: string): boolean {
33
+ if (resolvedKey === null) return false;
34
+ return (
35
+ resolvedKey.slice(0, resolvedKey.lastIndexOf("\u0000")) ===
36
+ requestKey.slice(0, requestKey.lastIndexOf("\u0000"))
37
+ );
38
+ }
39
+
31
40
  export function useCompactCostPage({
32
41
  view,
33
42
  load,
43
+ limit = 50,
34
44
  range,
35
45
  query,
36
46
  filters,
37
47
  }: {
38
48
  view: CostView;
39
49
  load: LoadCompactCostPage | undefined;
50
+ limit?: number;
40
51
  range: { from: string; to: string } | undefined;
41
52
  query: string | undefined;
42
53
  filters: CostFilterState;
@@ -54,10 +65,10 @@ export function useCompactCostPage({
54
65
  const rangeFrom = range?.from;
55
66
  const rangeTo = range?.to;
56
67
  const filterKey = Object.values(filters).join("\u0000");
57
- const paginationKey = `${view}\u0000${query ?? ""}\u0000${rangeFrom ?? ""}\u0000${rangeTo ?? ""}\u0000${filterKey}`;
68
+ const paginationKey = `${view}\u0000${limit}\u0000${query ?? ""}\u0000${rangeFrom ?? ""}\u0000${rangeTo ?? ""}\u0000${filterKey}`;
58
69
  const requestKey = `${paginationKey}\u0000${offset}`;
59
- const requestInputRef = useRef({ filters, query, rangeFrom, rangeTo });
60
- requestInputRef.current = { filters, query, rangeFrom, rangeTo };
70
+ const requestInputRef = useRef({ filters, limit, query, rangeFrom, rangeTo });
71
+ requestInputRef.current = { filters, limit, query, rangeFrom, rangeTo };
61
72
  useEffect(() => {
62
73
  void paginationKey;
63
74
  setOffset(0);
@@ -81,7 +92,7 @@ export function useCompactCostPage({
81
92
  void loadPage({
82
93
  kind,
83
94
  offset,
84
- limit: 50,
95
+ limit: requestInput.limit,
85
96
  ...(requestInput.rangeFrom === undefined || requestInput.rangeTo === undefined
86
97
  ? {}
87
98
  : { from: requestInput.rangeFrom, to: requestInput.rangeTo }),
@@ -112,6 +123,7 @@ export function useCompactCostPage({
112
123
  // A response for another tab, range, filter, or offset must never occupy
113
124
  // this view while the next request is in flight.
114
125
  compactPage: resolvedKey === requestKey ? page : null,
126
+ staleCompactPage: isSameCompactCostSurface(resolvedKey, requestKey) ? page : null,
115
127
  compactPageLoading: loading,
116
128
  compactPageError: error,
117
129
  retryCompactPage: () => setAttempt((value) => value + 1),
@@ -1,7 +1,9 @@
1
+ import { readFileSync } from "node:fs";
1
2
  import { describe, expect, it } from "vitest";
2
- import { isCurrentCostGroupPage } from "./use-cost-group-page.js";
3
+ import { isSameCompactCostSurface } from "./use-compact-cost-page.js";
4
+ import { isCurrentCostGroupPage, isSameCostGroupSurface } from "./use-cost-group-page.js";
3
5
 
4
- describe("Cost group page request identity", () => {
6
+ describe("useCostGroupPage loading lifecycle", () => {
5
7
  it("masks a resolved monthly page while the annual request is pending", () => {
6
8
  const monthly = "monthly\u0000\u0000\u0000\u0000\u00000";
7
9
  const annual = "annual\u0000\u0000\u0000\u0000\u00000";
@@ -9,4 +11,37 @@ describe("Cost group page request identity", () => {
9
11
  expect(isCurrentCostGroupPage(monthly, annual)).toBe(false);
10
12
  expect(isCurrentCostGroupPage(annual, annual)).toBe(true);
11
13
  });
14
+
15
+ it("never reuses rows from another tab while its slow request is pending", () => {
16
+ const daily = "daily\u000010\u00002026-08-01\u00002026-08-25\u0000\u00000";
17
+ const models = "models\u000010\u00002026-08-01\u00002026-08-25\u0000\u00000";
18
+ const executions = "executions\u000010\u0000\u00002026-08-01\u00002026-08-25\u0000\u00000";
19
+ const tasks = "tasks\u000010\u0000\u00002026-08-01\u00002026-08-25\u0000\u00000";
20
+
21
+ expect(isSameCostGroupSurface(daily, models)).toBe(false);
22
+ expect(isSameCompactCostSurface(executions, tasks)).toBe(false);
23
+ });
24
+
25
+ it("keeps previous rows only while the next page of the same tab is pending", () => {
26
+ const firstPage = "daily\u000010\u00002026-08-01\u00002026-08-25\u0000\u00000";
27
+ const secondPage = "daily\u000010\u00002026-08-01\u00002026-08-25\u0000\u000010";
28
+
29
+ expect(isSameCostGroupSurface(firstPage, secondPage)).toBe(true);
30
+ });
31
+
32
+ it("invalidates an aborted request before clearing an inactive group view", () => {
33
+ const source = readFileSync(new URL("./use-cost-group-page.ts", import.meta.url), "utf8");
34
+
35
+ expect(source).toContain("const current = ++generation.current;");
36
+ expect(source).toContain("if (!active || !loader.current)");
37
+ expect(source).toContain("setLoading(false);");
38
+ });
39
+
40
+ it("handles loader failures instead of leaving a rejected promise", () => {
41
+ const source = readFileSync(new URL("./use-cost-group-page.ts", import.meta.url), "utf8");
42
+
43
+ expect(source).toContain(".catch((cause: unknown) => {");
44
+ expect(source).toContain("setError(cause instanceof Error ? cause.message : String(cause));");
45
+ expect(source).toContain("retry: () => setAttempt");
46
+ });
12
47
  });