@zaip-ai/cli 1.3.7 → 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 (332) hide show
  1. package/README.md +86 -1
  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 +26 -0
  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 +3 -1
  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-install.d.ts +14 -0
  42. package/dist/services/stack-install.d.ts.map +1 -0
  43. package/dist/services/stack-install.js +176 -0
  44. package/dist/services/stack-install.js.map +1 -0
  45. package/dist/services/stack-node.d.ts +12 -0
  46. package/dist/services/stack-node.d.ts.map +1 -0
  47. package/dist/services/stack-node.js +150 -0
  48. package/dist/services/stack-node.js.map +1 -0
  49. package/dist/services/stack-package-manager.d.ts +12 -0
  50. package/dist/services/stack-package-manager.d.ts.map +1 -0
  51. package/dist/services/stack-package-manager.js +178 -0
  52. package/dist/services/stack-package-manager.js.map +1 -0
  53. package/dist/services/stack-plan.d.ts +35 -0
  54. package/dist/services/stack-plan.d.ts.map +1 -0
  55. package/dist/services/stack-plan.js +130 -0
  56. package/dist/services/stack-plan.js.map +1 -0
  57. package/dist/services/stack-terraform.d.ts +13 -0
  58. package/dist/services/stack-terraform.d.ts.map +1 -0
  59. package/dist/services/stack-terraform.js +274 -0
  60. package/dist/services/stack-terraform.js.map +1 -0
  61. package/dist/services/template-manifest.d.ts.map +1 -1
  62. package/dist/services/template-manifest.js +14 -1
  63. package/dist/services/template-manifest.js.map +1 -1
  64. package/dist/types.d.ts +4 -0
  65. package/dist/types.d.ts.map +1 -1
  66. package/package.json +1 -1
  67. package/templates/.agents/.env.example +34 -0
  68. package/templates/{README.md → .agents/README.md} +124 -60
  69. package/templates/.agents/config/agents/catalog.yaml +23 -23
  70. package/templates/.agents/config/clients/codex/agents/zaip-agent-analyst.toml +1 -0
  71. package/templates/.agents/config/clients/codex/agents/zaip-agent-auditor.toml +1 -0
  72. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +5 -4
  73. package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +1 -0
  74. package/templates/.agents/config/clients/codex/agents/zaip-agent-builder.toml +1 -0
  75. package/templates/.agents/config/clients/codex/agents/zaip-agent-cloud-engineer.toml +10 -7
  76. package/templates/.agents/config/clients/codex/agents/zaip-agent-data-analyst.toml +1 -0
  77. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +1 -0
  78. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +1 -0
  79. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +5 -4
  80. package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +1 -0
  81. package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +1 -0
  82. package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +1 -0
  83. package/templates/.agents/config/clients/codex/agents/zaip-agent-integrator.toml +1 -0
  84. package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +5 -4
  85. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -0
  86. package/templates/.agents/config/clients/codex/agents/zaip-agent-refactoring-specialist.toml +1 -0
  87. package/templates/.agents/config/clients/codex/agents/zaip-agent-reviewer.toml +1 -0
  88. package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +1 -0
  89. package/templates/.agents/config/clients/codex/agents/zaip-agent-security-auditor.toml +1 -0
  90. package/templates/.agents/config/clients/codex/agents/zaip-agent-task-executor.toml +1 -0
  91. package/templates/.agents/config/clients/codex/agents/zaip-agent-website-developer.toml +5 -4
  92. package/templates/.agents/config/clients/codex/agents/zaip-agent-writer.toml +1 -0
  93. package/templates/.agents/config/pricing/models.json +25 -1
  94. package/templates/.agents/config/project.yaml +2 -0
  95. package/templates/.agents/dashboard/.zaip-managed.json +214 -94
  96. package/templates/.agents/dashboard/DATA-DICTIONARY.md +1 -1
  97. package/templates/.agents/dashboard/README.md +58 -19
  98. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-DwVZOmMV.js → CostCalculationGuidePage-snUCQYTC.js} +1 -1
  99. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog--czwAlcH.js → DashboardHelpDialog-oZVlf-l5.js} +787 -723
  100. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CyjMXkm8.js → PricingSettingsDialog-BPtVUGgs.js} +1 -1
  101. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-3Awyaovx.js +3 -0
  102. package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-BomxqCBU.css → ProjectSettingsPage-GgzpZ_PY.css} +1 -1
  103. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CAqYVin1.js → TraceabilityWorkspace-BYjVOHI_.js} +1 -1
  104. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DbaLbU2q.js +68 -0
  105. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DXBOtWNI.js → icons-vendor-TPUT2IUt.js} +1 -1
  106. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Cmlx8OYH.css +1 -0
  107. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-ZJFXmK7m.js +2 -0
  108. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-DaNXQPyk.js → react-vendor-Ct48XHGL.js} +1 -1
  109. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-I4AKqVDX.js → traceability-BDe4_Ubl.js} +1 -1
  110. package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
  111. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +73 -67
  112. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +35 -7
  113. package/templates/.agents/dashboard/apps/frontend/src/AppWorkBoard.ts +55 -0
  114. package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.tsx +15 -2
  115. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +33 -6
  116. package/templates/.agents/dashboard/apps/frontend/src/app-loading-boundary.css +98 -0
  117. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +5 -5
  118. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api-reconciliation.ts +33 -0
  119. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +75 -0
  120. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +9 -8
  121. package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +15 -3
  122. package/templates/.agents/dashboard/apps/frontend/src/project-settings-page.css +0 -104
  123. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +61 -0
  124. package/templates/.agents/dashboard/apps/frontend/src/project-stack-versions.css +117 -0
  125. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +78 -2
  126. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +28 -0
  127. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +8 -5
  128. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +180 -1
  129. package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +7 -4
  130. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +13 -7
  131. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +14 -0
  132. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableDisplay.ts +211 -0
  133. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +152 -157
  134. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +155 -142
  135. package/templates/.agents/dashboard/modules/cost-monitor/src/react/InvocationManifestQuery.test.ts +145 -0
  136. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +7 -5
  137. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +4 -1
  138. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +4 -1
  139. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +4 -1
  140. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +4 -4
  141. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +79 -1
  142. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +105 -11
  143. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +477 -327
  144. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +430 -285
  145. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +14 -2
  146. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +6 -26
  147. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +1 -1
  148. package/templates/.agents/dashboard/packages/local-runtime/scripts/verify-persistent-launch.mjs +268 -0
  149. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +482 -332
  150. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +243 -1
  151. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +252 -41
  152. package/templates/.agents/dashboard/packages/local-runtime/src/server-agent-assets.ts +39 -0
  153. package/templates/.agents/dashboard/packages/local-runtime/src/server-browser-lease.ts +33 -0
  154. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +148 -11
  155. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-result.test.ts +68 -0
  156. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.test.ts +19 -0
  157. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +39 -43
  158. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.test.ts +42 -0
  159. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.ts +26 -0
  160. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.ts +34 -0
  161. package/templates/.agents/dashboard/packages/local-runtime/src/server-startup.ts +30 -0
  162. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.test.ts +89 -0
  163. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.ts +88 -0
  164. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +68 -7
  165. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +11 -5
  166. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +129 -99
  167. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.test.ts +111 -0
  168. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.ts +140 -0
  169. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +127 -4
  170. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +145 -5
  171. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +156 -23
  172. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +18 -9
  173. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-projection.ts +52 -0
  174. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +35 -13
  175. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-force-canonical.test.ts +56 -0
  176. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.test.ts +29 -0
  177. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.ts +136 -0
  178. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-refresh.test.ts +251 -0
  179. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +22 -7
  180. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +1 -1
  181. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical-parsing.ts +42 -0
  182. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical.ts +793 -0
  183. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-duration.test.ts +270 -0
  184. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +3 -2
  185. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +18 -4
  186. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.test.ts +87 -0
  187. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +105 -16
  188. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +131 -59
  189. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1000 -1115
  190. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +13 -92
  191. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-claims.test.mjs +79 -0
  192. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +27 -0
  193. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.mjs +6 -6
  194. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-prerequisites.mjs +1 -1
  195. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +2 -10
  196. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-server.mjs +142 -0
  197. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +231 -239
  198. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +18 -29
  199. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +6 -0
  200. package/templates/.agents/dashboard/stack-dependencies.json +14 -0
  201. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  202. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  203. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +47 -0
  204. package/templates/.agents/requirements/comparative-audit.md +1 -1
  205. package/templates/.agents/requirements/context-engine.md +1 -1
  206. package/templates/.agents/requirements/cost-observability.md +1 -1
  207. package/templates/.agents/requirements/dashboard-launch.md +2 -2
  208. package/templates/.agents/requirements/data-export.md +1 -1
  209. package/templates/.agents/requirements/design-system.md +1 -1
  210. package/templates/.agents/requirements/execution-engine.md +1 -1
  211. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  212. package/templates/.agents/requirements/model-orchestration.md +1 -1
  213. package/templates/.agents/requirements/public-distribution.md +1 -1
  214. package/templates/.agents/requirements/software-architecture.md +1 -1
  215. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  216. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  217. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  218. package/templates/.agents/rules/autonomy-decision.md +1 -1
  219. package/templates/.agents/rules/cloud-hostinger-autonomy.md +21 -0
  220. package/templates/.agents/rules/core.md +5 -4
  221. package/templates/.agents/rules/local-sqlite-storage.md +18 -0
  222. package/templates/.agents/rules/meta-mcp.md +21 -0
  223. package/templates/.agents/rules/workflow.md +1 -0
  224. package/templates/.agents/runtime/bin/zaip.mjs +305 -137
  225. package/templates/.agents/runtime/checksums.sha256 +2 -2
  226. package/templates/.agents/runtime/provenance.json +2 -2
  227. package/templates/.agents/skills/zaip-skill-api-to-mcp/SKILL.md +4 -0
  228. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +25 -9
  229. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +2 -2
  230. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +2 -2
  231. package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +15 -0
  232. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/SKILL.md +13 -5
  233. package/templates/.agents/skills/zaip-skill-data-analyst/SKILL.md +10 -5
  234. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +5 -4
  235. package/templates/.agents/skills/zaip-skill-frontend-launcher/SKILL.md +4 -3
  236. package/templates/.agents/skills/zaip-skill-frontend-launcher/references/launcher-contract.md +5 -3
  237. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +5 -5
  238. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.mjs +78 -13
  239. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.test.mjs +59 -0
  240. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/yaml-parser.mjs +157 -0
  241. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +11 -6
  242. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/SKILL.md +17 -0
  243. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/agents/openai.yaml +7 -0
  244. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-cloud-inputs.example.json +41 -0
  245. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-runner.md +84 -0
  246. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/operational-contract.md +19 -0
  247. package/templates/.agents/skills/zaip-skill-local-credentials/SKILL.md +43 -0
  248. package/templates/.agents/skills/zaip-skill-local-credentials/agents/openai.yaml +6 -0
  249. package/templates/.agents/skills/zaip-skill-local-credentials/references/dotenv-resolution.md +47 -0
  250. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +8 -3
  251. package/templates/.agents/skills/zaip-skill-meta-ads/SKILL.md +16 -0
  252. package/templates/.agents/skills/zaip-skill-meta-ads/agents/openai.yaml +4 -0
  253. package/templates/.agents/skills/zaip-skill-meta-ads/references/setup.md +3 -0
  254. package/templates/.agents/skills/zaip-skill-meta-ads-setup/SKILL.md +24 -0
  255. package/templates/.agents/skills/zaip-skill-meta-ads-setup/agents/openai.yaml +4 -0
  256. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/diagnostics.md +13 -0
  257. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/official-sources.md +10 -0
  258. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/SKILL.md +12 -0
  259. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/agents/openai.yaml +4 -0
  260. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/LICENSE +21 -0
  261. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/upstream.md +77 -0
  262. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/SKILL.md +12 -0
  263. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/agents/openai.yaml +4 -0
  264. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/LICENSE +21 -0
  265. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/upstream.md +126 -0
  266. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/SKILL.md +12 -0
  267. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/agents/openai.yaml +4 -0
  268. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/LICENSE +21 -0
  269. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/upstream.md +67 -0
  270. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/SKILL.md +12 -0
  271. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/agents/openai.yaml +4 -0
  272. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/LICENSE +21 -0
  273. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/upstream.md +69 -0
  274. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/SKILL.md +12 -0
  275. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/agents/openai.yaml +4 -0
  276. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/LICENSE +21 -0
  277. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/upstream.md +79 -0
  278. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/SKILL.md +14 -0
  279. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/agents/openai.yaml +4 -0
  280. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/LICENSE +21 -0
  281. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/upstream.md +144 -0
  282. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/SKILL.md +12 -0
  283. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/agents/openai.yaml +4 -0
  284. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/LICENSE +21 -0
  285. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/upstream.md +83 -0
  286. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/SKILL.md +10 -0
  287. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/agents/openai.yaml +4 -0
  288. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/LICENSE +21 -0
  289. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/setup.md +11 -0
  290. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/upstream.md +55 -0
  291. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/SKILL.md +12 -0
  292. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/agents/openai.yaml +4 -0
  293. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/LICENSE +21 -0
  294. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/upstream.md +56 -0
  295. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +12 -7
  296. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +11 -4
  297. package/templates/.agents/skills/zaip-skill-tool-setup/SKILL.md +6 -4
  298. package/templates/.agents/skills/zaip-skill-tool-setup/references/codex-config.md +4 -2
  299. package/templates/.agents/skills/zaip-skill-tool-setup/references/secrets.md +1 -0
  300. package/templates/.agents/tools/catalog.yaml +6 -0
  301. package/templates/.agents/tools/cloud-runner-dns.mjs +28 -0
  302. package/templates/.agents/tools/cloud-runner-terraform.mjs +212 -0
  303. package/templates/.agents/tools/cloud-runner.integration.test.mjs +239 -0
  304. package/templates/.agents/tools/cloud-runner.mjs +786 -0
  305. package/templates/.agents/tools/cloud-runner.test.mjs +534 -0
  306. package/templates/.agents/tools/cloud-terraform-plan-policy.mjs +262 -0
  307. package/templates/.agents/tools/mcp/development/meta-devtools/README.md +14 -0
  308. package/templates/.agents/tools/mcp/development/meta-devtools/manifest.yaml +28 -0
  309. package/templates/.agents/tools/mcp/infra/cloudflare/manifest.yaml +2 -3
  310. package/templates/.agents/tools/mcp/infra/hostinger/manifest.yaml +1 -3
  311. package/templates/.agents/tools/mcp/marketing/meta-ads/README.md +15 -13
  312. package/templates/.agents/tools/mcp/marketing/meta-ads/manifest.yaml +15 -10
  313. package/templates/.agents/tools/profiles/development.yaml +3 -0
  314. package/templates/infra/terraform/.terraform.lock.hcl +42 -0
  315. package/templates/infra/terraform/README.md +31 -0
  316. package/templates/infra/terraform/backend.tf.json +13 -0
  317. package/templates/infra/terraform/deployment-inputs.example.yaml +22 -0
  318. package/templates/infra/terraform/fixtures/approved-targets.json +32 -0
  319. package/templates/infra/terraform/fixtures/plan-approved.json +67 -0
  320. package/templates/infra/terraform/main.tf +77 -0
  321. package/templates/infra/terraform/outputs.tf +20 -0
  322. package/templates/infra/terraform/terraform.tfvars.example +26 -0
  323. package/templates/infra/terraform/variables.tf +91 -0
  324. package/templates/infra/terraform/versions.tf +18 -0
  325. package/templates/scripts/validate-cloud-hostinger-contract.mjs +89 -0
  326. package/templates/scripts/validate-cloud-terraform-plan.mjs +1 -0
  327. package/templates/template-manifest.json +613 -173
  328. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-B_Oj6Dlf.js +0 -3
  329. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-C09KmlSw.js +0 -68
  330. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-CblPpexd.js +0 -2
  331. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-hPPfVGQv.css +0 -1
  332. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-stale-seed.ts +0 -50
@@ -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,
@@ -794,8 +793,10 @@ export interface ProjectOverview {
794
793
  stack_components: Array<{
795
794
  name: string;
796
795
  version: string | null;
796
+ configured_version: string | null;
797
797
  source: string | null;
798
798
  verification: "exact" | "constraint" | "unavailable";
799
+ installation: "runtime" | "installed" | "missing" | "bundled";
799
800
  }>;
800
801
  areas: Array<{
801
802
  id:
@@ -9,7 +9,8 @@ export const stackComponentDefinitions = [
9
9
  { id: "auth_storage", defaultName: "Supabase Auth + Storage", scope: "backend" },
10
10
  { id: "cache_messaging", defaultName: "Redis", scope: "backend" },
11
11
  { id: "async_queue", defaultName: "BullMQ", scope: "backend" },
12
- { id: "backend_infrastructure", defaultName: "Hostinger VPS", scope: "backend" },
12
+ { id: "backend_infrastructure", defaultName: "Terraform", scope: "backend" },
13
+ { id: "backend_hosting", defaultName: "Hostinger VPS", scope: "backend" },
13
14
  { id: "web_framework", defaultName: "React", scope: "frontend" },
14
15
  { id: "web_language", defaultName: "TypeScript", scope: "frontend" },
15
16
  { id: "web_build", defaultName: "Vite", scope: "frontend" },
@@ -19,6 +20,14 @@ export const stackComponentDefinitions = [
19
20
  { id: "site_islands", defaultName: "React Islands", scope: "site" },
20
21
  { id: "site_build", defaultName: "Vite", scope: "site" },
21
22
  { id: "site_hosting", defaultName: "Cloudflare Pages", scope: "site" },
23
+ { id: "prometheus", defaultName: "Prometheus", scope: "observability" },
24
+ { id: "grafana", defaultName: "Grafana", scope: "observability" },
25
+ { id: "uptime_kuma", defaultName: "Uptime Kuma", scope: "observability" },
26
+ { id: "cadvisor", defaultName: "cAdvisor", scope: "observability" },
27
+ { id: "node_exporter", defaultName: "node-exporter", scope: "observability" },
28
+ { id: "redis_exporter", defaultName: "Redis Exporter", scope: "observability" },
29
+ { id: "k6", defaultName: "k6", scope: "testing" },
30
+ { id: "autocannon", defaultName: "autocannon", scope: "testing" },
22
31
  ] as const;
23
32
 
24
33
  export type StackComponentId = (typeof stackComponentDefinitions)[number]["id"];
@@ -60,7 +69,7 @@ export function withDetectedStackVersions(
60
69
  ): StackComponentsDraft {
61
70
  const versions = new Map(
62
71
  detected
63
- .filter((component) => component.verification === "exact" && component.version !== null)
72
+ .filter((component) => component.installation === "installed" && component.version !== null)
64
73
  .map((component) => [component.name.toLowerCase(), component.version as string]),
65
74
  );
66
75
  return Object.fromEntries(
@@ -107,13 +116,16 @@ export function buildProjectSettingsPayload(
107
116
  },
108
117
  ]),
109
118
  ) as StackComponentsDraft;
119
+ const existingComponents = isRecord(config.stack.overrides.components)
120
+ ? config.stack.overrides.components
121
+ : {};
110
122
  return {
111
123
  ...config,
112
124
  stack: {
113
125
  ...config.stack,
114
126
  overrides: {
115
127
  ...otherOverrides,
116
- components: normalized,
128
+ components: { ...existingComponents, ...normalized },
117
129
  languages: {
118
130
  frontend: componentNames(normalized, "frontend"),
119
131
  backend: componentNames(normalized, "backend"),
@@ -396,88 +396,6 @@
396
396
  line-height: 1.45;
397
397
  }
398
398
 
399
- .project-stack-versions {
400
- margin: 0 22px 22px;
401
- }
402
-
403
- .project-stack-versions h3 {
404
- margin: 0 0 12px;
405
- font-size: 0.9rem;
406
- }
407
-
408
- .project-stack-versions > div {
409
- display: grid;
410
- grid-template-columns: repeat(2, minmax(0, 1fr));
411
- gap: 8px;
412
- }
413
-
414
- .project-stack-versions article {
415
- display: grid;
416
- grid-template-columns: minmax(0, 1fr) auto;
417
- grid-template-areas:
418
- "identity badge"
419
- "source source";
420
- gap: 7px 12px;
421
- align-items: start;
422
- min-width: 0;
423
- border: 1px solid #dbe4ef;
424
- border-radius: 12px;
425
- padding: 9px 12px;
426
- background: #f8fafc;
427
- }
428
-
429
- .project-stack-version-identity {
430
- display: flex;
431
- grid-area: identity;
432
- align-items: baseline;
433
- gap: 8px;
434
- min-width: 0;
435
- }
436
-
437
- .project-stack-version-identity strong {
438
- min-width: 0;
439
- color: #334155;
440
- font-size: 0.8rem;
441
- }
442
-
443
- .project-stack-version-identity code {
444
- flex: none;
445
- min-width: 0;
446
- color: #0f172a;
447
- font-size: 0.8rem;
448
- overflow-wrap: anywhere;
449
- }
450
-
451
- .project-stack-versions article small {
452
- grid-area: source;
453
- min-width: 0;
454
- color: #64748b;
455
- font-size: 0.68rem;
456
- line-height: 1.35;
457
- overflow-wrap: anywhere;
458
- }
459
-
460
- .project-stack-versions article span {
461
- grid-area: badge;
462
- width: fit-content;
463
- border-radius: 999px;
464
- padding: 3px 6px;
465
- font-size: 0.68rem;
466
- font-weight: 700;
467
- }
468
- .project-stack-versions .is-exact {
469
- background: #f0fdf4;
470
- color: #166534;
471
- }
472
- .project-stack-versions .is-constraint {
473
- background: #fff7ed;
474
- color: #9a3412;
475
- }
476
- .project-stack-versions .is-unavailable {
477
- background: #fef2f2;
478
- color: #b91c1c;
479
- }
480
-
481
399
  .project-settings-advanced summary {
482
400
  min-height: 48px;
483
401
  padding-top: 15px;
@@ -739,28 +657,6 @@
739
657
  grid-template-columns: 1fr;
740
658
  }
741
659
 
742
- .project-stack-versions article {
743
- grid-template-areas:
744
- "identity badge"
745
- "source source";
746
- grid-template-columns: minmax(0, 1fr) auto;
747
- gap: 6px 10px;
748
- align-items: start;
749
- padding: 10px 12px;
750
- }
751
-
752
- .project-stack-versions > div {
753
- grid-template-columns: 1fr;
754
- }
755
-
756
- .project-stack-versions article span {
757
- align-self: start;
758
- }
759
-
760
- .project-stack-versions article small {
761
- text-align: left;
762
- }
763
-
764
660
  .project-stack-table {
765
661
  display: grid;
766
662
  gap: 12px;
@@ -4,6 +4,7 @@ import {
4
4
  buildProjectSettingsPayload,
5
5
  projectSettingsDraft,
6
6
  shortFrameworkVersion,
7
+ withDetectedStackVersions,
7
8
  } from "./project-settings-model.js";
8
9
 
9
10
  const config: ProjectConfigSettings = {
@@ -69,6 +70,7 @@ describe("project settings form mapping", () => {
69
70
  "Supabase Auth + Storage",
70
71
  "Redis",
71
72
  "BullMQ",
73
+ "Terraform",
72
74
  "Hostinger VPS",
73
75
  ],
74
76
  site: ["Astro", "TypeScript", "React Islands", "Vite", "Cloudflare Pages"],
@@ -97,6 +99,40 @@ describe("project settings form mapping", () => {
97
99
  ).toThrow("deve ser exata");
98
100
  });
99
101
 
102
+ it("keeps observability and on-demand testing tools editable with pinned-or-empty versions", () => {
103
+ const configured = {
104
+ ...config,
105
+ stack: {
106
+ ...config.stack,
107
+ overrides: {
108
+ ...config.stack.overrides,
109
+ components: { future_tool: { enabled: true } },
110
+ },
111
+ },
112
+ } satisfies ProjectConfigSettings;
113
+ const draft = projectSettingsDraft(configured);
114
+ const components = {
115
+ ...draft.components,
116
+ prometheus: { name: "Prometheus", version: "3.6.0", reference: "docker:prom/prometheus" },
117
+ grafana: { name: "Grafana", version: "", reference: "docker:grafana/grafana" },
118
+ k6: { name: "k6", version: "1.1.0", reference: "docker:grafana/k6" },
119
+ autocannon: { name: "autocannon", version: "", reference: "npm:autocannon" },
120
+ };
121
+
122
+ const payload = buildProjectSettingsPayload(configured, components, draft.otherOverrides);
123
+
124
+ expect(payload.stack.overrides).toMatchObject({
125
+ components: {
126
+ prometheus: components.prometheus,
127
+ grafana: components.grafana,
128
+ uptime_kuma: { name: "Uptime Kuma", version: "", reference: "" },
129
+ k6: components.k6,
130
+ autocannon: components.autocannon,
131
+ future_tool: { enabled: true },
132
+ },
133
+ });
134
+ });
135
+
100
136
  it("keeps execution and assurance settings in the save payload", () => {
101
137
  const configured: ProjectConfigSettings = {
102
138
  ...config,
@@ -121,4 +157,29 @@ describe("project settings form mapping", () => {
121
157
  expect(payload.execution).toEqual(configured.execution);
122
158
  expect(payload.assurance).toEqual(configured.assurance);
123
159
  });
160
+
161
+ it("uses only project-local installed package versions to fill an empty form field", () => {
162
+ const draft = projectSettingsDraft(config).components;
163
+ const detected = withDetectedStackVersions(draft, [
164
+ {
165
+ name: "Hono",
166
+ version: "4.12.35",
167
+ configured_version: "4.12.34",
168
+ source: "node_modules/hono/package.json",
169
+ verification: "exact",
170
+ installation: "installed",
171
+ },
172
+ {
173
+ name: "React",
174
+ version: "19.2.7",
175
+ configured_version: "19.2.7",
176
+ source: ".agents/dashboard/apps/frontend/dist",
177
+ verification: "constraint",
178
+ installation: "bundled",
179
+ },
180
+ ]);
181
+
182
+ expect(detected.backend_api.version).toBe("4.12.35");
183
+ expect(detected.web_framework.version).toBe("");
184
+ });
124
185
  });
@@ -0,0 +1,117 @@
1
+ .project-stack-versions {
2
+ margin: 0 22px 22px;
3
+ }
4
+
5
+ .project-stack-versions h3 {
6
+ margin: 0 0 12px;
7
+ font-size: 0.9rem;
8
+ }
9
+
10
+ .project-stack-versions > div {
11
+ display: grid;
12
+ grid-template-columns: repeat(2, minmax(0, 1fr));
13
+ gap: 8px;
14
+ }
15
+
16
+ .project-stack-versions article {
17
+ display: grid;
18
+ grid-template-columns: minmax(0, 1fr) auto;
19
+ grid-template-areas:
20
+ "identity badge"
21
+ "source source";
22
+ gap: 7px 12px;
23
+ align-items: start;
24
+ min-width: 0;
25
+ border: 1px solid #dbe4ef;
26
+ border-radius: 12px;
27
+ padding: 9px 12px;
28
+ background: #f8fafc;
29
+ }
30
+
31
+ .project-stack-version-identity {
32
+ display: grid;
33
+ grid-area: identity;
34
+ grid-template-columns: auto minmax(0, 1fr);
35
+ gap: 2px 8px;
36
+ min-width: 0;
37
+ }
38
+
39
+ .project-stack-version-identity strong {
40
+ min-width: 0;
41
+ color: #334155;
42
+ font-size: 0.8rem;
43
+ }
44
+
45
+ .project-stack-version-identity code {
46
+ min-width: 0;
47
+ color: #0f172a;
48
+ font-size: 0.8rem;
49
+ overflow-wrap: anywhere;
50
+ }
51
+
52
+ .project-stack-version-identity code + code {
53
+ grid-column: 2;
54
+ }
55
+
56
+ .project-stack-versions article small {
57
+ grid-area: source;
58
+ min-width: 0;
59
+ color: #64748b;
60
+ font-size: 0.68rem;
61
+ line-height: 1.35;
62
+ overflow-wrap: anywhere;
63
+ }
64
+
65
+ .project-stack-versions article span {
66
+ grid-area: badge;
67
+ width: fit-content;
68
+ border-radius: 999px;
69
+ padding: 3px 6px;
70
+ font-size: 0.68rem;
71
+ font-weight: 700;
72
+ }
73
+ .project-stack-versions .is-exact {
74
+ background: #f0fdf4;
75
+ color: #166534;
76
+ }
77
+ .project-stack-versions .is-constraint {
78
+ background: #fff7ed;
79
+ color: #9a3412;
80
+ }
81
+ .project-stack-versions .is-unavailable {
82
+ background: #fef2f2;
83
+ color: #b91c1c;
84
+ }
85
+
86
+ @media (max-width: 820px) {
87
+ .project-stack-versions article {
88
+ grid-template-areas:
89
+ "identity"
90
+ "badge"
91
+ "source";
92
+ grid-template-columns: minmax(0, 1fr);
93
+ gap: 6px 10px;
94
+ align-items: start;
95
+ padding: 10px 12px;
96
+ }
97
+
98
+ .project-stack-versions > div {
99
+ grid-template-columns: 1fr;
100
+ }
101
+
102
+ .project-stack-version-identity {
103
+ grid-template-columns: 1fr;
104
+ }
105
+
106
+ .project-stack-version-identity code + code {
107
+ grid-column: 1;
108
+ }
109
+
110
+ .project-stack-versions article span {
111
+ align-self: start;
112
+ }
113
+
114
+ .project-stack-versions article small {
115
+ text-align: left;
116
+ }
117
+ }
@@ -2,7 +2,20 @@ import { readFileSync } from "node:fs";
2
2
  import { describe, expect, it } from "vitest";
3
3
  import { isCurrentDashboardRequest, mergeBoardPages, needsAtomicCostSnapshot } from "./App.js";
4
4
  import { loadAtomicCostSnapshot } from "./snapshot-coherence.js";
5
- import { projectionRefreshDebounceMs, shouldRefreshVisibleProjection } from "./use-app-effects.js";
5
+ import {
6
+ isCommittedSnapshotRevision,
7
+ projectionRefreshDebounceMs,
8
+ settleSnapshotRevisionRefresh,
9
+ shouldPollSnapshotReady,
10
+ shouldRefreshForSnapshotRevision,
11
+ shouldRefreshVisibleProjection,
12
+ snapshotReadyPollAttempts,
13
+ snapshotReadyPollExhaustedMessage,
14
+ snapshotRevisionHiddenFailurePollMs,
15
+ snapshotRevisionHiddenPollMs,
16
+ snapshotRevisionPollDelay,
17
+ snapshotRevisionVisiblePollMs,
18
+ } from "./use-app-effects.js";
6
19
 
7
20
  describe("atomic Cost snapshot loading", () => {
8
21
  it("keeps Operations, Cost, and cards on one source generation while the source moves", async () => {
@@ -82,7 +95,7 @@ describe("atomic Cost snapshot loading", () => {
82
95
  expect(retry).not.toContain("loadCosts");
83
96
  });
84
97
 
85
- it("does not prefetch or poll a newer Operations snapshot in the background", () => {
98
+ it("does not prefetch a newer Operations snapshot in the background", () => {
86
99
  const app = readFileSync(new URL("./App.tsx", import.meta.url), "utf8");
87
100
  const effects = readFileSync(new URL("./use-app-effects.ts", import.meta.url), "utf8");
88
101
  const costLoaders = readFileSync(new URL("./use-app-cost-loaders.ts", import.meta.url), "utf8");
@@ -135,6 +148,69 @@ describe("atomic Cost snapshot loading", () => {
135
148
  expect(effects).toContain("void refresh(false, false, true)");
136
149
  });
137
150
 
151
+ it("polls ready only while a startup snapshot reports updating", () => {
152
+ expect(shouldPollSnapshotReady(true, 0)).toBe(true);
153
+ expect(shouldPollSnapshotReady(true, snapshotReadyPollAttempts - 1)).toBe(true);
154
+ expect(shouldPollSnapshotReady(true, snapshotReadyPollAttempts)).toBe(false);
155
+ expect(shouldPollSnapshotReady(false, 0)).toBe(false);
156
+ expect(snapshotReadyPollExhaustedMessage).toContain("desatualizados");
157
+ const effects = readFileSync(new URL("./use-app-effects.ts", import.meta.url), "utf8");
158
+ expect(effects).toContain("const ready = await api.ready()");
159
+ expect(effects).toContain("await refresh(false, false, false, true)");
160
+ expect(effects).not.toContain("refresh(false, true");
161
+ expect(effects).toContain("setSnapshotUpdating(false);");
162
+ expect(effects).toContain("setError(snapshotReadyPollExhaustedMessage)");
163
+ });
164
+
165
+ it("observes committed snapshot revisions from an open tab with GET-only refreshes", () => {
166
+ expect(snapshotRevisionPollDelay("visible", 0)).toBe(snapshotRevisionVisiblePollMs);
167
+ expect(snapshotRevisionPollDelay("hidden", 0)).toBe(snapshotRevisionHiddenPollMs);
168
+ expect(snapshotRevisionPollDelay("hidden", 1)).toBe(snapshotRevisionHiddenFailurePollMs);
169
+ const committedHash = "40fb07a77e3f";
170
+ const nextCommittedHash = "1c83d5ea7142";
171
+ expect(shouldRefreshForSnapshotRevision(null, committedHash)).toBe(false);
172
+ expect(shouldRefreshForSnapshotRevision(committedHash, undefined)).toBe(false);
173
+ expect(shouldRefreshForSnapshotRevision(committedHash, committedHash)).toBe(false);
174
+ // `updating` only means a later generation is being prepared. The hash
175
+ // still identifies the already committed SQLite generation and can make
176
+ // an open tab reload when it differs from the last applied one.
177
+ expect(isCommittedSnapshotRevision("updating", nextCommittedHash)).toBe(true);
178
+ expect(shouldRefreshForSnapshotRevision(committedHash, nextCommittedHash)).toBe(true);
179
+ expect(isCommittedSnapshotRevision("current", undefined)).toBe(false);
180
+ expect(isCommittedSnapshotRevision("current", nextCommittedHash)).toBe(true);
181
+
182
+ const afterFailedReload = settleSnapshotRevisionRefresh({
183
+ observedRevision: "revision-1",
184
+ pendingRevision: "revision-2",
185
+ requestedRevision: "revision-2",
186
+ succeeded: false,
187
+ });
188
+ expect(afterFailedReload).toEqual({
189
+ observedRevision: "revision-1",
190
+ pendingRevision: "revision-2",
191
+ });
192
+ expect(
193
+ settleSnapshotRevisionRefresh({
194
+ ...afterFailedReload,
195
+ requestedRevision: "revision-2",
196
+ succeeded: true,
197
+ }),
198
+ ).toEqual({ observedRevision: "revision-2", pendingRevision: null });
199
+
200
+ const effects = readFileSync(new URL("./use-app-effects.ts", import.meta.url), "utf8");
201
+ expect(effects).toContain("const observedSnapshotRevision = useRef<string | null>(null)");
202
+ expect(effects).toContain("pendingSnapshotRevisionRefresh");
203
+ expect(effects).toContain(
204
+ "isCommittedSnapshotRevision(ready.snapshot_state, ready.snapshot_revision)",
205
+ );
206
+ expect(effects).toContain("settleSnapshotRevisionRefresh({");
207
+ expect(effects).toContain(
208
+ "snapshotRevisionPollDelay(document.visibilityState, consecutiveFailures)",
209
+ );
210
+ expect(effects).toContain("void refresh(false, false, false, true)");
211
+ expect(effects).not.toContain("refresh(false, false, true, true)");
212
+ });
213
+
138
214
  it("does not reconcile open work automatically", () => {
139
215
  const app = readFileSync(new URL("./App.tsx", import.meta.url), "utf8");
140
216
  expect(app).not.toContain("startupReconciliation");
@@ -119,6 +119,34 @@ describe("daily Cost Monitor continuous pagination", () => {
119
119
  });
120
120
  });
121
121
 
122
+ describe("SQLite Cost group duration adapter", () => {
123
+ it("prefers total_duration over a synthesized duration_known zero and uses zero only when absent", async () => {
124
+ const api = {
125
+ sqliteViewPage: async () => ({
126
+ offset: 0,
127
+ limit: 10,
128
+ total_count: 2,
129
+ has_more: false,
130
+ rows: [
131
+ { key: "gpt-5.6-sol", total_duration: 285_480_000, duration_known: 0 },
132
+ { key: "unavailable", total_duration: null, duration_known: undefined },
133
+ ],
134
+ }),
135
+ };
136
+ const page = await loadSqliteCostGroupPage(api as never, {
137
+ view: "models",
138
+ offset: 0,
139
+ limit: 10,
140
+ filters: {} as never,
141
+ });
142
+
143
+ expect(page.rows).toMatchObject([
144
+ { key: "gpt-5.6-sol", duration_ms: 285_480_000, duration: { known_sum: 285_480_000 } },
145
+ { key: "unavailable", duration_ms: null, duration: { known_sum: 0 } },
146
+ ]);
147
+ });
148
+ });
149
+
122
150
  describe("adaptSqliteCostRun", () => {
123
151
  it("renders a sparse API invocation row without throwing and marks missing multipliers unavailable", () => {
124
152
  const run = adaptSqliteCostRun({
@@ -40,7 +40,9 @@ function costGroupQueryOptions(request: CostGroupLoadRequest, offset: number, li
40
40
  function adaptSqliteCostGroup(row: Record<string, unknown>) {
41
41
  const duration = costGroupDuration(row);
42
42
  const durationUnknown = Number(row.duration_unknown ?? (duration === null ? 1 : 0));
43
- const durationKnown = nullableSqlNumber(row.duration_known) ?? duration ?? 0;
43
+ // Model rows may expose only `total_duration`. Prefer it over a legacy
44
+ // synthesized zero in `duration_known` so the table and export agree.
45
+ const durationKnown = duration ?? nullableSqlNumber(row.duration_known) ?? 0;
44
46
  return {
45
47
  key: String(row.key ?? row.period_key ?? "total"),
46
48
  label: String(row.label ?? row.key ?? row.period_key ?? ""),
@@ -49,10 +51,11 @@ function adaptSqliteCostGroup(row: Record<string, unknown>) {
49
51
  job: typeof row.job === "string" ? row.job : null,
50
52
  run_count: Number(row.total_run_count ?? row.invocation_count ?? 0),
51
53
  task_count: nullableSqlNumber(row.task_count),
52
- prd_count: Number(row.prd_count ?? 0),
53
- spec_count: Number(row.spec_count ?? 0),
54
- evidence_count: Number(row.evidence_count ?? 0),
55
- test_count: Number(row.validation_count ?? 0),
54
+ conversation_count: Number(row.conversation_count ?? 0),
55
+ prd_count: nullableSqlNumber(row.prd_count),
56
+ spec_count: nullableSqlNumber(row.spec_count),
57
+ evidence_count: nullableSqlNumber(row.evidence_count),
58
+ test_count: nullableSqlNumber(row.validation_count),
56
59
  event_count: Number(row.source_event_count ?? 0),
57
60
  total_tokens: nullableSqlNumber(row.total_tokens),
58
61
  estimated_usd: nullableSqlNumber(row.total_cost_usd),