@zaip-ai/cli 1.3.8 → 1.4.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 (315) hide show
  1. package/README.md +58 -3
  2. package/dist/commands/init.d.ts +1 -0
  3. package/dist/commands/init.d.ts.map +1 -1
  4. package/dist/commands/init.js +17 -6
  5. package/dist/commands/init.js.map +1 -1
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/services/adapters.js +4 -4
  11. package/dist/services/adapters.js.map +1 -1
  12. package/dist/services/codex-config-template.d.ts +2 -2
  13. package/dist/services/codex-config-template.d.ts.map +1 -1
  14. package/dist/services/codex-config-template.js +3 -3
  15. package/dist/services/codex-config-template.js.map +1 -1
  16. package/dist/services/codex-rules.d.ts +1 -1
  17. package/dist/services/codex-rules.d.ts.map +1 -1
  18. package/dist/services/codex-rules.js +9 -24
  19. package/dist/services/codex-rules.js.map +1 -1
  20. package/dist/services/delivery-git-preflight.d.ts +23 -0
  21. package/dist/services/delivery-git-preflight.d.ts.map +1 -0
  22. package/dist/services/delivery-git-preflight.js +69 -0
  23. package/dist/services/delivery-git-preflight.js.map +1 -0
  24. package/dist/services/doctor.d.ts.map +1 -1
  25. package/dist/services/doctor.js +13 -1
  26. package/dist/services/doctor.js.map +1 -1
  27. package/dist/services/install-agents-template.d.ts.map +1 -1
  28. package/dist/services/install-agents-template.js +22 -28
  29. package/dist/services/install-agents-template.js.map +1 -1
  30. package/dist/services/install-profile.d.ts +8 -0
  31. package/dist/services/install-profile.d.ts.map +1 -0
  32. package/dist/services/install-profile.js +56 -0
  33. package/dist/services/install-profile.js.map +1 -0
  34. package/dist/services/logger.d.ts.map +1 -1
  35. package/dist/services/logger.js +6 -5
  36. package/dist/services/logger.js.map +1 -1
  37. package/dist/services/stack-git.d.ts +9 -0
  38. package/dist/services/stack-git.d.ts.map +1 -0
  39. package/dist/services/stack-git.js +188 -0
  40. package/dist/services/stack-git.js.map +1 -0
  41. package/dist/services/stack-plan.d.ts +3 -1
  42. package/dist/services/stack-plan.d.ts.map +1 -1
  43. package/dist/services/stack-plan.js +15 -1
  44. package/dist/services/stack-plan.js.map +1 -1
  45. package/dist/services/stack-terraform.d.ts +13 -0
  46. package/dist/services/stack-terraform.d.ts.map +1 -0
  47. package/dist/services/stack-terraform.js +274 -0
  48. package/dist/services/stack-terraform.js.map +1 -0
  49. package/dist/services/template-manifest.d.ts.map +1 -1
  50. package/dist/services/template-manifest.js +14 -1
  51. package/dist/services/template-manifest.js.map +1 -1
  52. package/dist/types.d.ts +1 -0
  53. package/dist/types.d.ts.map +1 -1
  54. package/package.json +1 -1
  55. package/templates/.agents/.env.example +34 -0
  56. package/templates/{README.md → .agents/README.md} +124 -60
  57. package/templates/.agents/config/agents/catalog.yaml +23 -23
  58. package/templates/.agents/config/clients/codex/agents/zaip-agent-analyst.toml +1 -0
  59. package/templates/.agents/config/clients/codex/agents/zaip-agent-auditor.toml +1 -0
  60. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +5 -4
  61. package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +1 -0
  62. package/templates/.agents/config/clients/codex/agents/zaip-agent-builder.toml +1 -0
  63. package/templates/.agents/config/clients/codex/agents/zaip-agent-cloud-engineer.toml +10 -7
  64. package/templates/.agents/config/clients/codex/agents/zaip-agent-data-analyst.toml +1 -0
  65. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +1 -0
  66. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +1 -0
  67. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +5 -4
  68. package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +1 -0
  69. package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +1 -0
  70. package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +1 -0
  71. package/templates/.agents/config/clients/codex/agents/zaip-agent-integrator.toml +1 -0
  72. package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +5 -4
  73. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -0
  74. package/templates/.agents/config/clients/codex/agents/zaip-agent-refactoring-specialist.toml +1 -0
  75. package/templates/.agents/config/clients/codex/agents/zaip-agent-reviewer.toml +1 -0
  76. package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +1 -0
  77. package/templates/.agents/config/clients/codex/agents/zaip-agent-security-auditor.toml +1 -0
  78. package/templates/.agents/config/clients/codex/agents/zaip-agent-task-executor.toml +1 -0
  79. package/templates/.agents/config/clients/codex/agents/zaip-agent-website-developer.toml +5 -4
  80. package/templates/.agents/config/clients/codex/agents/zaip-agent-writer.toml +1 -0
  81. package/templates/.agents/config/pricing/models.json +25 -1
  82. package/templates/.agents/config/project.yaml +2 -0
  83. package/templates/.agents/dashboard/.zaip-managed.json +200 -88
  84. package/templates/.agents/dashboard/DATA-DICTIONARY.md +1 -1
  85. package/templates/.agents/dashboard/README.md +58 -19
  86. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-DwVZOmMV.js → CostCalculationGuidePage-snUCQYTC.js} +1 -1
  87. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-VcLx9iOO.js → DashboardHelpDialog-oZVlf-l5.js} +72 -8
  88. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CyjMXkm8.js → PricingSettingsDialog-BPtVUGgs.js} +1 -1
  89. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-3Awyaovx.js +3 -0
  90. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-DFZwIXSC.js → TraceabilityWorkspace-BYjVOHI_.js} +1 -1
  91. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DbaLbU2q.js +68 -0
  92. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DXBOtWNI.js → icons-vendor-TPUT2IUt.js} +1 -1
  93. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Cmlx8OYH.css +1 -0
  94. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-ZJFXmK7m.js +2 -0
  95. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-DaNXQPyk.js → react-vendor-Ct48XHGL.js} +1 -1
  96. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-I4AKqVDX.js → traceability-BDe4_Ubl.js} +1 -1
  97. package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
  98. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +73 -67
  99. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +35 -7
  100. package/templates/.agents/dashboard/apps/frontend/src/AppWorkBoard.ts +55 -0
  101. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +11 -0
  102. package/templates/.agents/dashboard/apps/frontend/src/app-loading-boundary.css +98 -0
  103. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +5 -5
  104. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api-reconciliation.ts +33 -0
  105. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +75 -0
  106. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +7 -8
  107. package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +14 -2
  108. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +35 -0
  109. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +78 -2
  110. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +28 -0
  111. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +8 -5
  112. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +180 -1
  113. package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +7 -4
  114. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +13 -7
  115. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +14 -0
  116. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableDisplay.ts +211 -0
  117. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +152 -157
  118. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +155 -142
  119. package/templates/.agents/dashboard/modules/cost-monitor/src/react/InvocationManifestQuery.test.ts +145 -0
  120. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +7 -5
  121. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +4 -1
  122. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +4 -1
  123. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +4 -1
  124. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +4 -4
  125. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +79 -1
  126. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +105 -11
  127. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +482 -332
  128. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +430 -285
  129. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +14 -2
  130. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +6 -26
  131. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +1 -1
  132. package/templates/.agents/dashboard/packages/local-runtime/scripts/verify-persistent-launch.mjs +268 -0
  133. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +482 -332
  134. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +90 -1
  135. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +112 -22
  136. package/templates/.agents/dashboard/packages/local-runtime/src/server-agent-assets.ts +39 -0
  137. package/templates/.agents/dashboard/packages/local-runtime/src/server-browser-lease.ts +33 -0
  138. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +148 -11
  139. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-result.test.ts +68 -0
  140. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.test.ts +19 -0
  141. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +39 -43
  142. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.test.ts +42 -0
  143. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.ts +26 -0
  144. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.ts +34 -0
  145. package/templates/.agents/dashboard/packages/local-runtime/src/server-startup.ts +30 -0
  146. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.test.ts +89 -0
  147. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.ts +88 -0
  148. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +68 -7
  149. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +11 -5
  150. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +129 -99
  151. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.test.ts +111 -0
  152. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.ts +140 -0
  153. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +127 -4
  154. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +145 -5
  155. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +156 -23
  156. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +18 -9
  157. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-projection.ts +52 -0
  158. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +35 -13
  159. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-force-canonical.test.ts +56 -0
  160. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.test.ts +29 -0
  161. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.ts +136 -0
  162. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-refresh.test.ts +251 -0
  163. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +22 -7
  164. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +1 -1
  165. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical-parsing.ts +42 -0
  166. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical.ts +793 -0
  167. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-duration.test.ts +270 -0
  168. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +3 -2
  169. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +18 -4
  170. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.test.ts +87 -0
  171. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +105 -16
  172. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +131 -59
  173. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1000 -1115
  174. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +13 -92
  175. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-claims.test.mjs +79 -0
  176. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +27 -0
  177. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.mjs +6 -6
  178. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-prerequisites.mjs +1 -1
  179. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +2 -10
  180. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-server.mjs +142 -0
  181. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +231 -239
  182. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +18 -29
  183. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +6 -0
  184. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  185. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  186. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +47 -0
  187. package/templates/.agents/requirements/comparative-audit.md +1 -1
  188. package/templates/.agents/requirements/context-engine.md +1 -1
  189. package/templates/.agents/requirements/cost-observability.md +1 -1
  190. package/templates/.agents/requirements/dashboard-launch.md +2 -2
  191. package/templates/.agents/requirements/data-export.md +1 -1
  192. package/templates/.agents/requirements/design-system.md +1 -1
  193. package/templates/.agents/requirements/execution-engine.md +1 -1
  194. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  195. package/templates/.agents/requirements/model-orchestration.md +1 -1
  196. package/templates/.agents/requirements/public-distribution.md +1 -1
  197. package/templates/.agents/requirements/software-architecture.md +1 -1
  198. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  199. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  200. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  201. package/templates/.agents/rules/autonomy-decision.md +1 -1
  202. package/templates/.agents/rules/cloud-hostinger-autonomy.md +21 -0
  203. package/templates/.agents/rules/core.md +5 -4
  204. package/templates/.agents/rules/local-sqlite-storage.md +18 -0
  205. package/templates/.agents/rules/meta-mcp.md +21 -0
  206. package/templates/.agents/rules/workflow.md +1 -0
  207. package/templates/.agents/runtime/bin/zaip.mjs +305 -137
  208. package/templates/.agents/runtime/checksums.sha256 +2 -2
  209. package/templates/.agents/runtime/provenance.json +2 -2
  210. package/templates/.agents/skills/zaip-skill-api-to-mcp/SKILL.md +4 -0
  211. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +25 -9
  212. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +2 -2
  213. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +2 -2
  214. package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +15 -0
  215. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/SKILL.md +13 -5
  216. package/templates/.agents/skills/zaip-skill-data-analyst/SKILL.md +10 -5
  217. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +5 -4
  218. package/templates/.agents/skills/zaip-skill-frontend-launcher/SKILL.md +4 -3
  219. package/templates/.agents/skills/zaip-skill-frontend-launcher/references/launcher-contract.md +5 -3
  220. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +5 -5
  221. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.mjs +78 -13
  222. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.test.mjs +59 -0
  223. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/yaml-parser.mjs +157 -0
  224. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +11 -6
  225. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/SKILL.md +17 -0
  226. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/agents/openai.yaml +7 -0
  227. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-cloud-inputs.example.json +41 -0
  228. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-runner.md +84 -0
  229. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/operational-contract.md +19 -0
  230. package/templates/.agents/skills/zaip-skill-local-credentials/SKILL.md +43 -0
  231. package/templates/.agents/skills/zaip-skill-local-credentials/agents/openai.yaml +6 -0
  232. package/templates/.agents/skills/zaip-skill-local-credentials/references/dotenv-resolution.md +47 -0
  233. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +8 -3
  234. package/templates/.agents/skills/zaip-skill-meta-ads/SKILL.md +16 -0
  235. package/templates/.agents/skills/zaip-skill-meta-ads/agents/openai.yaml +4 -0
  236. package/templates/.agents/skills/zaip-skill-meta-ads/references/setup.md +3 -0
  237. package/templates/.agents/skills/zaip-skill-meta-ads-setup/SKILL.md +24 -0
  238. package/templates/.agents/skills/zaip-skill-meta-ads-setup/agents/openai.yaml +4 -0
  239. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/diagnostics.md +13 -0
  240. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/official-sources.md +10 -0
  241. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/SKILL.md +12 -0
  242. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/agents/openai.yaml +4 -0
  243. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/LICENSE +21 -0
  244. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/upstream.md +77 -0
  245. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/SKILL.md +12 -0
  246. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/agents/openai.yaml +4 -0
  247. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/LICENSE +21 -0
  248. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/upstream.md +126 -0
  249. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/SKILL.md +12 -0
  250. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/agents/openai.yaml +4 -0
  251. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/LICENSE +21 -0
  252. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/upstream.md +67 -0
  253. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/SKILL.md +12 -0
  254. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/agents/openai.yaml +4 -0
  255. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/LICENSE +21 -0
  256. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/upstream.md +69 -0
  257. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/SKILL.md +12 -0
  258. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/agents/openai.yaml +4 -0
  259. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/LICENSE +21 -0
  260. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/upstream.md +79 -0
  261. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/SKILL.md +14 -0
  262. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/agents/openai.yaml +4 -0
  263. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/LICENSE +21 -0
  264. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/upstream.md +144 -0
  265. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/SKILL.md +12 -0
  266. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/agents/openai.yaml +4 -0
  267. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/LICENSE +21 -0
  268. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/upstream.md +83 -0
  269. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/SKILL.md +10 -0
  270. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/agents/openai.yaml +4 -0
  271. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/LICENSE +21 -0
  272. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/setup.md +11 -0
  273. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/upstream.md +55 -0
  274. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/SKILL.md +12 -0
  275. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/agents/openai.yaml +4 -0
  276. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/LICENSE +21 -0
  277. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/upstream.md +56 -0
  278. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +12 -7
  279. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +11 -4
  280. package/templates/.agents/skills/zaip-skill-tool-setup/SKILL.md +6 -4
  281. package/templates/.agents/skills/zaip-skill-tool-setup/references/codex-config.md +4 -2
  282. package/templates/.agents/skills/zaip-skill-tool-setup/references/secrets.md +1 -0
  283. package/templates/.agents/tools/catalog.yaml +6 -0
  284. package/templates/.agents/tools/cloud-runner-dns.mjs +28 -0
  285. package/templates/.agents/tools/cloud-runner-terraform.mjs +212 -0
  286. package/templates/.agents/tools/cloud-runner.integration.test.mjs +239 -0
  287. package/templates/.agents/tools/cloud-runner.mjs +786 -0
  288. package/templates/.agents/tools/cloud-runner.test.mjs +534 -0
  289. package/templates/.agents/tools/cloud-terraform-plan-policy.mjs +262 -0
  290. package/templates/.agents/tools/mcp/development/meta-devtools/README.md +14 -0
  291. package/templates/.agents/tools/mcp/development/meta-devtools/manifest.yaml +28 -0
  292. package/templates/.agents/tools/mcp/infra/cloudflare/manifest.yaml +2 -3
  293. package/templates/.agents/tools/mcp/infra/hostinger/manifest.yaml +1 -3
  294. package/templates/.agents/tools/mcp/marketing/meta-ads/README.md +15 -13
  295. package/templates/.agents/tools/mcp/marketing/meta-ads/manifest.yaml +15 -10
  296. package/templates/.agents/tools/profiles/development.yaml +3 -0
  297. package/templates/infra/terraform/.terraform.lock.hcl +42 -0
  298. package/templates/infra/terraform/README.md +31 -0
  299. package/templates/infra/terraform/backend.tf.json +13 -0
  300. package/templates/infra/terraform/deployment-inputs.example.yaml +22 -0
  301. package/templates/infra/terraform/fixtures/approved-targets.json +32 -0
  302. package/templates/infra/terraform/fixtures/plan-approved.json +67 -0
  303. package/templates/infra/terraform/main.tf +77 -0
  304. package/templates/infra/terraform/outputs.tf +20 -0
  305. package/templates/infra/terraform/terraform.tfvars.example +26 -0
  306. package/templates/infra/terraform/variables.tf +91 -0
  307. package/templates/infra/terraform/versions.tf +18 -0
  308. package/templates/scripts/validate-cloud-hostinger-contract.mjs +89 -0
  309. package/templates/scripts/validate-cloud-terraform-plan.mjs +1 -0
  310. package/templates/template-manifest.json +599 -167
  311. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BwnJpl02.js +0 -3
  312. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-C09KmlSw.js +0 -68
  313. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Dyo_wpL4.js +0 -2
  314. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-hPPfVGQv.css +0 -1
  315. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-stale-seed.ts +0 -50
@@ -23,6 +23,17 @@ import {
23
23
  projectSetupPath,
24
24
  renderAppSetupRoute,
25
25
  } from "./AppSetupRoutes.js";
26
+ import {
27
+ type BoardStageState,
28
+ initialWorkBoardStages,
29
+ type OverviewClassification,
30
+ type OverviewClassificationPages,
31
+ type OverviewStatusKind,
32
+ type OverviewStatusPages,
33
+ overviewClassificationPageKey,
34
+ overviewStatusPageKey,
35
+ traceTaskSearchStages,
36
+ } from "./AppWorkBoard.js";
26
37
  import { currencyKey, initialDashboardView, savedLocale } from "./app-preferences.js";
27
38
  import { readDashboardSessionToken } from "./app-session-token.js";
28
39
  import { renderDashboardAlternateContent } from "./DashboardAlternateContent.js";
@@ -39,6 +50,7 @@ import type {
39
50
  WorkBoardStage,
40
51
  } from "./dashboard-api.js";
41
52
  import { DashboardApiClient } from "./dashboard-api.js";
53
+ import { reconciliationCommandOutcome } from "./dashboard-api-reconciliation.js";
42
54
  import {
43
55
  defaultOperationsPeriod,
44
56
  isAllTimeDisplayRangeChange,
@@ -58,53 +70,6 @@ import { useOperationsDetailExport } from "./use-operations-detail-export.js";
58
70
  import "./loading-feedback.css";
59
71
  import "./period-transition-overlay.css";
60
72
 
61
- type BoardStageState = { loading: boolean; error: string | null };
62
- type OverviewStatusKind = "task" | "spec" | "prd";
63
- type OverviewClassification = "lite" | "standard" | "critical" | "empty" | "structured" | "rework";
64
- type OverviewStatusPage = {
65
- items:
66
- | OperationsProjection["tasks"]
67
- | OperationsProjection["specs"]
68
- | OperationsProjection["prds"];
69
- total: number;
70
- loading: boolean;
71
- error: string | null;
72
- };
73
- type OverviewStatusPages = Partial<
74
- Record<`${WorkBoardStage}:${OverviewStatusKind}`, OverviewStatusPage>
75
- >;
76
- const overviewStatusPageKey = (stage: WorkBoardStage, kind: OverviewStatusKind) =>
77
- `${stage}:${kind}` as const;
78
- type OverviewClassificationPages = Partial<
79
- Record<`${OverviewStatusKind}:${OverviewClassification}`, OverviewStatusPage>
80
- >;
81
- const overviewClassificationPageKey = (
82
- kind: OverviewStatusKind,
83
- classification: OverviewClassification,
84
- ) => `${kind}:${classification}` as const;
85
- const initialWorkBoardStages: readonly WorkBoardStage[] = [
86
- "definition",
87
- "planning",
88
- "ready",
89
- "executing",
90
- "verifying",
91
- "completed",
92
- "blocked",
93
- "cancelled",
94
- "failed",
95
- ];
96
- const traceTaskSearchStages: readonly WorkBoardStage[] = [
97
- "completed",
98
- "executing",
99
- "ready",
100
- "blocked",
101
- "verifying",
102
- "failed",
103
- "cancelled",
104
- "planning",
105
- "definition",
106
- ];
107
-
108
73
  export {
109
74
  isAbortError,
110
75
  isCurrentCostSummaryRequest,
@@ -119,6 +84,14 @@ export {
119
84
  shouldLoadCostSummary,
120
85
  } from "./AppRequestGuards.js";
121
86
  export function App() {
87
+ useEffect(() => {
88
+ const token = readDashboardSessionToken();
89
+ if (!token) return undefined;
90
+ // EventSource remains open for hidden tabs and reconnects after reload,
91
+ // giving the local runtime a browser-owned lifecycle without a fast poll.
92
+ const lease = new EventSource(`/api/v2/browser-lease?token=${encodeURIComponent(token)}`);
93
+ return () => lease.close();
94
+ }, []);
122
95
  const [snapshotUpdating, setSnapshotUpdating] = useState(false);
123
96
  const [snapshotNoticeDismissed, setSnapshotNoticeDismissed] = useState(false);
124
97
  const [snapshotNoticeDelayElapsed, setSnapshotNoticeDelayElapsed] = useState(false);
@@ -275,7 +248,12 @@ export function App() {
275
248
  // forcing a second full aggregate calculation.
276
249
  const initialAggregateFreshRef = useRef(false);
277
250
  const refresh = useCallback(
278
- async (visible = true, reconcileOpenWork = false, refreshProjection = false) => {
251
+ async (
252
+ visible = true,
253
+ reconcileOpenWork = false,
254
+ refreshProjection = false,
255
+ reloadCostSummary = false,
256
+ ) => {
279
257
  operationsDetailCancelRef.current?.();
280
258
  operationsDetailCancelRef.current = null;
281
259
  operationsDetailAbortRef.current?.abort();
@@ -310,20 +288,36 @@ export function App() {
310
288
  setError(null);
311
289
  setFailedArea(null);
312
290
  try {
313
- if (reconcileOpenWork || refreshProjection) {
314
- let snapshot: Awaited<ReturnType<typeof api.refreshProjection>>;
315
- let reconciliation:
316
- | Awaited<ReturnType<typeof api.reconcileOpenWork>>["reconciliation"]
317
- | null = null;
318
- if (reconcileOpenWork) {
319
- const reconciledSnapshot = await api.reconcileOpenWork(currency, period);
320
- reconciliation = reconciledSnapshot.reconciliation;
321
- snapshot = reconciledSnapshot;
322
- } else {
323
- snapshot = await api.refreshProjection(currency, period);
291
+ if (reconcileOpenWork) {
292
+ const reconciliationResponse = await api.reconcileOpenWork(currency, period);
293
+ const commandOutcome = reconciliationCommandOutcome(
294
+ reconciliationResponse,
295
+ isCurrentRequest(),
296
+ );
297
+ // The compact reconciliation outcome is useful on its own. Reveal
298
+ // it before the optional quick GET so a historical snapshot load
299
+ // cannot keep the operator in the progress dialog.
300
+ setReconciliationResult(commandOutcome.reconciliation);
301
+ if (commandOutcome.dismissProgressDialog) setRefreshDialog(null);
302
+ if (!commandOutcome.applySnapshotState) return false;
303
+ setSnapshotUpdating(reconciliationResponse.snapshot_state === "updating");
304
+ // Reconciliation returns promptly while a new SQLite generation may
305
+ // still be promoted. Only read a compact Operations projection when
306
+ // that promotion is already complete; the revision observer handles
307
+ // the deferred case without fetching Cost Monitor detail data.
308
+ if (commandOutcome.reloadQuickProjection) {
309
+ const projection = await api.operations(period, "quick", undefined, true);
310
+ if (!isCurrentRequest()) return false;
311
+ initialAggregateFreshRef.current = false;
312
+ setOperations(projection);
313
+ quickOverviewMetricsRef.current = projection.summary.overview_metrics;
314
+ operationsDetailLoadedRef.current = false;
315
+ setLastUpdatedAt(projection.summary.source_latest_at);
324
316
  }
325
- if (!isCurrentRequest()) return;
326
- if (reconciliation !== null) setReconciliationResult(reconciliation);
317
+ } else if (refreshProjection) {
318
+ let snapshot: Awaited<ReturnType<typeof api.refreshProjection>>;
319
+ snapshot = await api.refreshProjection(currency, period);
320
+ if (!isCurrentRequest()) return false;
327
321
  setOperations(snapshot.operations);
328
322
  quickOverviewMetricsRef.current = snapshot.operations.summary.overview_metrics;
329
323
  operationsDetailLoadedRef.current = true;
@@ -346,11 +340,11 @@ export function App() {
346
340
  // An English first paint must have the factual USD summary
347
341
  // before Operations formats currency. BRL retains the fast
348
342
  // Operations-only path until the user opens Cost Monitor.
349
- currency === "USD"
343
+ currency === "USD" || reloadCostSummary
350
344
  ? api.costMonitor(currency, period, "summary")
351
345
  : Promise.resolve(null),
352
346
  ]);
353
- if (!isCurrentRequest()) return;
347
+ if (!isCurrentRequest()) return false;
354
348
  initialAggregateFreshRef.current = false;
355
349
  setOperations(projection);
356
350
  quickOverviewMetricsRef.current = projection.summary.overview_metrics;
@@ -365,10 +359,12 @@ export function App() {
365
359
  setLastUpdatedAt(projection.summary.source_latest_at);
366
360
  }
367
361
  }
362
+ return true;
368
363
  } catch (cause) {
369
- if (!isCurrentRequest()) return;
364
+ if (!isCurrentRequest()) return false;
370
365
  setError(cause instanceof Error ? cause.message : String(cause));
371
366
  setFailedArea(activeDashboardView === "cost-monitor" ? "costs" : "operations");
367
+ return false;
372
368
  } finally {
373
369
  if (
374
370
  isCurrentRequest() &&
@@ -737,8 +733,14 @@ export function App() {
737
733
  setReconciliationResult(null);
738
734
  }
739
735
  setRefreshDialog(reconcileOpenWork ? "reconciling" : "refreshing");
740
- await refresh(false, reconcileOpenWork, !reconcileOpenWork);
741
- setRefreshDialog(null);
736
+ try {
737
+ await refresh(false, reconcileOpenWork, !reconcileOpenWork);
738
+ } finally {
739
+ // A transport/runtime failure must not trap the user in the progress
740
+ // overlay. `refresh` normally reports the error on the affected panel;
741
+ // this also covers an unexpected rejection before it reaches that path.
742
+ setRefreshDialog(null);
743
+ }
742
744
  };
743
745
  useAppEffects({
744
746
  api,
@@ -754,10 +756,12 @@ export function App() {
754
756
  pricing,
755
757
  refresh,
756
758
  refreshVersion,
759
+ setSnapshotUpdating,
757
760
  setError,
758
761
  setFxRate,
759
762
  setPathname,
760
763
  setPricing,
764
+ snapshotUpdating,
761
765
  });
762
766
 
763
767
  const navigate = (nextPath: string) => {
@@ -972,7 +976,9 @@ export function App() {
972
976
  navigate,
973
977
  pathname,
974
978
  pricing,
975
- refresh,
979
+ refresh: async () => {
980
+ await refresh();
981
+ },
976
982
  setFxRate,
977
983
  setPricing,
978
984
  });
@@ -2,6 +2,22 @@ import type { OperationsProjection } from "@zaip-ai/dashboard-operations/contrac
2
2
  import type { ReactNode } from "react";
3
3
  import { DashboardSkeleton } from "./AppLoadingPanels.js";
4
4
  import { PeriodTransitionOverlay } from "./AppRequestGuards.js";
5
+ import "./app-loading-boundary.css";
6
+
7
+ const errorCopy = {
8
+ "pt-BR": {
9
+ action: "Tentar novamente",
10
+ guidance:
11
+ "Abra a pasta dashboard dentro de .agents e dê dois cliques novamente no arquivo executável.",
12
+ title: "Não foi possível carregar o dashboard",
13
+ },
14
+ "en-US": {
15
+ action: "Try again",
16
+ guidance:
17
+ "Open the dashboard folder inside .agents, then double-click the executable file again.",
18
+ title: "Unable to load the dashboard",
19
+ },
20
+ } as const;
5
21
 
6
22
  export function AppLoadingBoundary({
7
23
  children,
@@ -24,14 +40,26 @@ export function AppLoadingBoundary({
24
40
  }) {
25
41
  if (setupRoute !== null) return setupRoute;
26
42
  if (error && operations === null) {
43
+ const copy = errorCopy[locale];
27
44
  return (
28
- <main className="app-state">
29
- <p>Dashboard local indisponível</p>
30
- <h1>Não foi possível ler o workspace V2.</h1>
31
- <pre>{error}</pre>
32
- <button onClick={onRefresh} type="button">
33
- Tentar novamente
34
- </button>
45
+ <main className="app-loading-boundary" aria-labelledby="dashboard-load-error-title">
46
+ <section className="app-loading-boundary__card" role="alert">
47
+ <div aria-hidden="true" className="app-loading-boundary__status-icon">
48
+ <svg aria-hidden="true" focusable="false" viewBox="0 0 24 24">
49
+ <path d="M12 8v4m0 4h.01M10.29 3.86 2.82 16.5A2 2 0 0 0 4.54 19.5h14.92a2 2 0 0 0 1.72-3L13.71 3.86a2 2 0 0 0-3.42 0Z" />
50
+ </svg>
51
+ </div>
52
+ <div className="app-loading-boundary__content">
53
+ <p className="app-loading-boundary__eyebrow">
54
+ {locale === "pt-BR" ? "Dashboard indisponível" : "Dashboard unavailable"}
55
+ </p>
56
+ <h1 id="dashboard-load-error-title">{copy.title}</h1>
57
+ <p>{copy.guidance}</p>
58
+ </div>
59
+ <button onClick={onRefresh} type="button">
60
+ {copy.action}
61
+ </button>
62
+ </section>
35
63
  </main>
36
64
  );
37
65
  }
@@ -0,0 +1,55 @@
1
+ import type { OperationsProjection } from "@zaip-ai/dashboard-operations/contracts";
2
+ import type { WorkBoardStage } from "./dashboard-api.js";
3
+
4
+ export type BoardStageState = { loading: boolean; error: string | null };
5
+ export type OverviewStatusKind = "task" | "spec" | "prd";
6
+ export type OverviewClassification =
7
+ | "lite"
8
+ | "standard"
9
+ | "critical"
10
+ | "empty"
11
+ | "structured"
12
+ | "rework";
13
+ export type OverviewStatusPage = {
14
+ items:
15
+ | OperationsProjection["tasks"]
16
+ | OperationsProjection["specs"]
17
+ | OperationsProjection["prds"];
18
+ total: number;
19
+ loading: boolean;
20
+ error: string | null;
21
+ };
22
+ export type OverviewStatusPages = Partial<
23
+ Record<`${WorkBoardStage}:${OverviewStatusKind}`, OverviewStatusPage>
24
+ >;
25
+ export const overviewStatusPageKey = (stage: WorkBoardStage, kind: OverviewStatusKind) =>
26
+ `${stage}:${kind}` as const;
27
+ export type OverviewClassificationPages = Partial<
28
+ Record<`${OverviewStatusKind}:${OverviewClassification}`, OverviewStatusPage>
29
+ >;
30
+ export const overviewClassificationPageKey = (
31
+ kind: OverviewStatusKind,
32
+ classification: OverviewClassification,
33
+ ) => `${kind}:${classification}` as const;
34
+ export const initialWorkBoardStages: readonly WorkBoardStage[] = [
35
+ "definition",
36
+ "planning",
37
+ "ready",
38
+ "executing",
39
+ "verifying",
40
+ "completed",
41
+ "blocked",
42
+ "cancelled",
43
+ "failed",
44
+ ];
45
+ export const traceTaskSearchStages: readonly WorkBoardStage[] = [
46
+ "completed",
47
+ "executing",
48
+ "ready",
49
+ "blocked",
50
+ "verifying",
51
+ "failed",
52
+ "cancelled",
53
+ "planning",
54
+ "definition",
55
+ ];
@@ -17,6 +17,7 @@ const componentLabels: Record<StackComponentId, { pt: string; en: string }> = {
17
17
  cache_messaging: { pt: "Cache e mensageria", en: "Cache and messaging" },
18
18
  async_queue: { pt: "Filas e processamento assíncrono", en: "Queues and async processing" },
19
19
  backend_infrastructure: { pt: "Infraestrutura backend", en: "Backend infrastructure" },
20
+ backend_hosting: { pt: "Hospedagem do backend", en: "Backend hosting" },
20
21
  web_framework: { pt: "Framework do Web App", en: "Web App framework" },
21
22
  web_language: { pt: "Linguagem do Web App", en: "Web App language" },
22
23
  web_build: { pt: "Build do Web App", en: "Web App build" },
@@ -26,6 +27,14 @@ const componentLabels: Record<StackComponentId, { pt: string; en: string }> = {
26
27
  site_islands: { pt: "Componentes interativos", en: "Interactive components" },
27
28
  site_build: { pt: "Build do site", en: "Site build" },
28
29
  site_hosting: { pt: "Hospedagem do site", en: "Site hosting" },
30
+ prometheus: { pt: "Coleta de métricas", en: "Metrics collection" },
31
+ grafana: { pt: "Dashboards", en: "Dashboards" },
32
+ uptime_kuma: { pt: "Monitoramento de disponibilidade", en: "Availability monitoring" },
33
+ cadvisor: { pt: "Métricas de containers", en: "Container metrics" },
34
+ node_exporter: { pt: "Métricas de host", en: "Host metrics" },
35
+ redis_exporter: { pt: "Métricas do Redis", en: "Redis metrics" },
36
+ k6: { pt: "Teste de carga", en: "Load testing" },
37
+ autocannon: { pt: "Teste HTTP de carga", en: "HTTP load testing" },
29
38
  };
30
39
 
31
40
  export function StackComponentEditor({
@@ -48,6 +57,8 @@ export function StackComponentEditor({
48
57
  scope: "site",
49
58
  title: locale === "pt-BR" ? "Site e landing pages" : "Site and landing pages",
50
59
  },
60
+ { scope: "observability", title: locale === "pt-BR" ? "Observabilidade" : "Observability" },
61
+ { scope: "testing", title: locale === "pt-BR" ? "Testes sob demanda" : "On-demand testing" },
51
62
  ] as const;
52
63
  const update = (
53
64
  id: StackComponentId,
@@ -0,0 +1,98 @@
1
+ .app-loading-boundary {
2
+ display: grid;
3
+ min-height: 100vh;
4
+ padding: 24px;
5
+ place-items: center;
6
+ }
7
+
8
+ .app-loading-boundary__card {
9
+ display: grid;
10
+ width: min(100%, 520px);
11
+ gap: 24px;
12
+ padding: clamp(24px, 5vw, 40px);
13
+ border: 1px solid rgba(255, 255, 255, 0.12);
14
+ border-radius: 24px;
15
+ background: linear-gradient(145deg, rgba(17, 24, 39, 0.94), rgba(10, 14, 23, 0.98));
16
+ box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);
17
+ text-align: center;
18
+ }
19
+
20
+ .app-loading-boundary__status-icon {
21
+ display: grid;
22
+ width: 48px;
23
+ height: 48px;
24
+ margin-inline: auto;
25
+ border: 1px solid rgba(220, 38, 38, 0.42);
26
+ border-radius: 999px;
27
+ background: rgba(220, 38, 38, 0.14);
28
+ color: #fecaca;
29
+ place-items: center;
30
+ }
31
+
32
+ .app-loading-boundary__status-icon svg {
33
+ width: 24px;
34
+ height: 24px;
35
+ fill: none;
36
+ stroke: currentColor;
37
+ stroke-linecap: round;
38
+ stroke-linejoin: round;
39
+ stroke-width: 1.8;
40
+ }
41
+
42
+ .app-loading-boundary__content {
43
+ display: grid;
44
+ gap: 10px;
45
+ }
46
+
47
+ .app-loading-boundary__eyebrow {
48
+ margin: 0;
49
+ color: #fecaca;
50
+ font-size: 0.75rem;
51
+ font-weight: 700;
52
+ letter-spacing: 0.12em;
53
+ line-height: 1.3;
54
+ text-transform: uppercase;
55
+ }
56
+
57
+ .app-loading-boundary h1 {
58
+ margin: 0;
59
+ color: #f1f5f9;
60
+ font-size: clamp(1.75rem, 4vw, 2.25rem);
61
+ font-weight: 650;
62
+ letter-spacing: -0.035em;
63
+ line-height: 1.15;
64
+ }
65
+
66
+ .app-loading-boundary__content > p:last-child {
67
+ max-width: 44ch;
68
+ margin: 0 auto;
69
+ color: #cbd5e1;
70
+ font-size: 1rem;
71
+ line-height: 1.65;
72
+ }
73
+
74
+ .app-loading-boundary button {
75
+ min-height: 44px;
76
+ justify-self: center;
77
+ border: 1px solid transparent;
78
+ border-radius: 999px;
79
+ padding: 12px 22px;
80
+ background: linear-gradient(135deg, #00b0f0 0%, #72f4fb 100%);
81
+ color: #042330;
82
+ cursor: pointer;
83
+ font-weight: 700;
84
+ }
85
+
86
+ .app-loading-boundary button:hover {
87
+ filter: brightness(1.06);
88
+ }
89
+
90
+ @media (max-width: 420px) {
91
+ .app-loading-boundary {
92
+ padding: 16px;
93
+ }
94
+
95
+ .app-loading-boundary__card {
96
+ gap: 20px;
97
+ }
98
+ }
@@ -137,7 +137,7 @@ describe("Cost period refresh", () => {
137
137
  'api.operations(period, "quick", undefined, initialAggregateFreshRef.current)',
138
138
  );
139
139
  expect(compactRefresh).toContain(
140
- 'currency === "USD" ? api.costMonitor(currency, period, "summary") : Promise.resolve(null)',
140
+ 'currency === "USD" || reloadCostSummary ? api.costMonitor(currency, period, "summary") : Promise.resolve(null)',
141
141
  );
142
142
  expect(refresh).toContain("setDisplayedCosts(null);");
143
143
  expect(refresh).toContain("const activeDashboardView = dashboardViewRef.current");
@@ -168,10 +168,10 @@ describe("Cost period refresh", () => {
168
168
  expect(loaders).toMatch(/api\.sqliteViewPage\(\s*"cost_monitor_agent_invocations"/);
169
169
  expect(loaders).toContain("const loadCostGroupPage = useCallback<LoadCostGroupPage>");
170
170
  expect(loaders).toContain("task_count: nullableSqlNumber(row.task_count)");
171
- expect(loaders).toContain("prd_count: Number(row.prd_count ?? 0)");
172
- expect(loaders).toContain("spec_count: Number(row.spec_count ?? 0)");
173
- expect(loaders).toContain("evidence_count: Number(row.evidence_count ?? 0)");
174
- expect(loaders).toContain("test_count: Number(row.validation_count ?? 0)");
171
+ expect(loaders).toContain("prd_count: nullableSqlNumber(row.prd_count)");
172
+ expect(loaders).toContain("spec_count: nullableSqlNumber(row.spec_count)");
173
+ expect(loaders).toContain("evidence_count: nullableSqlNumber(row.evidence_count)");
174
+ expect(loaders).toContain("test_count: nullableSqlNumber(row.validation_count)");
175
175
  for (const dimension of [
176
176
  "provider",
177
177
  "model",
@@ -0,0 +1,33 @@
1
+ import type { ReconciliationResult } from "./dashboard-api.js";
2
+
3
+ export interface ReconcileOpenWorkResponse {
4
+ reconciliation: ReconciliationResult;
5
+ snapshot_state: "current" | "updating";
6
+ projection_refresh_completed: boolean;
7
+ }
8
+
9
+ export function shouldReloadQuickProjectionAfterReconciliation(
10
+ response: ReconcileOpenWorkResponse,
11
+ ): boolean {
12
+ return response.projection_refresh_completed;
13
+ }
14
+
15
+ export function reconciliationCommandOutcome(
16
+ response: ReconcileOpenWorkResponse,
17
+ isCurrentRequest: boolean,
18
+ ): {
19
+ reconciliation: ReconciliationResult;
20
+ dismissProgressDialog: true;
21
+ applySnapshotState: boolean;
22
+ reloadQuickProjection: boolean;
23
+ } {
24
+ return {
25
+ // The command result belongs to the operator's POST, rather than to the
26
+ // projection request version. It must survive a passive GET race.
27
+ reconciliation: response.reconciliation,
28
+ dismissProgressDialog: true,
29
+ applySnapshotState: isCurrentRequest,
30
+ reloadQuickProjection:
31
+ isCurrentRequest && shouldReloadQuickProjectionAfterReconciliation(response),
32
+ };
33
+ }
@@ -1,5 +1,9 @@
1
1
  import { afterEach, describe, expect, it, vi } from "vitest";
2
2
  import { DashboardApiClient, reconciliationRequestTimeoutMs } from "./dashboard-api.js";
3
+ import {
4
+ reconciliationCommandOutcome,
5
+ shouldReloadQuickProjectionAfterReconciliation,
6
+ } from "./dashboard-api-reconciliation.js";
3
7
 
4
8
  describe("DashboardApiClient reconciliation", () => {
5
9
  afterEach(() => {
@@ -53,6 +57,47 @@ describe("DashboardApiClient reconciliation", () => {
53
57
  );
54
58
  });
55
59
 
60
+ it("accepts the compact reconciliation result and reloads only after its projection is ready", async () => {
61
+ vi.stubGlobal("window", globalThis);
62
+ const response = {
63
+ reconciliation: { reconciled_ids: ["TASK-1"], blocked: [], not_reconciled: [] },
64
+ snapshot_state: "updating" as const,
65
+ projection_refresh_completed: false,
66
+ };
67
+ vi.stubGlobal(
68
+ "fetch",
69
+ vi.fn(async () => new Response(JSON.stringify(response), { status: 200 })),
70
+ );
71
+
72
+ await expect(new DashboardApiClient("session").reconcileOpenWork()).resolves.toEqual(response);
73
+ expect(shouldReloadQuickProjectionAfterReconciliation(response)).toBe(false);
74
+ expect(
75
+ shouldReloadQuickProjectionAfterReconciliation({
76
+ ...response,
77
+ snapshot_state: "current",
78
+ projection_refresh_completed: true,
79
+ }),
80
+ ).toBe(true);
81
+ });
82
+
83
+ it("keeps a delayed reconciliation result visible when a passive GET supersedes its request", () => {
84
+ const response = {
85
+ reconciliation: { reconciled_ids: ["TASK-1"], blocked: [], not_reconciled: [] },
86
+ snapshot_state: "current" as const,
87
+ projection_refresh_completed: true,
88
+ };
89
+ // A passive revision GET has advanced requestVersion while the POST was
90
+ // in flight. Its stale quick data must not overwrite the newer screen,
91
+ // while the command outcome still belongs to the user's button click.
92
+ const afterPassiveGet = reconciliationCommandOutcome(response, false);
93
+ expect(afterPassiveGet).toEqual({
94
+ reconciliation: response.reconciliation,
95
+ dismissProgressDialog: true,
96
+ applySnapshotState: false,
97
+ reloadQuickProjection: false,
98
+ });
99
+ });
100
+
56
101
  it("refreshes the disposable projection without calling the reconciliation route", async () => {
57
102
  vi.stubGlobal("window", globalThis);
58
103
  const fetchMock = vi.fn(async () => new Response(JSON.stringify({}), { status: 200 }));
@@ -70,6 +115,36 @@ describe("DashboardApiClient reconciliation", () => {
70
115
  });
71
116
  });
72
117
 
118
+ describe("DashboardApiClient readiness", () => {
119
+ afterEach(() => {
120
+ vi.unstubAllGlobals();
121
+ });
122
+
123
+ it("reads a committed snapshot revision through the authenticated GET endpoint", async () => {
124
+ vi.stubGlobal("window", globalThis);
125
+ const fetchMock = vi.fn(
126
+ async (_path: string, _init?: RequestInit) =>
127
+ new Response(
128
+ JSON.stringify({ snapshot_state: "current", snapshot_revision: "revision-2" }),
129
+ {
130
+ status: 200,
131
+ },
132
+ ),
133
+ );
134
+ vi.stubGlobal("fetch", fetchMock);
135
+
136
+ await expect(new DashboardApiClient("session").ready()).resolves.toEqual({
137
+ snapshot_state: "current",
138
+ snapshot_revision: "revision-2",
139
+ });
140
+ expect(fetchMock).toHaveBeenCalledWith(
141
+ "/api/v2/ready",
142
+ expect.objectContaining({ headers: { "x-zaip-session": "session" } }),
143
+ );
144
+ expect(fetchMock.mock.calls[0]?.[1]).not.toHaveProperty("method");
145
+ });
146
+ });
147
+
73
148
  describe("DashboardApiClient cost monitor", () => {
74
149
  afterEach(() => {
75
150
  vi.unstubAllGlobals();
@@ -10,6 +10,7 @@ import type {
10
10
  TaskDetailProjection,
11
11
  } from "@zaip-ai/dashboard-operations/contracts";
12
12
  import type { TaskTrace } from "@zaip-ai/dashboard-traceability/contracts";
13
+ import type { ReconcileOpenWorkResponse } from "./dashboard-api-reconciliation.js";
13
14
  export interface ReconciliationBlockedItem {
14
15
  id: string;
15
16
  reason: string | null;
@@ -220,7 +221,11 @@ export interface CostMonitorFilters {
220
221
  export class DashboardApiClient {
221
222
  constructor(private readonly token: string) {}
222
223
 
223
- ready(): Promise<{ snapshot_state?: "current" | "updating" }> {
224
+ ready(): Promise<{
225
+ snapshot_state?: "current" | "updating";
226
+ /** Changes only after the local SQLite snapshot is committed. */
227
+ snapshot_revision?: string;
228
+ }> {
224
229
  return this.get("/api/v2/ready");
225
230
  }
226
231
 
@@ -416,13 +421,7 @@ export class DashboardApiClient {
416
421
  reconcileOpenWork(
417
422
  currency: "BRL" | "USD" = "BRL",
418
423
  period?: { from: string | null; to: string | null },
419
- ): Promise<{
420
- operations: OperationsProjection;
421
- costs: CostMonitorProjection;
422
- dashboard?: { title: string; repository_name: string };
423
- projection: { source_latest_at: string | null; served_at: string };
424
- reconciliation: ReconciliationResult;
425
- }> {
424
+ ): Promise<ReconcileOpenWorkResponse> {
426
425
  return this.post(
427
426
  withPeriod(`${dashboardApiPaths.reconcileOpenWork}?currency=${currency}`, period),
428
427
  reconciliationRequestTimeoutMs,