@zaip-ai/cli 1.0.8 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/README.md +1 -1
  2. package/dist/services/adapters.d.ts +1 -1
  3. package/dist/services/adapters.js +1 -1
  4. package/dist/services/agent-adapters.js +3 -3
  5. package/dist/services/agent-adapters.js.map +1 -1
  6. package/dist/services/agent-adapters.test.js +7 -1
  7. package/dist/services/agent-adapters.test.js.map +1 -1
  8. package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
  9. package/dist/services/codex-pre-tool-hook-template.js +89 -7
  10. package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
  11. package/dist/services/codex-rules.d.ts +1 -1
  12. package/dist/services/codex-rules.d.ts.map +1 -1
  13. package/dist/services/codex-rules.js +7 -0
  14. package/dist/services/codex-rules.js.map +1 -1
  15. package/dist/services/install-agents-template.d.ts.map +1 -1
  16. package/dist/services/install-agents-template.js +1 -0
  17. package/dist/services/install-agents-template.js.map +1 -1
  18. package/dist/services/logger.test.js +2 -2
  19. package/dist/services/runtime-template.test.js +31 -7
  20. package/dist/services/runtime-template.test.js.map +1 -1
  21. package/dist/services/template-manifest.d.ts.map +1 -1
  22. package/dist/services/template-manifest.js +12 -3
  23. package/dist/services/template-manifest.js.map +1 -1
  24. package/package.json +1 -1
  25. package/templates/.agents/architecture/software-architecture.md +131 -0
  26. package/templates/.agents/branding/brandbook.docx +0 -0
  27. package/templates/.agents/branding/brandbook.md +60 -0
  28. package/templates/.agents/config/agents/catalog.yaml +7 -7
  29. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +2 -2
  30. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +2 -2
  31. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +1 -1
  32. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +8 -8
  33. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -1
  34. package/templates/.agents/dashboard/.tmp-run-list.json +254 -0
  35. package/templates/.agents/dashboard/.zaip-managed.json +457 -173
  36. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-Z60df2r-.js → CostCalculationGuidePage-ovxurkIk.js} +1 -1
  37. package/templates/.agents/dashboard/apps/frontend/dist/assets/DashboardHelpDialog-BgVgrfhQ.js +2310 -0
  38. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-Coc6pA-1.js → PricingSettingsDialog-vSvvOHQp.js} +1 -1
  39. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BomxqCBU.css +1 -0
  40. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-zH758vfs.js +3 -0
  41. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-DLn-Mz2M.js +1 -0
  42. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CaKwQP43.css → TraceabilityWorkspace-DvvavDLY.css} +1 -1
  43. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DKwP1stV.js +68 -0
  44. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-PoJA2PI1.css +1 -0
  45. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DFBuc_qp.js → icons-vendor-B_lDDHn1.js} +1 -1
  46. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-B31GMG5O.css +1 -0
  47. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-CZQlsLV9.js +2 -0
  48. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-CCQOHdVI.js → react-vendor-B3TJrD4O.js} +1 -1
  49. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-pC2_0J8x.js → traceability-7f1j5wx3.js} +1 -1
  50. package/templates/.agents/dashboard/apps/frontend/dist/index.html +7 -7
  51. package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
  52. package/templates/.agents/dashboard/apps/frontend/playwright.dom.config.ts +32 -0
  53. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +199 -140
  54. package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +0 -21
  55. package/templates/.agents/dashboard/apps/frontend/src/DashboardAlternateContent.tsx +20 -11
  56. package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.tsx +14 -7
  57. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +4 -2
  58. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.dom.e2e.ts +56 -0
  59. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.test.ts +21 -2
  60. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.tsx +89 -19
  61. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +19 -4
  62. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +49 -3
  63. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +29 -10
  64. package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +4 -4
  65. package/templates/.agents/dashboard/apps/frontend/src/overview-status-page.test.ts +4 -2
  66. package/templates/.agents/dashboard/apps/frontend/src/project-settings-page.css +36 -26
  67. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +2 -2
  68. package/templates/.agents/dashboard/apps/frontend/src/snapshot-banner-semantics.test.ts +47 -6
  69. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +59 -9
  70. package/templates/.agents/dashboard/apps/frontend/src/sqlite-page-export.test.ts +32 -0
  71. package/templates/.agents/dashboard/apps/frontend/src/sqlite-page-export.ts +21 -0
  72. package/templates/.agents/dashboard/apps/frontend/src/styles.css +19 -0
  73. package/templates/.agents/dashboard/apps/frontend/src/traceability-task-pagination.test.ts +32 -0
  74. package/templates/.agents/dashboard/apps/frontend/src/traceability-task-pagination.ts +36 -0
  75. package/templates/.agents/dashboard/apps/frontend/src/traceability-workspace.css +6 -2
  76. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +137 -23
  77. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +229 -35
  78. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +61 -7
  79. package/templates/.agents/dashboard/apps/frontend/src/use-app-operations-pages.ts +3 -6
  80. package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.ts +7 -1
  81. package/templates/.agents/dashboard/apps/frontend/src/use-app-work-list-page.test.ts +163 -0
  82. package/templates/.agents/dashboard/apps/frontend/src/use-app-work-list-page.ts +136 -0
  83. package/templates/.agents/dashboard/apps/frontend/src/use-operations-detail-export.ts +76 -0
  84. package/templates/.agents/dashboard/apps/frontend/src/work-list-load-dedup.test.ts +23 -0
  85. package/templates/.agents/dashboard/apps/frontend/src/work-list-load-dedup.ts +27 -0
  86. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.test.ts +39 -15
  87. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.ts +3 -4
  88. package/templates/.agents/dashboard/apps/frontend/vite.config.ts +4 -1
  89. package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
  90. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.test.ts +89 -19
  91. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.tsx +31 -16
  92. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.test.ts +43 -14
  93. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.tsx +5 -5
  94. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionPanel.tsx +60 -0
  95. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionTable.tsx +191 -0
  96. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.loading.test.ts +14 -0
  97. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.run-page.test.ts +18 -0
  98. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.tsx +18 -3
  99. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostModelsAuditTable.tsx +1 -1
  100. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +75 -44
  101. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorLoadErrors.tsx +35 -0
  102. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +162 -40
  103. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +221 -79
  104. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableLoading.test.ts +132 -0
  105. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableLoading.tsx +70 -0
  106. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +203 -155
  107. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +164 -125
  108. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-copy.ts +2 -0
  109. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-views.ts +3 -1
  110. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor.css +5 -2
  111. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-page-range.ts +35 -0
  112. package/templates/.agents/dashboard/modules/cost-monitor/src/react/index.ts +2 -2
  113. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +2 -1
  114. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +32 -9
  115. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.test.ts +37 -2
  116. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +131 -9
  117. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +34 -17
  118. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.test.ts +0 -24
  119. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.test.ts +225 -424
  120. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +24 -15
  121. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-terminal-day.test.ts +6 -1
  122. package/templates/.agents/dashboard/modules/cost-monitor/src/server/source-size-cost-server/build-cost-monitor-projection-agents.test.ts +31 -0
  123. package/templates/.agents/dashboard/modules/cost-monitor/src/server/source-size-cost-server/build-cost-summary-projection-duration.test.ts +414 -0
  124. package/templates/.agents/dashboard/modules/operations/package.json +1 -1
  125. package/templates/.agents/dashboard/modules/operations/src/contracts.ts +46 -26
  126. package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.test.ts +54 -0
  127. package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.tsx +247 -0
  128. package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +31 -11
  129. package/templates/.agents/dashboard/modules/operations/src/react/OperationsMainContent.tsx +39 -13
  130. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +107 -2
  131. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.macro-metrics.test.ts +55 -0
  132. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +23 -29
  133. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +211 -184
  134. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewTopPanel.tsx +127 -0
  135. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +60 -51
  136. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageNavigation.tsx +1 -4
  137. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +3 -0
  138. package/templates/.agents/dashboard/modules/operations/src/react/PagedWorkList.test.tsx +43 -0
  139. package/templates/.agents/dashboard/modules/operations/src/react/PagedWorkList.tsx +181 -0
  140. package/templates/.agents/dashboard/modules/operations/src/react/PeriodSummaryTable.tsx +32 -10
  141. package/templates/.agents/dashboard/modules/operations/src/react/TaskDetailsDialog.tsx +9 -3
  142. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.tsx +13 -5
  143. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.test.ts +16 -6
  144. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.tsx +14 -4
  145. package/templates/.agents/dashboard/modules/operations/src/react/conversation-rows.ts +1 -2
  146. package/templates/.agents/dashboard/modules/operations/src/react/developer-identities.test.ts +18 -0
  147. package/templates/.agents/dashboard/modules/operations/src/react/developer-identities.ts +30 -0
  148. package/templates/.agents/dashboard/modules/operations/src/react/execution-metrics.ts +1 -0
  149. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +393 -4
  150. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +484 -61
  151. package/templates/.agents/dashboard/modules/operations/src/react/export/export-layout.test.ts +23 -1
  152. package/templates/.agents/dashboard/modules/operations/src/react/export/xlsx-runtime-e2e.test.ts +248 -0
  153. package/templates/.agents/dashboard/modules/operations/src/react/index.ts +1 -0
  154. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.test.ts +4 -4
  155. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.ts +6 -4
  156. package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +4 -3
  157. package/templates/.agents/dashboard/modules/operations/src/react/operations-date-filters.css +1 -1
  158. package/templates/.agents/dashboard/modules/operations/src/react/operations-global-filters.test.ts +4 -1
  159. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +22 -12
  160. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.css +4 -0
  161. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.status-cards.test.ts +1 -1
  162. package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +53 -6
  163. package/templates/.agents/dashboard/modules/operations/src/react/operations-view-order.test.ts +2 -4
  164. package/templates/.agents/dashboard/modules/operations/src/react/period-summary.test.ts +82 -6
  165. package/templates/.agents/dashboard/modules/operations/src/react/period-summary.ts +1 -0
  166. package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.test.ts +2 -1
  167. package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.ts +1 -1
  168. package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.test.ts +19 -7
  169. package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.ts +4 -1
  170. package/templates/.agents/dashboard/modules/operations/src/server/metric-coverage.ts +1 -0
  171. package/templates/.agents/dashboard/modules/operations/src/server/quick-daily-operations-projection.ts +11 -5
  172. package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
  173. package/templates/.agents/dashboard/packages/branding/package.json +1 -1
  174. package/templates/.agents/dashboard/packages/branding/src/index.ts +1 -1
  175. package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
  176. package/templates/.agents/dashboard/packages/contracts/src/index.ts +1 -0
  177. package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
  178. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +533 -338
  179. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +506 -311
  180. package/templates/.agents/dashboard/packages/local-runtime/gitignore.template +2 -0
  181. package/templates/.agents/dashboard/packages/local-runtime/package.json +1 -1
  182. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-dashboard.mjs +15 -7
  183. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +44 -13
  184. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +8 -3
  185. package/templates/.agents/dashboard/packages/local-runtime/scripts/source-size-runtime-scripts/sqlite-read-model-fixture.mjs +397 -0
  186. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-read-model.mjs +472 -145
  187. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-unix-launchers.mjs +15 -3
  188. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +141 -33
  189. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +533 -338
  190. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +6 -2
  191. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +3 -3
  192. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.test.ts +89 -16
  193. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.ts +33 -16
  194. package/templates/.agents/dashboard/packages/local-runtime/src/server-classification-page.test.ts +87 -0
  195. package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.test.ts +44 -0
  196. package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.ts +125 -0
  197. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +66 -0
  198. package/templates/.agents/dashboard/packages/local-runtime/src/server-request-authority.test.ts +61 -0
  199. package/templates/.agents/dashboard/packages/local-runtime/src/server-request-authority.ts +18 -0
  200. package/templates/.agents/dashboard/packages/local-runtime/src/server-run-projections.test.ts +42 -0
  201. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +97 -0
  202. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +22 -5
  203. package/templates/.agents/dashboard/packages/local-runtime/src/server-sqlite-task-trace.ts +91 -0
  204. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +7 -3
  205. package/templates/.agents/dashboard/packages/local-runtime/src/server.test-fixtures.ts +36 -0
  206. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +230 -132
  207. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +224 -182
  208. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-artifact-counts.ts +28 -0
  209. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +63 -0
  210. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +109 -0
  211. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +497 -0
  212. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-fast-page-parity.test-support.ts +65 -0
  213. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-page.test.ts +176 -0
  214. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-page.ts +119 -0
  215. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +41 -0
  216. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.test.ts +16 -0
  217. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.ts +29 -0
  218. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-cache.ts +355 -0
  219. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +121 -0
  220. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-timing.ts +27 -0
  221. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-metric-contract.ts +16 -2
  222. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +15 -0
  223. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-work-list.test.ts +223 -0
  224. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-work-list.ts +304 -0
  225. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +23 -0
  226. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-first-fold-types.ts +89 -0
  227. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +227 -0
  228. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +691 -0
  229. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +81 -0
  230. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +1655 -567
  231. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +2168 -1268
  232. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +129 -0
  233. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-install.test.ts +26 -0
  234. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-install.ts +28 -0
  235. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-seed.test.ts +116 -0
  236. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +146 -0
  237. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.test.mjs +165 -0
  238. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +140 -0
  239. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +255 -289
  240. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +94 -88
  241. package/templates/.agents/dashboard/packages/ui/package.json +1 -1
  242. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +5 -2
  243. package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
  244. package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +4 -1
  245. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-loading.spec.ts +142 -0
  246. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-pagination.spec.ts +232 -0
  247. package/templates/.agents/dashboard/tests/e2e/specs/dashboard-performance-matrix.spec.ts +743 -0
  248. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +189 -214
  249. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  250. package/templates/.agents/design/.zaip-managed.json +3 -3
  251. package/templates/.agents/design/apps/catalog/package.json +1 -1
  252. package/templates/.agents/design/packages/local-runtime/package.json +1 -1
  253. package/templates/.agents/growth/estrategia-bootstrap-90-dias.md +169 -0
  254. package/templates/.agents/growth/strategy.md +27 -0
  255. package/templates/.agents/marketing/estrategia-marketing-zaip-90-dias.md +197 -0
  256. package/templates/.agents/marketing/strategy.md +29 -0
  257. package/templates/.agents/requirements/autonomous-assurance.md +6 -6
  258. package/templates/.agents/requirements/comparative-audit.md +1 -1
  259. package/templates/.agents/requirements/context-engine.md +1 -1
  260. package/templates/.agents/requirements/cost-observability.md +1 -1
  261. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  262. package/templates/.agents/requirements/data-export.md +1 -1
  263. package/templates/.agents/requirements/design-system.md +1 -1
  264. package/templates/.agents/requirements/execution-engine.md +3 -3
  265. package/templates/.agents/requirements/kanban-dashboard.md +10 -2
  266. package/templates/.agents/requirements/model-orchestration.md +1 -1
  267. package/templates/.agents/requirements/public-distribution.md +1 -1
  268. package/templates/.agents/requirements/software-architecture.md +1 -1
  269. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  270. package/templates/.agents/requirements/work-lifecycle.md +4 -4
  271. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  272. package/templates/.agents/rules/dashboard-ui-consistency.md +14 -0
  273. package/templates/.agents/rules/frontend-lite.md +4 -4
  274. package/templates/.agents/rules/run-lifecycle-closure.md +4 -4
  275. package/templates/.agents/runtime/VERSION +1 -1
  276. package/templates/.agents/runtime/bin/zaip.mjs +1142 -1041
  277. package/templates/.agents/runtime/checksums.sha256 +6 -6
  278. package/templates/.agents/runtime/manifest.json +1 -1
  279. package/templates/.agents/runtime/pnpm-lock.sha256 +1 -1
  280. package/templates/.agents/runtime/provenance.json +5 -5
  281. package/templates/.agents/runtime/sbom.cdx.json +3 -3
  282. package/templates/.agents/sales/playbook.md +32 -0
  283. package/templates/.agents/sales/roteiro-comercial-zaip.md +252 -0
  284. package/templates/.agents/skills/criar-pasta-obsidian/scripts/generate-obsidian-vault.test.mjs +20 -16
  285. package/templates/.agents/skills/zaip-aios/SKILL.md +1 -1
  286. package/templates/.agents/skills/zaip-skill-agent-catalog/SKILL.md +22 -0
  287. package/templates/.agents/skills/zaip-skill-agent-catalog/agents/openai.yaml +4 -0
  288. package/templates/.agents/skills/zaip-skill-author-skills/SKILL.md +3 -2
  289. package/templates/.agents/skills/zaip-skill-author-skills/scripts/audit-skill-catalog.mjs +49 -0
  290. package/templates/.agents/skills/zaip-skill-author-skills/scripts/author-skill-scripts.test.mjs +27 -0
  291. package/templates/.agents/skills/zaip-skill-author-skills/scripts/run-skill-tests.mjs +37 -0
  292. package/templates/.agents/skills/zaip-skill-backend/SKILL.md +1 -0
  293. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +9 -2
  294. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +39 -0
  295. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +1 -1
  296. package/templates/.agents/skills/zaip-skill-change/SKILL.md +1 -1
  297. package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +83 -0
  298. package/templates/.agents/skills/zaip-skill-dashboard-diagnostics/SKILL.md +22 -0
  299. package/templates/.agents/skills/zaip-skill-dashboard-diagnostics/agents/openai.yaml +4 -0
  300. package/templates/.agents/skills/zaip-skill-enforce-architecture/SKILL.md +2 -0
  301. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +1 -1
  302. package/templates/.agents/skills/zaip-skill-frontend/SKILL.md +4 -3
  303. package/templates/.agents/skills/zaip-skill-frontend/agents/openai.yaml +2 -2
  304. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +10 -10
  305. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +6 -0
  306. package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +27 -0
  307. package/templates/.agents/skills/zaip-skill-lifecycle-closure/agents/openai.yaml +4 -0
  308. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +6 -0
  309. package/templates/.agents/skills/zaip-skill-prd/SKILL.md +4 -0
  310. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +111 -104
  311. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-explicit-recovery.mjs +223 -0
  312. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +55 -9
  313. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +145 -14
  314. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.mjs +19 -9
  315. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +82 -5
  316. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/source-size-telemetry/model-identity-reconciliation.mjs +77 -0
  317. package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +20 -0
  318. package/templates/.agents/skills/zaip-skill-release-qualification/agents/openai.yaml +4 -0
  319. package/templates/.agents/skills/zaip-skill-review/SKILL.md +5 -0
  320. package/templates/.agents/skills/zaip-skill-rules/SKILL.md +1 -1
  321. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +6 -0
  322. package/templates/.agents/skills/zaip-skill-spec/SKILL.md +4 -0
  323. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +22 -90
  324. package/templates/.agents/skills/zaip-skill-sqlite-read-model/scripts/ensure-node.mjs +28 -5
  325. package/templates/.agents/zaip-aios/INDEX.md +57 -22
  326. package/templates/.agents/zaip-aios/README.md +24 -13
  327. package/templates/.agents/zaip-aios/VERSION +1 -1
  328. package/templates/.agents/zaip-aios/guides/02-requirements.md +5 -0
  329. package/templates/.agents/zaip-aios/guides/06-execucao.md +13 -4
  330. package/templates/.agents/zaip-aios/guides/07-revisao-e-validacao.md +7 -0
  331. package/templates/.agents/zaip-aios/guides/10-seguranca.md +23 -0
  332. package/templates/.agents/zaip-aios/guides/11-observabilidade-e-custos.md +6 -6
  333. package/templates/.agents/zaip-aios/guides/13-boas-praticas.md +3 -0
  334. package/templates/.agents/zaip-aios/guides/16-lifecycle-e-reconciliacao.md +12 -3
  335. package/templates/.agents/zaip-aios/guides/17-como-o-custo-e-calculado.md +5 -5
  336. package/templates/.agents/zaip-aios/guides/18-catalogo-de-agentes.md +76 -0
  337. package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +95 -0
  338. package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +366 -0
  339. package/templates/.agents/zaip-aios/guides/21-telemetria-monitor-custos.md +46 -0
  340. package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +74 -0
  341. package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +483 -0
  342. package/templates/.agents/zaip-aios/guides/24-calculo-de-custos-openai.md +85 -0
  343. package/templates/.agents/zaip-aios/guides/25-fontes-de-precos-openai.md +19 -0
  344. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +111 -0
  345. package/templates/.agents/zaip-aios/guides/27-telemetria-git-native.md +172 -0
  346. package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +149 -0
  347. package/templates/.agents/zaip-aios/guides/assets/diagrams/lifecycles-task-spec-prd.png +0 -0
  348. package/templates/.agents/zaip-aios/guides/assets/diagrams/prd-lifecycle.png +0 -0
  349. package/templates/.agents/zaip-aios/guides/assets/diagrams/spec-lifecycle.png +0 -0
  350. package/templates/.agents/zaip-aios/guides/assets/diagrams/task-lifecycle.png +0 -0
  351. package/templates/template-manifest.json +756 -235
  352. package/templates/.agents/dashboard/apps/frontend/dist/assets/DashboardHelpDialog-Cs6dzDmW.js +0 -1505
  353. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-QE1k9yZB.css +0 -1
  354. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-_Pf5DA_A.js +0 -3
  355. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BVlz3DLZ.js +0 -1
  356. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-BZ5WdaMN.css +0 -1
  357. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Cf9ctPVt.js +0 -68
  358. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-8A4DcNXM.js +0 -2
  359. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DK_enB5Z.css +0 -1
  360. package/templates/.agents/dashboard/apps/frontend/src/use-idle-cost-prefetch.ts +0 -45
@@ -0,0 +1,129 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { copyFile, mkdir, readdir, rename, rm, stat } from "node:fs/promises";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { DatabaseSync } from "node:sqlite";
5
+
6
+ export async function pruneSnapshotCache(directory: string, activePath: string): Promise<void> {
7
+ const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);
8
+ const stale = await Promise.all(
9
+ entries
10
+ .filter(
11
+ (entry) =>
12
+ entry.isFile() &&
13
+ entry.name.endsWith(".sqlite") &&
14
+ resolve(directory, entry.name) !== activePath,
15
+ )
16
+ .map(async (entry) => ({
17
+ path: resolve(directory, entry.name),
18
+ mtime: (await stat(resolve(directory, entry.name))).mtimeMs,
19
+ })),
20
+ );
21
+ for (const entry of stale.sort((a, b) => b.mtime - a.mtime).slice(1))
22
+ await rm(entry.path, { force: true }).catch(() => {});
23
+ for (const entry of entries) {
24
+ if (!entry.isFile() || !entry.name.includes(".sqlite.") || !entry.name.includes(".tmp"))
25
+ continue;
26
+ const candidate = resolve(directory, entry.name);
27
+ if (Date.now() - (await stat(candidate)).mtimeMs > 60_000)
28
+ await rm(candidate, { force: true }).catch(() => {});
29
+ }
30
+ }
31
+
32
+ export function readSnapshotFileCount(
33
+ path: string,
34
+ fingerprint: string,
35
+ schemaVersion: string,
36
+ hasRequiredPayloads: (database: DatabaseSync) => boolean,
37
+ ): number | null {
38
+ try {
39
+ const database = new DatabaseSync(path, { readOnly: true });
40
+ const value = (key: string) =>
41
+ (
42
+ database.prepare("SELECT value FROM zaip_vw_read_model_metadata WHERE key = ?").get(key) as
43
+ | { value?: string }
44
+ | undefined
45
+ )?.value;
46
+ const files = Number(value("source_file_count") ?? 0);
47
+ const result =
48
+ value("source_fingerprint") === fingerprint &&
49
+ value("schema_version") === schemaVersion &&
50
+ hasRequiredPayloads(database)
51
+ ? files
52
+ : null;
53
+ database.close();
54
+ return result;
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+
60
+ export async function mostRecentCompatibleSnapshot(
61
+ directory: string,
62
+ schemaVersion: string,
63
+ hasRequiredPayloads: (database: DatabaseSync) => boolean,
64
+ ): Promise<{ path: string; files: number } | null> {
65
+ const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);
66
+ const candidates = await Promise.all(
67
+ entries
68
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".sqlite"))
69
+ .map(async (entry) => ({
70
+ path: join(directory, entry.name),
71
+ mtime: (await stat(join(directory, entry.name))).mtimeMs,
72
+ })),
73
+ );
74
+ for (const candidate of candidates.sort((left, right) => right.mtime - left.mtime)) {
75
+ const files = readCompatibleSnapshotFileCount(
76
+ candidate.path,
77
+ schemaVersion,
78
+ hasRequiredPayloads,
79
+ );
80
+ if (files !== null) return { path: candidate.path, files };
81
+ }
82
+ return null;
83
+ }
84
+
85
+ function readCompatibleSnapshotFileCount(
86
+ path: string,
87
+ schemaVersion: string,
88
+ hasRequiredPayloads: (database: DatabaseSync) => boolean,
89
+ ): number | null {
90
+ try {
91
+ const database = new DatabaseSync(path, { readOnly: true });
92
+ const value = (key: string) =>
93
+ (
94
+ database.prepare("SELECT value FROM zaip_vw_read_model_metadata WHERE key = ?").get(key) as
95
+ | { value?: string }
96
+ | undefined
97
+ )?.value;
98
+ const files = Number(value("source_file_count") ?? 0);
99
+ const result =
100
+ value("schema_version") === schemaVersion && hasRequiredPayloads(database) ? files : null;
101
+ database.close();
102
+ return result;
103
+ } catch {
104
+ return null;
105
+ }
106
+ }
107
+
108
+ export function installLegacyViewAliases(database: DatabaseSync): void {
109
+ const views = database
110
+ .prepare("SELECT name FROM sqlite_master WHERE type = 'view' AND name LIKE 'zaip_vw_%'")
111
+ .all() as Array<{ name: string }>;
112
+ for (const { name } of views) {
113
+ const alias = name.slice("zaip_vw_".length);
114
+ const quoted = (identifier: string) => `"${identifier.replaceAll('"', '""')}"`;
115
+ database.exec(`CREATE TEMP VIEW ${quoted(alias)} AS SELECT * FROM main.${quoted(name)}`);
116
+ }
117
+ }
118
+
119
+ export async function materializeSnapshot(source: string, destination: string): Promise<void> {
120
+ await mkdir(dirname(destination), { recursive: true });
121
+ const temporaryPath = `${destination}.${process.pid}.${randomUUID()}.tmp`;
122
+ await copyFile(source, temporaryPath);
123
+ try {
124
+ await rename(temporaryPath, destination);
125
+ } catch (error) {
126
+ await rm(temporaryPath, { force: true });
127
+ throw error;
128
+ }
129
+ }
@@ -0,0 +1,26 @@
1
+ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { activateStagedSqliteSnapshot } from "./sqlite-snapshot-install.js";
6
+
7
+ const temporaryDirectories: string[] = [];
8
+
9
+ afterEach(async () => {
10
+ await Promise.all(
11
+ temporaryDirectories.splice(0).map((directory) => rm(directory, { recursive: true })),
12
+ );
13
+ });
14
+
15
+ describe("SQLite snapshot handoff", () => {
16
+ it("renames the validated stage synchronously during the close/reopen handoff", async () => {
17
+ const directory = await mkdtemp(join(tmpdir(), "zaip-snapshot-install-"));
18
+ temporaryDirectories.push(directory);
19
+ const staged = join(directory, "next.sqlite");
20
+ const destination = join(directory, "read-model.sqlite");
21
+ await writeFile(staged, "next-generation");
22
+
23
+ expect(activateStagedSqliteSnapshot(staged, destination)).toBeUndefined();
24
+ await expect(readFile(destination, "utf8")).resolves.toBe("next-generation");
25
+ });
26
+ });
@@ -0,0 +1,28 @@
1
+ import { renameSync } from "node:fs";
2
+ import { copyFile, mkdir, rm } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+
5
+ export async function stageSqliteSnapshot(
6
+ source: string,
7
+ destination: string,
8
+ validate: (path: string) => unknown,
9
+ ) {
10
+ await mkdir(dirname(destination), { recursive: true });
11
+ const staged = `${destination}.${process.pid}.refresh.tmp`;
12
+ await copyFile(source, staged);
13
+ try {
14
+ validate(staged);
15
+ return staged;
16
+ } catch (error) {
17
+ await rm(staged, { force: true });
18
+ throw error;
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Called after the old connection is closed. Keep the handoff in this tick so
24
+ * no request can observe a closed SQLite handle between close and replacement.
25
+ */
26
+ export function activateStagedSqliteSnapshot(staged: string, destination: string) {
27
+ renameSync(staged, destination);
28
+ }
@@ -0,0 +1,116 @@
1
+ import { copyFile, mkdir, mkdtemp, readdir, rm, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { DatabaseSync } from "node:sqlite";
5
+ import { afterEach, describe, expect, it } from "vitest";
6
+ import { hasRequiredFirstFoldPayloads } from "./sqlite-first-fold-persistence.js";
7
+ import { rebuildSqliteReadModel, SqliteReadModelFileSystem } from "./sqlite-read-model.js";
8
+
9
+ const roots: string[] = [];
10
+
11
+ afterEach(async () => {
12
+ await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
13
+ });
14
+
15
+ describe("SQLite snapshot seed publication", () => {
16
+ it("skips a newer incomplete seed and materializes the prior complete cache", async () => {
17
+ const root = await mkdtemp(join(tmpdir(), "zaip-snapshot-seed-"));
18
+ roots.push(root);
19
+ const task = join(root, ".agents", "records", "tasks", "task.md");
20
+ await mkdir(join(root, ".agents", "records", "tasks"), { recursive: true });
21
+ await mkdir(join(root, ".agents", "config"), { recursive: true });
22
+ await writeFile(join(root, ".agents", "config", "project.yaml"), "zaip:\n enabled: true\n");
23
+ await writeFile(task, "# original\n");
24
+ await expect(rebuildSqliteReadModel(root)).resolves.toMatchObject({ cache: "rebuilt" });
25
+ const cacheDirectory = join(root, ".local", "dashboard-sqlite", "cache");
26
+ const completeName = (await readdir(cacheDirectory)).find((name) => name.endsWith(".sqlite"));
27
+ expect(completeName).toBeDefined();
28
+ if (completeName === undefined) throw new Error("Expected a complete SQLite seed");
29
+ const incomplete = join(cacheDirectory, "v67-ff5-incomplete-newest.sqlite");
30
+ await copyFile(join(cacheDirectory, completeName), incomplete);
31
+ const incompleteDatabase = new DatabaseSync(incomplete);
32
+ incompleteDatabase
33
+ .prepare("DELETE FROM zaip_aios_activities WHERE payload_type='first_fold_all_time_cache'")
34
+ .run();
35
+ incompleteDatabase.close();
36
+ await writeFile(task, "# changed canonically\n");
37
+ const previousPath = process.env.ZAIP_SQLITE_DB_PATH;
38
+ const previousSeed = process.env.ZAIP_SQLITE_PREFER_LAST_VALID_SEED;
39
+ try {
40
+ process.env.ZAIP_SQLITE_DB_PATH = join(root, ".local", "dashboard-sqlite", "next.sqlite");
41
+ process.env.ZAIP_SQLITE_PREFER_LAST_VALID_SEED = "1";
42
+ await expect(rebuildSqliteReadModel(root)).resolves.toMatchObject({
43
+ cache: "stale_seed",
44
+ files: 2,
45
+ });
46
+ const served = new SqliteReadModelFileSystem(root);
47
+ try {
48
+ expect(served.firstFoldAllTime()).toBeDefined();
49
+ expect(served.firstFoldBoardAllTime()).toBeDefined();
50
+ } finally {
51
+ served.close();
52
+ }
53
+ } finally {
54
+ if (previousPath === undefined) delete process.env.ZAIP_SQLITE_DB_PATH;
55
+ else process.env.ZAIP_SQLITE_DB_PATH = previousPath;
56
+ if (previousSeed === undefined) delete process.env.ZAIP_SQLITE_PREFER_LAST_VALID_SEED;
57
+ else process.env.ZAIP_SQLITE_PREFER_LAST_VALID_SEED = previousSeed;
58
+ }
59
+ });
60
+
61
+ it("rejects a seed whose board payload is truncated below canonical work", async () => {
62
+ const root = await mkdtemp(join(tmpdir(), "zaip-snapshot-truncated-"));
63
+ roots.push(root);
64
+ await mkdir(join(root, ".agents", "records", "tasks"), { recursive: true });
65
+ await mkdir(join(root, ".agents", "config"), { recursive: true });
66
+ await writeFile(join(root, ".agents", "config", "project.yaml"), "zaip:\n enabled: true\n");
67
+ await writeFile(
68
+ join(root, ".agents", "records", "tasks", "task.md"),
69
+ "---\nid: TASK-SEED-POPULATION\ntitle: Seed population\nstatus: ready\nrisk: standard\ncreated_at: 2026-08-01T12:00:00.000Z\n---\n# Seed population\n",
70
+ );
71
+ await rebuildSqliteReadModel(root);
72
+ const seed = join(
73
+ root,
74
+ ".local",
75
+ "dashboard-sqlite",
76
+ "cache",
77
+ (await readdir(join(root, ".local", "dashboard-sqlite", "cache"))).find((name) =>
78
+ name.endsWith(".sqlite"),
79
+ ) ?? "missing.sqlite",
80
+ );
81
+ const database = new DatabaseSync(seed);
82
+ try {
83
+ database
84
+ .prepare(
85
+ "UPDATE zaip_aios_activities SET payload_json=json_set(payload_json, '$.pages.task', json('[]')) WHERE payload_type='first_fold_all_time_board_cache'",
86
+ )
87
+ .run();
88
+ expect(hasRequiredFirstFoldPayloads(database)).toBe(false);
89
+ } finally {
90
+ database.close();
91
+ }
92
+ });
93
+
94
+ it("accepts a complete monthly payload when historical work was recorded on rebuild day", async () => {
95
+ const root = await mkdtemp(join(tmpdir(), "zaip-snapshot-monthly-day-"));
96
+ roots.push(root);
97
+ await mkdir(join(root, ".agents", "records", "tasks"), { recursive: true });
98
+ await mkdir(join(root, ".agents", "config"), { recursive: true });
99
+ await writeFile(join(root, ".agents", "config", "project.yaml"), "zaip:\n enabled: true\n");
100
+ await writeFile(
101
+ join(root, ".agents", "records", "tasks", "historical-task.md"),
102
+ "---\nid: TASK-SEED-HISTORICAL\ntitle: Historical seed task\nstatus: ready\nrisk: standard\ncreated_at: 2026-07-01T12:00:00.000Z\n---\n# Historical seed task\n",
103
+ );
104
+ await rebuildSqliteReadModel(root);
105
+ const cacheDirectory = join(root, ".local", "dashboard-sqlite", "cache");
106
+ const seedName = (await readdir(cacheDirectory)).find((name) => name.endsWith(".sqlite"));
107
+ expect(seedName).toBeDefined();
108
+ if (seedName === undefined) throw new Error("Expected a complete SQLite seed");
109
+ const database = new DatabaseSync(join(cacheDirectory, seedName), { readOnly: true });
110
+ try {
111
+ expect(hasRequiredFirstFoldPayloads(database)).toBe(true);
112
+ } finally {
113
+ database.close();
114
+ }
115
+ });
116
+ });
@@ -0,0 +1,146 @@
1
+ import { createServer as createHttpServer } from "node:http";
2
+
3
+ export async function createDashboardLaunchPage({ port, host, runtimeKind, healthPath, delay }) {
4
+ let redirectUrl = null;
5
+ let failure = null;
6
+ let progress = {
7
+ percent: 4,
8
+ phase: "Iniciando o launcher local",
9
+ detail: "Abrindo a página de preparação.",
10
+ };
11
+ const connections = new Set();
12
+ let resolveRedirectObserved;
13
+ const redirectObserved = new Promise((resolveObserved) => {
14
+ resolveRedirectObserved = resolveObserved;
15
+ });
16
+ let resolveFailureObserved;
17
+ const failureObserved = new Promise((resolveObserved) => {
18
+ resolveFailureObserved = resolveObserved;
19
+ });
20
+ const server = createHttpServer((request, response) => {
21
+ response.setHeader("Cache-Control", "no-store");
22
+ if (request.url?.startsWith("/api/v2/ready")) {
23
+ if (redirectUrl !== null) resolveRedirectObserved(true);
24
+ if (failure !== null) resolveFailureObserved(true);
25
+ response.writeHead(failure === null ? 200 : 503, {
26
+ "Content-Type": "application/json; charset=utf-8",
27
+ });
28
+ response.end(JSON.stringify({ target: redirectUrl, failure }));
29
+ return;
30
+ }
31
+ if (request.url?.startsWith("/api/v2/progress")) {
32
+ if (failure !== null) resolveFailureObserved(true);
33
+ response.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
34
+ response.end(JSON.stringify({ ...progress, failure }));
35
+ return;
36
+ }
37
+ if (request.url?.startsWith(healthPath)) {
38
+ response.writeHead(503, { "Content-Type": "application/json; charset=utf-8" });
39
+ response.end(JSON.stringify({ kind: "zaip-dashboard-launcher", status: "starting" }));
40
+ return;
41
+ }
42
+ response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
43
+ response.end(`<!doctype html>
44
+ <html lang="pt-BR">
45
+ <head>
46
+ <meta charset="utf-8">
47
+ <meta name="viewport" content="width=device-width,initial-scale=1">
48
+ <title>Dashboard Zaip AIOS</title>
49
+ <style>
50
+ :root{color-scheme:dark;font-family:Inter,system-ui,sans-serif;background:#010610;color:#f1f5f9}
51
+ *{box-sizing:border-box}body{margin:0;background:#010610}.launch-overlay{position:fixed;inset:0;width:100vw;height:100vh;display:grid;place-items:center;padding:clamp(18px,5vw,72px);background:radial-gradient(circle at 50% 35%,rgba(0,176,240,.11),transparent 42%),#010610}
52
+ .progress{width:min(100%,760px);padding:clamp(24px,4vw,42px);border:1px solid rgba(125,211,252,.2);border-radius:20px;background:rgba(3,9,22,.94);box-shadow:0 24px 80px rgba(0,0,0,.45);backdrop-filter:blur(10px)}.progress strong,.progress span:not(.handoff):not(.failure){display:block}.progress strong{font-size:clamp(1.05rem,2vw,1.35rem)}.progress span{color:#b6c6db;font-size:clamp(.88rem,1.5vw,1rem);margin-top:7px}.track{position:relative;height:9px;margin-top:20px;border-radius:999px;background:#172033;overflow:hidden}.track::after{content:"";position:absolute;inset:0;background:linear-gradient(90deg,transparent 0%,rgba(224,242,254,.14) 35%,rgba(224,242,254,.8) 50%,rgba(224,242,254,.14) 65%,transparent 100%);transform:translateX(-100%);animation:launch-activity 1.35s ease-in-out infinite;pointer-events:none}.progress.terminal-failure .track::after{animation:none;transform:none;background:rgba(254,202,202,.2)}.bar{position:relative;z-index:1;height:100%;width:4%;border-radius:inherit;background:linear-gradient(90deg,#38bdf8,#818cf8);transition:width .35s ease}@keyframes launch-activity{to{transform:translateX(100%)}}@media (prefers-reduced-motion:reduce){.track::after{animation:none;transform:none;background:linear-gradient(90deg,transparent 0%,rgba(224,242,254,.2) 50%,transparent 100%)}}.handoff,.failure{display:none;margin-top:16px;color:#dbeafe}.failure{color:#fecaca}
53
+ </style>
54
+ </head>
55
+ <body>
56
+ <main class="launch-overlay" aria-label="Preparando dashboard">
57
+ <section class="progress" aria-live="polite" role="status"><strong id="phase">Iniciando o launcher local</strong><span id="detail">Abrindo a página de preparação.</span><div class="track" id="track" aria-label="Preparação em andamento"><div class="bar" id="bar"></div></div><span class="handoff" id="handoff">O dashboard está pronto. Abrindo a tela principal automaticamente…</span><span class="failure" id="failure" role="alert"></span></section>
58
+ </main>
59
+ <script>
60
+ let handoffTarget=null,progressRequestInFlight=false,terminalFailure=false;
61
+ function showHandoff(target){handoffTarget=target;document.getElementById("handoff").style.display="block";document.getElementById("phase").textContent="Dashboard pronto";document.getElementById("detail").textContent="Concluindo a abertura da tela principal."}
62
+ function showFailure(next){terminalFailure=true;clearInterval(progressPoll);document.getElementById("phase").textContent=next.phase;document.getElementById("detail").textContent="A preparação não foi concluída. Consulte a mensagem abaixo para agir.";document.getElementById("bar").style.width="100%";document.getElementById("handoff").style.display="none";document.getElementById("failure").textContent=next.detail;document.getElementById("failure").style.display="block";document.querySelector(".progress").classList.add("terminal-failure")}
63
+ async function updateProgress(){if(progressRequestInFlight||terminalFailure)return terminalFailure;progressRequestInFlight=true;try{const r=await fetch("/api/v2/progress",{cache:"no-store",signal:AbortSignal.timeout(1000)});const p=await r.json();if(terminalFailure)return true;if(p.failure){showFailure(p.failure);return true}if(!handoffTarget){document.getElementById("phase").textContent=p.phase;document.getElementById("detail").textContent=p.detail};if(!terminalFailure)document.getElementById("bar").style.width=Math.max(4,Math.min(100,Number(p.percent)||4))+"%"}catch{}finally{progressRequestInFlight=false}return terminalFailure}
64
+ const progressPoll=setInterval(updateProgress,250);
65
+ async function waitForDashboard(){try{if(await updateProgress())return;const ready=await fetch("/api/v2/ready",{cache:"no-store",signal:AbortSignal.timeout(1000)});const next=await ready.json();if(next.failure){showFailure(next.failure);return}if(next.target){showHandoff(next.target);clearInterval(progressPoll);setTimeout(()=>location.replace(next.target),0);return}const response=await fetch("${healthPath}",{cache:"no-store",signal:AbortSignal.timeout(1000)});const payload=await response.json();if(response.ok&&payload.kind==="${runtimeKind}"){clearInterval(progressPoll);location.reload();return}}catch{}setTimeout(waitForDashboard,250)}
66
+ updateProgress();setTimeout(waitForDashboard,100)
67
+ setTimeout(()=>{if(handoffTarget)showHandoff(handoffTarget)},4000)
68
+ </script>
69
+ </body>
70
+ </html>`);
71
+ });
72
+ server.on("connection", (connection) => {
73
+ connections.add(connection);
74
+ connection.once("close", () => connections.delete(connection));
75
+ });
76
+ await new Promise((resolveListen, rejectListen) => {
77
+ server.once("error", rejectListen);
78
+ server.listen({ host, port }, resolveListen);
79
+ });
80
+ server.unref();
81
+ const address = server.address();
82
+ const listeningPort = typeof address === "object" && address !== null ? address.port : port;
83
+ return {
84
+ port: listeningPort,
85
+ redirect: (target) => {
86
+ redirectUrl = target;
87
+ },
88
+ setProgress: (next) => {
89
+ progress = {
90
+ ...progress,
91
+ ...next,
92
+ percent: Math.max(progress.percent, next.percent ?? progress.percent),
93
+ };
94
+ },
95
+ setFailure: (next) => {
96
+ failure = { phase: next.phase, detail: next.detail };
97
+ progress = {
98
+ ...progress,
99
+ ...next,
100
+ percent: Math.max(progress.percent, next.percent ?? progress.percent),
101
+ };
102
+ },
103
+ waitForRedirect: async (timeoutMs = 1_500) =>
104
+ Promise.race([redirectObserved, delay(timeoutMs).then(() => false)]),
105
+ closeAfterHandoffGrace: (graceMs = 12_000) => {
106
+ const timer = setTimeout(() => {
107
+ void server.close();
108
+ server.closeAllConnections?.();
109
+ for (const connection of connections) connection.destroy();
110
+ }, graceMs);
111
+ timer.unref?.();
112
+ },
113
+ closeAfterFailureObservation: (graceMs = 12_000) => {
114
+ server.ref();
115
+ return Promise.race([failureObserved, delay(graceMs)]).then(
116
+ () =>
117
+ new Promise((resolveClosed) => {
118
+ setTimeout(() => {
119
+ server.closeAllConnections?.();
120
+ for (const connection of connections) connection.destroy();
121
+ void server.close(() => resolveClosed());
122
+ }, 250);
123
+ }),
124
+ );
125
+ },
126
+ close: ({ forceAfterMs = 750 } = {}) =>
127
+ new Promise((resolveClose, rejectClose) => {
128
+ if (!server.listening) {
129
+ resolveClose();
130
+ return;
131
+ }
132
+ let forceTimer = null;
133
+ const finish = (error) => {
134
+ if (forceTimer !== null) clearTimeout(forceTimer);
135
+ if (error) rejectClose(error);
136
+ else resolveClose();
137
+ };
138
+ server.close(finish);
139
+ forceTimer = setTimeout(() => {
140
+ server.closeAllConnections?.();
141
+ for (const connection of connections) connection.destroy();
142
+ }, forceAfterMs);
143
+ forceTimer.unref?.();
144
+ }),
145
+ };
146
+ }
@@ -0,0 +1,165 @@
1
+ import { connect } from "node:net";
2
+ import { chromium } from "@playwright/test";
3
+ import { describe, expect, it } from "vitest";
4
+ import { host, startDashboardLaunchPage } from "./start-dashboard.mjs";
5
+
6
+ describe("dashboard launch page", () => {
7
+ it("serves an accessible active progress overlay and an automatic handoff while the runtime starts", async () => {
8
+ const launchPage = await startDashboardLaunchPage(0);
9
+ try {
10
+ const page = await fetch(`http://${host}:${launchPage.port}/?token=temporary`);
11
+ const health = await fetch(`http://${host}:${launchPage.port}/api/v2/health`);
12
+
13
+ expect(page.status).toBe(200);
14
+ const html = await page.text();
15
+ expect(html).toContain('class="launch-overlay"');
16
+ expect(html).toContain("position:fixed;inset:0;width:100vw;height:100vh");
17
+ expect(html).toContain("display:grid;place-items:center");
18
+ expect(html).toContain('aria-live="polite" role="status"');
19
+ expect(html).toContain('aria-label="Preparação em andamento"');
20
+ expect(html).toContain("animation:launch-activity 1.35s ease-in-out infinite");
21
+ expect(html).toContain("@media (prefers-reduced-motion:reduce)");
22
+ expect(html).toContain("O dashboard está pronto. Abrindo a tela principal automaticamente…");
23
+ expect(html).not.toContain("abra o dashboard agora");
24
+ expect(html).not.toContain('id="handoff-link"');
25
+ expect(html).toContain("function showHandoff(target)");
26
+ const progress = await fetch(`http://${host}:${launchPage.port}/api/v2/progress`);
27
+ await expect(progress.json()).resolves.toMatchObject({
28
+ phase: "Iniciando o launcher local",
29
+ percent: expect.any(Number),
30
+ });
31
+ expect(health.status).toBe(503);
32
+ await expect(health.json()).resolves.toEqual({
33
+ kind: "zaip-dashboard-launcher",
34
+ status: "starting",
35
+ });
36
+
37
+ launchPage.redirect("http://127.0.0.1:4317/?token=reused-session");
38
+ const ready = await fetch(`http://${host}:${launchPage.port}/api/v2/ready`);
39
+ await expect(ready.json()).resolves.toEqual({
40
+ target: "http://127.0.0.1:4317/?token=reused-session",
41
+ failure: null,
42
+ });
43
+ await expect(launchPage.waitForRedirect(10)).resolves.toBe(true);
44
+ } finally {
45
+ await launchPage.close();
46
+ }
47
+ });
48
+
49
+ it("keeps the handoff hidden until a real handoff target exists while progress polling continues", async () => {
50
+ const launchPage = await startDashboardLaunchPage(0);
51
+ const browser = await chromium.launch();
52
+ try {
53
+ const page = await browser.newPage();
54
+ await page.route("**/api/v2/ready", (route) =>
55
+ route.fulfill({ contentType: "application/json", body: JSON.stringify({ target: null }) }),
56
+ );
57
+ await page.route("**/api/v2/health", () => new Promise(() => {}));
58
+ await page.goto(`http://${host}:${launchPage.port}/?token=temporary`);
59
+
60
+ await expect
61
+ .poll(() =>
62
+ page.locator("#handoff").evaluate((element) => getComputedStyle(element).display),
63
+ )
64
+ .toBe("none");
65
+ launchPage.setProgress({ percent: 64, phase: "Atualizando progresso independente" });
66
+ await expect
67
+ .poll(() => page.locator("#phase").textContent())
68
+ .toBe("Atualizando progresso independente");
69
+
70
+ await page.evaluate(() => window.showHandoff("http://127.0.0.1:4417/?token=ready"));
71
+ await expect
72
+ .poll(() =>
73
+ page.locator("#handoff").evaluate((element) => getComputedStyle(element).display),
74
+ )
75
+ .toBe("block");
76
+ } finally {
77
+ await browser.close();
78
+ await launchPage.close();
79
+ }
80
+ });
81
+
82
+ it("publishes a durable preparation failure without returning a false ready target", async () => {
83
+ const launchPage = await startDashboardLaunchPage(0);
84
+ const browser = await chromium.launch();
85
+ try {
86
+ launchPage.setFailure({
87
+ percent: 100,
88
+ phase: "Não foi possível preparar o dashboard",
89
+ detail: "A verificação local falhou.",
90
+ });
91
+ const ready = await fetch(`http://${host}:${launchPage.port}/api/v2/ready`);
92
+ expect(ready.status).toBe(503);
93
+ await expect(ready.json()).resolves.toEqual({
94
+ target: null,
95
+ failure: {
96
+ phase: "Não foi possível preparar o dashboard",
97
+ detail: "A verificação local falhou.",
98
+ },
99
+ });
100
+
101
+ const page = await browser.newPage();
102
+ await page.goto(`http://${host}:${launchPage.port}/?token=temporary`);
103
+ await expect
104
+ .poll(() => page.locator("#failure").textContent())
105
+ .toBe("A verificação local falhou.");
106
+ await expect
107
+ .poll(() =>
108
+ page.locator("#failure").evaluate((element) => getComputedStyle(element).display),
109
+ )
110
+ .toBe("block");
111
+ await expect
112
+ .poll(() =>
113
+ page.locator("#handoff").evaluate((element) => getComputedStyle(element).display),
114
+ )
115
+ .toBe("none");
116
+ await expect
117
+ .poll(() => page.locator("#phase").textContent())
118
+ .toBe("Não foi possível preparar o dashboard");
119
+ await expect
120
+ .poll(() => page.locator("#detail").textContent())
121
+ .toBe("A preparação não foi concluída. Consulte a mensagem abaixo para agir.");
122
+ await expect
123
+ .poll(() =>
124
+ page
125
+ .locator(".progress")
126
+ .evaluate((element) => element.classList.contains("terminal-failure")),
127
+ )
128
+ .toBe(true);
129
+ await expect
130
+ .poll(() =>
131
+ page
132
+ .locator("#track")
133
+ .evaluate((element) => getComputedStyle(element, "::after").animationName),
134
+ )
135
+ .toBe("none");
136
+ await expect
137
+ .poll(() => page.locator("#bar").evaluate((element) => getComputedStyle(element).width))
138
+ .not.toBe("4px");
139
+
140
+ await launchPage.closeAfterFailureObservation(0);
141
+ await expect
142
+ .poll(() => page.locator("#failure").textContent())
143
+ .toBe("A verificação local falhou.");
144
+ } finally {
145
+ await browser.close();
146
+ await launchPage.close();
147
+ }
148
+ });
149
+
150
+ it("bounds launch-page shutdown when a browser connection never finishes polling", async () => {
151
+ const launchPage = await startDashboardLaunchPage(0);
152
+ const connection = connect({ host, port: launchPage.port });
153
+ await new Promise((resolveConnected, rejectConnected) => {
154
+ connection.once("connect", resolveConnected);
155
+ connection.once("error", rejectConnected);
156
+ });
157
+ const closed = new Promise((resolveClosed) => connection.once("close", resolveClosed));
158
+ const startedAt = Date.now();
159
+ await launchPage.close({ forceAfterMs: 20 });
160
+ await closed;
161
+
162
+ expect(Date.now() - startedAt).toBeLessThan(500);
163
+ expect(connection.destroyed).toBe(true);
164
+ });
165
+ });