@zaip-ai/cli 1.0.7 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (444) hide show
  1. package/README.md +6 -4
  2. package/dist/services/adapters.d.ts +1 -1
  3. package/dist/services/adapters.d.ts.map +1 -1
  4. package/dist/services/adapters.js +15 -5
  5. package/dist/services/adapters.js.map +1 -1
  6. package/dist/services/adapters.test.js +1 -1
  7. package/dist/services/agent-adapters.d.ts +9 -0
  8. package/dist/services/agent-adapters.d.ts.map +1 -1
  9. package/dist/services/agent-adapters.js +39 -4
  10. package/dist/services/agent-adapters.js.map +1 -1
  11. package/dist/services/agent-adapters.test.js +13 -1
  12. package/dist/services/agent-adapters.test.js.map +1 -1
  13. package/dist/services/codex-config-template.d.ts +2 -1
  14. package/dist/services/codex-config-template.d.ts.map +1 -1
  15. package/dist/services/codex-config-template.js +13 -2
  16. package/dist/services/codex-config-template.js.map +1 -1
  17. package/dist/services/codex-rules.d.ts +1 -1
  18. package/dist/services/codex-rules.d.ts.map +1 -1
  19. package/dist/services/codex-rules.js +31 -7
  20. package/dist/services/codex-rules.js.map +1 -1
  21. package/dist/services/codex-subagent-hook-template.d.ts +1 -1
  22. package/dist/services/codex-subagent-hook-template.d.ts.map +1 -1
  23. package/dist/services/codex-subagent-hook-template.js +4 -1
  24. package/dist/services/codex-subagent-hook-template.js.map +1 -1
  25. package/dist/services/doctor.d.ts.map +1 -1
  26. package/dist/services/doctor.js +3 -1
  27. package/dist/services/doctor.js.map +1 -1
  28. package/dist/services/global-codex-config.d.ts +6 -3
  29. package/dist/services/global-codex-config.d.ts.map +1 -1
  30. package/dist/services/global-codex-config.js +82 -13
  31. package/dist/services/global-codex-config.js.map +1 -1
  32. package/dist/services/global-codex-config.test.js +57 -10
  33. package/dist/services/global-codex-config.test.js.map +1 -1
  34. package/dist/services/install-agents-template.d.ts.map +1 -1
  35. package/dist/services/install-agents-template.js +15 -2
  36. package/dist/services/install-agents-template.js.map +1 -1
  37. package/dist/services/logger.js +1 -1
  38. package/dist/services/logger.js.map +1 -1
  39. package/dist/services/logger.test.js +3 -3
  40. package/dist/services/logger.test.js.map +1 -1
  41. package/dist/services/runtime-template.test.js +21 -5
  42. package/dist/services/runtime-template.test.js.map +1 -1
  43. package/dist/services/template-manifest.d.ts.map +1 -1
  44. package/dist/services/template-manifest.js +7 -0
  45. package/dist/services/template-manifest.js.map +1 -1
  46. package/package.json +1 -1
  47. package/templates/.agents/architecture/software-architecture.md +131 -0
  48. package/templates/.agents/branding/brandbook.docx +0 -0
  49. package/templates/.agents/branding/brandbook.md +60 -0
  50. package/templates/.agents/config/agents/catalog.yaml +310 -10
  51. package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +30 -0
  52. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +5 -5
  53. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +2 -2
  54. package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +30 -0
  55. package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +30 -0
  56. package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +30 -0
  57. package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +8 -8
  58. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +5 -3
  59. package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +30 -0
  60. package/templates/.agents/config/project.yaml +1 -1
  61. package/templates/.agents/dashboard/.tmp-run-list.json +254 -0
  62. package/templates/.agents/dashboard/.zaip-managed.json +775 -163
  63. package/templates/.agents/dashboard/DATA-DICTIONARY.md +2227 -0
  64. package/templates/.agents/dashboard/README.md +48 -97
  65. package/templates/.agents/dashboard/apps/frontend/dist/assets/CostCalculationGuidePage-DxR_pgSV.js +1 -0
  66. package/templates/.agents/dashboard/apps/frontend/dist/assets/DashboardHelpDialog-4jZLTWE_.js +2156 -0
  67. package/templates/.agents/dashboard/apps/frontend/dist/assets/PricingSettingsDialog-CKcsqb-r.js +1 -0
  68. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BomxqCBU.css +1 -0
  69. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-DmmJD2Th.js +3 -0
  70. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BwtitWa_.js +1 -0
  71. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-CaKwQP43.css +1 -0
  72. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-BZ5WdaMN.css +1 -0
  73. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-N5Csr0RP.js +68 -0
  74. package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-C98LGC-Q.js → dashboard-localization-CLJ0lT9v.js} +1 -1
  75. package/templates/.agents/dashboard/apps/frontend/dist/assets/icons-vendor-C0fj2uU9.js +1 -0
  76. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-C4aIB1PV.js +2 -0
  77. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DK_enB5Z.css +1 -0
  78. package/templates/.agents/dashboard/apps/frontend/dist/assets/react-vendor-BEsJ-FYH.js +9 -0
  79. package/templates/.agents/dashboard/apps/frontend/dist/assets/rolldown-runtime-D9-fqq9M.js +1 -0
  80. package/templates/.agents/dashboard/apps/frontend/dist/assets/traceability-B6y6Gw12.js +1 -0
  81. package/templates/.agents/dashboard/apps/frontend/dist/assets/traceability-DFY7ZDlu.css +1 -0
  82. package/templates/.agents/dashboard/apps/frontend/dist/index.html +9 -2
  83. package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
  84. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +1129 -600
  85. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +48 -0
  86. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingPanels.tsx +2 -0
  87. package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +247 -0
  88. package/templates/.agents/dashboard/apps/frontend/src/AppRequestGuards.tsx +95 -0
  89. package/templates/.agents/dashboard/apps/frontend/src/AppSetupRoutes.tsx +87 -0
  90. package/templates/.agents/dashboard/apps/frontend/src/CostCalculationGuidePage.tsx +641 -0
  91. package/templates/.agents/dashboard/apps/frontend/src/DashboardAlternateContent.tsx +159 -0
  92. package/templates/.agents/dashboard/apps/frontend/src/DashboardCapabilityNav.tsx +13 -13
  93. package/templates/.agents/dashboard/apps/frontend/src/DashboardGlobalSettings.tsx +56 -0
  94. package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.tsx +266 -315
  95. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +151 -0
  96. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.test.ts +37 -0
  97. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.tsx +114 -12
  98. package/templates/.agents/dashboard/apps/frontend/src/app-preferences.ts +15 -0
  99. package/templates/.agents/dashboard/apps/frontend/src/app-session-token.ts +20 -0
  100. package/templates/.agents/dashboard/apps/frontend/src/cost-calculation-guide.css +267 -0
  101. package/templates/.agents/dashboard/apps/frontend/src/cost-calculation-guide.test.ts +69 -0
  102. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +85 -0
  103. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +133 -0
  104. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +453 -13
  105. package/templates/.agents/dashboard/apps/frontend/src/dashboard-localization.ts +1 -1
  106. package/templates/.agents/dashboard/apps/frontend/src/main.tsx +1 -0
  107. package/templates/.agents/dashboard/apps/frontend/src/operations-detail-hydration.test.ts +55 -0
  108. package/templates/.agents/dashboard/apps/frontend/src/operations-detail-hydration.ts +36 -0
  109. package/templates/.agents/dashboard/apps/frontend/src/operations-entity-page-projection.test.ts +126 -0
  110. package/templates/.agents/dashboard/apps/frontend/src/operations-entity-page-projection.ts +181 -0
  111. package/templates/.agents/dashboard/apps/frontend/src/operations-period-contract.test.ts +138 -0
  112. package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +105 -0
  113. package/templates/.agents/dashboard/apps/frontend/src/operations-period.test.ts +6 -3
  114. package/templates/.agents/dashboard/apps/frontend/src/operations-period.ts +9 -5
  115. package/templates/.agents/dashboard/apps/frontend/src/overview-status-page.test.ts +26 -0
  116. package/templates/.agents/dashboard/apps/frontend/src/period-transition-overlay.css +44 -0
  117. package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +121 -62
  118. package/templates/.agents/dashboard/apps/frontend/src/project-settings-page.css +482 -3
  119. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +84 -17
  120. package/templates/.agents/dashboard/apps/frontend/src/snapshot-banner-semantics.test.ts +78 -0
  121. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +234 -39
  122. package/templates/.agents/dashboard/apps/frontend/src/snapshot-notice.ts +15 -0
  123. package/templates/.agents/dashboard/apps/frontend/src/sqlite-cost-adapters.ts +26 -0
  124. package/templates/.agents/dashboard/apps/frontend/src/styles.css +14 -2
  125. package/templates/.agents/dashboard/apps/frontend/src/traceability-workspace.css +38 -2
  126. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +253 -0
  127. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +380 -0
  128. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +175 -0
  129. package/templates/.agents/dashboard/apps/frontend/src/use-app-operations-pages.ts +395 -0
  130. package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.ts +141 -0
  131. package/templates/.agents/dashboard/apps/frontend/src/use-idle-cost-prefetch.ts +45 -0
  132. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.test.ts +46 -0
  133. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.ts +36 -0
  134. package/templates/.agents/dashboard/apps/frontend/vite.config.ts +58 -5
  135. package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
  136. package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +47 -6
  137. package/templates/.agents/dashboard/modules/cost-monitor/src/duration-estimate.ts +13 -0
  138. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.test.ts +134 -0
  139. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.tsx +290 -0
  140. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.test.ts +133 -19
  141. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.tsx +134 -43
  142. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.tsx +137 -19
  143. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostModelsAuditTable.test.ts +2 -0
  144. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostModelsAuditTable.tsx +13 -10
  145. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +328 -0
  146. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +302 -6
  147. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +627 -400
  148. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostSummary.tsx +5 -13
  149. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +370 -72
  150. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +467 -326
  151. package/templates/.agents/dashboard/modules/cost-monitor/src/react/MacroCostTable.test.ts +17 -0
  152. package/templates/.agents/dashboard/modules/cost-monitor/src/react/MacroCostTable.tsx +25 -6
  153. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-copy.ts +0 -2
  154. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-page-helpers.ts +17 -0
  155. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-responsive.css +6 -3
  156. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-row-filters.ts +123 -0
  157. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-summary.ts +8 -3
  158. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-views.ts +32 -0
  159. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor.css +55 -14
  160. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-presentation.ts +121 -0
  161. package/templates/.agents/dashboard/modules/cost-monitor/src/react/index.ts +3 -0
  162. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.test.ts +68 -0
  163. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +33 -5
  164. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +119 -0
  165. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.test.ts +12 -0
  166. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +146 -0
  167. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +118 -0
  168. package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.test.ts +2 -2
  169. package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.ts +5 -3
  170. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-pricing-fallback.test.ts +53 -0
  171. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.basics.test.ts +34 -0
  172. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.test.ts +168 -161
  173. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.ts +188 -29
  174. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.test.ts +757 -0
  175. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +784 -0
  176. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-terminal-day.test.ts +100 -0
  177. package/templates/.agents/dashboard/modules/cost-monitor/src/server/index.ts +1 -0
  178. package/templates/.agents/dashboard/modules/cost-monitor/src/server/refactor-map-xr9l.json +61 -0
  179. package/templates/.agents/dashboard/modules/cost-monitor/src/server/source-size-cost-server/build-cost-monitor-projection-agents.test.ts +31 -0
  180. package/templates/.agents/dashboard/modules/cost-monitor/src/server/source-size-cost-server/build-cost-summary-projection-duration.test.ts +414 -0
  181. package/templates/.agents/dashboard/modules/cost-monitor/src/task-cost-groups.test.ts +41 -0
  182. package/templates/.agents/dashboard/modules/cost-monitor/src/task-cost-groups.ts +55 -11
  183. package/templates/.agents/dashboard/modules/operations/package.json +2 -1
  184. package/templates/.agents/dashboard/modules/operations/src/contracts.ts +185 -0
  185. package/templates/.agents/dashboard/modules/operations/src/duration-population.test.ts +63 -1
  186. package/templates/.agents/dashboard/modules/operations/src/duration-population.ts +60 -5
  187. package/templates/.agents/dashboard/modules/operations/src/react/EntityDetailsDialog.tsx +14 -3
  188. package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.test.ts +44 -1
  189. package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +345 -18
  190. package/templates/.agents/dashboard/modules/operations/src/react/MacroPlanningDialog.tsx +1 -0
  191. package/templates/.agents/dashboard/modules/operations/src/react/OperationDateFilters.test.ts +9 -5
  192. package/templates/.agents/dashboard/modules/operations/src/react/OperationDateFilters.tsx +2 -2
  193. package/templates/.agents/dashboard/modules/operations/src/react/OperationsMainContent.tsx +168 -0
  194. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +95 -6
  195. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +367 -14
  196. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +562 -131
  197. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +353 -355
  198. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageNavigation.tsx +65 -0
  199. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +169 -0
  200. package/templates/.agents/dashboard/modules/operations/src/react/PeriodSummaryTable.tsx +73 -8
  201. package/templates/.agents/dashboard/modules/operations/src/react/PrdDocumentDialog.test.tsx +32 -0
  202. package/templates/.agents/dashboard/modules/operations/src/react/PrdDocumentDialog.tsx +62 -6
  203. package/templates/.agents/dashboard/modules/operations/src/react/TaskBoard.test.ts +55 -0
  204. package/templates/.agents/dashboard/modules/operations/src/react/TaskBoard.tsx +27 -19
  205. package/templates/.agents/dashboard/modules/operations/src/react/TaskDetailsDialog.tsx +105 -42
  206. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.priority.test.ts +19 -0
  207. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.tsx +265 -36
  208. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.test.ts +171 -8
  209. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.tsx +98 -68
  210. package/templates/.agents/dashboard/modules/operations/src/react/conversation-rows.test.ts +72 -1
  211. package/templates/.agents/dashboard/modules/operations/src/react/conversation-rows.ts +78 -17
  212. package/templates/.agents/dashboard/modules/operations/src/react/execution-metrics.ts +6 -4
  213. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +431 -1
  214. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +574 -20
  215. package/templates/.agents/dashboard/modules/operations/src/react/export/export-layout.test.ts +42 -4
  216. package/templates/.agents/dashboard/modules/operations/src/react/export/xlsx-runtime-e2e.test.ts +248 -0
  217. package/templates/.agents/dashboard/modules/operations/src/react/macro-planning-dialog-content.css +49 -0
  218. package/templates/.agents/dashboard/modules/operations/src/react/macro-planning-dialog.css +93 -50
  219. package/templates/.agents/dashboard/modules/operations/src/react/manual-work-priority.test.ts +82 -0
  220. package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.test.ts +29 -2
  221. package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.ts +22 -1
  222. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.test.ts +77 -4
  223. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.ts +67 -5
  224. package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +54 -0
  225. package/templates/.agents/dashboard/modules/operations/src/react/operations-global-filters.test.ts +22 -0
  226. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +149 -12
  227. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.status-cards.test.ts +27 -0
  228. package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +176 -0
  229. package/templates/.agents/dashboard/modules/operations/src/react/operations-period-scope.test.ts +160 -0
  230. package/templates/.agents/dashboard/modules/operations/src/react/operations-period-scope.ts +47 -0
  231. package/templates/.agents/dashboard/modules/operations/src/react/operations-query.ts +10 -0
  232. package/templates/.agents/dashboard/modules/operations/src/react/operations-responsive.css +3 -0
  233. package/templates/.agents/dashboard/modules/operations/src/react/operations-view-labels.ts +28 -0
  234. package/templates/.agents/dashboard/modules/operations/src/react/operations-view-order.test.ts +21 -7
  235. package/templates/.agents/dashboard/modules/operations/src/react/operations.css +42 -0
  236. package/templates/.agents/dashboard/modules/operations/src/react/period-summary-population.test.ts +54 -0
  237. package/templates/.agents/dashboard/modules/operations/src/react/period-summary-population.ts +32 -0
  238. package/templates/.agents/dashboard/modules/operations/src/react/period-summary.test.ts +241 -6
  239. package/templates/.agents/dashboard/modules/operations/src/react/period-summary.ts +148 -6
  240. package/templates/.agents/dashboard/modules/operations/src/react/popup-localization.test.ts +1 -1
  241. package/templates/.agents/dashboard/modules/operations/src/react/unified-work-board.css +132 -0
  242. package/templates/.agents/dashboard/modules/operations/src/react/unified-work-board.css.test.ts +13 -0
  243. package/templates/.agents/dashboard/modules/operations/src/react/unified-work-board.header.test.ts +16 -0
  244. package/templates/.agents/dashboard/modules/operations/src/react/use-infinite-column.ts +11 -5
  245. package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.test.ts +7 -4
  246. package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.ts +5 -84
  247. package/templates/.agents/dashboard/modules/operations/src/server/build-operations-projection.test.ts +291 -15
  248. package/templates/.agents/dashboard/modules/operations/src/server/build-operations-projection.ts +182 -472
  249. package/templates/.agents/dashboard/modules/operations/src/server/canonical-compat.test.ts +40 -0
  250. package/templates/.agents/dashboard/modules/operations/src/server/canonical-compat.ts +19 -6
  251. package/templates/.agents/dashboard/modules/operations/src/server/canonical-run-projection.ts +31 -6
  252. package/templates/.agents/dashboard/modules/operations/src/server/daily-duration-projection.ts +220 -0
  253. package/templates/.agents/dashboard/modules/operations/src/server/execution-duration-projection.ts +28 -0
  254. package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.test.ts +39 -2
  255. package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.ts +25 -13
  256. package/templates/.agents/dashboard/modules/operations/src/server/filter.test.ts +3 -3
  257. package/templates/.agents/dashboard/modules/operations/src/server/filter.ts +7 -2
  258. package/templates/.agents/dashboard/modules/operations/src/server/index.ts +3 -0
  259. package/templates/.agents/dashboard/modules/operations/src/server/macro.ts +3 -12
  260. package/templates/.agents/dashboard/modules/operations/src/server/manifest-usage.ts +65 -0
  261. package/templates/.agents/dashboard/modules/operations/src/server/operations-projection-aggregates.ts +297 -0
  262. package/templates/.agents/dashboard/modules/operations/src/server/quick-daily-operations-projection.ts +330 -0
  263. package/templates/.agents/dashboard/modules/operations/src/server/quick-operations-projection.test.ts +505 -0
  264. package/templates/.agents/dashboard/modules/operations/src/server/quick-operations-projection.ts +609 -0
  265. package/templates/.agents/dashboard/modules/operations/src/server/runs.ts +18 -19
  266. package/templates/.agents/dashboard/modules/operations/src/work-board-model.ts +76 -0
  267. package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
  268. package/templates/.agents/dashboard/modules/traceability/src/server/build-task-trace.ts +3 -1
  269. package/templates/.agents/dashboard/packages/branding/package.json +1 -1
  270. package/templates/.agents/dashboard/packages/branding/src/index.ts +1 -1
  271. package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
  272. package/templates/.agents/dashboard/packages/contracts/src/index.ts +2 -0
  273. package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
  274. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +1759 -0
  275. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs.metafile.json +7333 -0
  276. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +1611 -0
  277. package/templates/.agents/dashboard/packages/local-runtime/package.json +8 -3
  278. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-dashboard.mjs +20 -0
  279. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +114 -0
  280. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +31 -0
  281. package/templates/.agents/dashboard/packages/local-runtime/scripts/source-size-runtime-scripts/sqlite-read-model-fixture.mjs +397 -0
  282. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-read-model.mjs +589 -0
  283. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-unix-launchers.mjs +53 -0
  284. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +203 -0
  285. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +1646 -107
  286. package/templates/.agents/dashboard/packages/local-runtime/src/agent-adapter-sync.ts +30 -0
  287. package/templates/.agents/dashboard/packages/local-runtime/src/agent-catalog-settings.test.ts +3 -2
  288. package/templates/.agents/dashboard/packages/local-runtime/src/agent-catalog-settings.ts +5 -0
  289. package/templates/.agents/dashboard/packages/local-runtime/src/manifest-runs.ts +72 -33
  290. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +339 -4
  291. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +392 -1
  292. package/templates/.agents/dashboard/packages/local-runtime/src/runtime.mjs +5 -5
  293. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.test.ts +110 -0
  294. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.ts +189 -0
  295. package/templates/.agents/dashboard/packages/local-runtime/src/server-cross-layer-parity.test.ts +245 -0
  296. package/templates/.agents/dashboard/packages/local-runtime/src/server-daily-read-models-route.ts +64 -0
  297. package/templates/.agents/dashboard/packages/local-runtime/src/server-daily-read-models.test.ts +204 -0
  298. package/templates/.agents/dashboard/packages/local-runtime/src/server-daily-read-models.ts +194 -0
  299. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-pages.test.ts +163 -0
  300. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-pages.ts +354 -0
  301. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-runs-route.test.ts +74 -0
  302. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-runs-route.ts +113 -0
  303. package/templates/.agents/dashboard/packages/local-runtime/src/server-overview.test.ts +68 -0
  304. package/templates/.agents/dashboard/packages/local-runtime/src/server-period.ts +47 -0
  305. package/templates/.agents/dashboard/packages/local-runtime/src/server-projections.ts +483 -0
  306. package/templates/.agents/dashboard/packages/local-runtime/src/server-settings.test.ts +5 -2
  307. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +178 -84
  308. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +149 -0
  309. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +675 -267
  310. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +1212 -309
  311. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-metric-contract.test.ts +53 -0
  312. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-metric-contract.ts +722 -0
  313. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +3743 -0
  314. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +7017 -0
  315. package/templates/.agents/dashboard/packages/local-runtime/src/work-priority-contract.test.ts +33 -0
  316. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-history.mjs +41 -0
  317. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-processes.mjs +210 -0
  318. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +514 -190
  319. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +324 -24
  320. package/templates/.agents/dashboard/packages/ui/package.json +1 -1
  321. package/templates/.agents/dashboard/packages/windows-launcher/LauncherVerificationBehaviorTests.cs +206 -0
  322. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +95 -8
  323. package/templates/.agents/dashboard/packages/windows-launcher/build-dashboard-launcher.ps1 +2 -2
  324. package/templates/.agents/dashboard/packages/windows-launcher/run-launcher-verification-tests.ps1 +22 -0
  325. package/templates/.agents/dashboard/read-model.dbml +2201 -0
  326. package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
  327. package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +1 -1
  328. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +216 -41
  329. package/templates/.agents/dashboard/work-priority.json +34 -0
  330. package/templates/.agents/dashboard/zaip-dashboard-linux.sh +45 -0
  331. package/templates/.agents/dashboard/zaip-dashboard-mac.command +3 -0
  332. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  333. package/templates/.agents/design/.zaip-managed.json +3 -3
  334. package/templates/.agents/design/apps/catalog/package.json +1 -1
  335. package/templates/.agents/design/packages/local-runtime/package.json +1 -1
  336. package/templates/.agents/growth/estrategia-bootstrap-90-dias.md +169 -0
  337. package/templates/.agents/growth/strategy.md +27 -0
  338. package/templates/.agents/marketing/estrategia-marketing-zaip-90-dias.md +197 -0
  339. package/templates/.agents/marketing/strategy.md +29 -0
  340. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  341. package/templates/.agents/requirements/comparative-audit.md +1 -1
  342. package/templates/.agents/requirements/context-engine.md +1 -1
  343. package/templates/.agents/requirements/cost-observability.md +1 -1
  344. package/templates/.agents/requirements/dashboard-launch.md +24 -0
  345. package/templates/.agents/requirements/data-export.md +1 -1
  346. package/templates/.agents/requirements/design-system.md +1 -1
  347. package/templates/.agents/requirements/execution-engine.md +1 -1
  348. package/templates/.agents/requirements/kanban-dashboard.md +4 -3
  349. package/templates/.agents/requirements/model-orchestration.md +1 -1
  350. package/templates/.agents/requirements/public-distribution.md +9 -6
  351. package/templates/.agents/requirements/software-architecture.md +1 -1
  352. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  353. package/templates/.agents/requirements/work-lifecycle.md +9 -3
  354. package/templates/.agents/requirements/zaip-strategic-95.md +4 -4
  355. package/templates/.agents/rules/autonomy-decision.md +15 -0
  356. package/templates/.agents/rules/core.md +1 -1
  357. package/templates/.agents/rules/run-lifecycle-closure.md +1 -1
  358. package/templates/.agents/rules/specialist-routing.md +19 -0
  359. package/templates/.agents/rules/workflow.md +1 -1
  360. package/templates/.agents/runtime/VERSION +1 -1
  361. package/templates/.agents/runtime/bin/zaip.mjs +118 -67
  362. package/templates/.agents/runtime/checksums.sha256 +6 -4
  363. package/templates/.agents/runtime/manifest.json +3 -1
  364. package/templates/.agents/runtime/pnpm-lock.sha256 +1 -0
  365. package/templates/.agents/runtime/provenance.json +11 -0
  366. package/templates/.agents/runtime/sbom.cdx.json +1929 -39
  367. package/templates/.agents/sales/playbook.md +32 -0
  368. package/templates/.agents/sales/roteiro-comercial-zaip.md +252 -0
  369. package/templates/.agents/skills/zaip-aios/SKILL.md +1 -1
  370. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +22 -0
  371. package/templates/.agents/skills/zaip-skill-brand-governance/agents/openai.yaml +4 -0
  372. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +39 -0
  373. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +30 -0
  374. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/SKILL.md +25 -0
  375. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/agents/openai.yaml +4 -0
  376. package/templates/.agents/skills/zaip-skill-enforce-architecture/SKILL.md +2 -0
  377. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/SKILL.md +18 -0
  378. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/agents/openai.yaml +6 -0
  379. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-fence.mjs +71 -0
  380. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-fence.test.mjs +60 -0
  381. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/scheduled-ready-queue-contract.test.mjs +111 -0
  382. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +2 -2
  383. package/templates/.agents/skills/zaip-skill-frontend-launcher/references/launcher-contract.md +3 -1
  384. package/templates/.agents/skills/zaip-skill-general-operations/SKILL.md +12 -0
  385. package/templates/.agents/skills/zaip-skill-general-operations/agents/openai.yaml +4 -0
  386. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +9 -7
  387. package/templates/.agents/skills/zaip-skill-github-deploy/agents/openai.yaml +2 -2
  388. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs +11 -38
  389. package/templates/.agents/skills/zaip-skill-governance-compliance/SKILL.md +12 -0
  390. package/templates/.agents/skills/zaip-skill-governance-compliance/agents/openai.yaml +4 -0
  391. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +21 -0
  392. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/agents/openai.yaml +4 -0
  393. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/references/growth-revops-framework.md +25 -0
  394. package/templates/.agents/skills/zaip-skill-instalar-agendamento/SKILL.md +17 -0
  395. package/templates/.agents/skills/zaip-skill-instalar-agendamento/agents/openai.yaml +6 -0
  396. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +9 -3
  397. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +99 -93
  398. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +132 -23
  399. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +149 -1
  400. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.mjs +76 -19
  401. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +217 -7
  402. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/source-size-telemetry/model-identity-reconciliation.mjs +77 -0
  403. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +20 -0
  404. package/templates/.agents/skills/zaip-skill-sales-methodology/agents/openai.yaml +4 -0
  405. package/templates/.agents/skills/zaip-skill-sales-methodology/references/sales-process-framework.md +77 -0
  406. package/templates/.agents/skills/zaip-skill-setup/SKILL.md +5 -0
  407. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +96 -0
  408. package/templates/.agents/skills/zaip-skill-sqlite-read-model/agents/openai.yaml +4 -0
  409. package/templates/.agents/skills/zaip-skill-sqlite-read-model/scripts/ensure-node.mjs +41 -0
  410. package/templates/.agents/zaip-aios/INDEX.md +11 -0
  411. package/templates/.agents/zaip-aios/README.md +38 -8
  412. package/templates/.agents/zaip-aios/VERSION +1 -1
  413. package/templates/.agents/zaip-aios/guides/09-agentes-e-squads.md +43 -6
  414. package/templates/.agents/zaip-aios/guides/10-seguranca.md +21 -7
  415. package/templates/.agents/zaip-aios/guides/17-como-o-custo-e-calculado.md +32 -0
  416. package/templates/.agents/zaip-aios/guides/18-catalogo-de-agentes.md +76 -0
  417. package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +72 -0
  418. package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +330 -0
  419. package/templates/.agents/zaip-aios/guides/21-telemetria-monitor-custos.md +52 -0
  420. package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +69 -0
  421. package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +483 -0
  422. package/templates/.agents/zaip-aios/guides/24-calculo-de-custos-openai.md +85 -0
  423. package/templates/.agents/zaip-aios/guides/25-fontes-de-precos-openai.md +18 -0
  424. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +44 -0
  425. package/templates/.agents/zaip-aios/guides/27-telemetria-git-native.md +167 -0
  426. package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +144 -0
  427. package/templates/.agents/zaip-aios/guides/assets/diagrams/lifecycles-task-spec-prd.png +0 -0
  428. package/templates/.agents/zaip-aios/guides/assets/diagrams/prd-lifecycle.png +0 -0
  429. package/templates/.agents/zaip-aios/guides/assets/diagrams/spec-lifecycle.png +0 -0
  430. package/templates/.agents/zaip-aios/guides/assets/diagrams/task-lifecycle.png +0 -0
  431. package/templates/template-manifest.json +1082 -215
  432. package/templates/.agents/dashboard/apps/frontend/dist/assets/AgentCatalogSettingsPage-CLYIi7Vo.js +0 -1
  433. package/templates/.agents/dashboard/apps/frontend/dist/assets/AgentCatalogSettingsPage-DTDNVQGX.css +0 -1
  434. package/templates/.agents/dashboard/apps/frontend/dist/assets/DashboardHelpDialog-Cv-JcoxP.js +0 -1320
  435. package/templates/.agents/dashboard/apps/frontend/dist/assets/PricingSettingsDialog-CzEggcRb.js +0 -1
  436. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-05YKn6Z4.js +0 -3
  437. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-YO3yqtgV.css +0 -1
  438. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-8AzCnl3a.js +0 -77
  439. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DdBYQ_8D.css +0 -1
  440. package/templates/.agents/dashboard/apps/frontend/src/AgentCatalogSettingsPage.tsx +0 -617
  441. package/templates/.agents/dashboard/apps/frontend/src/agent-catalog-settings-page.css +0 -660
  442. package/templates/.agents/dashboard/apps/frontend/src/agent-catalog-settings.test.ts +0 -249
  443. package/templates/.agents/dashboard/dashboard.config.json +0 -4
  444. package/templates/.agents/dashboard/zaip-dashboard.exe +0 -0
@@ -1,7 +1,8 @@
1
- import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
1
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join, resolve } from "node:path";
4
- import { describe, expect, it } from "vitest";
4
+ import { WorkspaceFileSystem } from "@zaip-ai/core";
5
+ import { describe, expect, it, vi } from "vitest";
5
6
  import {
6
7
  createDashboardApp,
7
8
  dashboardWorkspaceKey,
@@ -11,12 +12,223 @@ import {
11
12
  openWorkIds,
12
13
  projectionPeriodFilter,
13
14
  reconciliationResult,
15
+ SqliteReadModelFileSystem,
14
16
  } from "./server.js";
15
17
  import { invocationSummary } from "./server.test-fixtures.js";
18
+ import { classificationPage } from "./server-board.js";
19
+ import {
20
+ compactRunProjection,
21
+ mergeKnownManifestRuns,
22
+ mergeLifecycleRunPage,
23
+ runTimestampInRange,
24
+ } from "./server-projections.js";
25
+ import { rebuildSqliteReadModel } from "./sqlite-read-model.js";
16
26
 
17
27
  const workspaceRoot = resolve(import.meta.dirname, "../../../../..");
18
-
19
28
  describe("dashboard backend", () => {
29
+ it.each([
30
+ ["task", "lite", "TASK-LITE"],
31
+ ["task", "standard", "TASK-STANDARD"],
32
+ ["task", "critical", "TASK-CRITICAL"],
33
+ ["spec", "empty", "SPEC-EMPTY"],
34
+ ["spec", "structured", "SPEC-STRUCTURED"],
35
+ ["spec", "rework", "SPEC-REWORK"],
36
+ ["prd", "empty", "PRD-EMPTY"],
37
+ ["prd", "structured", "PRD-STRUCTURED"],
38
+ ["prd", "rework", "PRD-REWORK"],
39
+ ] as const)("returns only the canonical %s %s classification page", (kind, classification, expectedId) => {
40
+ const projection = {
41
+ tasks: [
42
+ { id: "TASK-LITE", risk: "lite", created_at: "2026-08-21T00:00:00.000Z", updated_at: null },
43
+ {
44
+ id: "TASK-STANDARD",
45
+ risk: "standard",
46
+ created_at: "2026-08-21T00:00:00.000Z",
47
+ updated_at: null,
48
+ },
49
+ {
50
+ id: "TASK-CRITICAL",
51
+ risk: "critical",
52
+ created_at: "2026-08-21T00:00:00.000Z",
53
+ updated_at: null,
54
+ },
55
+ ],
56
+ specs: [
57
+ {
58
+ id: "SPEC-EMPTY",
59
+ task_count: 0,
60
+ rework_run_count: 0,
61
+ created_at: "2026-08-21T00:00:00.000Z",
62
+ updated_at: null,
63
+ },
64
+ {
65
+ id: "SPEC-STRUCTURED",
66
+ task_count: 1,
67
+ rework_run_count: 0,
68
+ created_at: "2026-08-21T00:00:00.000Z",
69
+ updated_at: null,
70
+ },
71
+ {
72
+ id: "SPEC-REWORK",
73
+ task_count: 1,
74
+ rework_run_count: 1,
75
+ created_at: "2026-08-21T00:00:00.000Z",
76
+ updated_at: null,
77
+ },
78
+ ],
79
+ prds: [
80
+ {
81
+ id: "PRD-EMPTY",
82
+ task_count: 0,
83
+ rework_run_count: 0,
84
+ created_at: "2026-08-21T00:00:00.000Z",
85
+ updated_at: null,
86
+ },
87
+ {
88
+ id: "PRD-STRUCTURED",
89
+ task_count: 1,
90
+ rework_run_count: 0,
91
+ created_at: "2026-08-21T00:00:00.000Z",
92
+ updated_at: null,
93
+ },
94
+ {
95
+ id: "PRD-REWORK",
96
+ task_count: 1,
97
+ rework_run_count: 1,
98
+ created_at: "2026-08-21T00:00:00.000Z",
99
+ updated_at: null,
100
+ },
101
+ ],
102
+ changes: [],
103
+ } as unknown as Parameters<typeof classificationPage>[0];
104
+
105
+ const page = classificationPage(projection, kind, classification, 0, 1);
106
+ const items = kind === "task" ? page.tasks : kind === "spec" ? page.specs : page.prds;
107
+
108
+ expect(page).toMatchObject({ offset: 0, total: 1 });
109
+ expect(items.map((item) => item.id)).toEqual([expectedId]);
110
+ expect(kind === "task" ? page.specs : page.tasks).toEqual([]);
111
+ expect(kind === "prd" ? page.specs : page.prds).toEqual([]);
112
+ });
113
+
114
+ it("keeps Runs on the final all-time summary day inside the page-first range", () => {
115
+ expect(runTimestampInRange("2026-08-07T23:59:59.000Z", "2026-08-01", "2026-08-07")).toBe(true);
116
+ expect(runTimestampInRange("2026-08-08T00:00:00.000Z", "2026-08-01", "2026-08-07")).toBe(false);
117
+ });
118
+ it("keeps a lifecycle Run with no InvocationSummary as a null-telemetry page row", () => {
119
+ const run = compactRunProjection({
120
+ id: "RUN-NO-SUMMARY",
121
+ started_at: "2026-08-07T02:59:59.999Z",
122
+ finished_at: null,
123
+ task_id: null,
124
+ execution_id: null,
125
+ conversation_id: null,
126
+ agent: null,
127
+ agent_name: null,
128
+ provider: null,
129
+ model: null,
130
+ spec_id: null,
131
+ prd_id: null,
132
+ change_id: null,
133
+ phase: "execution",
134
+ parent_run_id: null,
135
+ status: "completed",
136
+ duration_ms: null,
137
+ total_tokens: null,
138
+ cost: null,
139
+ currency: null,
140
+ events: null,
141
+ });
142
+ expect(run).toMatchObject({
143
+ id: "RUN-NO-SUMMARY",
144
+ duration_ms: null,
145
+ source_duration_ms: null,
146
+ });
147
+ expect(run.usage.total.tokens.total).toBeNull();
148
+ expect(run.usage.total.cost.brl).toBeNull();
149
+ });
150
+ it("projects factual compact Run dimensions without replacing absent data", () => {
151
+ const run = compactRunProjection({
152
+ id: "RUN-FACTUAL-DIMENSIONS",
153
+ started_at: "2026-08-08T03:00:00.000Z",
154
+ finished_at: null,
155
+ task_id: "TASK-1",
156
+ execution_id: "EXEC-1",
157
+ conversation_id: "CONV-1",
158
+ agent: "agent-builder",
159
+ agent_name: "Builder",
160
+ provider: "openai",
161
+ model: "gpt-5.6",
162
+ spec_id: "SPEC-1",
163
+ prd_id: "PRD-1",
164
+ change_id: "CHG-1",
165
+ phase: "execution",
166
+ parent_run_id: null,
167
+ status: "completed",
168
+ run_kind: "run",
169
+ duration_ms: 12_000,
170
+ total_tokens: 42,
171
+ cost: 4.2,
172
+ currency: "BRL",
173
+ events: 1,
174
+ });
175
+ expect(run).toMatchObject({
176
+ agent: "agent-builder",
177
+ agent_name: "Builder",
178
+ provider: "openai",
179
+ model: "gpt-5.6",
180
+ effective_model: "gpt-5.6",
181
+ execution_id: "EXEC-1",
182
+ conversation_id: "CONV-1",
183
+ task_id: "TASK-1",
184
+ spec_id: "SPEC-1",
185
+ prd_id: "PRD-1",
186
+ change_id: "CHG-1",
187
+ });
188
+ });
189
+ it("left-joins telemetry without changing lifecycle page IDs or length", () => {
190
+ const page = ["RUN-PAGE-1", "RUN-PAGE-2"].map((id) =>
191
+ compactRunProjection({
192
+ id,
193
+ started_at: "2026-08-07T03:00:00.000Z",
194
+ finished_at: null,
195
+ task_id: null,
196
+ execution_id: null,
197
+ conversation_id: null,
198
+ agent: null,
199
+ agent_name: null,
200
+ provider: null,
201
+ model: null,
202
+ spec_id: null,
203
+ prd_id: null,
204
+ change_id: null,
205
+ phase: "execution",
206
+ parent_run_id: null,
207
+ status: "completed",
208
+ duration_ms: null,
209
+ total_tokens: null,
210
+ cost: null,
211
+ currency: null,
212
+ events: null,
213
+ }),
214
+ );
215
+ const result = mergeLifecycleRunPage(page, [
216
+ invocationSummary({ run_id: "RUN-PAGE-1" }),
217
+ invocationSummary({ run_id: "RUN-NOT-ON-PAGE" }),
218
+ ]);
219
+ expect(result).toHaveLength(2);
220
+ expect(result.map((run) => run.id)).toEqual(["RUN-PAGE-1", "RUN-PAGE-2"]);
221
+ expect(result[1]?.usage.total.tokens.total).toBeNull();
222
+ });
223
+ it("does not reintroduce a Run excluded by the canonical lifecycle period", () => {
224
+ const retained = invocationSummary({ run_id: "RUN-IN-PERIOD" });
225
+ const excluded = invocationSummary({ run_id: "RUN-OUTSIDE-PERIOD" });
226
+ expect(
227
+ mergeKnownManifestRuns([manifestRun(retained, [retained])], [retained, excluded]).map(
228
+ (run) => run.id,
229
+ ),
230
+ ).toEqual(["RUN-IN-PERIOD"]);
231
+ });
20
232
  it("normalizes GitHub HTTPS and SSH remotes for display", () => {
21
233
  expect(githubRepositoryFromRemote("git@github.com:zaip-ai/zaip-aios.git")).toBe(
22
234
  "zaip-ai/zaip-aios",
@@ -28,14 +240,12 @@ describe("dashboard backend", () => {
28
240
  expect(githubRepositoryFromRemote("")).toBeNull();
29
241
  expect(githubRepositoryFromRemote("https://gitlab.com/zaip-ai/zaip-aios.git")).toBeNull();
30
242
  });
31
-
32
243
  it("exposes a modern read-only health endpoint", async () => {
33
244
  const app = createDashboardApp(workspaceRoot, "test-session");
34
245
  const response = await app.request("/api/v2/health");
35
-
36
246
  expect(response.status).toBe(200);
37
247
  await expect(response.json()).resolves.toMatchObject({
38
- kind: "zaip-dashboard-local-runtime",
248
+ kind: "zaip-dashboard-sqlite-runtime",
39
249
  workspaceKey: dashboardWorkspaceKey(workspaceRoot),
40
250
  api_version: 2,
41
251
  read_only: true,
@@ -43,36 +253,361 @@ describe("dashboard backend", () => {
43
253
  workspace_layout: 3,
44
254
  });
45
255
  });
46
-
47
- it("serves compact overview aggregates without Run detail", async () => {
48
- const app = createDashboardApp(workspaceRoot, "test-session");
49
- const response = await app.request("/api/v2/operations?scope=overview", {
256
+ it("permits canonical open-work reconciliation while serving a read-only SQLite snapshot", async () => {
257
+ const reconcile = vi.fn(async () => ({
258
+ reconciled_ids: ["TASK-RECONCILED"],
259
+ blocked: [],
260
+ not_reconciled: [],
261
+ }));
262
+ const refresh = vi.fn(async () => undefined);
263
+ const canonicalFilesystem = new WorkspaceFileSystem(workspaceRoot);
264
+ const app = createDashboardApp(workspaceRoot, "test-session", undefined, {
265
+ snapshotReadOnly: true,
266
+ canonicalFilesystem,
267
+ reconcileOpenWork: reconcile,
268
+ refreshReadModel: refresh,
269
+ });
270
+ const response = await app.request("/api/v2/reconcile-open-work", {
271
+ method: "POST",
50
272
  headers: { "x-zaip-session": "test-session" },
51
273
  });
52
-
53
- expect(response.status).toBe(200);
54
- const payload = await response.json();
55
- if (
56
- typeof payload !== "object" ||
57
- payload === null ||
58
- !("summary" in payload) ||
59
- typeof payload.summary !== "object" ||
60
- payload.summary === null ||
61
- !("overview_metrics" in payload.summary)
62
- ) {
63
- throw new Error("Operations overview payload is not an object with summary");
274
+ expect(response.status, await response.clone().text()).toBe(200);
275
+ await expect(response.json()).resolves.toMatchObject({
276
+ reconciliation: { reconciled_ids: ["TASK-RECONCILED"] },
277
+ });
278
+ expect(reconcile).toHaveBeenCalledOnce();
279
+ expect(reconcile).toHaveBeenCalledWith(canonicalFilesystem, workspaceRoot);
280
+ expect(refresh).toHaveBeenCalledOnce();
281
+ const rejected = await app.request("/api/v2/reconcile-open-work", { method: "POST" });
282
+ expect(rejected.status).toBe(401);
283
+ }, 130_000);
284
+ it("rebuilds a stale immutable projection without reconciling lifecycle work", async () => {
285
+ const fixtureRoot = await mkdtemp(join(tmpdir(), "zaip-dashboard-stale-projection-"));
286
+ const taskIndexPath = join(
287
+ fixtureRoot,
288
+ ".agents",
289
+ "records",
290
+ "lifecycle",
291
+ "indexes",
292
+ "task.json",
293
+ );
294
+ const taskRecordPath = join(
295
+ fixtureRoot,
296
+ ".agents",
297
+ "records",
298
+ "tasks",
299
+ "2026",
300
+ "08",
301
+ "TASK-STALE-PROJECTION.md",
302
+ );
303
+ await mkdir(join(fixtureRoot, ".agents", "config"), { recursive: true });
304
+ await mkdir(join(fixtureRoot, ".agents", "records", "lifecycle", "indexes"), {
305
+ recursive: true,
306
+ });
307
+ await mkdir(join(fixtureRoot, ".agents", "records", "tasks", "2026", "08"), {
308
+ recursive: true,
309
+ });
310
+ await writeFile(
311
+ join(fixtureRoot, ".agents", "config", "project.yaml"),
312
+ "schema_version: 1\nproject:\n id: stale-projection\n name: stale-projection\n timezone: UTC\nstack:\n id: zaip-aios-stack\n overrides: {}\nclients:\n codex: true\n claude: true\ntracking:\n enabled: true\n currency: BRL\n storage_mode: tracked_sanitized\n",
313
+ );
314
+ await writeFile(
315
+ join(fixtureRoot, ".agents", "config", "framework.lock.yaml"),
316
+ "lock_version: 1\nframework:\n version: 1.3.0\n distribution_sha256: null\nlayout:\n version: 3\nresolved_profile:\n id: null\n sha256: null\nmanaged_files: {}\n",
317
+ );
318
+ const task = (status: "executing" | "completed") => ({
319
+ schema_version: 1,
320
+ entities: {
321
+ "TASK-STALE-PROJECTION": {
322
+ kind: "task",
323
+ id: "TASK-STALE-PROJECTION",
324
+ title: "Stale projection",
325
+ status,
326
+ risk: "standard",
327
+ priority: "medium",
328
+ updated_at: "2026-08-22T12:00:00.000Z",
329
+ relations: [],
330
+ usage: null,
331
+ data: { created_at: "2026-08-22T12:00:00.000Z" },
332
+ },
333
+ },
334
+ });
335
+ await writeFile(taskIndexPath, `${JSON.stringify(task("executing"))}\n`);
336
+ const taskRecord = (status: "executing" | "completed") =>
337
+ `---\nid: TASK-STALE-PROJECTION\ntitle: Stale projection\nstatus: ${status}\nrisk: standard\ncreated_at: 2026-08-22T12:00:00.000Z\n---\n# Stale projection\n`;
338
+ await writeFile(taskRecordPath, taskRecord("executing"));
339
+ await rebuildSqliteReadModel(fixtureRoot);
340
+ const filesystem = new SqliteReadModelFileSystem(fixtureRoot);
341
+ const reconcile = vi.fn(async () => ({ reconciled_ids: [], blocked: [], not_reconciled: [] }));
342
+ try {
343
+ const app = createDashboardApp(fixtureRoot, "test-session", undefined, {
344
+ canonicalFilesystem: new WorkspaceFileSystem(fixtureRoot),
345
+ filesystem,
346
+ reconcileOpenWork: reconcile,
347
+ });
348
+ const before = await app.request("/api/v2/operations?scope=quick", {
349
+ headers: { "x-zaip-session": "test-session" },
350
+ });
351
+ const beforePayload = (await before.json()) as {
352
+ summary: { overview_metrics: { task_status_counts: Record<string, number> } };
353
+ };
354
+ expect(beforePayload.summary.overview_metrics.task_status_counts.executing).toBe(1);
355
+
356
+ await writeFile(taskIndexPath, `${JSON.stringify(task("completed"))}\n`);
357
+ await writeFile(taskRecordPath, taskRecord("completed"));
358
+ const previousSeedPreference = process.env.ZAIP_SQLITE_PREFER_LAST_VALID_SEED;
359
+ process.env.ZAIP_SQLITE_PREFER_LAST_VALID_SEED = "1";
360
+ const refreshed = await app.request("/api/v2/refresh-projection", {
361
+ method: "POST",
362
+ headers: { "x-zaip-session": "test-session" },
363
+ });
364
+ if (previousSeedPreference === undefined)
365
+ delete process.env.ZAIP_SQLITE_PREFER_LAST_VALID_SEED;
366
+ else process.env.ZAIP_SQLITE_PREFER_LAST_VALID_SEED = previousSeedPreference;
367
+
368
+ expect(refreshed.status, await refreshed.clone().text()).toBe(200);
369
+ const payload = (await refreshed.json()) as {
370
+ operations: {
371
+ summary: { overview_metrics: { task_status_counts: Record<string, number> } };
372
+ };
373
+ };
374
+ expect(payload.operations.summary.overview_metrics.task_status_counts.executing ?? 0).toBe(0);
375
+ expect(payload.operations.summary.overview_metrics.task_status_counts.completed).toBe(1);
376
+ expect(reconcile).not.toHaveBeenCalled();
377
+ const rejected = await app.request("/api/v2/refresh-projection", { method: "POST" });
378
+ expect(rejected.status).toBe(401);
379
+ } finally {
380
+ filesystem.close();
381
+ await rm(fixtureRoot, { recursive: true, force: true });
64
382
  }
65
- expect(payload).toMatchObject({ runs: [], changes: [], executions: [], decisions: [] });
66
- expect(payload.summary.overview_metrics).toEqual(
67
- expect.objectContaining({
68
- duration_ms: expect.any(Number),
69
- execution_day_count: expect.any(Number),
70
- tokens: expect.any(Number),
71
- cost_brl: expect.any(Number),
72
- }),
383
+ }, 130_000);
384
+ it("persists Kanban priority canonically while a SQLite snapshot blocks other settings writes", async () => {
385
+ const canonicalRoot = await mkdtemp(join(tmpdir(), "zaip-dashboard-priority-"));
386
+ try {
387
+ const canonicalFilesystem = new WorkspaceFileSystem(canonicalRoot);
388
+ const originalReadText = canonicalFilesystem.readText.bind(canonicalFilesystem);
389
+ const priorityReads = vi.fn(async (path: string) => originalReadText(path));
390
+ vi.spyOn(canonicalFilesystem, "readText").mockImplementation(priorityReads);
391
+ const app = createDashboardApp(workspaceRoot, "test-session", undefined, {
392
+ snapshotReadOnly: true,
393
+ canonicalFilesystem,
394
+ });
395
+ const before = await app.request(
396
+ "/api/v2/operations?scope=board&stage=completed&from=2000-01-01&to=2099-12-31&offset=0&limit=10",
397
+ { headers: { "x-zaip-session": "test-session" } },
398
+ );
399
+ expect(before.status, await before.clone().text()).toBe(200);
400
+ const beforeBoard = (await before.json()) as {
401
+ prds: Array<{ id: string }>;
402
+ specs: Array<{ id: string }>;
403
+ tasks: Array<{ id: string }>;
404
+ };
405
+ expect(beforeBoard.tasks.length).toBeGreaterThan(1);
406
+ const concurrent = await Promise.all([
407
+ app.request(
408
+ "/api/v2/operations?scope=board&stage=completed&from=2000-01-01&to=2099-12-31&offset=0&limit=10",
409
+ { headers: { "x-zaip-session": "test-session" } },
410
+ ),
411
+ app.request(
412
+ "/api/v2/operations?scope=board&stage=blocked&from=2000-01-01&to=2099-12-31&offset=0&limit=10",
413
+ { headers: { "x-zaip-session": "test-session" } },
414
+ ),
415
+ ]);
416
+ expect(concurrent.every((response) => response.status === 200)).toBe(true);
417
+ expect(
418
+ priorityReads.mock.calls.filter(
419
+ ([path]) => path === ".agents/dashboard/work-priority.json",
420
+ ),
421
+ ).toHaveLength(1);
422
+ const priority = { item_ids: [beforeBoard.tasks.at(-1)!.id], updated_at: null };
423
+ const persisted = await app.request("/api/v2/work-priority", {
424
+ method: "PUT",
425
+ headers: { "content-type": "application/json", "x-zaip-session": "test-session" },
426
+ body: JSON.stringify(priority),
427
+ });
428
+ expect(persisted.status, await persisted.clone().text()).toBe(200);
429
+ await expect(persisted.json()).resolves.toMatchObject({
430
+ schema_version: 1,
431
+ item_ids: priority.item_ids,
432
+ });
433
+ await expect(
434
+ canonicalFilesystem.readText(".agents/dashboard/work-priority.json"),
435
+ ).resolves.toContain(priority.item_ids[0]!);
436
+ const readsAfterPut = priorityReads.mock.calls.filter(
437
+ ([path]) => path === ".agents/dashboard/work-priority.json",
438
+ ).length;
439
+ const after = await app.request(
440
+ "/api/v2/operations?scope=board&stage=completed&from=2000-01-01&to=2099-12-31&offset=0&limit=10",
441
+ { headers: { "x-zaip-session": "test-session" } },
442
+ );
443
+ expect(after.status, await after.clone().text()).toBe(200);
444
+ const afterBoard = (await after.json()) as typeof beforeBoard;
445
+ expect(afterBoard.tasks[0]?.id).toBe(priority.item_ids[0]);
446
+ expect(
447
+ priorityReads.mock.calls.filter(([path]) => path === ".agents/dashboard/work-priority.json")
448
+ .length,
449
+ ).toBe(readsAfterPut + 1);
450
+ const blocked = await app.request("/api/v2/pricing", {
451
+ method: "PUT",
452
+ headers: { "content-type": "application/json", "x-zaip-session": "test-session" },
453
+ body: JSON.stringify({}),
454
+ });
455
+ expect(blocked.status).toBe(405);
456
+ } finally {
457
+ await rm(canonicalRoot, { recursive: true, force: true });
458
+ }
459
+ });
460
+ it("serves a cursor-sized Kanban stage from entity-only read models", async () => {
461
+ const app = createDashboardApp(workspaceRoot, "test-session");
462
+ const response = await app.request(
463
+ "/api/v2/operations?scope=board&stage=completed&offset=0&limit=10",
464
+ { headers: { "x-zaip-session": "test-session" } },
465
+ );
466
+ expect(response.status).toBe(200);
467
+ const payload = (await response.json()) as {
468
+ prds: unknown[];
469
+ specs: unknown[];
470
+ tasks: unknown[];
471
+ };
472
+ expect(payload).toMatchObject({
473
+ stage: "completed",
474
+ offset: 0,
475
+ total: expect.any(Number),
476
+ prds: expect.any(Array),
477
+ specs: expect.any(Array),
478
+ tasks: expect.any(Array),
479
+ changes: expect.any(Array),
480
+ });
481
+ expect(payload.prds.length + payload.specs.length + payload.tasks.length).toBeLessThanOrEqual(
482
+ 10,
73
483
  );
74
484
  });
75
-
485
+ it("applies Kanban filters before pagination without requesting aggregate telemetry", async () => {
486
+ const app = createDashboardApp(workspaceRoot, "test-session");
487
+ const response = await app.request(
488
+ "/api/v2/operations?scope=board&stage=blocked&offset=0&limit=10&risk=critical",
489
+ { headers: { "x-zaip-session": "test-session" } },
490
+ );
491
+ expect(response.status).toBe(200);
492
+ const payload = (await response.json()) as {
493
+ total: number;
494
+ prds: unknown[];
495
+ specs: unknown[];
496
+ tasks: Array<{ risk: string }>;
497
+ };
498
+ expect(payload.prds).toEqual([]);
499
+ expect(payload.specs).toEqual([]);
500
+ expect(payload.tasks.every((task) => task.risk === "critical")).toBe(true);
501
+ expect(payload.tasks.length).toBeLessThanOrEqual(10);
502
+ expect(payload.total).toBeGreaterThanOrEqual(payload.tasks.length);
503
+ });
504
+ it("serves a canonical cross-stage classification page by work kind", async () => {
505
+ const app = createDashboardApp(workspaceRoot, "test-session");
506
+ const response = await app.request(
507
+ "/api/v2/operations?scope=classification&kind=task&classification=critical&offset=0&limit=10",
508
+ { headers: { "x-zaip-session": "test-session" } },
509
+ );
510
+ expect(response.status).toBe(200);
511
+ const payload = (await response.json()) as {
512
+ offset: number;
513
+ total: number;
514
+ tasks: Array<{ risk: string }>;
515
+ specs: unknown[];
516
+ prds: unknown[];
517
+ };
518
+ expect(payload).toMatchObject({ offset: 0, total: expect.any(Number), specs: [], prds: [] });
519
+ expect(payload.tasks.every((task) => task.risk === "critical")).toBe(true);
520
+ expect(payload.tasks.length).toBeLessThanOrEqual(10);
521
+ });
522
+ it("classifies SPEC pages from their canonical task and rework facts", async () => {
523
+ const app = createDashboardApp(workspaceRoot, "test-session");
524
+ const response = await app.request(
525
+ "/api/v2/operations?scope=classification&kind=spec&classification=empty&offset=0&limit=10",
526
+ { headers: { "x-zaip-session": "test-session" } },
527
+ );
528
+ expect(response.status).toBe(200);
529
+ const payload = (await response.json()) as {
530
+ tasks: unknown[];
531
+ prds: unknown[];
532
+ specs: Array<{ task_count: number; rework_run_count: number }>;
533
+ };
534
+ expect(payload.tasks).toEqual([]);
535
+ expect(payload.prds).toEqual([]);
536
+ expect(
537
+ payload.specs.every((spec) => spec.task_count === 0 && spec.rework_run_count === 0),
538
+ ).toBe(true);
539
+ });
540
+ it("serves a compact cost summary and paginated Run metrics from the manifest", async () => {
541
+ const app = createDashboardApp(workspaceRoot, "test-session");
542
+ const headers = { "x-zaip-session": "test-session" };
543
+ const costs = await app.request("/api/v2/cost-monitor?currency=BRL&scope=summary", { headers });
544
+ expect(costs.status).toBe(200);
545
+ await expect(costs.json()).resolves.toMatchObject({
546
+ aggregate_only: true,
547
+ data_source: "lifecycle_manifest_summary",
548
+ runs: [],
549
+ groups: { daily: expect.any(Array), monthly: expect.any(Array), annual: expect.any(Array) },
550
+ });
551
+ const runs = await app.request("/api/v2/operations/runs?offset=0&limit=2", { headers });
552
+ expect(runs.status).toBe(200);
553
+ await expect(runs.json()).resolves.toMatchObject({
554
+ data_source: "lifecycle_manifest_summary",
555
+ offset: 0,
556
+ limit: 2,
557
+ rows: expect.any(Array),
558
+ });
559
+ });
560
+ it("serves a Cost Run transport page without materializing aggregate totals", async () => {
561
+ const app = createDashboardApp(workspaceRoot, "test-session");
562
+ const headers = { "x-zaip-session": "test-session" };
563
+ const page = await app.request(
564
+ "/api/v2/cost-monitor?currency=BRL&scope=runs&offset=0&limit=1",
565
+ { headers },
566
+ );
567
+ expect(page.status).toBe(200);
568
+ const pagePayload = (await page.json()) as {
569
+ overview: { total_tokens: number | null };
570
+ runs: unknown[];
571
+ page_only: boolean;
572
+ run_page: { total: number | null };
573
+ };
574
+ expect(pagePayload.runs.length).toBeLessThanOrEqual(1);
575
+ expect(pagePayload.page_only).toBe(true);
576
+ expect(pagePayload.run_page.total).toBeGreaterThan(0);
577
+ expect(pagePayload.overview.total_tokens).toBeNull();
578
+ });
579
+ it("filters a Cost Run transport page by eligible provider model and phase dimensions", async () => {
580
+ const app = createDashboardApp(workspaceRoot, "test-session");
581
+ const headers = { "x-zaip-session": "test-session" };
582
+ const query = new URLSearchParams({
583
+ currency: "BRL",
584
+ scope: "runs",
585
+ limit: "1",
586
+ phase: "execution",
587
+ });
588
+ const response = await app.request(`/api/v2/cost-monitor?${query.toString()}`, { headers });
589
+ expect(response.status).toBe(200);
590
+ const page = (await response.json()) as {
591
+ runs: Array<{ provider: string | null; model: string; phase: string }>;
592
+ run_page: { total: number | null };
593
+ };
594
+ expect(page.run_page.total).toBeGreaterThan(0);
595
+ expect(page.runs.length).toBeLessThanOrEqual(1);
596
+ expect(page.runs.every((run) => run.phase === "execution")).toBe(true);
597
+ });
598
+ it("serves operation detail without building the cost snapshot", async () => {
599
+ const app = createDashboardApp(workspaceRoot, "test-session");
600
+ const response = await app.request("/api/v2/operations?scope=detail", {
601
+ headers: { "x-zaip-session": "test-session" },
602
+ });
603
+ expect(response.status).toBe(200);
604
+ expect(response.headers.get("X-ZAIP-Snapshot-Cache")).toBeNull();
605
+ await expect(response.json()).resolves.toMatchObject({
606
+ source: ".agents/records",
607
+ tasks: expect.any(Array),
608
+ runs: expect.any(Array),
609
+ });
610
+ });
76
611
  it("serves only allowlisted canonical agent portraits", async () => {
77
612
  const app = createDashboardApp(workspaceRoot, "test-session");
78
613
  const agentIds = [
@@ -103,24 +638,29 @@ describe("dashboard backend", () => {
103
638
  expect(portrait.headers.get("content-type"), agentId).toBe("image/png");
104
639
  expect((await portrait.arrayBuffer()).byteLength, agentId).toBeGreaterThan(1_000);
105
640
  }
106
-
107
641
  const traversal = await app.request(
108
642
  "/api/v2/agent-catalog-assets/%2E%2E%2Fproject?token=test-session",
109
643
  );
110
644
  expect(traversal.status).toBe(404);
111
645
  });
112
-
113
646
  it("treats date-only dashboard filters as whole calendar days", () => {
114
647
  const context = {
115
648
  req: { query: (name: string) => (name === "from" ? "2026-07-10" : "2026-07-26") },
116
649
  };
117
-
118
650
  expect(projectionPeriodFilter(context as never)).toEqual({
119
- from: "2026-07-10T00:00:00.000Z",
120
- to: "2026-07-26T23:59:59.999Z",
651
+ from: "2026-07-10T03:00:00.000Z",
652
+ to: "2026-07-27T02:59:59.999Z",
653
+ });
654
+ });
655
+ it("uses the IANA Sao Paulo offset for historical date-only filters", () => {
656
+ const context = {
657
+ req: { query: (name: string) => (name === "from" ? "2018-02-16" : "2018-02-16") },
658
+ };
659
+ expect(projectionPeriodFilter(context as never)).toEqual({
660
+ from: "2018-02-16T02:00:00.000Z",
661
+ to: "2018-02-17T01:59:59.999Z",
121
662
  });
122
663
  });
123
-
124
664
  it("dual-reads manifest summaries without losing legacy runs, costs, coverage, or hierarchy", () => {
125
665
  const root = invocationSummary({
126
666
  agent_invocation_id: "AINV-ROOT",
@@ -139,11 +679,9 @@ describe("dashboard backend", () => {
139
679
  });
140
680
  const legacyRoot = { ...manifestRun(root, [root, child]), client_thread_id: "legacy-thread" };
141
681
  const legacyOnly = { ...legacyRoot, id: "RUN-LEGACY" };
142
-
143
682
  const merged = mergeManifestRuns([legacyRoot, legacyOnly], [root, child]);
144
683
  const mergedRoot = merged.find((run) => run.id === "RUN-ROOT");
145
684
  const mergedChild = merged.find((run) => run.id === "RUN-CHILD");
146
-
147
685
  expect(merged).toHaveLength(3);
148
686
  expect(mergedRoot).toMatchObject({
149
687
  client_thread_id: "legacy-thread",
@@ -162,7 +700,50 @@ describe("dashboard backend", () => {
162
700
  parent_run_id: "RUN-ROOT",
163
701
  });
164
702
  });
165
-
703
+ it("keeps lifecycle calendar and accounting authoritative while enriching dimensions", () => {
704
+ const summary = invocationSummary({ run_id: "RUN-CANONICAL" });
705
+ summary.occurred_at = "2026-08-08T03:10:00.000Z";
706
+ const projected = manifestRun(summary, [summary]);
707
+ const legacy = {
708
+ ...projected,
709
+ started_at: "2026-08-07T02:57:00.000Z",
710
+ finished_at: "2026-08-07T03:06:00.000Z",
711
+ provider: null,
712
+ usage: {
713
+ ...projected.usage,
714
+ total: {
715
+ ...projected.usage.total,
716
+ tokens: { ...projected.usage.total.tokens, total: 999 },
717
+ cost: { usd: 1, brl: 9 },
718
+ },
719
+ },
720
+ };
721
+ expect(mergeManifestRuns([legacy], [summary])[0]).toMatchObject({
722
+ started_at: "2026-08-07T02:57:00.000Z",
723
+ finished_at: "2026-08-07T03:06:00.000Z",
724
+ provider: "openai",
725
+ usage: { total: { tokens: { total: 999 }, cost: { brl: 9 } } },
726
+ });
727
+ });
728
+ it("enriches a missing lifecycle token breakdown without replacing known accounting", () => {
729
+ const summary = invocationSummary({ run_id: "RUN-TOKEN-DETAIL" });
730
+ const projected = manifestRun(summary, [summary]);
731
+ const legacy = {
732
+ ...projected,
733
+ usage: {
734
+ ...projected.usage,
735
+ total: {
736
+ ...projected.usage.total,
737
+ tokens: { ...projected.usage.total.tokens, input: null, total: null },
738
+ },
739
+ },
740
+ };
741
+ const merged = mergeManifestRuns([legacy], [summary])[0]!;
742
+ expect(merged.usage.total.tokens).toMatchObject({
743
+ input: projected.usage.total.tokens.input,
744
+ total: projected.usage.total.tokens.total,
745
+ });
746
+ });
166
747
  it("retains bounded lifecycle duration when a manifest summary has no duration", () => {
167
748
  const summary = invocationSummary();
168
749
  summary.time.duration_ms = null;
@@ -182,9 +763,7 @@ describe("dashboard backend", () => {
182
763
  source_finished_at: "2026-07-26T12:00:00.000Z",
183
764
  capture_lag_ms: 12_000,
184
765
  };
185
-
186
766
  const merged = mergeManifestRuns([legacy], [summary]);
187
-
188
767
  expect(merged[0]).toMatchObject({
189
768
  duration_ms: 12_000,
190
769
  source_duration_ms: null,
@@ -193,7 +772,6 @@ describe("dashboard backend", () => {
193
772
  capture_lag_ms: null,
194
773
  });
195
774
  });
196
-
197
775
  it("does not let an unavailable manifest duration erase recovered source timing", () => {
198
776
  const summary = invocationSummary();
199
777
  summary.time.duration_ms = null;
@@ -205,7 +783,6 @@ describe("dashboard backend", () => {
205
783
  source_finished_at: "2026-07-26T12:00:12.000Z",
206
784
  capture_lag_ms: 0,
207
785
  };
208
-
209
786
  expect(mergeManifestRuns([legacy], [summary])[0]).toMatchObject({
210
787
  duration_ms: 12_000,
211
788
  source_duration_ms: 12_000,
@@ -213,15 +790,12 @@ describe("dashboard backend", () => {
213
790
  source_finished_at: "2026-07-26T12:00:12.000Z",
214
791
  });
215
792
  });
216
-
217
793
  it("rejects mutating methods before route dispatch", async () => {
218
794
  const app = createDashboardApp(workspaceRoot, "test-session");
219
795
  const response = await app.request("/api/v2/operations", { method: "POST" });
220
-
221
796
  expect(response.status).toBe(405);
222
797
  expect(response.headers.get("allow")).toBe("GET, HEAD");
223
798
  });
224
-
225
799
  it("selects only entities represented in open work stages for reconciliation", () => {
226
800
  expect(
227
801
  openWorkIds([
@@ -237,7 +811,6 @@ describe("dashboard backend", () => {
237
811
  ]),
238
812
  ).toEqual(["EXEC-RUNNING", "SPEC-ACTIVE", "TASK-PLANNED", "TASK-VERIFYING"]);
239
813
  });
240
-
241
814
  it("separates canonical reconciliation outcomes from blocked and unresolved work", () => {
242
815
  expect(
243
816
  reconciliationResult({
@@ -269,7 +842,7 @@ describe("dashboard backend", () => {
269
842
  unresolved_running_run_ids: ["RUN-ACTIVE"],
270
843
  }),
271
844
  ).toEqual({
272
- reconciled_ids: ["CHG-COMPLETED", "TASK-COMPLETED"],
845
+ reconciled_ids: ["CHG-COMPLETED", "TASK-BLOCKED", "TASK-COMPLETED"],
273
846
  blocked: [
274
847
  {
275
848
  id: "EXEC-ORPHANED",
@@ -284,27 +857,22 @@ describe("dashboard backend", () => {
284
857
  ],
285
858
  });
286
859
  });
287
-
288
860
  it("requires the local session for projections", async () => {
289
861
  const app = createDashboardApp(workspaceRoot, "test-session");
290
862
  const response = await app.request("/api/v2/operations");
291
-
292
863
  expect(response.status).toBe(401);
293
864
  });
294
-
295
865
  it("bootstraps a protected browser session from the dashboard document", async () => {
296
866
  const app = createDashboardApp(workspaceRoot, "test-session");
297
867
  const document = await app.request("/", {
298
868
  headers: { "sec-fetch-site": "none" },
299
869
  });
300
870
  const cookie = document.headers.get("set-cookie");
301
-
302
- expect(cookie).toContain("zaip-dashboard-session=test-session");
871
+ expect(cookie).toContain("zaip-dashboard-sqlite-session=test-session");
303
872
  expect(cookie).toContain("HttpOnly");
304
873
  expect(cookie).toContain("SameSite=Strict");
305
874
  expect(cookie).toContain("Path=/");
306
-
307
- const response = await app.request("/api/v2/operations", {
875
+ const response = await app.request("/api/v2/operations?scope=quick", {
308
876
  headers: {
309
877
  cookie: cookie?.split(";", 1)[0] ?? "",
310
878
  "x-zaip-session": "",
@@ -312,19 +880,16 @@ describe("dashboard backend", () => {
312
880
  });
313
881
  expect(response.status, await response.clone().text()).toBe(200);
314
882
  });
315
-
316
883
  it("does not bootstrap sessions for cross-site requests", async () => {
317
884
  const app = createDashboardApp(workspaceRoot, "test-session");
318
885
  const response = await app.request("/", {
319
886
  headers: { "sec-fetch-site": "cross-site" },
320
887
  });
321
-
322
888
  expect(response.headers.get("set-cookie")).toBeNull();
323
889
  });
324
-
325
890
  it("keeps origin checks and accepts a valid cookie alongside a stale header", async () => {
326
891
  const app = createDashboardApp(workspaceRoot, "test-session");
327
- const cookie = "zaip-dashboard-session=test-session";
892
+ const cookie = "zaip-dashboard-sqlite-session=test-session";
328
893
  const rejectedOrigin = await app.request("/api/v2/pricing", {
329
894
  method: "PUT",
330
895
  headers: { cookie, origin: "https://example.com" },
@@ -335,237 +900,80 @@ describe("dashboard backend", () => {
335
900
  const recoveredSession = await app.request("/api/v2/pricing", {
336
901
  headers: { cookie, "x-zaip-session": "stale-session" },
337
902
  });
338
-
339
903
  expect(rejectedOrigin.status).toBe(403);
340
904
  expect(rejectedHeader.status).toBe(401);
341
905
  expect(recoveredSession.status, await recoveredSession.clone().text()).toBe(200);
342
906
  });
343
-
344
- it("allowlists authenticated project settings updates and rejects invalid config", async () => {
345
- const fixtureRoot = await mkdtemp(join(tmpdir(), "zaip-dashboard-project-settings-"));
346
- const configPath = join(fixtureRoot, ".agents", "config", "project.yaml");
347
- const projectSource = await readFile(
348
- join(workspaceRoot, ".agents", "config", "project.yaml"),
349
- "utf8",
350
- );
351
- await mkdir(join(fixtureRoot, ".agents", "config"), { recursive: true });
352
- await writeFile(configPath, projectSource, "utf8");
353
-
354
- try {
355
- const app = createDashboardApp(fixtureRoot, "test-session");
356
- const unauthorized = await app.request("/api/v2/project-settings", { method: "PUT" });
357
- const settings = await app.request("/api/v2/project-settings", {
358
- headers: { "x-zaip-session": "test-session" },
359
- });
360
- const payload = (await settings.json()) as {
361
- zaip?: { enabled: boolean };
362
- project: { id: string };
363
- stack: { profile: string; overrides: Record<string, unknown> };
364
- tracking: { currency: string };
365
- };
366
- const updated = await app.request("/api/v2/project-settings", {
367
- method: "PUT",
368
- headers: {
369
- "content-type": "application/json",
370
- "x-zaip-session": "test-session",
371
- },
372
- body: JSON.stringify({
373
- ...payload,
374
- zaip: { enabled: false },
375
- stack: {
376
- ...payload.stack,
377
- overrides: {
378
- ...payload.stack.overrides,
379
- languages: {
380
- frontend: ["TypeScript"],
381
- backend: ["TypeScript"],
382
- site: ["Astro"],
383
- },
384
- },
385
- },
386
- }),
387
- });
388
- const invalid = await app.request("/api/v2/project-settings", {
389
- method: "PUT",
390
- headers: {
391
- "content-type": "application/json",
392
- "x-zaip-session": "test-session",
393
- },
394
- body: JSON.stringify({
395
- ...payload,
396
- tracking: { ...payload.tracking, currency: "real" },
397
- }),
398
- });
399
-
400
- expect(unauthorized.status).toBe(401);
401
- expect(updated.status, await updated.clone().text()).toBe(200);
402
- await expect(updated.json()).resolves.toMatchObject({
403
- zaip: { enabled: false },
404
- stack: {
405
- overrides: {
406
- languages: {
407
- frontend: ["TypeScript"],
408
- backend: ["TypeScript"],
409
- site: ["Astro"],
410
- },
411
- },
412
- },
413
- });
414
- expect(invalid.status).toBe(400);
415
- } finally {
416
- await rm(fixtureRoot, { recursive: true, force: true });
417
- }
418
- });
419
-
420
- it("serves and atomically updates the central agent catalog with adapter sync", async () => {
421
- const fixtureRoot = await mkdtemp(join(tmpdir(), "zaip-dashboard-agent-catalog-"));
422
- const catalogSource = await readFile(
423
- join(workspaceRoot, ".agents", "config", "agents", "catalog.yaml"),
424
- "utf8",
425
- );
426
- const projectSource = await readFile(
427
- join(workspaceRoot, ".agents", "config", "project.yaml"),
428
- "utf8",
429
- );
430
- await mkdir(join(fixtureRoot, ".agents", "config", "agents"), { recursive: true });
431
- await writeFile(
432
- join(fixtureRoot, ".agents", "config", "agents", "catalog.yaml"),
433
- catalogSource,
434
- "utf8",
435
- );
436
- await writeFile(join(fixtureRoot, ".agents", "config", "project.yaml"), projectSource, "utf8");
437
- let syncCalls = 0;
438
-
439
- try {
440
- const app = createDashboardApp(fixtureRoot, "test-session", undefined, {
441
- syncAgentAdapters: async () => {
442
- syncCalls += 1;
443
- return { valid: true, drifted: [] };
444
- },
445
- });
446
- const settings = await app.request("/api/v2/agent-catalog", {
447
- headers: { "x-zaip-session": "test-session" },
448
- });
449
- expect(settings.status, await settings.clone().text()).toBe(200);
450
- const payload = (await settings.json()) as {
451
- catalog: {
452
- agents: Array<{
453
- id: string;
454
- model: { preferred: string[] };
455
- persona: Record<string, unknown>;
456
- }>;
457
- };
458
- };
459
- const update = await app.request("/api/v2/agent-catalog", {
460
- method: "PUT",
461
- headers: {
462
- "content-type": "application/json",
463
- "x-zaip-session": "test-session",
464
- },
465
- body: JSON.stringify({
466
- agents: payload.catalog.agents.map((agent) =>
467
- agent.id === "agent-orchestrator"
468
- ? { ...agent, persona: { ...agent.persona, nome: "renata", cargo: "owner" } }
469
- : agent,
470
- ),
471
- }),
472
- });
473
-
474
- expect(update.status, await update.clone().text()).toBe(200);
475
- await expect(update.json()).resolves.toMatchObject({
476
- sync: { required: true, valid: true, drifted: [] },
477
- });
478
- expect(syncCalls).toBe(1);
479
- expect(
480
- await readFile(join(fixtureRoot, ".agents", "config", "agents", "catalog.yaml"), "utf8"),
481
- ).toContain("renata_owner_zaip_agent");
482
- } finally {
483
- await rm(fixtureRoot, { recursive: true, force: true });
484
- }
485
- });
486
-
487
- it("rolls back the agent catalog when adapter synchronization fails", async () => {
488
- const fixtureRoot = await mkdtemp(join(tmpdir(), "zaip-dashboard-agent-rollback-"));
489
- const catalogPath = join(fixtureRoot, ".agents", "config", "agents", "catalog.yaml");
490
- const original = await readFile(
491
- join(workspaceRoot, ".agents", "config", "agents", "catalog.yaml"),
492
- "utf8",
493
- );
494
- const projectSource = await readFile(
495
- join(workspaceRoot, ".agents", "config", "project.yaml"),
496
- "utf8",
497
- );
498
- await mkdir(join(fixtureRoot, ".agents", "config", "agents"), { recursive: true });
499
- await writeFile(catalogPath, original, "utf8");
500
- await writeFile(join(fixtureRoot, ".agents", "config", "project.yaml"), projectSource, "utf8");
501
-
502
- try {
503
- const app = createDashboardApp(fixtureRoot, "test-session", undefined, {
504
- syncAgentAdapters: async () => {
505
- throw new Error("sync unavailable");
506
- },
507
- });
508
- const settings = await app.request("/api/v2/agent-catalog", {
509
- headers: { "x-zaip-session": "test-session" },
510
- });
511
- const payload = (await settings.json()) as {
512
- catalog: {
513
- agents: Array<{
514
- id: string;
515
- model: { preferred: string[] };
516
- persona: Record<string, unknown>;
517
- }>;
518
- };
519
- };
520
- const response = await app.request("/api/v2/agent-catalog", {
521
- method: "PUT",
522
- headers: {
523
- "content-type": "application/json",
524
- "x-zaip-session": "test-session",
525
- },
526
- body: JSON.stringify({
527
- agents: payload.catalog.agents.map((agent) =>
528
- agent.id === "agent-orchestrator"
529
- ? { ...agent, persona: { ...agent.persona, nome: "renata", cargo: "owner" } }
530
- : agent,
531
- ),
532
- }),
533
- });
534
-
535
- expect(response.status).toBe(500);
536
- await expect(response.json()).resolves.toMatchObject({
537
- error: expect.stringContaining("adapter synchronization failed"),
538
- });
539
- await expect(readFile(catalogPath, "utf8")).resolves.toBe(original);
540
- } finally {
541
- await rm(fixtureRoot, { recursive: true, force: true });
542
- }
543
- });
544
-
545
907
  it("does not serve files outside the configured UI root", async () => {
546
908
  const parent = await mkdtemp(join(tmpdir(), "zaip-dashboard-static-boundary-"));
547
909
  const uiRoot = join(parent, "ui");
548
910
  await mkdir(uiRoot);
549
911
  await writeFile(join(uiRoot, "index.html"), "<title>Dashboard</title>", "utf8");
550
912
  await writeFile(join(parent, "secret.txt"), "must-not-be-served", "utf8");
551
-
552
913
  try {
553
914
  const app = createDashboardApp(workspaceRoot, "test-session", uiRoot);
554
915
  const response = await app.request("/..%2Fsecret.txt");
555
-
556
916
  expect(response.status).not.toBe(200);
557
917
  await expect(response.text()).resolves.not.toContain("must-not-be-served");
558
918
  } finally {
559
919
  await rm(parent, { recursive: true, force: true });
560
920
  }
561
921
  });
562
-
922
+ it("proves canonical projection readiness without exposing authenticated data", async () => {
923
+ const app = createDashboardApp(workspaceRoot, "test-session");
924
+ const ready = await app.request("/api/v2/ready");
925
+ const protectedProjection = await app.request("/api/v2/operations?scope=quick");
926
+ expect(ready.status, await ready.clone().text()).toBe(200);
927
+ await expect(ready.json()).resolves.toMatchObject({ status: "ready" });
928
+ expect(protectedProjection.status).toBe(401);
929
+ });
930
+ it("uses the SQLite fact for snapshot readiness without lifecycle daily views", async () => {
931
+ const source = await import("node:fs/promises").then(({ readFile }) =>
932
+ readFile(new URL("./server.ts", import.meta.url), "utf8"),
933
+ );
934
+ const ready = source.slice(
935
+ source.indexOf('app.on(["GET", "HEAD"], `${API_PREFIX}/ready`'),
936
+ source.indexOf('app.on(["GET", "HEAD"], `${API_PREFIX}/operations`'),
937
+ );
938
+ expect(ready).toContain("if (isSqliteSnapshot)");
939
+ expect(ready).not.toContain("firstFoldAllTime()");
940
+ expect(ready).not.toContain("readAggregate(");
941
+ expect(ready).not.toContain("dashboard_sqlite_prewarm");
942
+ expect(ready).not.toContain("firstFoldViewportAllTime()");
943
+ expect(ready).toContain("day_count: metadata.daily_aggregate_count");
944
+ expect(ready).toContain("storage_mode: metadata.storage_mode");
945
+ });
946
+ it("loads daily periods only for the explicit overview/detail projection", async () => {
947
+ const source = await import("node:fs/promises").then(({ readFile }) =>
948
+ readFile(new URL("./server.ts", import.meta.url), "utf8"),
949
+ );
950
+ const projection = source.slice(
951
+ source.indexOf("function buildSqliteOperationsProjection"),
952
+ source.indexOf("function nullableMetric"),
953
+ );
954
+ expect(projection).toContain(
955
+ "options.includePeriods === true ? filesystem.dailyPeriods(filter) : {}",
956
+ );
957
+ expect(source).toContain('includePeriods: scope === "detail" || scope === "overview"');
958
+ });
959
+ it("rebuilds a fresh aggregate after an in-flight normal read instead of sharing its payload", async () => {
960
+ const source = await import("node:fs/promises").then(({ readFile }) =>
961
+ readFile(new URL("./server.ts", import.meta.url), "utf8"),
962
+ );
963
+ const aggregate = source.slice(
964
+ source.indexOf("const readAggregate"),
965
+ source.indexOf("const snapshotKey"),
966
+ );
967
+ expect(aggregate).toContain("if (!fresh) return pending as Promise<T>;");
968
+ expect(aggregate).toContain("await pending;");
969
+ expect(aggregate).toContain("return readAggregate(key, build, true);");
970
+ expect(aggregate).toContain("aggregatePayloads.set(key, { payload, cachedAt: Date.now() });");
971
+ });
563
972
  it("serves the UI root index for the dashboard root and SPA routes", async () => {
564
973
  const parent = await mkdtemp(join(tmpdir(), "zaip-dashboard-static-root-"));
565
974
  const uiRoot = join(parent, "ui");
566
975
  await mkdir(uiRoot);
567
976
  await writeFile(join(uiRoot, "index.html"), "<title>ZAIP Dashboard</title>", "utf8");
568
-
569
977
  try {
570
978
  const app = createDashboardApp(workspaceRoot, "test-session", uiRoot);
571
979
  for (const path of ["/", "/operations/overview"]) {