@zaip-ai/cli 1.0.8 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/README.md +1 -1
  2. package/dist/services/adapters.d.ts +1 -1
  3. package/dist/services/adapters.js +1 -1
  4. package/dist/services/agent-adapters.js +2 -2
  5. package/dist/services/agent-adapters.js.map +1 -1
  6. package/dist/services/agent-adapters.test.js +5 -0
  7. package/dist/services/agent-adapters.test.js.map +1 -1
  8. package/dist/services/logger.test.js +2 -2
  9. package/dist/services/runtime-template.test.js +6 -6
  10. package/dist/services/template-manifest.d.ts.map +1 -1
  11. package/dist/services/template-manifest.js +5 -0
  12. package/dist/services/template-manifest.js.map +1 -1
  13. package/package.json +1 -1
  14. package/templates/.agents/architecture/software-architecture.md +131 -0
  15. package/templates/.agents/branding/brandbook.docx +0 -0
  16. package/templates/.agents/branding/brandbook.md +60 -0
  17. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +1 -1
  18. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -1
  19. package/templates/.agents/dashboard/.tmp-run-list.json +254 -0
  20. package/templates/.agents/dashboard/.zaip-managed.json +252 -136
  21. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-Z60df2r-.js → CostCalculationGuidePage-DxR_pgSV.js} +1 -1
  22. package/templates/.agents/dashboard/apps/frontend/dist/assets/DashboardHelpDialog-4jZLTWE_.js +2156 -0
  23. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-Coc6pA-1.js → PricingSettingsDialog-CKcsqb-r.js} +1 -1
  24. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BomxqCBU.css +1 -0
  25. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-DmmJD2Th.js +3 -0
  26. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BwtitWa_.js +1 -0
  27. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-N5Csr0RP.js +68 -0
  28. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DFBuc_qp.js → icons-vendor-C0fj2uU9.js} +1 -1
  29. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-C4aIB1PV.js +2 -0
  30. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-CCQOHdVI.js → react-vendor-BEsJ-FYH.js} +1 -1
  31. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-pC2_0J8x.js → traceability-B6y6Gw12.js} +1 -1
  32. package/templates/.agents/dashboard/apps/frontend/dist/index.html +5 -5
  33. package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
  34. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +204 -32
  35. package/templates/.agents/dashboard/apps/frontend/src/DashboardAlternateContent.tsx +11 -11
  36. package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.tsx +14 -7
  37. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +4 -2
  38. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.test.ts +3 -1
  39. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.tsx +14 -11
  40. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +19 -4
  41. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +24 -2
  42. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +22 -1
  43. package/templates/.agents/dashboard/apps/frontend/src/overview-status-page.test.ts +3 -1
  44. package/templates/.agents/dashboard/apps/frontend/src/project-settings-page.css +36 -26
  45. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +2 -2
  46. package/templates/.agents/dashboard/apps/frontend/src/snapshot-banner-semantics.test.ts +40 -6
  47. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +39 -1
  48. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +89 -23
  49. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +219 -29
  50. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +61 -7
  51. package/templates/.agents/dashboard/apps/frontend/src/use-app-operations-pages.ts +3 -6
  52. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.test.ts +2 -2
  53. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.ts +3 -4
  54. package/templates/.agents/dashboard/apps/frontend/vite.config.ts +4 -1
  55. package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
  56. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.test.ts +89 -19
  57. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.tsx +31 -16
  58. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.test.ts +43 -14
  59. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.tsx +5 -5
  60. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostModelsAuditTable.tsx +1 -1
  61. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +67 -42
  62. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +115 -39
  63. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +188 -38
  64. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +75 -17
  65. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +147 -58
  66. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor.css +5 -2
  67. package/templates/.agents/dashboard/modules/cost-monitor/src/react/index.ts +2 -2
  68. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +2 -1
  69. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +18 -7
  70. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +95 -9
  71. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +29 -17
  72. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.test.ts +0 -24
  73. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.test.ts +225 -424
  74. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +24 -15
  75. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-terminal-day.test.ts +6 -1
  76. package/templates/.agents/dashboard/modules/cost-monitor/src/server/source-size-cost-server/build-cost-monitor-projection-agents.test.ts +31 -0
  77. package/templates/.agents/dashboard/modules/cost-monitor/src/server/source-size-cost-server/build-cost-summary-projection-duration.test.ts +414 -0
  78. package/templates/.agents/dashboard/modules/operations/package.json +1 -1
  79. package/templates/.agents/dashboard/modules/operations/src/contracts.ts +26 -23
  80. package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +31 -11
  81. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +3 -1
  82. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +51 -21
  83. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +47 -5
  84. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageNavigation.tsx +1 -4
  85. package/templates/.agents/dashboard/modules/operations/src/react/PeriodSummaryTable.tsx +32 -10
  86. package/templates/.agents/dashboard/modules/operations/src/react/TaskDetailsDialog.tsx +9 -3
  87. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.tsx +13 -5
  88. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.test.ts +12 -4
  89. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.tsx +1 -4
  90. package/templates/.agents/dashboard/modules/operations/src/react/conversation-rows.ts +1 -2
  91. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +385 -4
  92. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +481 -61
  93. package/templates/.agents/dashboard/modules/operations/src/react/export/export-layout.test.ts +14 -1
  94. package/templates/.agents/dashboard/modules/operations/src/react/export/xlsx-runtime-e2e.test.ts +248 -0
  95. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.test.ts +4 -4
  96. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.ts +6 -4
  97. package/templates/.agents/dashboard/modules/operations/src/react/operations-global-filters.test.ts +4 -1
  98. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +1 -1
  99. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.status-cards.test.ts +1 -1
  100. package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +46 -6
  101. package/templates/.agents/dashboard/modules/operations/src/react/operations-view-order.test.ts +1 -4
  102. package/templates/.agents/dashboard/modules/operations/src/react/period-summary.test.ts +82 -6
  103. package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.test.ts +2 -1
  104. package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.ts +1 -1
  105. package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.test.ts +19 -7
  106. package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.ts +4 -1
  107. package/templates/.agents/dashboard/modules/operations/src/server/quick-daily-operations-projection.ts +11 -5
  108. package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
  109. package/templates/.agents/dashboard/packages/branding/package.json +1 -1
  110. package/templates/.agents/dashboard/packages/branding/src/index.ts +1 -1
  111. package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
  112. package/templates/.agents/dashboard/packages/contracts/src/index.ts +1 -0
  113. package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
  114. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +152 -149
  115. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs.metafile.json +7333 -0
  116. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +149 -146
  117. package/templates/.agents/dashboard/packages/local-runtime/package.json +1 -1
  118. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-dashboard.mjs +15 -7
  119. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +44 -13
  120. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +5 -2
  121. package/templates/.agents/dashboard/packages/local-runtime/scripts/source-size-runtime-scripts/sqlite-read-model-fixture.mjs +397 -0
  122. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-read-model.mjs +472 -145
  123. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-unix-launchers.mjs +15 -3
  124. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +129 -31
  125. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +153 -150
  126. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +6 -2
  127. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +3 -3
  128. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.test.ts +55 -16
  129. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.ts +23 -13
  130. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +19 -4
  131. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +7 -3
  132. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +132 -9
  133. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +126 -60
  134. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-metric-contract.ts +16 -2
  135. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +1861 -350
  136. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1218 -352
  137. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +119 -42
  138. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +45 -20
  139. package/templates/.agents/dashboard/packages/ui/package.json +1 -1
  140. package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
  141. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +7 -5
  142. package/templates/.agents/design/.zaip-managed.json +3 -3
  143. package/templates/.agents/design/apps/catalog/package.json +1 -1
  144. package/templates/.agents/design/packages/local-runtime/package.json +1 -1
  145. package/templates/.agents/growth/estrategia-bootstrap-90-dias.md +169 -0
  146. package/templates/.agents/growth/strategy.md +27 -0
  147. package/templates/.agents/marketing/estrategia-marketing-zaip-90-dias.md +197 -0
  148. package/templates/.agents/marketing/strategy.md +29 -0
  149. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  150. package/templates/.agents/requirements/comparative-audit.md +1 -1
  151. package/templates/.agents/requirements/context-engine.md +1 -1
  152. package/templates/.agents/requirements/cost-observability.md +1 -1
  153. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  154. package/templates/.agents/requirements/data-export.md +1 -1
  155. package/templates/.agents/requirements/design-system.md +1 -1
  156. package/templates/.agents/requirements/execution-engine.md +1 -1
  157. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  158. package/templates/.agents/requirements/model-orchestration.md +1 -1
  159. package/templates/.agents/requirements/public-distribution.md +1 -1
  160. package/templates/.agents/requirements/software-architecture.md +1 -1
  161. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  162. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  163. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  164. package/templates/.agents/runtime/VERSION +1 -1
  165. package/templates/.agents/runtime/bin/zaip.mjs +30 -29
  166. package/templates/.agents/runtime/checksums.sha256 +6 -6
  167. package/templates/.agents/runtime/manifest.json +1 -1
  168. package/templates/.agents/runtime/pnpm-lock.sha256 +1 -1
  169. package/templates/.agents/runtime/provenance.json +5 -5
  170. package/templates/.agents/runtime/sbom.cdx.json +3 -3
  171. package/templates/.agents/sales/playbook.md +32 -0
  172. package/templates/.agents/sales/roteiro-comercial-zaip.md +252 -0
  173. package/templates/.agents/skills/zaip-aios/SKILL.md +1 -1
  174. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +9 -2
  175. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +39 -0
  176. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +1 -1
  177. package/templates/.agents/skills/zaip-skill-enforce-architecture/SKILL.md +2 -0
  178. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +6 -0
  179. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +6 -0
  180. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +39 -82
  181. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +13 -8
  182. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +44 -14
  183. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.mjs +19 -9
  184. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +29 -5
  185. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/source-size-telemetry/model-identity-reconciliation.mjs +77 -0
  186. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +6 -0
  187. package/templates/.agents/skills/zaip-skill-sqlite-read-model/scripts/ensure-node.mjs +28 -5
  188. package/templates/.agents/zaip-aios/INDEX.md +10 -0
  189. package/templates/.agents/zaip-aios/README.md +2 -1
  190. package/templates/.agents/zaip-aios/VERSION +1 -1
  191. package/templates/.agents/zaip-aios/guides/18-catalogo-de-agentes.md +76 -0
  192. package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +72 -0
  193. package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +330 -0
  194. package/templates/.agents/zaip-aios/guides/21-telemetria-monitor-custos.md +52 -0
  195. package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +69 -0
  196. package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +483 -0
  197. package/templates/.agents/zaip-aios/guides/24-calculo-de-custos-openai.md +85 -0
  198. package/templates/.agents/zaip-aios/guides/25-fontes-de-precos-openai.md +18 -0
  199. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +44 -0
  200. package/templates/.agents/zaip-aios/guides/27-telemetria-git-native.md +167 -0
  201. package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +144 -0
  202. package/templates/.agents/zaip-aios/guides/assets/diagrams/lifecycles-task-spec-prd.png +0 -0
  203. package/templates/.agents/zaip-aios/guides/assets/diagrams/prd-lifecycle.png +0 -0
  204. package/templates/.agents/zaip-aios/guides/assets/diagrams/spec-lifecycle.png +0 -0
  205. package/templates/.agents/zaip-aios/guides/assets/diagrams/task-lifecycle.png +0 -0
  206. package/templates/template-manifest.json +313 -177
  207. package/templates/.agents/dashboard/apps/frontend/dist/assets/DashboardHelpDialog-Cs6dzDmW.js +0 -1505
  208. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-QE1k9yZB.css +0 -1
  209. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-_Pf5DA_A.js +0 -3
  210. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BVlz3DLZ.js +0 -1
  211. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Cf9ctPVt.js +0 -68
  212. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-8A4DcNXM.js +0 -2
@@ -34,6 +34,7 @@ import type {
34
34
  OperationsPageFilters,
35
35
  PricingCatalog,
36
36
  ReconciliationResult,
37
+ SqliteViewName,
37
38
  WorkBoardStage,
38
39
  } from "./dashboard-api.js";
39
40
  import { DashboardApiClient } from "./dashboard-api.js";
@@ -52,19 +53,40 @@ import "./loading-feedback.css";
52
53
  import "./period-transition-overlay.css";
53
54
 
54
55
  type BoardStageState = { loading: boolean; error: string | null };
56
+ type OperationsExportView =
57
+ | "work-kanban"
58
+ | "work-list"
59
+ | "changes"
60
+ | "decisions"
61
+ | "executions"
62
+ | "runs"
63
+ | "conversations"
64
+ | "daily"
65
+ | "monthly"
66
+ | "yearly";
55
67
  type OverviewStatusKind = "task" | "spec" | "prd";
56
68
  type OverviewClassification = "lite" | "standard" | "critical" | "empty" | "structured" | "rework";
57
69
  type OverviewStatusPage = {
58
- items: OperationsProjection["tasks"] | OperationsProjection["specs"] | OperationsProjection["prds"];
70
+ items:
71
+ | OperationsProjection["tasks"]
72
+ | OperationsProjection["specs"]
73
+ | OperationsProjection["prds"];
59
74
  total: number;
60
75
  loading: boolean;
61
76
  error: string | null;
62
77
  };
63
- type OverviewStatusPages = Partial<Record<`${WorkBoardStage}:${OverviewStatusKind}`, OverviewStatusPage>>;
64
- const overviewStatusPageKey = (stage: WorkBoardStage, kind: OverviewStatusKind) => `${stage}:${kind}` as const;
65
- type OverviewClassificationPages = Partial<Record<`${OverviewStatusKind}:${OverviewClassification}`, OverviewStatusPage>>;
66
- const overviewClassificationPageKey = (kind: OverviewStatusKind, classification: OverviewClassification) =>
67
- `${kind}:${classification}` as const;
78
+ type OverviewStatusPages = Partial<
79
+ Record<`${WorkBoardStage}:${OverviewStatusKind}`, OverviewStatusPage>
80
+ >;
81
+ const overviewStatusPageKey = (stage: WorkBoardStage, kind: OverviewStatusKind) =>
82
+ `${stage}:${kind}` as const;
83
+ type OverviewClassificationPages = Partial<
84
+ Record<`${OverviewStatusKind}:${OverviewClassification}`, OverviewStatusPage>
85
+ >;
86
+ const overviewClassificationPageKey = (
87
+ kind: OverviewStatusKind,
88
+ classification: OverviewClassification,
89
+ ) => `${kind}:${classification}` as const;
68
90
  const initialWorkBoardStages: readonly WorkBoardStage[] = [
69
91
  "definition",
70
92
  "planning",
@@ -93,9 +115,75 @@ function dailyCostSeed(
93
115
  currency: "BRL" | "USD",
94
116
  filters: CostMonitorProjection["filters"],
95
117
  ): CostMonitorProjection {
96
- const metric = { events: 0, tokens: { input: null, cached_input: null, output: null, reasoning: null, total: null }, cost: { usd: null, brl: null }, coverage: { events: 0, tokens: 0, usd: 0, brl: 0 } };
97
- const usage = { conversation: metric, planning: metric, execution: metric, total: metric, new_work: metric, rework: metric };
98
- return { schema_version: 2, layout_version: 2, readonly: true, source: ".agents/records", aggregate_only: true, data_source: "run_records", legacy_effort_baseline: null, currency, pricing: { catalog_updated_at: null, fx_rate: null, conversion_applied: false }, filters, overview: { task_count: 0, run_count: 0, event_count: 0, total_tokens: null, estimated_usd: null, estimated_brl: null, recorded_usd: null, recorded_brl: null, average_run_cost_usd: null, average_run_cost_brl: null, average_run_duration_ms: null, known_tokens_per_cycle: null, top_10_token_share: null, coverage: { events: 0, tokens: 0, usd: 0, brl: 0 } }, filters_available: { providers: [], agents: [], models: [], statuses: [], priorities: [], risks: [], phases: [], origins: [], work_types: [] }, configured_agents: [], usage, origins: { conversation: usage, task: usage, spec: usage, prd: usage }, prds: [], specs: [], runs: [], groups: { conversations: [], tasks: [], providers: [], agents: [], models: [], daily: [], monthly: [], annual: [] } };
118
+ const metric = {
119
+ events: 0,
120
+ tokens: { input: null, cached_input: null, output: null, reasoning: null, total: null },
121
+ cost: { usd: null, brl: null },
122
+ coverage: { events: 0, tokens: 0, usd: 0, brl: 0 },
123
+ };
124
+ const usage = {
125
+ conversation: metric,
126
+ planning: metric,
127
+ execution: metric,
128
+ total: metric,
129
+ new_work: metric,
130
+ rework: metric,
131
+ };
132
+ return {
133
+ schema_version: 2,
134
+ layout_version: 2,
135
+ readonly: true,
136
+ source: ".agents/records",
137
+ aggregate_only: true,
138
+ data_source: "run_records",
139
+ legacy_effort_baseline: null,
140
+ currency,
141
+ pricing: { catalog_updated_at: null, fx_rate: null, conversion_applied: false },
142
+ filters,
143
+ overview: {
144
+ task_count: 0,
145
+ run_count: 0,
146
+ event_count: 0,
147
+ total_tokens: null,
148
+ estimated_usd: null,
149
+ estimated_brl: null,
150
+ recorded_usd: null,
151
+ recorded_brl: null,
152
+ average_run_cost_usd: null,
153
+ average_run_cost_brl: null,
154
+ average_run_duration_ms: null,
155
+ known_tokens_per_cycle: null,
156
+ top_10_token_share: null,
157
+ coverage: { events: 0, tokens: 0, usd: 0, brl: 0 },
158
+ },
159
+ filters_available: {
160
+ providers: [],
161
+ agents: [],
162
+ models: [],
163
+ statuses: [],
164
+ priorities: [],
165
+ risks: [],
166
+ phases: [],
167
+ origins: [],
168
+ work_types: [],
169
+ },
170
+ configured_agents: [],
171
+ usage,
172
+ origins: { conversation: usage, task: usage, spec: usage, prd: usage },
173
+ prds: [],
174
+ specs: [],
175
+ runs: [],
176
+ groups: {
177
+ conversations: [],
178
+ tasks: [],
179
+ providers: [],
180
+ agents: [],
181
+ models: [],
182
+ daily: [],
183
+ monthly: [],
184
+ annual: [],
185
+ },
186
+ };
99
187
  }
100
188
 
101
189
  export {
@@ -115,7 +203,8 @@ export function App() {
115
203
  const [snapshotNoticeDelayElapsed, setSnapshotNoticeDelayElapsed] = useState(false);
116
204
  const token = useMemo(readDashboardSessionToken, []);
117
205
  const api = useMemo(() => new DashboardApiClient(token), [token]);
118
- const { loadCostDetail, loadCostReadModelPage, loadCostRunPage, loadCostGroupPage } = useAppCostLoaders(api);
206
+ const { loadCostDetail, loadCostReadModelPage, loadCostRunPage, loadCostGroupPage } =
207
+ useAppCostLoaders(api);
119
208
  const [dashboardView, setDashboardView] = useState<DashboardView | null>(() =>
120
209
  initialDashboardView(window.location.pathname),
121
210
  );
@@ -141,7 +230,8 @@ export function App() {
141
230
  {},
142
231
  );
143
232
  const [overviewStatusPages, setOverviewStatusPages] = useState<OverviewStatusPages>({});
144
- const [overviewClassificationPages, setOverviewClassificationPages] = useState<OverviewClassificationPages>({});
233
+ const [overviewClassificationPages, setOverviewClassificationPages] =
234
+ useState<OverviewClassificationPages>({});
145
235
  const [costs, setCosts] = useState<CostMonitorProjection | null>(null);
146
236
  const [displayedCosts, setDisplayedCosts] = useState<CostMonitorProjection | null>(null);
147
237
  const [costLocale, setCostLocale] = useState<CostLocale>(savedLocale);
@@ -171,7 +261,10 @@ export function App() {
171
261
  "choose" | "refreshing" | "reconciling" | null
172
262
  >(null);
173
263
  useEffect(() => {
174
- void api.ready().then((ready) => setSnapshotUpdating(ready.snapshot_state === "updating")).catch(() => {});
264
+ void api
265
+ .ready()
266
+ .then((ready) => setSnapshotUpdating(ready.snapshot_state === "updating"))
267
+ .catch(() => {});
175
268
  }, [api]);
176
269
  const [error, setError] = useState<string | null>(null);
177
270
  const [reconciliationResult, setReconciliationResult] = useState<ReconciliationResult | null>(
@@ -238,7 +331,7 @@ export function App() {
238
331
  // forcing a second full aggregate calculation.
239
332
  const initialAggregateFreshRef = useRef(false);
240
333
  const refresh = useCallback(
241
- async (visible = true, reconcileOpenWork = false) => {
334
+ async (visible = true, reconcileOpenWork = false, refreshProjection = false) => {
242
335
  operationsDetailCancelRef.current?.();
243
336
  operationsDetailCancelRef.current = null;
244
337
  operationsDetailAbortRef.current?.abort();
@@ -271,10 +364,20 @@ export function App() {
271
364
  setError(null);
272
365
  setFailedArea(null);
273
366
  try {
274
- if (reconcileOpenWork) {
275
- const snapshot = await api.reconcileOpenWork(currency, period);
367
+ if (reconcileOpenWork || refreshProjection) {
368
+ let snapshot: Awaited<ReturnType<typeof api.refreshProjection>>;
369
+ let reconciliation:
370
+ | Awaited<ReturnType<typeof api.reconcileOpenWork>>["reconciliation"]
371
+ | null = null;
372
+ if (reconcileOpenWork) {
373
+ const reconciledSnapshot = await api.reconcileOpenWork(currency, period);
374
+ reconciliation = reconciledSnapshot.reconciliation;
375
+ snapshot = reconciledSnapshot;
376
+ } else {
377
+ snapshot = await api.refreshProjection(currency, period);
378
+ }
276
379
  if (!isCurrentRequest()) return;
277
- setReconciliationResult(snapshot.reconciliation);
380
+ if (reconciliation !== null) setReconciliationResult(reconciliation);
278
381
  setOperations(snapshot.operations);
279
382
  quickOverviewMetricsRef.current = snapshot.operations.summary.overview_metrics;
280
383
  operationsDetailLoadedRef.current = true;
@@ -297,7 +400,9 @@ export function App() {
297
400
  // An English first paint must have the factual USD summary
298
401
  // before Operations formats currency. BRL retains the fast
299
402
  // Operations-only path until the user opens Cost Monitor.
300
- currency === "USD" ? api.costMonitor(currency, period, "summary") : Promise.resolve(null),
403
+ currency === "USD"
404
+ ? api.costMonitor(currency, period, "summary")
405
+ : Promise.resolve(null),
301
406
  ]);
302
407
  if (!isCurrentRequest()) return;
303
408
  initialAggregateFreshRef.current = false;
@@ -497,7 +602,10 @@ export function App() {
497
602
  const normalizedFilters = normalizeWorkListFilters(filters, workListFiltersRef.current);
498
603
  workListFiltersRef.current = normalizedFilters;
499
604
  const requestedKind = selectedWorkListKinds(normalizedFilters.kind);
500
- const loadKindStage = async (stage: WorkBoardStage, kind: "prd" | "spec" | "task") => {
605
+ const loadKindStage = async (
606
+ stage: WorkBoardStage,
607
+ kind: "prd" | "spec" | "task" | "standalone-task",
608
+ ) => {
501
609
  return collectWorkListPages(async (offset) => {
502
610
  const page = await api.operationsBoard(
503
611
  period,
@@ -528,8 +636,9 @@ export function App() {
528
636
  period.to !== requestPeriodForOperations(operationsPeriodRef.current).to
529
637
  )
530
638
  return;
531
- const unique = <T extends { id: string }>(rows: readonly T[]) =>
532
- [...new Map(rows.map((row) => [row.id, row] as const)).values()];
639
+ const unique = <T extends { id: string }>(rows: readonly T[]) => [
640
+ ...new Map(rows.map((row) => [row.id, row] as const)).values(),
641
+ ];
533
642
  setOperations((current) =>
534
643
  current === null
535
644
  ? current
@@ -621,7 +730,8 @@ export function App() {
621
730
  requestVersion !== boardRequestVersionRef.current ||
622
731
  period.from !== requestPeriodForOperations(operationsPeriodRef.current).from ||
623
732
  period.to !== requestPeriodForOperations(operationsPeriodRef.current).to
624
- ) return;
733
+ )
734
+ return;
625
735
  const items = kind === "task" ? page.tasks : kind === "spec" ? page.specs : page.prds;
626
736
  setOverviewClassificationPages((previous) => ({
627
737
  ...previous,
@@ -637,8 +747,10 @@ export function App() {
637
747
  setOverviewClassificationPages((previous) => ({
638
748
  ...previous,
639
749
  [key]: {
640
- items: previous[key]?.items ?? [], total: previous[key]?.total ?? 0,
641
- loading: false, error: cause instanceof Error ? cause.message : String(cause),
750
+ items: previous[key]?.items ?? [],
751
+ total: previous[key]?.total ?? 0,
752
+ loading: false,
753
+ error: cause instanceof Error ? cause.message : String(cause),
642
754
  },
643
755
  }));
644
756
  } finally {
@@ -683,6 +795,55 @@ export function App() {
683
795
  },
684
796
  [api, operationsPeriod],
685
797
  );
798
+ const detailExportSource = useCallback(
799
+ (
800
+ view: OperationsExportView,
801
+ filters: { query: string; status: string; risk: string; origin: string },
802
+ ) => {
803
+ const period = requestPeriodForOperations(operationsPeriod);
804
+ if (view === "work-kanban") return undefined;
805
+ const sqliteView = {
806
+ "work-list": "operations_work_list",
807
+ changes: "operations_changes",
808
+ decisions: "operations_decisions",
809
+ executions: "operations_executions",
810
+ runs: "operations_agent_invocations",
811
+ conversations: "operations_conversations",
812
+ daily: "operations_daily",
813
+ monthly: "operations_monthly",
814
+ yearly: "operations_annual",
815
+ } as const satisfies Partial<Record<OperationsExportView, SqliteViewName>>;
816
+ const source = sqliteView[view];
817
+ if (source === undefined) return undefined;
818
+ const options = (offset: number) => ({
819
+ offset,
820
+ limit: 100,
821
+ ...(filters.query ? { query: filters.query } : {}),
822
+ ...(filters.status ? { status: filters.status } : {}),
823
+ ...(filters.risk ? { risk: filters.risk } : {}),
824
+ ...(filters.origin ? { origin: filters.origin } : {}),
825
+ });
826
+ return {
827
+ prepare: async () => {
828
+ const firstPage = await api.sqliteViewPage(source, period, options(0));
829
+ return {
830
+ totalRows: firstPage.total_count,
831
+ pages: async function* () {
832
+ let page = firstPage;
833
+ while (true) {
834
+ if (page.rows.length === 0) return;
835
+ yield page.rows;
836
+ const nextOffset = page.offset + page.rows.length;
837
+ if (nextOffset >= page.total_count) return;
838
+ page = await api.sqliteViewPage(source, period, options(nextOffset));
839
+ }
840
+ },
841
+ };
842
+ },
843
+ };
844
+ },
845
+ [api, operationsPeriod],
846
+ );
686
847
  useEffect(() => {
687
848
  if (dashboardView !== "operations" || operations === null) return;
688
849
  let cancelled = false;
@@ -712,7 +873,7 @@ export function App() {
712
873
  const refreshFromDialog = async (reconcileOpenWork: boolean) => {
713
874
  if (reconcileOpenWork) setReconciliationResult(null);
714
875
  setRefreshDialog(reconcileOpenWork ? "reconciling" : "refreshing");
715
- await refresh(false, reconcileOpenWork);
876
+ await refresh(false, reconcileOpenWork, !reconcileOpenWork);
716
877
  setRefreshDialog(null);
717
878
  };
718
879
  useAppEffects({
@@ -824,9 +985,9 @@ export function App() {
824
985
  () =>
825
986
  new Map(
826
987
  (displayedCosts ?? costs)?.runs.map(
827
- (run) => [run.run_id, currency === "USD" ? run.estimated_usd : run.estimated_brl] as const,
828
- ) ??
829
- [],
988
+ (run) =>
989
+ [run.run_id, currency === "USD" ? run.estimated_usd : run.estimated_brl] as const,
990
+ ) ?? [],
830
991
  ),
831
992
  [costs, currency, displayedCosts],
832
993
  );
@@ -884,7 +1045,10 @@ export function App() {
884
1045
  setSnapshotNoticeDelayElapsed(false);
885
1046
  return undefined;
886
1047
  }
887
- const timeout = window.setTimeout(() => setSnapshotNoticeDelayElapsed(true), snapshotNoticeDelayMs);
1048
+ const timeout = window.setTimeout(
1049
+ () => setSnapshotNoticeDelayElapsed(true),
1050
+ snapshotNoticeDelayMs,
1051
+ );
888
1052
  return () => window.clearTimeout(timeout);
889
1053
  }, [initialDataPending, snapshotNoticeDismissed, snapshotUpdating]);
890
1054
  const snapshotNoticeVisible = shouldShowSnapshotNotice({
@@ -895,9 +1059,16 @@ export function App() {
895
1059
  });
896
1060
  const snapshotNotice = snapshotNoticeVisible ? (
897
1061
  <div aria-live="polite" className="app-notice app-snapshot-status" role="status">
898
- <span>Exibindo o último snapshot local validado. Os dados canônicos estão sendo atualizados em segundo plano e estarão atuais na próxima abertura.</span>
1062
+ <span>
1063
+ Exibindo o último snapshot local validado. Os dados canônicos estão sendo atualizados em
1064
+ segundo plano e estarão atuais na próxima abertura.
1065
+ </span>
899
1066
  <button
900
- aria-label={costLocale === "pt-BR" ? "Fechar aviso de atualização do snapshot" : "Dismiss snapshot update notice"}
1067
+ aria-label={
1068
+ costLocale === "pt-BR"
1069
+ ? "Fechar aviso de atualização do snapshot"
1070
+ : "Dismiss snapshot update notice"
1071
+ }
901
1072
  onClick={() => setSnapshotNoticeDismissed(true)}
902
1073
  type="button"
903
1074
  >
@@ -971,6 +1142,7 @@ export function App() {
971
1142
  boardTotals={boardTotals}
972
1143
  boardKindTotals={boardKindTotals}
973
1144
  boardExportSource={boardExportSource}
1145
+ detailExportSource={detailExportSource}
974
1146
  boardStates={boardStates}
975
1147
  data={renderedOperations}
976
1148
  datePreset={operationsPeriod.preset}
@@ -995,8 +1167,8 @@ export function App() {
995
1167
  : view === "executions"
996
1168
  ? "executions"
997
1169
  : view === "runs"
998
- ? "runs"
999
- : null;
1170
+ ? "runs"
1171
+ : null;
1000
1172
  if (kind !== null) void loadOperationsEntityPage(kind);
1001
1173
  else if (view === "changes" || view === "decisions")
1002
1174
  void loadOperationsLifecyclePage(view);
@@ -112,17 +112,17 @@ export function renderDashboardAlternateContent({
112
112
  if (dashboardView === "traceability") {
113
113
  return () => (
114
114
  <Suspense fallback={<LoadingPanel area="operations" locale={costLocale} />}>
115
- <TraceabilityWorkspace
116
- loadingTaskId={traceLoadingTaskId}
117
- locale={costLocale}
118
- onOpenChange={onOpenTraceChange}
119
- onOpenPrd={(prdId) => onOpenTraceMacro("PRD", prdId)}
120
- onOpenSpec={(specId) => onOpenTraceMacro("SPEC", specId)}
121
- onSelectTask={onSelectTraceTask}
122
- selectedTaskId={selectedTraceTaskId}
123
- tasks={traceTasks.length > 0 ? traceTasks : operations.tasks}
124
- trace={trace}
125
- />
115
+ <TraceabilityWorkspace
116
+ loadingTaskId={traceLoadingTaskId}
117
+ locale={costLocale}
118
+ onOpenChange={onOpenTraceChange}
119
+ onOpenPrd={(prdId) => onOpenTraceMacro("PRD", prdId)}
120
+ onOpenSpec={(specId) => onOpenTraceMacro("SPEC", specId)}
121
+ onSelectTask={onSelectTraceTask}
122
+ selectedTaskId={selectedTraceTaskId}
123
+ tasks={traceTasks.length > 0 ? traceTasks : operations.tasks}
124
+ trace={trace}
125
+ />
126
126
  </Suspense>
127
127
  );
128
128
  }
@@ -4,16 +4,16 @@ import type {
4
4
  ProjectConfigSettings,
5
5
  ProjectOverview,
6
6
  } from "./dashboard-api.js";
7
+ import { StackComponentEditor, StackVersions } from "./ProjectStackEditor.js";
7
8
  import {
8
9
  buildProjectSettingsPayload,
9
10
  defaultAutonomousExecution,
10
11
  lineList,
11
12
  projectSettingsDraft,
12
- shortFrameworkVersion,
13
13
  type StackComponentsDraft,
14
+ shortFrameworkVersion,
14
15
  withDetectedStackVersions,
15
16
  } from "./project-settings-model.js";
16
- import { StackComponentEditor, StackVersions } from "./ProjectStackEditor.js";
17
17
  import "./project-settings-page.css";
18
18
 
19
19
  type Locale = "pt-BR" | "en-US";
@@ -22,7 +22,7 @@ const copy = {
22
22
  eyebrow: "Configurações",
23
23
  title: "Configuração do projeto",
24
24
  intro: "Edite o arquivo central do projeto com validação do schema antes da gravação.",
25
- loading: "Carregando configurações do projeto…",
25
+ loading: "Carregando…",
26
26
  back: "Voltar ao dashboard",
27
27
  save: "Salvar configurações",
28
28
  saving: "Validando e salvando…",
@@ -190,10 +190,15 @@ export function ProjectSettingsPage({
190
190
  }
191
191
 
192
192
  if (config === null) {
193
+ const isLoading = error === null;
193
194
  return (
194
- <main className="project-settings-state">
195
- <div className="project-settings-state-card">
196
- <span aria-hidden="true" className="project-settings-loader" />
195
+ <main aria-busy={isLoading} className="project-settings-state">
196
+ <div
197
+ aria-live={isLoading ? "polite" : "assertive"}
198
+ className="project-settings-state-card"
199
+ role={isLoading ? "status" : "alert"}
200
+ >
201
+ {isLoading ? <span aria-hidden="true" className="project-settings-loader" /> : null}
197
202
  <p>{text.eyebrow}</p>
198
203
  <h1>{error ?? text.loading}</h1>
199
204
  <button onClick={onClose} type="button">
@@ -229,7 +234,9 @@ export function ProjectSettingsPage({
229
234
  <div>
230
235
  <h2>
231
236
  {text.framework}
232
- {shortFrameworkVersion(overview?.framework_version) ? ` - ${shortFrameworkVersion(overview?.framework_version)}` : ""}
237
+ {shortFrameworkVersion(overview?.framework_version)
238
+ ? ` - ${shortFrameworkVersion(overview?.framework_version)}`
239
+ : ""}
233
240
  </h2>
234
241
  <p>{text.frameworkHint}</p>
235
242
  </div>
@@ -131,8 +131,10 @@ export function StackVersions({
131
131
  <div>
132
132
  {components.map((component) => (
133
133
  <article key={component.name}>
134
- <strong>{component.name}</strong>
135
- <code>{component.version ?? text.versionUnavailable}</code>
134
+ <div className="project-stack-version-identity">
135
+ <strong>{component.name}</strong>
136
+ <code>{component.version ?? text.versionUnavailable}</code>
137
+ </div>
136
138
  <span className={`is-${component.verification}`}>
137
139
  {component.verification === "exact"
138
140
  ? text.exact
@@ -9,7 +9,9 @@ describe("TraceabilityWorkspace task search", () => {
9
9
  ] as never[];
10
10
 
11
11
  it("filters tasks by either title or ID", () => {
12
- expect(filterTraceabilityTasks(tasks, "lançamento").map((task) => task.id)).toEqual(["TASK-ALPHA"]);
12
+ expect(filterTraceabilityTasks(tasks, "lançamento").map((task) => task.id)).toEqual([
13
+ "TASK-ALPHA",
14
+ ]);
13
15
  expect(filterTraceabilityTasks(tasks, "beta").map((task) => task.id)).toEqual(["TASK-BETA"]);
14
16
  expect(filterTraceabilityTasks(tasks, "missing")).toEqual([]);
15
17
  });
@@ -1,7 +1,7 @@
1
- import { useEffect, useMemo, useState } from "react";
2
1
  import type { OperationsProjection } from "@zaip-ai/dashboard-operations/contracts";
3
2
  import type { TaskTrace } from "@zaip-ai/dashboard-traceability/contracts";
4
3
  import { TaskTracePanel } from "@zaip-ai/dashboard-traceability/react";
4
+ import { useEffect, useMemo, useState } from "react";
5
5
  import "./traceability-workspace.css";
6
6
 
7
7
  type TraceabilityTask = OperationsProjection["tasks"][number];
@@ -62,11 +62,8 @@ export function TraceabilityWorkspace({
62
62
 
63
63
  useEffect(() => {
64
64
  setQuery(selectedTask?.title ?? selectedTaskId);
65
- }, [selectedTask?.title, selectedTaskId]);
66
-
67
- useEffect(() => {
68
65
  setActiveIndex(0);
69
- }, [query]);
66
+ }, [selectedTask?.title, selectedTaskId]);
70
67
 
71
68
  const selectTask = (task: TraceabilityTask) => {
72
69
  setQuery(task.title);
@@ -109,6 +106,7 @@ export function TraceabilityWorkspace({
109
106
  id={searchInputId}
110
107
  onChange={(event) => {
111
108
  setQuery(event.target.value);
109
+ setActiveIndex(0);
112
110
  setIsListOpen(true);
113
111
  }}
114
112
  onFocus={() => setIsListOpen(true)}
@@ -118,10 +116,15 @@ export function TraceabilityWorkspace({
118
116
  value={query}
119
117
  />
120
118
  {isListOpen ? (
121
- <ul aria-label={searchLabel} className="traceability-task-results" id={resultsId} role="listbox">
119
+ <div
120
+ aria-label={searchLabel}
121
+ className="traceability-task-results"
122
+ id={resultsId}
123
+ role="listbox"
124
+ >
122
125
  {filteredTasks.length > 0 ? (
123
126
  filteredTasks.map((task, index) => (
124
- <li key={task.id} role="presentation">
127
+ <div key={task.id} role="presentation">
125
128
  <button
126
129
  aria-selected={task.id === selectedTaskId}
127
130
  className={index === activeIndex ? "is-active" : undefined}
@@ -133,14 +136,14 @@ export function TraceabilityWorkspace({
133
136
  <strong>{task.title}</strong>
134
137
  <small>{task.id}</small>
135
138
  </button>
136
- </li>
139
+ </div>
137
140
  ))
138
141
  ) : (
139
- <li className="traceability-task-no-results" role="status">
142
+ <div className="traceability-task-no-results" role="status">
140
143
  {locale === "pt-BR" ? "Nenhuma task encontrada." : "No tasks found."}
141
- </li>
144
+ </div>
142
145
  )}
143
- </ul>
146
+ </div>
144
147
  ) : null}
145
148
  </div>
146
149
  <small>
@@ -22,12 +22,17 @@ describe("Cost period refresh", () => {
22
22
  app.indexOf("const refresh = useCallback"),
23
23
  app.indexOf("const loadOperationsDetails"),
24
24
  );
25
+ const compactRefresh = refresh.replace(/\s+/g, " ");
25
26
 
26
27
  expect(refresh).not.toContain("dailyCostSeed(currency, period)");
27
28
  expect(refresh).toContain("initialAggregateFreshRef.current");
28
29
  expect(refresh).not.toContain("loadAtomicCostSnapshot(() => api.snapshot(currency, period))");
29
- expect(refresh).toContain('api.operations(period, "quick", undefined, initialAggregateFreshRef.current)');
30
- expect(refresh).toContain('currency === "USD" ? api.costMonitor(currency, period, "summary") : Promise.resolve(null)');
30
+ expect(refresh).toContain(
31
+ 'api.operations(period, "quick", undefined, initialAggregateFreshRef.current)',
32
+ );
33
+ expect(compactRefresh).toContain(
34
+ 'currency === "USD" ? api.costMonitor(currency, period, "summary") : Promise.resolve(null)',
35
+ );
31
36
  expect(refresh).toContain("setDisplayedCosts(null);");
32
37
  expect(refresh).toContain("const activeDashboardView = dashboardViewRef.current");
33
38
  expect(refresh).toContain("[api, currency, operationsPeriod]");
@@ -54,14 +59,24 @@ describe("Cost period refresh", () => {
54
59
  const loaders = readFileSync(new URL("./use-app-cost-loaders.ts", import.meta.url), "utf8");
55
60
  expect(app).toContain("useAppCostLoaders(api)");
56
61
  expect(loaders).toContain("const loadCostRunPage = useCallback<LoadCostRunPage>");
57
- expect(loaders).toContain('api.sqliteViewPage("cost_monitor_agent_invocations"');
62
+ expect(loaders).toMatch(/api\.sqliteViewPage\(\s*"cost_monitor_agent_invocations"/);
58
63
  expect(loaders).toContain("const loadCostGroupPage = useCallback<LoadCostGroupPage>");
59
64
  expect(loaders).toContain("task_count: nullableSqlNumber(row.task_count)");
60
65
  expect(loaders).toContain("prd_count: Number(row.prd_count ?? 0)");
61
66
  expect(loaders).toContain("spec_count: Number(row.spec_count ?? 0)");
62
67
  expect(loaders).toContain("evidence_count: Number(row.evidence_count ?? 0)");
63
68
  expect(loaders).toContain("test_count: Number(row.validation_count ?? 0)");
64
- for (const dimension of ["provider", "model", "agent", "phase", "status", "origin", "workType", "priority", "risk"])
69
+ for (const dimension of [
70
+ "provider",
71
+ "model",
72
+ "agent",
73
+ "phase",
74
+ "status",
75
+ "origin",
76
+ "workType",
77
+ "priority",
78
+ "risk",
79
+ ])
65
80
  expect(loaders).toContain(`filters.${dimension}`);
66
81
  expect(app).toContain("loadRunPage={loadCostRunPage}");
67
82
  expect(app).toContain("loadReadModelPage={loadCostReadModelPage}");
@@ -31,8 +31,14 @@ describe("DashboardApiClient reconciliation", () => {
31
31
 
32
32
  it("posts to the exact canonical reconciliation path without a trailing slash", async () => {
33
33
  vi.stubGlobal("window", globalThis);
34
- const fetchMock = vi.fn(async () =>
35
- new Response(JSON.stringify({ reconciliation: { reconciled_ids: [], blocked: [], not_reconciled: [] } }), { status: 200 }),
34
+ const fetchMock = vi.fn(
35
+ async () =>
36
+ new Response(
37
+ JSON.stringify({
38
+ reconciliation: { reconciled_ids: [], blocked: [], not_reconciled: [] },
39
+ }),
40
+ { status: 200 },
41
+ ),
36
42
  );
37
43
  vi.stubGlobal("fetch", fetchMock);
38
44
 
@@ -46,6 +52,22 @@ describe("DashboardApiClient reconciliation", () => {
46
52
  expect.objectContaining({ method: "POST", headers: { "x-zaip-session": "session" } }),
47
53
  );
48
54
  });
55
+
56
+ it("refreshes the disposable projection without calling the reconciliation route", async () => {
57
+ vi.stubGlobal("window", globalThis);
58
+ const fetchMock = vi.fn(async () => new Response(JSON.stringify({}), { status: 200 }));
59
+ vi.stubGlobal("fetch", fetchMock);
60
+
61
+ await new DashboardApiClient("session").refreshProjection("BRL", {
62
+ from: "2026-08-12",
63
+ to: "2026-08-12",
64
+ });
65
+
66
+ expect(fetchMock).toHaveBeenCalledWith(
67
+ "/api/v2/refresh-projection?currency=BRL&from=2026-08-12&to=2026-08-12",
68
+ expect.objectContaining({ method: "POST", headers: { "x-zaip-session": "session" } }),
69
+ );
70
+ });
49
71
  });
50
72
 
51
73
  describe("DashboardApiClient cost monitor", () => {