@zaip-ai/cli 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (410) hide show
  1. package/README.md +6 -4
  2. package/dist/services/adapters.d.ts +1 -1
  3. package/dist/services/adapters.d.ts.map +1 -1
  4. package/dist/services/adapters.js +15 -5
  5. package/dist/services/adapters.js.map +1 -1
  6. package/dist/services/adapters.test.js +1 -1
  7. package/dist/services/agent-adapters.d.ts +9 -0
  8. package/dist/services/agent-adapters.d.ts.map +1 -1
  9. package/dist/services/agent-adapters.js +38 -3
  10. package/dist/services/agent-adapters.js.map +1 -1
  11. package/dist/services/agent-adapters.test.js +8 -1
  12. package/dist/services/agent-adapters.test.js.map +1 -1
  13. package/dist/services/codex-config-template.d.ts +2 -1
  14. package/dist/services/codex-config-template.d.ts.map +1 -1
  15. package/dist/services/codex-config-template.js +13 -2
  16. package/dist/services/codex-config-template.js.map +1 -1
  17. package/dist/services/codex-rules.d.ts +1 -1
  18. package/dist/services/codex-rules.d.ts.map +1 -1
  19. package/dist/services/codex-rules.js +31 -7
  20. package/dist/services/codex-rules.js.map +1 -1
  21. package/dist/services/codex-subagent-hook-template.d.ts +1 -1
  22. package/dist/services/codex-subagent-hook-template.d.ts.map +1 -1
  23. package/dist/services/codex-subagent-hook-template.js +4 -1
  24. package/dist/services/codex-subagent-hook-template.js.map +1 -1
  25. package/dist/services/doctor.d.ts.map +1 -1
  26. package/dist/services/doctor.js +3 -1
  27. package/dist/services/doctor.js.map +1 -1
  28. package/dist/services/global-codex-config.d.ts +6 -3
  29. package/dist/services/global-codex-config.d.ts.map +1 -1
  30. package/dist/services/global-codex-config.js +82 -13
  31. package/dist/services/global-codex-config.js.map +1 -1
  32. package/dist/services/global-codex-config.test.js +57 -10
  33. package/dist/services/global-codex-config.test.js.map +1 -1
  34. package/dist/services/install-agents-template.d.ts.map +1 -1
  35. package/dist/services/install-agents-template.js +15 -2
  36. package/dist/services/install-agents-template.js.map +1 -1
  37. package/dist/services/logger.js +1 -1
  38. package/dist/services/logger.js.map +1 -1
  39. package/dist/services/logger.test.js +3 -3
  40. package/dist/services/logger.test.js.map +1 -1
  41. package/dist/services/runtime-template.test.js +21 -5
  42. package/dist/services/runtime-template.test.js.map +1 -1
  43. package/dist/services/template-manifest.d.ts.map +1 -1
  44. package/dist/services/template-manifest.js +2 -0
  45. package/dist/services/template-manifest.js.map +1 -1
  46. package/package.json +1 -1
  47. package/templates/.agents/config/agents/catalog.yaml +310 -10
  48. package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +30 -0
  49. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +5 -5
  50. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +1 -1
  51. package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +30 -0
  52. package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +30 -0
  53. package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +30 -0
  54. package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +8 -8
  55. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +5 -3
  56. package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +30 -0
  57. package/templates/.agents/config/project.yaml +1 -1
  58. package/templates/.agents/dashboard/.zaip-managed.json +659 -163
  59. package/templates/.agents/dashboard/DATA-DICTIONARY.md +2227 -0
  60. package/templates/.agents/dashboard/README.md +48 -97
  61. package/templates/.agents/dashboard/apps/frontend/dist/assets/CostCalculationGuidePage-Z60df2r-.js +1 -0
  62. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-Cv-JcoxP.js → DashboardHelpDialog-Cs6dzDmW.js} +294 -109
  63. package/templates/.agents/dashboard/apps/frontend/dist/assets/PricingSettingsDialog-Coc6pA-1.js +1 -0
  64. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-QE1k9yZB.css +1 -0
  65. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-_Pf5DA_A.js +3 -0
  66. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BVlz3DLZ.js +1 -0
  67. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-CaKwQP43.css +1 -0
  68. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-BZ5WdaMN.css +1 -0
  69. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Cf9ctPVt.js +68 -0
  70. package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-C98LGC-Q.js → dashboard-localization-CLJ0lT9v.js} +1 -1
  71. package/templates/.agents/dashboard/apps/frontend/dist/assets/icons-vendor-DFBuc_qp.js +1 -0
  72. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-8A4DcNXM.js +2 -0
  73. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DK_enB5Z.css +1 -0
  74. package/templates/.agents/dashboard/apps/frontend/dist/assets/react-vendor-CCQOHdVI.js +9 -0
  75. package/templates/.agents/dashboard/apps/frontend/dist/assets/rolldown-runtime-D9-fqq9M.js +1 -0
  76. package/templates/.agents/dashboard/apps/frontend/dist/assets/traceability-DFY7ZDlu.css +1 -0
  77. package/templates/.agents/dashboard/apps/frontend/dist/assets/traceability-pC2_0J8x.js +1 -0
  78. package/templates/.agents/dashboard/apps/frontend/dist/index.html +9 -2
  79. package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
  80. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +952 -595
  81. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +48 -0
  82. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingPanels.tsx +2 -0
  83. package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +247 -0
  84. package/templates/.agents/dashboard/apps/frontend/src/AppRequestGuards.tsx +95 -0
  85. package/templates/.agents/dashboard/apps/frontend/src/AppSetupRoutes.tsx +87 -0
  86. package/templates/.agents/dashboard/apps/frontend/src/CostCalculationGuidePage.tsx +641 -0
  87. package/templates/.agents/dashboard/apps/frontend/src/DashboardAlternateContent.tsx +159 -0
  88. package/templates/.agents/dashboard/apps/frontend/src/DashboardCapabilityNav.tsx +13 -13
  89. package/templates/.agents/dashboard/apps/frontend/src/DashboardGlobalSettings.tsx +56 -0
  90. package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.tsx +257 -313
  91. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +149 -0
  92. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.test.ts +35 -0
  93. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.tsx +111 -12
  94. package/templates/.agents/dashboard/apps/frontend/src/app-preferences.ts +15 -0
  95. package/templates/.agents/dashboard/apps/frontend/src/app-session-token.ts +20 -0
  96. package/templates/.agents/dashboard/apps/frontend/src/cost-calculation-guide.css +267 -0
  97. package/templates/.agents/dashboard/apps/frontend/src/cost-calculation-guide.test.ts +69 -0
  98. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +70 -0
  99. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +111 -0
  100. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +432 -13
  101. package/templates/.agents/dashboard/apps/frontend/src/dashboard-localization.ts +1 -1
  102. package/templates/.agents/dashboard/apps/frontend/src/main.tsx +1 -0
  103. package/templates/.agents/dashboard/apps/frontend/src/operations-detail-hydration.test.ts +55 -0
  104. package/templates/.agents/dashboard/apps/frontend/src/operations-detail-hydration.ts +36 -0
  105. package/templates/.agents/dashboard/apps/frontend/src/operations-entity-page-projection.test.ts +126 -0
  106. package/templates/.agents/dashboard/apps/frontend/src/operations-entity-page-projection.ts +181 -0
  107. package/templates/.agents/dashboard/apps/frontend/src/operations-period-contract.test.ts +138 -0
  108. package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +105 -0
  109. package/templates/.agents/dashboard/apps/frontend/src/operations-period.test.ts +6 -3
  110. package/templates/.agents/dashboard/apps/frontend/src/operations-period.ts +9 -5
  111. package/templates/.agents/dashboard/apps/frontend/src/overview-status-page.test.ts +24 -0
  112. package/templates/.agents/dashboard/apps/frontend/src/period-transition-overlay.css +44 -0
  113. package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +121 -62
  114. package/templates/.agents/dashboard/apps/frontend/src/project-settings-page.css +471 -2
  115. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +84 -17
  116. package/templates/.agents/dashboard/apps/frontend/src/snapshot-banner-semantics.test.ts +44 -0
  117. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +196 -39
  118. package/templates/.agents/dashboard/apps/frontend/src/snapshot-notice.ts +15 -0
  119. package/templates/.agents/dashboard/apps/frontend/src/sqlite-cost-adapters.ts +26 -0
  120. package/templates/.agents/dashboard/apps/frontend/src/styles.css +14 -2
  121. package/templates/.agents/dashboard/apps/frontend/src/traceability-workspace.css +38 -2
  122. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +187 -0
  123. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +190 -0
  124. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +121 -0
  125. package/templates/.agents/dashboard/apps/frontend/src/use-app-operations-pages.ts +398 -0
  126. package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.ts +141 -0
  127. package/templates/.agents/dashboard/apps/frontend/src/use-idle-cost-prefetch.ts +45 -0
  128. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.test.ts +46 -0
  129. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.ts +37 -0
  130. package/templates/.agents/dashboard/apps/frontend/vite.config.ts +55 -5
  131. package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
  132. package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +47 -6
  133. package/templates/.agents/dashboard/modules/cost-monitor/src/duration-estimate.ts +13 -0
  134. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.test.ts +64 -0
  135. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.tsx +275 -0
  136. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.test.ts +100 -15
  137. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.tsx +131 -40
  138. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.tsx +137 -19
  139. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostModelsAuditTable.test.ts +2 -0
  140. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostModelsAuditTable.tsx +13 -10
  141. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +303 -0
  142. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +226 -6
  143. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +479 -402
  144. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostSummary.tsx +5 -13
  145. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +312 -72
  146. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +373 -321
  147. package/templates/.agents/dashboard/modules/cost-monitor/src/react/MacroCostTable.test.ts +17 -0
  148. package/templates/.agents/dashboard/modules/cost-monitor/src/react/MacroCostTable.tsx +25 -6
  149. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-copy.ts +0 -2
  150. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-page-helpers.ts +17 -0
  151. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-responsive.css +6 -3
  152. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-row-filters.ts +123 -0
  153. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-summary.ts +8 -3
  154. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-views.ts +32 -0
  155. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor.css +52 -14
  156. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-presentation.ts +121 -0
  157. package/templates/.agents/dashboard/modules/cost-monitor/src/react/index.ts +3 -0
  158. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.test.ts +68 -0
  159. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +32 -5
  160. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +108 -0
  161. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.test.ts +12 -0
  162. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +60 -0
  163. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +106 -0
  164. package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.test.ts +2 -2
  165. package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.ts +5 -3
  166. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-pricing-fallback.test.ts +53 -0
  167. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.basics.test.ts +34 -0
  168. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.test.ts +168 -137
  169. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.ts +188 -29
  170. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.test.ts +956 -0
  171. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +775 -0
  172. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-terminal-day.test.ts +95 -0
  173. package/templates/.agents/dashboard/modules/cost-monitor/src/server/index.ts +1 -0
  174. package/templates/.agents/dashboard/modules/cost-monitor/src/server/refactor-map-xr9l.json +61 -0
  175. package/templates/.agents/dashboard/modules/cost-monitor/src/task-cost-groups.test.ts +41 -0
  176. package/templates/.agents/dashboard/modules/cost-monitor/src/task-cost-groups.ts +55 -11
  177. package/templates/.agents/dashboard/modules/operations/package.json +2 -1
  178. package/templates/.agents/dashboard/modules/operations/src/contracts.ts +182 -0
  179. package/templates/.agents/dashboard/modules/operations/src/duration-population.test.ts +63 -1
  180. package/templates/.agents/dashboard/modules/operations/src/duration-population.ts +60 -5
  181. package/templates/.agents/dashboard/modules/operations/src/react/EntityDetailsDialog.tsx +14 -3
  182. package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.test.ts +44 -1
  183. package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +325 -18
  184. package/templates/.agents/dashboard/modules/operations/src/react/MacroPlanningDialog.tsx +1 -0
  185. package/templates/.agents/dashboard/modules/operations/src/react/OperationDateFilters.test.ts +9 -5
  186. package/templates/.agents/dashboard/modules/operations/src/react/OperationDateFilters.tsx +2 -2
  187. package/templates/.agents/dashboard/modules/operations/src/react/OperationsMainContent.tsx +168 -0
  188. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +95 -6
  189. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +365 -14
  190. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +532 -131
  191. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +314 -358
  192. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageNavigation.tsx +68 -0
  193. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +169 -0
  194. package/templates/.agents/dashboard/modules/operations/src/react/PeriodSummaryTable.tsx +51 -8
  195. package/templates/.agents/dashboard/modules/operations/src/react/PrdDocumentDialog.test.tsx +32 -0
  196. package/templates/.agents/dashboard/modules/operations/src/react/PrdDocumentDialog.tsx +62 -6
  197. package/templates/.agents/dashboard/modules/operations/src/react/TaskBoard.test.ts +55 -0
  198. package/templates/.agents/dashboard/modules/operations/src/react/TaskBoard.tsx +27 -19
  199. package/templates/.agents/dashboard/modules/operations/src/react/TaskDetailsDialog.tsx +99 -42
  200. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.priority.test.ts +19 -0
  201. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.tsx +255 -34
  202. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.test.ts +163 -8
  203. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.tsx +101 -68
  204. package/templates/.agents/dashboard/modules/operations/src/react/conversation-rows.test.ts +72 -1
  205. package/templates/.agents/dashboard/modules/operations/src/react/conversation-rows.ts +78 -16
  206. package/templates/.agents/dashboard/modules/operations/src/react/execution-metrics.ts +6 -4
  207. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +50 -1
  208. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +143 -9
  209. package/templates/.agents/dashboard/modules/operations/src/react/export/export-layout.test.ts +29 -4
  210. package/templates/.agents/dashboard/modules/operations/src/react/macro-planning-dialog-content.css +49 -0
  211. package/templates/.agents/dashboard/modules/operations/src/react/macro-planning-dialog.css +93 -50
  212. package/templates/.agents/dashboard/modules/operations/src/react/manual-work-priority.test.ts +82 -0
  213. package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.test.ts +29 -2
  214. package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.ts +22 -1
  215. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.test.ts +77 -4
  216. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.ts +65 -5
  217. package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +54 -0
  218. package/templates/.agents/dashboard/modules/operations/src/react/operations-global-filters.test.ts +19 -0
  219. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +149 -12
  220. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.status-cards.test.ts +27 -0
  221. package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +136 -0
  222. package/templates/.agents/dashboard/modules/operations/src/react/operations-period-scope.test.ts +160 -0
  223. package/templates/.agents/dashboard/modules/operations/src/react/operations-period-scope.ts +47 -0
  224. package/templates/.agents/dashboard/modules/operations/src/react/operations-query.ts +10 -0
  225. package/templates/.agents/dashboard/modules/operations/src/react/operations-responsive.css +3 -0
  226. package/templates/.agents/dashboard/modules/operations/src/react/operations-view-labels.ts +28 -0
  227. package/templates/.agents/dashboard/modules/operations/src/react/operations-view-order.test.ts +24 -7
  228. package/templates/.agents/dashboard/modules/operations/src/react/operations.css +42 -0
  229. package/templates/.agents/dashboard/modules/operations/src/react/period-summary-population.test.ts +54 -0
  230. package/templates/.agents/dashboard/modules/operations/src/react/period-summary-population.ts +32 -0
  231. package/templates/.agents/dashboard/modules/operations/src/react/period-summary.test.ts +165 -6
  232. package/templates/.agents/dashboard/modules/operations/src/react/period-summary.ts +148 -6
  233. package/templates/.agents/dashboard/modules/operations/src/react/popup-localization.test.ts +1 -1
  234. package/templates/.agents/dashboard/modules/operations/src/react/unified-work-board.css +132 -0
  235. package/templates/.agents/dashboard/modules/operations/src/react/unified-work-board.css.test.ts +13 -0
  236. package/templates/.agents/dashboard/modules/operations/src/react/unified-work-board.header.test.ts +16 -0
  237. package/templates/.agents/dashboard/modules/operations/src/react/use-infinite-column.ts +11 -5
  238. package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.test.ts +5 -3
  239. package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.ts +4 -83
  240. package/templates/.agents/dashboard/modules/operations/src/server/build-operations-projection.test.ts +291 -15
  241. package/templates/.agents/dashboard/modules/operations/src/server/build-operations-projection.ts +182 -472
  242. package/templates/.agents/dashboard/modules/operations/src/server/canonical-compat.test.ts +40 -0
  243. package/templates/.agents/dashboard/modules/operations/src/server/canonical-compat.ts +19 -6
  244. package/templates/.agents/dashboard/modules/operations/src/server/canonical-run-projection.ts +31 -6
  245. package/templates/.agents/dashboard/modules/operations/src/server/daily-duration-projection.ts +220 -0
  246. package/templates/.agents/dashboard/modules/operations/src/server/execution-duration-projection.ts +28 -0
  247. package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.test.ts +27 -2
  248. package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.ts +22 -13
  249. package/templates/.agents/dashboard/modules/operations/src/server/filter.test.ts +3 -3
  250. package/templates/.agents/dashboard/modules/operations/src/server/filter.ts +7 -2
  251. package/templates/.agents/dashboard/modules/operations/src/server/index.ts +3 -0
  252. package/templates/.agents/dashboard/modules/operations/src/server/macro.ts +3 -12
  253. package/templates/.agents/dashboard/modules/operations/src/server/manifest-usage.ts +65 -0
  254. package/templates/.agents/dashboard/modules/operations/src/server/operations-projection-aggregates.ts +297 -0
  255. package/templates/.agents/dashboard/modules/operations/src/server/quick-daily-operations-projection.ts +324 -0
  256. package/templates/.agents/dashboard/modules/operations/src/server/quick-operations-projection.test.ts +505 -0
  257. package/templates/.agents/dashboard/modules/operations/src/server/quick-operations-projection.ts +609 -0
  258. package/templates/.agents/dashboard/modules/operations/src/server/runs.ts +18 -19
  259. package/templates/.agents/dashboard/modules/operations/src/work-board-model.ts +76 -0
  260. package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
  261. package/templates/.agents/dashboard/modules/traceability/src/server/build-task-trace.ts +3 -1
  262. package/templates/.agents/dashboard/packages/branding/package.json +1 -1
  263. package/templates/.agents/dashboard/packages/branding/src/index.ts +1 -1
  264. package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
  265. package/templates/.agents/dashboard/packages/contracts/src/index.ts +1 -0
  266. package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
  267. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +1756 -0
  268. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +1608 -0
  269. package/templates/.agents/dashboard/packages/local-runtime/package.json +8 -3
  270. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-dashboard.mjs +12 -0
  271. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +83 -0
  272. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +28 -0
  273. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-read-model.mjs +262 -0
  274. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-unix-launchers.mjs +41 -0
  275. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +105 -0
  276. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +1643 -107
  277. package/templates/.agents/dashboard/packages/local-runtime/src/agent-adapter-sync.ts +30 -0
  278. package/templates/.agents/dashboard/packages/local-runtime/src/agent-catalog-settings.test.ts +3 -2
  279. package/templates/.agents/dashboard/packages/local-runtime/src/agent-catalog-settings.ts +5 -0
  280. package/templates/.agents/dashboard/packages/local-runtime/src/manifest-runs.ts +72 -33
  281. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +335 -4
  282. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +392 -1
  283. package/templates/.agents/dashboard/packages/local-runtime/src/runtime.mjs +5 -5
  284. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.test.ts +71 -0
  285. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.ts +179 -0
  286. package/templates/.agents/dashboard/packages/local-runtime/src/server-cross-layer-parity.test.ts +245 -0
  287. package/templates/.agents/dashboard/packages/local-runtime/src/server-daily-read-models-route.ts +64 -0
  288. package/templates/.agents/dashboard/packages/local-runtime/src/server-daily-read-models.test.ts +204 -0
  289. package/templates/.agents/dashboard/packages/local-runtime/src/server-daily-read-models.ts +194 -0
  290. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-pages.test.ts +163 -0
  291. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-pages.ts +354 -0
  292. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-runs-route.test.ts +74 -0
  293. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-runs-route.ts +113 -0
  294. package/templates/.agents/dashboard/packages/local-runtime/src/server-overview.test.ts +68 -0
  295. package/templates/.agents/dashboard/packages/local-runtime/src/server-period.ts +47 -0
  296. package/templates/.agents/dashboard/packages/local-runtime/src/server-projections.ts +483 -0
  297. package/templates/.agents/dashboard/packages/local-runtime/src/server-settings.test.ts +5 -2
  298. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +163 -84
  299. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +145 -0
  300. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +551 -266
  301. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +1145 -308
  302. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-metric-contract.test.ts +53 -0
  303. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-metric-contract.ts +708 -0
  304. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +2232 -0
  305. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +6151 -0
  306. package/templates/.agents/dashboard/packages/local-runtime/src/work-priority-contract.test.ts +33 -0
  307. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-history.mjs +41 -0
  308. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-processes.mjs +210 -0
  309. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +437 -190
  310. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +299 -24
  311. package/templates/.agents/dashboard/packages/ui/package.json +1 -1
  312. package/templates/.agents/dashboard/packages/windows-launcher/LauncherVerificationBehaviorTests.cs +206 -0
  313. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +95 -8
  314. package/templates/.agents/dashboard/packages/windows-launcher/build-dashboard-launcher.ps1 +2 -2
  315. package/templates/.agents/dashboard/packages/windows-launcher/run-launcher-verification-tests.ps1 +22 -0
  316. package/templates/.agents/dashboard/read-model.dbml +2201 -0
  317. package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
  318. package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +1 -1
  319. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +214 -41
  320. package/templates/.agents/dashboard/work-priority.json +34 -0
  321. package/templates/.agents/dashboard/zaip-dashboard-linux.sh +45 -0
  322. package/templates/.agents/dashboard/zaip-dashboard-mac.command +3 -0
  323. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  324. package/templates/.agents/design/.zaip-managed.json +3 -3
  325. package/templates/.agents/design/apps/catalog/package.json +1 -1
  326. package/templates/.agents/design/packages/local-runtime/package.json +1 -1
  327. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  328. package/templates/.agents/requirements/comparative-audit.md +1 -1
  329. package/templates/.agents/requirements/context-engine.md +1 -1
  330. package/templates/.agents/requirements/cost-observability.md +1 -1
  331. package/templates/.agents/requirements/dashboard-launch.md +24 -0
  332. package/templates/.agents/requirements/data-export.md +1 -1
  333. package/templates/.agents/requirements/design-system.md +1 -1
  334. package/templates/.agents/requirements/execution-engine.md +1 -1
  335. package/templates/.agents/requirements/kanban-dashboard.md +4 -3
  336. package/templates/.agents/requirements/model-orchestration.md +1 -1
  337. package/templates/.agents/requirements/public-distribution.md +9 -6
  338. package/templates/.agents/requirements/software-architecture.md +1 -1
  339. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  340. package/templates/.agents/requirements/work-lifecycle.md +9 -3
  341. package/templates/.agents/requirements/zaip-strategic-95.md +4 -4
  342. package/templates/.agents/rules/autonomy-decision.md +15 -0
  343. package/templates/.agents/rules/core.md +1 -1
  344. package/templates/.agents/rules/run-lifecycle-closure.md +1 -1
  345. package/templates/.agents/rules/specialist-routing.md +19 -0
  346. package/templates/.agents/rules/workflow.md +1 -1
  347. package/templates/.agents/runtime/VERSION +1 -1
  348. package/templates/.agents/runtime/bin/zaip.mjs +114 -64
  349. package/templates/.agents/runtime/checksums.sha256 +6 -4
  350. package/templates/.agents/runtime/manifest.json +3 -1
  351. package/templates/.agents/runtime/pnpm-lock.sha256 +1 -0
  352. package/templates/.agents/runtime/provenance.json +11 -0
  353. package/templates/.agents/runtime/sbom.cdx.json +1929 -39
  354. package/templates/.agents/skills/zaip-aios/SKILL.md +1 -1
  355. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +15 -0
  356. package/templates/.agents/skills/zaip-skill-brand-governance/agents/openai.yaml +4 -0
  357. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +30 -0
  358. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/SKILL.md +25 -0
  359. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/agents/openai.yaml +4 -0
  360. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/SKILL.md +18 -0
  361. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/agents/openai.yaml +6 -0
  362. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-fence.mjs +71 -0
  363. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-fence.test.mjs +60 -0
  364. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/scheduled-ready-queue-contract.test.mjs +111 -0
  365. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +2 -2
  366. package/templates/.agents/skills/zaip-skill-frontend-launcher/references/launcher-contract.md +3 -1
  367. package/templates/.agents/skills/zaip-skill-general-operations/SKILL.md +12 -0
  368. package/templates/.agents/skills/zaip-skill-general-operations/agents/openai.yaml +4 -0
  369. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +9 -7
  370. package/templates/.agents/skills/zaip-skill-github-deploy/agents/openai.yaml +2 -2
  371. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs +11 -38
  372. package/templates/.agents/skills/zaip-skill-governance-compliance/SKILL.md +12 -0
  373. package/templates/.agents/skills/zaip-skill-governance-compliance/agents/openai.yaml +4 -0
  374. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +15 -0
  375. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/agents/openai.yaml +4 -0
  376. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/references/growth-revops-framework.md +25 -0
  377. package/templates/.agents/skills/zaip-skill-instalar-agendamento/SKILL.md +17 -0
  378. package/templates/.agents/skills/zaip-skill-instalar-agendamento/agents/openai.yaml +6 -0
  379. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +3 -3
  380. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +76 -27
  381. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +128 -24
  382. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +122 -4
  383. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.mjs +66 -19
  384. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +193 -7
  385. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +14 -0
  386. package/templates/.agents/skills/zaip-skill-sales-methodology/agents/openai.yaml +4 -0
  387. package/templates/.agents/skills/zaip-skill-sales-methodology/references/sales-process-framework.md +77 -0
  388. package/templates/.agents/skills/zaip-skill-setup/SKILL.md +5 -0
  389. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +96 -0
  390. package/templates/.agents/skills/zaip-skill-sqlite-read-model/agents/openai.yaml +4 -0
  391. package/templates/.agents/skills/zaip-skill-sqlite-read-model/scripts/ensure-node.mjs +18 -0
  392. package/templates/.agents/zaip-aios/INDEX.md +1 -0
  393. package/templates/.agents/zaip-aios/README.md +37 -8
  394. package/templates/.agents/zaip-aios/VERSION +1 -1
  395. package/templates/.agents/zaip-aios/guides/09-agentes-e-squads.md +43 -6
  396. package/templates/.agents/zaip-aios/guides/10-seguranca.md +21 -7
  397. package/templates/.agents/zaip-aios/guides/17-como-o-custo-e-calculado.md +32 -0
  398. package/templates/template-manifest.json +945 -214
  399. package/templates/.agents/dashboard/apps/frontend/dist/assets/AgentCatalogSettingsPage-CLYIi7Vo.js +0 -1
  400. package/templates/.agents/dashboard/apps/frontend/dist/assets/AgentCatalogSettingsPage-DTDNVQGX.css +0 -1
  401. package/templates/.agents/dashboard/apps/frontend/dist/assets/PricingSettingsDialog-CzEggcRb.js +0 -1
  402. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-05YKn6Z4.js +0 -3
  403. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-YO3yqtgV.css +0 -1
  404. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-8AzCnl3a.js +0 -77
  405. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DdBYQ_8D.css +0 -1
  406. package/templates/.agents/dashboard/apps/frontend/src/AgentCatalogSettingsPage.tsx +0 -617
  407. package/templates/.agents/dashboard/apps/frontend/src/agent-catalog-settings-page.css +0 -660
  408. package/templates/.agents/dashboard/apps/frontend/src/agent-catalog-settings.test.ts +0 -249
  409. package/templates/.agents/dashboard/dashboard.config.json +0 -4
  410. package/templates/.agents/dashboard/zaip-dashboard.exe +0 -0
@@ -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,
@@ -13,10 +14,219 @@ import {
13
14
  reconciliationResult,
14
15
  } from "./server.js";
15
16
  import { invocationSummary } from "./server.test-fixtures.js";
17
+ import { classificationPage } from "./server-board.js";
18
+ import {
19
+ compactRunProjection,
20
+ mergeKnownManifestRuns,
21
+ mergeLifecycleRunPage,
22
+ runTimestampInRange,
23
+ } from "./server-projections.js";
16
24
 
17
25
  const workspaceRoot = resolve(import.meta.dirname, "../../../../..");
18
-
19
26
  describe("dashboard backend", () => {
27
+ it.each([
28
+ ["task", "lite", "TASK-LITE"],
29
+ ["task", "standard", "TASK-STANDARD"],
30
+ ["task", "critical", "TASK-CRITICAL"],
31
+ ["spec", "empty", "SPEC-EMPTY"],
32
+ ["spec", "structured", "SPEC-STRUCTURED"],
33
+ ["spec", "rework", "SPEC-REWORK"],
34
+ ["prd", "empty", "PRD-EMPTY"],
35
+ ["prd", "structured", "PRD-STRUCTURED"],
36
+ ["prd", "rework", "PRD-REWORK"],
37
+ ] as const)("returns only the canonical %s %s classification page", (kind, classification, expectedId) => {
38
+ const projection = {
39
+ tasks: [
40
+ { id: "TASK-LITE", risk: "lite", created_at: "2026-08-21T00:00:00.000Z", updated_at: null },
41
+ {
42
+ id: "TASK-STANDARD",
43
+ risk: "standard",
44
+ created_at: "2026-08-21T00:00:00.000Z",
45
+ updated_at: null,
46
+ },
47
+ {
48
+ id: "TASK-CRITICAL",
49
+ risk: "critical",
50
+ created_at: "2026-08-21T00:00:00.000Z",
51
+ updated_at: null,
52
+ },
53
+ ],
54
+ specs: [
55
+ {
56
+ id: "SPEC-EMPTY",
57
+ task_count: 0,
58
+ rework_run_count: 0,
59
+ created_at: "2026-08-21T00:00:00.000Z",
60
+ updated_at: null,
61
+ },
62
+ {
63
+ id: "SPEC-STRUCTURED",
64
+ task_count: 1,
65
+ rework_run_count: 0,
66
+ created_at: "2026-08-21T00:00:00.000Z",
67
+ updated_at: null,
68
+ },
69
+ {
70
+ id: "SPEC-REWORK",
71
+ task_count: 1,
72
+ rework_run_count: 1,
73
+ created_at: "2026-08-21T00:00:00.000Z",
74
+ updated_at: null,
75
+ },
76
+ ],
77
+ prds: [
78
+ {
79
+ id: "PRD-EMPTY",
80
+ task_count: 0,
81
+ rework_run_count: 0,
82
+ created_at: "2026-08-21T00:00:00.000Z",
83
+ updated_at: null,
84
+ },
85
+ {
86
+ id: "PRD-STRUCTURED",
87
+ task_count: 1,
88
+ rework_run_count: 0,
89
+ created_at: "2026-08-21T00:00:00.000Z",
90
+ updated_at: null,
91
+ },
92
+ {
93
+ id: "PRD-REWORK",
94
+ task_count: 1,
95
+ rework_run_count: 1,
96
+ created_at: "2026-08-21T00:00:00.000Z",
97
+ updated_at: null,
98
+ },
99
+ ],
100
+ changes: [],
101
+ } as unknown as Parameters<typeof classificationPage>[0];
102
+
103
+ const page = classificationPage(projection, kind, classification, 0, 1);
104
+ const items = kind === "task" ? page.tasks : kind === "spec" ? page.specs : page.prds;
105
+
106
+ expect(page).toMatchObject({ offset: 0, total: 1 });
107
+ expect(items.map((item) => item.id)).toEqual([expectedId]);
108
+ expect(kind === "task" ? page.specs : page.tasks).toEqual([]);
109
+ expect(kind === "prd" ? page.specs : page.prds).toEqual([]);
110
+ });
111
+
112
+ it("keeps Runs on the final all-time summary day inside the page-first range", () => {
113
+ expect(runTimestampInRange("2026-08-07T23:59:59.000Z", "2026-08-01", "2026-08-07")).toBe(true);
114
+ expect(runTimestampInRange("2026-08-08T00:00:00.000Z", "2026-08-01", "2026-08-07")).toBe(false);
115
+ });
116
+ it("keeps a lifecycle Run with no InvocationSummary as a null-telemetry page row", () => {
117
+ const run = compactRunProjection({
118
+ id: "RUN-NO-SUMMARY",
119
+ started_at: "2026-08-07T02:59:59.999Z",
120
+ finished_at: null,
121
+ task_id: null,
122
+ execution_id: null,
123
+ conversation_id: null,
124
+ agent: null,
125
+ agent_name: null,
126
+ provider: null,
127
+ model: null,
128
+ spec_id: null,
129
+ prd_id: null,
130
+ change_id: null,
131
+ phase: "execution",
132
+ parent_run_id: null,
133
+ status: "completed",
134
+ duration_ms: null,
135
+ total_tokens: null,
136
+ cost: null,
137
+ currency: null,
138
+ events: null,
139
+ });
140
+ expect(run).toMatchObject({
141
+ id: "RUN-NO-SUMMARY",
142
+ duration_ms: null,
143
+ source_duration_ms: null,
144
+ });
145
+ expect(run.usage.total.tokens.total).toBeNull();
146
+ expect(run.usage.total.cost.brl).toBeNull();
147
+ });
148
+ it("projects factual compact Run dimensions without replacing absent data", () => {
149
+ const run = compactRunProjection({
150
+ id: "RUN-FACTUAL-DIMENSIONS",
151
+ started_at: "2026-08-08T03:00:00.000Z",
152
+ finished_at: null,
153
+ task_id: "TASK-1",
154
+ execution_id: "EXEC-1",
155
+ conversation_id: "CONV-1",
156
+ agent: "agent-builder",
157
+ agent_name: "Builder",
158
+ provider: "openai",
159
+ model: "gpt-5.6",
160
+ spec_id: "SPEC-1",
161
+ prd_id: "PRD-1",
162
+ change_id: "CHG-1",
163
+ phase: "execution",
164
+ parent_run_id: null,
165
+ status: "completed",
166
+ run_kind: "run",
167
+ duration_ms: 12_000,
168
+ total_tokens: 42,
169
+ cost: 4.2,
170
+ currency: "BRL",
171
+ events: 1,
172
+ });
173
+ expect(run).toMatchObject({
174
+ agent: "agent-builder",
175
+ agent_name: "Builder",
176
+ provider: "openai",
177
+ model: "gpt-5.6",
178
+ effective_model: "gpt-5.6",
179
+ execution_id: "EXEC-1",
180
+ conversation_id: "CONV-1",
181
+ task_id: "TASK-1",
182
+ spec_id: "SPEC-1",
183
+ prd_id: "PRD-1",
184
+ change_id: "CHG-1",
185
+ });
186
+ });
187
+ it("left-joins telemetry without changing lifecycle page IDs or length", () => {
188
+ const page = ["RUN-PAGE-1", "RUN-PAGE-2"].map((id) =>
189
+ compactRunProjection({
190
+ id,
191
+ started_at: "2026-08-07T03:00:00.000Z",
192
+ finished_at: null,
193
+ task_id: null,
194
+ execution_id: null,
195
+ conversation_id: null,
196
+ agent: null,
197
+ agent_name: null,
198
+ provider: null,
199
+ model: null,
200
+ spec_id: null,
201
+ prd_id: null,
202
+ change_id: null,
203
+ phase: "execution",
204
+ parent_run_id: null,
205
+ status: "completed",
206
+ duration_ms: null,
207
+ total_tokens: null,
208
+ cost: null,
209
+ currency: null,
210
+ events: null,
211
+ }),
212
+ );
213
+ const result = mergeLifecycleRunPage(page, [
214
+ invocationSummary({ run_id: "RUN-PAGE-1" }),
215
+ invocationSummary({ run_id: "RUN-NOT-ON-PAGE" }),
216
+ ]);
217
+ expect(result).toHaveLength(2);
218
+ expect(result.map((run) => run.id)).toEqual(["RUN-PAGE-1", "RUN-PAGE-2"]);
219
+ expect(result[1]?.usage.total.tokens.total).toBeNull();
220
+ });
221
+ it("does not reintroduce a Run excluded by the canonical lifecycle period", () => {
222
+ const retained = invocationSummary({ run_id: "RUN-IN-PERIOD" });
223
+ const excluded = invocationSummary({ run_id: "RUN-OUTSIDE-PERIOD" });
224
+ expect(
225
+ mergeKnownManifestRuns([manifestRun(retained, [retained])], [retained, excluded]).map(
226
+ (run) => run.id,
227
+ ),
228
+ ).toEqual(["RUN-IN-PERIOD"]);
229
+ });
20
230
  it("normalizes GitHub HTTPS and SSH remotes for display", () => {
21
231
  expect(githubRepositoryFromRemote("git@github.com:zaip-ai/zaip-aios.git")).toBe(
22
232
  "zaip-ai/zaip-aios",
@@ -28,14 +238,12 @@ describe("dashboard backend", () => {
28
238
  expect(githubRepositoryFromRemote("")).toBeNull();
29
239
  expect(githubRepositoryFromRemote("https://gitlab.com/zaip-ai/zaip-aios.git")).toBeNull();
30
240
  });
31
-
32
241
  it("exposes a modern read-only health endpoint", async () => {
33
242
  const app = createDashboardApp(workspaceRoot, "test-session");
34
243
  const response = await app.request("/api/v2/health");
35
-
36
244
  expect(response.status).toBe(200);
37
245
  await expect(response.json()).resolves.toMatchObject({
38
- kind: "zaip-dashboard-local-runtime",
246
+ kind: "zaip-dashboard-sqlite-runtime",
39
247
  workspaceKey: dashboardWorkspaceKey(workspaceRoot),
40
248
  api_version: 2,
41
249
  read_only: true,
@@ -43,36 +251,242 @@ describe("dashboard backend", () => {
43
251
  workspace_layout: 3,
44
252
  });
45
253
  });
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", {
254
+ it("permits canonical open-work reconciliation while serving a read-only SQLite snapshot", async () => {
255
+ const reconcile = vi.fn(async () => ({ reconciled_ids: ["TASK-RECONCILED"], blocked: [], not_reconciled: [] }));
256
+ const refresh = vi.fn(async () => undefined);
257
+ const canonicalFilesystem = new WorkspaceFileSystem(workspaceRoot);
258
+ const app = createDashboardApp(workspaceRoot, "test-session", undefined, {
259
+ snapshotReadOnly: true,
260
+ canonicalFilesystem,
261
+ reconcileOpenWork: reconcile,
262
+ refreshReadModel: refresh,
263
+ });
264
+ const response = await app.request("/api/v2/reconcile-open-work", {
265
+ method: "POST",
50
266
  headers: { "x-zaip-session": "test-session" },
51
267
  });
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");
268
+ expect(response.status, await response.clone().text()).toBe(200);
269
+ await expect(response.json()).resolves.toMatchObject({
270
+ reconciliation: { reconciled_ids: ["TASK-RECONCILED"] },
271
+ });
272
+ expect(reconcile).toHaveBeenCalledOnce();
273
+ expect(reconcile).toHaveBeenCalledWith(canonicalFilesystem, workspaceRoot);
274
+ expect(refresh).toHaveBeenCalledOnce();
275
+ const rejected = await app.request("/api/v2/reconcile-open-work", { method: "POST" });
276
+ expect(rejected.status).toBe(401);
277
+ }, 130_000);
278
+ it("persists Kanban priority canonically while a SQLite snapshot blocks other settings writes", async () => {
279
+ const canonicalRoot = await mkdtemp(join(tmpdir(), "zaip-dashboard-priority-"));
280
+ try {
281
+ const canonicalFilesystem = new WorkspaceFileSystem(canonicalRoot);
282
+ const originalReadText = canonicalFilesystem.readText.bind(canonicalFilesystem);
283
+ const priorityReads = vi.fn(async (path: string) => originalReadText(path));
284
+ vi.spyOn(canonicalFilesystem, "readText").mockImplementation(priorityReads);
285
+ const app = createDashboardApp(workspaceRoot, "test-session", undefined, {
286
+ snapshotReadOnly: true,
287
+ canonicalFilesystem,
288
+ });
289
+ const before = await app.request(
290
+ "/api/v2/operations?scope=board&stage=completed&from=2000-01-01&to=2099-12-31&offset=0&limit=10",
291
+ { headers: { "x-zaip-session": "test-session" } },
292
+ );
293
+ expect(before.status, await before.clone().text()).toBe(200);
294
+ const beforeBoard = (await before.json()) as {
295
+ prds: Array<{ id: string }>;
296
+ specs: Array<{ id: string }>;
297
+ tasks: Array<{ id: string }>;
298
+ };
299
+ expect(beforeBoard.tasks.length).toBeGreaterThan(1);
300
+ const concurrent = await Promise.all([
301
+ app.request("/api/v2/operations?scope=board&stage=completed&from=2000-01-01&to=2099-12-31&offset=0&limit=10", { headers: { "x-zaip-session": "test-session" } }),
302
+ app.request("/api/v2/operations?scope=board&stage=blocked&from=2000-01-01&to=2099-12-31&offset=0&limit=10", { headers: { "x-zaip-session": "test-session" } }),
303
+ ]);
304
+ expect(concurrent.every((response) => response.status === 200)).toBe(true);
305
+ expect(priorityReads.mock.calls.filter(([path]) => path === ".agents/dashboard/work-priority.json")).toHaveLength(1);
306
+ const priority = { item_ids: [beforeBoard.tasks.at(-1)!.id], updated_at: null };
307
+ const persisted = await app.request("/api/v2/work-priority", {
308
+ method: "PUT",
309
+ headers: { "content-type": "application/json", "x-zaip-session": "test-session" },
310
+ body: JSON.stringify(priority),
311
+ });
312
+ expect(persisted.status, await persisted.clone().text()).toBe(200);
313
+ await expect(persisted.json()).resolves.toMatchObject({
314
+ schema_version: 1,
315
+ item_ids: priority.item_ids,
316
+ });
317
+ await expect(
318
+ canonicalFilesystem.readText(".agents/dashboard/work-priority.json"),
319
+ ).resolves.toContain(priority.item_ids[0]!);
320
+ const readsAfterPut = priorityReads.mock.calls.filter(
321
+ ([path]) => path === ".agents/dashboard/work-priority.json",
322
+ ).length;
323
+ const after = await app.request(
324
+ "/api/v2/operations?scope=board&stage=completed&from=2000-01-01&to=2099-12-31&offset=0&limit=10",
325
+ { headers: { "x-zaip-session": "test-session" } },
326
+ );
327
+ expect(after.status, await after.clone().text()).toBe(200);
328
+ const afterBoard = (await after.json()) as typeof beforeBoard;
329
+ expect(afterBoard.tasks[0]?.id).toBe(priority.item_ids[0]);
330
+ expect(priorityReads.mock.calls.filter(([path]) => path === ".agents/dashboard/work-priority.json").length).toBe(readsAfterPut + 1);
331
+ const blocked = await app.request("/api/v2/pricing", {
332
+ method: "PUT",
333
+ headers: { "content-type": "application/json", "x-zaip-session": "test-session" },
334
+ body: JSON.stringify({}),
335
+ });
336
+ expect(blocked.status).toBe(405);
337
+ } finally {
338
+ await rm(canonicalRoot, { recursive: true, force: true });
64
339
  }
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
- }),
340
+ });
341
+ it("serves a cursor-sized Kanban stage from entity-only read models", async () => {
342
+ const app = createDashboardApp(workspaceRoot, "test-session");
343
+ const response = await app.request(
344
+ "/api/v2/operations?scope=board&stage=completed&offset=0&limit=10",
345
+ { headers: { "x-zaip-session": "test-session" } },
346
+ );
347
+ expect(response.status).toBe(200);
348
+ const payload = (await response.json()) as {
349
+ prds: unknown[];
350
+ specs: unknown[];
351
+ tasks: unknown[];
352
+ };
353
+ expect(payload).toMatchObject({
354
+ stage: "completed",
355
+ offset: 0,
356
+ total: expect.any(Number),
357
+ prds: expect.any(Array),
358
+ specs: expect.any(Array),
359
+ tasks: expect.any(Array),
360
+ changes: expect.any(Array),
361
+ });
362
+ expect(payload.prds.length + payload.specs.length + payload.tasks.length).toBeLessThanOrEqual(
363
+ 10,
73
364
  );
74
365
  });
75
-
366
+ it("applies Kanban filters before pagination without requesting aggregate telemetry", async () => {
367
+ const app = createDashboardApp(workspaceRoot, "test-session");
368
+ const response = await app.request(
369
+ "/api/v2/operations?scope=board&stage=blocked&offset=0&limit=10&risk=critical",
370
+ { headers: { "x-zaip-session": "test-session" } },
371
+ );
372
+ expect(response.status).toBe(200);
373
+ const payload = (await response.json()) as {
374
+ total: number;
375
+ prds: unknown[];
376
+ specs: unknown[];
377
+ tasks: Array<{ risk: string }>;
378
+ };
379
+ expect(payload.prds).toEqual([]);
380
+ expect(payload.specs).toEqual([]);
381
+ expect(payload.tasks.every((task) => task.risk === "critical")).toBe(true);
382
+ expect(payload.tasks.length).toBeLessThanOrEqual(10);
383
+ expect(payload.total).toBeGreaterThanOrEqual(payload.tasks.length);
384
+ });
385
+ it("serves a canonical cross-stage classification page by work kind", async () => {
386
+ const app = createDashboardApp(workspaceRoot, "test-session");
387
+ const response = await app.request(
388
+ "/api/v2/operations?scope=classification&kind=task&classification=critical&offset=0&limit=10",
389
+ { headers: { "x-zaip-session": "test-session" } },
390
+ );
391
+ expect(response.status).toBe(200);
392
+ const payload = (await response.json()) as {
393
+ offset: number;
394
+ total: number;
395
+ tasks: Array<{ risk: string }>;
396
+ specs: unknown[];
397
+ prds: unknown[];
398
+ };
399
+ expect(payload).toMatchObject({ offset: 0, total: expect.any(Number), specs: [], prds: [] });
400
+ expect(payload.tasks.every((task) => task.risk === "critical")).toBe(true);
401
+ expect(payload.tasks.length).toBeLessThanOrEqual(10);
402
+ });
403
+ it("classifies SPEC pages from their canonical task and rework facts", async () => {
404
+ const app = createDashboardApp(workspaceRoot, "test-session");
405
+ const response = await app.request(
406
+ "/api/v2/operations?scope=classification&kind=spec&classification=empty&offset=0&limit=10",
407
+ { headers: { "x-zaip-session": "test-session" } },
408
+ );
409
+ expect(response.status).toBe(200);
410
+ const payload = (await response.json()) as {
411
+ tasks: unknown[];
412
+ prds: unknown[];
413
+ specs: Array<{ task_count: number; rework_run_count: number }>;
414
+ };
415
+ expect(payload.tasks).toEqual([]);
416
+ expect(payload.prds).toEqual([]);
417
+ expect(payload.specs.every((spec) => spec.task_count === 0 && spec.rework_run_count === 0)).toBe(true);
418
+ });
419
+ it("serves a compact cost summary and paginated Run metrics from the manifest", async () => {
420
+ const app = createDashboardApp(workspaceRoot, "test-session");
421
+ const headers = { "x-zaip-session": "test-session" };
422
+ const costs = await app.request("/api/v2/cost-monitor?currency=BRL&scope=summary", { headers });
423
+ expect(costs.status).toBe(200);
424
+ await expect(costs.json()).resolves.toMatchObject({
425
+ aggregate_only: true,
426
+ data_source: "lifecycle_manifest_summary",
427
+ runs: [],
428
+ groups: { daily: expect.any(Array), monthly: expect.any(Array), annual: expect.any(Array) },
429
+ });
430
+ const runs = await app.request("/api/v2/operations/runs?offset=0&limit=2", { headers });
431
+ expect(runs.status).toBe(200);
432
+ await expect(runs.json()).resolves.toMatchObject({
433
+ data_source: "lifecycle_manifest_summary",
434
+ offset: 0,
435
+ limit: 2,
436
+ rows: expect.any(Array),
437
+ });
438
+ });
439
+ it("serves a Cost Run transport page without materializing aggregate totals", async () => {
440
+ const app = createDashboardApp(workspaceRoot, "test-session");
441
+ const headers = { "x-zaip-session": "test-session" };
442
+ const page = await app.request(
443
+ "/api/v2/cost-monitor?currency=BRL&scope=runs&offset=0&limit=1",
444
+ { headers },
445
+ );
446
+ expect(page.status).toBe(200);
447
+ const pagePayload = (await page.json()) as {
448
+ overview: { total_tokens: number | null };
449
+ runs: unknown[];
450
+ page_only: boolean;
451
+ run_page: { total: number | null };
452
+ };
453
+ expect(pagePayload.runs.length).toBeLessThanOrEqual(1);
454
+ expect(pagePayload.page_only).toBe(true);
455
+ expect(pagePayload.run_page.total).toBeGreaterThan(0);
456
+ expect(pagePayload.overview.total_tokens).toBeNull();
457
+ });
458
+ it("filters a Cost Run transport page by eligible provider model and phase dimensions", async () => {
459
+ const app = createDashboardApp(workspaceRoot, "test-session");
460
+ const headers = { "x-zaip-session": "test-session" };
461
+ const query = new URLSearchParams({
462
+ currency: "BRL",
463
+ scope: "runs",
464
+ limit: "1",
465
+ phase: "execution",
466
+ });
467
+ const response = await app.request(`/api/v2/cost-monitor?${query.toString()}`, { headers });
468
+ expect(response.status).toBe(200);
469
+ const page = (await response.json()) as {
470
+ runs: Array<{ provider: string | null; model: string; phase: string }>;
471
+ run_page: { total: number | null };
472
+ };
473
+ expect(page.run_page.total).toBeGreaterThan(0);
474
+ expect(page.runs.length).toBeLessThanOrEqual(1);
475
+ expect(page.runs.every((run) => run.phase === "execution")).toBe(true);
476
+ });
477
+ it("serves operation detail without building the cost snapshot", async () => {
478
+ const app = createDashboardApp(workspaceRoot, "test-session");
479
+ const response = await app.request("/api/v2/operations?scope=detail", {
480
+ headers: { "x-zaip-session": "test-session" },
481
+ });
482
+ expect(response.status).toBe(200);
483
+ expect(response.headers.get("X-ZAIP-Snapshot-Cache")).toBeNull();
484
+ await expect(response.json()).resolves.toMatchObject({
485
+ source: ".agents/records",
486
+ tasks: expect.any(Array),
487
+ runs: expect.any(Array),
488
+ });
489
+ });
76
490
  it("serves only allowlisted canonical agent portraits", async () => {
77
491
  const app = createDashboardApp(workspaceRoot, "test-session");
78
492
  const agentIds = [
@@ -103,24 +517,29 @@ describe("dashboard backend", () => {
103
517
  expect(portrait.headers.get("content-type"), agentId).toBe("image/png");
104
518
  expect((await portrait.arrayBuffer()).byteLength, agentId).toBeGreaterThan(1_000);
105
519
  }
106
-
107
520
  const traversal = await app.request(
108
521
  "/api/v2/agent-catalog-assets/%2E%2E%2Fproject?token=test-session",
109
522
  );
110
523
  expect(traversal.status).toBe(404);
111
524
  });
112
-
113
525
  it("treats date-only dashboard filters as whole calendar days", () => {
114
526
  const context = {
115
527
  req: { query: (name: string) => (name === "from" ? "2026-07-10" : "2026-07-26") },
116
528
  };
117
-
118
529
  expect(projectionPeriodFilter(context as never)).toEqual({
119
- from: "2026-07-10T00:00:00.000Z",
120
- to: "2026-07-26T23:59:59.999Z",
530
+ from: "2026-07-10T03:00:00.000Z",
531
+ to: "2026-07-27T02:59:59.999Z",
532
+ });
533
+ });
534
+ it("uses the IANA Sao Paulo offset for historical date-only filters", () => {
535
+ const context = {
536
+ req: { query: (name: string) => (name === "from" ? "2018-02-16" : "2018-02-16") },
537
+ };
538
+ expect(projectionPeriodFilter(context as never)).toEqual({
539
+ from: "2018-02-16T02:00:00.000Z",
540
+ to: "2018-02-17T01:59:59.999Z",
121
541
  });
122
542
  });
123
-
124
543
  it("dual-reads manifest summaries without losing legacy runs, costs, coverage, or hierarchy", () => {
125
544
  const root = invocationSummary({
126
545
  agent_invocation_id: "AINV-ROOT",
@@ -139,11 +558,9 @@ describe("dashboard backend", () => {
139
558
  });
140
559
  const legacyRoot = { ...manifestRun(root, [root, child]), client_thread_id: "legacy-thread" };
141
560
  const legacyOnly = { ...legacyRoot, id: "RUN-LEGACY" };
142
-
143
561
  const merged = mergeManifestRuns([legacyRoot, legacyOnly], [root, child]);
144
562
  const mergedRoot = merged.find((run) => run.id === "RUN-ROOT");
145
563
  const mergedChild = merged.find((run) => run.id === "RUN-CHILD");
146
-
147
564
  expect(merged).toHaveLength(3);
148
565
  expect(mergedRoot).toMatchObject({
149
566
  client_thread_id: "legacy-thread",
@@ -162,7 +579,50 @@ describe("dashboard backend", () => {
162
579
  parent_run_id: "RUN-ROOT",
163
580
  });
164
581
  });
165
-
582
+ it("keeps lifecycle calendar and accounting authoritative while enriching dimensions", () => {
583
+ const summary = invocationSummary({ run_id: "RUN-CANONICAL" });
584
+ summary.occurred_at = "2026-08-08T03:10:00.000Z";
585
+ const projected = manifestRun(summary, [summary]);
586
+ const legacy = {
587
+ ...projected,
588
+ started_at: "2026-08-07T02:57:00.000Z",
589
+ finished_at: "2026-08-07T03:06:00.000Z",
590
+ provider: null,
591
+ usage: {
592
+ ...projected.usage,
593
+ total: {
594
+ ...projected.usage.total,
595
+ tokens: { ...projected.usage.total.tokens, total: 999 },
596
+ cost: { usd: 1, brl: 9 },
597
+ },
598
+ },
599
+ };
600
+ expect(mergeManifestRuns([legacy], [summary])[0]).toMatchObject({
601
+ started_at: "2026-08-07T02:57:00.000Z",
602
+ finished_at: "2026-08-07T03:06:00.000Z",
603
+ provider: "openai",
604
+ usage: { total: { tokens: { total: 999 }, cost: { brl: 9 } } },
605
+ });
606
+ });
607
+ it("enriches a missing lifecycle token breakdown without replacing known accounting", () => {
608
+ const summary = invocationSummary({ run_id: "RUN-TOKEN-DETAIL" });
609
+ const projected = manifestRun(summary, [summary]);
610
+ const legacy = {
611
+ ...projected,
612
+ usage: {
613
+ ...projected.usage,
614
+ total: {
615
+ ...projected.usage.total,
616
+ tokens: { ...projected.usage.total.tokens, input: null, total: null },
617
+ },
618
+ },
619
+ };
620
+ const merged = mergeManifestRuns([legacy], [summary])[0]!;
621
+ expect(merged.usage.total.tokens).toMatchObject({
622
+ input: projected.usage.total.tokens.input,
623
+ total: projected.usage.total.tokens.total,
624
+ });
625
+ });
166
626
  it("retains bounded lifecycle duration when a manifest summary has no duration", () => {
167
627
  const summary = invocationSummary();
168
628
  summary.time.duration_ms = null;
@@ -182,9 +642,7 @@ describe("dashboard backend", () => {
182
642
  source_finished_at: "2026-07-26T12:00:00.000Z",
183
643
  capture_lag_ms: 12_000,
184
644
  };
185
-
186
645
  const merged = mergeManifestRuns([legacy], [summary]);
187
-
188
646
  expect(merged[0]).toMatchObject({
189
647
  duration_ms: 12_000,
190
648
  source_duration_ms: null,
@@ -193,7 +651,6 @@ describe("dashboard backend", () => {
193
651
  capture_lag_ms: null,
194
652
  });
195
653
  });
196
-
197
654
  it("does not let an unavailable manifest duration erase recovered source timing", () => {
198
655
  const summary = invocationSummary();
199
656
  summary.time.duration_ms = null;
@@ -205,7 +662,6 @@ describe("dashboard backend", () => {
205
662
  source_finished_at: "2026-07-26T12:00:12.000Z",
206
663
  capture_lag_ms: 0,
207
664
  };
208
-
209
665
  expect(mergeManifestRuns([legacy], [summary])[0]).toMatchObject({
210
666
  duration_ms: 12_000,
211
667
  source_duration_ms: 12_000,
@@ -213,15 +669,12 @@ describe("dashboard backend", () => {
213
669
  source_finished_at: "2026-07-26T12:00:12.000Z",
214
670
  });
215
671
  });
216
-
217
672
  it("rejects mutating methods before route dispatch", async () => {
218
673
  const app = createDashboardApp(workspaceRoot, "test-session");
219
674
  const response = await app.request("/api/v2/operations", { method: "POST" });
220
-
221
675
  expect(response.status).toBe(405);
222
676
  expect(response.headers.get("allow")).toBe("GET, HEAD");
223
677
  });
224
-
225
678
  it("selects only entities represented in open work stages for reconciliation", () => {
226
679
  expect(
227
680
  openWorkIds([
@@ -237,7 +690,6 @@ describe("dashboard backend", () => {
237
690
  ]),
238
691
  ).toEqual(["EXEC-RUNNING", "SPEC-ACTIVE", "TASK-PLANNED", "TASK-VERIFYING"]);
239
692
  });
240
-
241
693
  it("separates canonical reconciliation outcomes from blocked and unresolved work", () => {
242
694
  expect(
243
695
  reconciliationResult({
@@ -284,27 +736,22 @@ describe("dashboard backend", () => {
284
736
  ],
285
737
  });
286
738
  });
287
-
288
739
  it("requires the local session for projections", async () => {
289
740
  const app = createDashboardApp(workspaceRoot, "test-session");
290
741
  const response = await app.request("/api/v2/operations");
291
-
292
742
  expect(response.status).toBe(401);
293
743
  });
294
-
295
744
  it("bootstraps a protected browser session from the dashboard document", async () => {
296
745
  const app = createDashboardApp(workspaceRoot, "test-session");
297
746
  const document = await app.request("/", {
298
747
  headers: { "sec-fetch-site": "none" },
299
748
  });
300
749
  const cookie = document.headers.get("set-cookie");
301
-
302
- expect(cookie).toContain("zaip-dashboard-session=test-session");
750
+ expect(cookie).toContain("zaip-dashboard-sqlite-session=test-session");
303
751
  expect(cookie).toContain("HttpOnly");
304
752
  expect(cookie).toContain("SameSite=Strict");
305
753
  expect(cookie).toContain("Path=/");
306
-
307
- const response = await app.request("/api/v2/operations", {
754
+ const response = await app.request("/api/v2/operations?scope=quick", {
308
755
  headers: {
309
756
  cookie: cookie?.split(";", 1)[0] ?? "",
310
757
  "x-zaip-session": "",
@@ -312,19 +759,16 @@ describe("dashboard backend", () => {
312
759
  });
313
760
  expect(response.status, await response.clone().text()).toBe(200);
314
761
  });
315
-
316
762
  it("does not bootstrap sessions for cross-site requests", async () => {
317
763
  const app = createDashboardApp(workspaceRoot, "test-session");
318
764
  const response = await app.request("/", {
319
765
  headers: { "sec-fetch-site": "cross-site" },
320
766
  });
321
-
322
767
  expect(response.headers.get("set-cookie")).toBeNull();
323
768
  });
324
-
325
769
  it("keeps origin checks and accepts a valid cookie alongside a stale header", async () => {
326
770
  const app = createDashboardApp(workspaceRoot, "test-session");
327
- const cookie = "zaip-dashboard-session=test-session";
771
+ const cookie = "zaip-dashboard-sqlite-session=test-session";
328
772
  const rejectedOrigin = await app.request("/api/v2/pricing", {
329
773
  method: "PUT",
330
774
  headers: { cookie, origin: "https://example.com" },
@@ -335,237 +779,78 @@ describe("dashboard backend", () => {
335
779
  const recoveredSession = await app.request("/api/v2/pricing", {
336
780
  headers: { cookie, "x-zaip-session": "stale-session" },
337
781
  });
338
-
339
782
  expect(rejectedOrigin.status).toBe(403);
340
783
  expect(rejectedHeader.status).toBe(401);
341
784
  expect(recoveredSession.status, await recoveredSession.clone().text()).toBe(200);
342
785
  });
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
786
  it("does not serve files outside the configured UI root", async () => {
546
787
  const parent = await mkdtemp(join(tmpdir(), "zaip-dashboard-static-boundary-"));
547
788
  const uiRoot = join(parent, "ui");
548
789
  await mkdir(uiRoot);
549
790
  await writeFile(join(uiRoot, "index.html"), "<title>Dashboard</title>", "utf8");
550
791
  await writeFile(join(parent, "secret.txt"), "must-not-be-served", "utf8");
551
-
552
792
  try {
553
793
  const app = createDashboardApp(workspaceRoot, "test-session", uiRoot);
554
794
  const response = await app.request("/..%2Fsecret.txt");
555
-
556
795
  expect(response.status).not.toBe(200);
557
796
  await expect(response.text()).resolves.not.toContain("must-not-be-served");
558
797
  } finally {
559
798
  await rm(parent, { recursive: true, force: true });
560
799
  }
561
800
  });
562
-
801
+ it("proves canonical projection readiness without exposing authenticated data", async () => {
802
+ const app = createDashboardApp(workspaceRoot, "test-session");
803
+ const ready = await app.request("/api/v2/ready");
804
+ const protectedProjection = await app.request("/api/v2/operations?scope=quick");
805
+ expect(ready.status, await ready.clone().text()).toBe(200);
806
+ await expect(ready.json()).resolves.toMatchObject({ status: "ready" });
807
+ expect(protectedProjection.status).toBe(401);
808
+ });
809
+ it("uses the SQLite fact for snapshot readiness without lifecycle daily views", async () => {
810
+ const source = await import("node:fs/promises").then(({ readFile }) =>
811
+ readFile(new URL("./server.ts", import.meta.url), "utf8"),
812
+ );
813
+ const ready = source.slice(
814
+ source.indexOf('app.on(["GET", "HEAD"], `${API_PREFIX}/ready`'),
815
+ source.indexOf('app.on(["GET", "HEAD"], `${API_PREFIX}/operations`'),
816
+ );
817
+ expect(ready).toContain("if (isSqliteSnapshot)");
818
+ expect(ready).not.toContain("firstFoldAllTime()");
819
+ expect(ready).not.toContain("readAggregate(");
820
+ expect(ready).not.toContain("dashboard_sqlite_prewarm");
821
+ expect(ready).not.toContain("firstFoldViewportAllTime()");
822
+ expect(ready).toContain("day_count: metadata.daily_aggregate_count");
823
+ expect(ready).toContain("storage_mode: metadata.storage_mode");
824
+ });
825
+ it("loads daily periods only for the explicit overview/detail projection", async () => {
826
+ const source = await import("node:fs/promises").then(({ readFile }) =>
827
+ readFile(new URL("./server.ts", import.meta.url), "utf8"),
828
+ );
829
+ const projection = source.slice(
830
+ source.indexOf("function buildSqliteOperationsProjection"),
831
+ source.indexOf("function nullableMetric"),
832
+ );
833
+ expect(projection).toContain("options.includePeriods === true ? filesystem.dailyPeriods(filter) : {}");
834
+ expect(source).toContain("includePeriods: scope === \"detail\" || scope === \"overview\"");
835
+ });
836
+ it("rebuilds a fresh aggregate after an in-flight normal read instead of sharing its payload", async () => {
837
+ const source = await import("node:fs/promises").then(({ readFile }) =>
838
+ readFile(new URL("./server.ts", import.meta.url), "utf8"),
839
+ );
840
+ const aggregate = source.slice(
841
+ source.indexOf("const readAggregate"),
842
+ source.indexOf("const snapshotKey"),
843
+ );
844
+ expect(aggregate).toContain("if (!fresh) return pending as Promise<T>;");
845
+ expect(aggregate).toContain("await pending;");
846
+ expect(aggregate).toContain("return readAggregate(key, build, true);");
847
+ expect(aggregate).toContain("aggregatePayloads.set(key, { payload, cachedAt: Date.now() });");
848
+ });
563
849
  it("serves the UI root index for the dashboard root and SPA routes", async () => {
564
850
  const parent = await mkdtemp(join(tmpdir(), "zaip-dashboard-static-root-"));
565
851
  const uiRoot = join(parent, "ui");
566
852
  await mkdir(uiRoot);
567
853
  await writeFile(join(uiRoot, "index.html"), "<title>ZAIP Dashboard</title>", "utf8");
568
-
569
854
  try {
570
855
  const app = createDashboardApp(workspaceRoot, "test-session", uiRoot);
571
856
  for (const path of ["/", "/operations/overview"]) {