@zaip-ai/cli 1.0.8 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/README.md +1 -1
  2. package/dist/services/adapters.d.ts +1 -1
  3. package/dist/services/adapters.js +1 -1
  4. package/dist/services/agent-adapters.js +2 -2
  5. package/dist/services/agent-adapters.js.map +1 -1
  6. package/dist/services/agent-adapters.test.js +5 -0
  7. package/dist/services/agent-adapters.test.js.map +1 -1
  8. package/dist/services/logger.test.js +2 -2
  9. package/dist/services/runtime-template.test.js +6 -6
  10. package/dist/services/template-manifest.d.ts.map +1 -1
  11. package/dist/services/template-manifest.js +5 -0
  12. package/dist/services/template-manifest.js.map +1 -1
  13. package/package.json +1 -1
  14. package/templates/.agents/architecture/software-architecture.md +131 -0
  15. package/templates/.agents/branding/brandbook.docx +0 -0
  16. package/templates/.agents/branding/brandbook.md +60 -0
  17. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +1 -1
  18. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -1
  19. package/templates/.agents/dashboard/.tmp-run-list.json +254 -0
  20. package/templates/.agents/dashboard/.zaip-managed.json +252 -136
  21. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-Z60df2r-.js → CostCalculationGuidePage-DxR_pgSV.js} +1 -1
  22. package/templates/.agents/dashboard/apps/frontend/dist/assets/DashboardHelpDialog-4jZLTWE_.js +2156 -0
  23. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-Coc6pA-1.js → PricingSettingsDialog-CKcsqb-r.js} +1 -1
  24. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BomxqCBU.css +1 -0
  25. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-DmmJD2Th.js +3 -0
  26. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BwtitWa_.js +1 -0
  27. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-N5Csr0RP.js +68 -0
  28. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DFBuc_qp.js → icons-vendor-C0fj2uU9.js} +1 -1
  29. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-C4aIB1PV.js +2 -0
  30. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-CCQOHdVI.js → react-vendor-BEsJ-FYH.js} +1 -1
  31. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-pC2_0J8x.js → traceability-B6y6Gw12.js} +1 -1
  32. package/templates/.agents/dashboard/apps/frontend/dist/index.html +5 -5
  33. package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
  34. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +204 -32
  35. package/templates/.agents/dashboard/apps/frontend/src/DashboardAlternateContent.tsx +11 -11
  36. package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.tsx +14 -7
  37. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +4 -2
  38. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.test.ts +3 -1
  39. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.tsx +14 -11
  40. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +19 -4
  41. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +24 -2
  42. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +22 -1
  43. package/templates/.agents/dashboard/apps/frontend/src/overview-status-page.test.ts +3 -1
  44. package/templates/.agents/dashboard/apps/frontend/src/project-settings-page.css +36 -26
  45. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +2 -2
  46. package/templates/.agents/dashboard/apps/frontend/src/snapshot-banner-semantics.test.ts +40 -6
  47. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +39 -1
  48. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +89 -23
  49. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +219 -29
  50. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +61 -7
  51. package/templates/.agents/dashboard/apps/frontend/src/use-app-operations-pages.ts +3 -6
  52. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.test.ts +2 -2
  53. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.ts +3 -4
  54. package/templates/.agents/dashboard/apps/frontend/vite.config.ts +4 -1
  55. package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
  56. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.test.ts +89 -19
  57. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.tsx +31 -16
  58. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.test.ts +43 -14
  59. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.tsx +5 -5
  60. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostModelsAuditTable.tsx +1 -1
  61. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +67 -42
  62. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +115 -39
  63. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +188 -38
  64. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +75 -17
  65. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +147 -58
  66. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor.css +5 -2
  67. package/templates/.agents/dashboard/modules/cost-monitor/src/react/index.ts +2 -2
  68. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +2 -1
  69. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +18 -7
  70. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +95 -9
  71. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +29 -17
  72. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.test.ts +0 -24
  73. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.test.ts +225 -424
  74. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +24 -15
  75. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-terminal-day.test.ts +6 -1
  76. package/templates/.agents/dashboard/modules/cost-monitor/src/server/source-size-cost-server/build-cost-monitor-projection-agents.test.ts +31 -0
  77. package/templates/.agents/dashboard/modules/cost-monitor/src/server/source-size-cost-server/build-cost-summary-projection-duration.test.ts +414 -0
  78. package/templates/.agents/dashboard/modules/operations/package.json +1 -1
  79. package/templates/.agents/dashboard/modules/operations/src/contracts.ts +26 -23
  80. package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +31 -11
  81. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +3 -1
  82. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +51 -21
  83. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +47 -5
  84. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageNavigation.tsx +1 -4
  85. package/templates/.agents/dashboard/modules/operations/src/react/PeriodSummaryTable.tsx +32 -10
  86. package/templates/.agents/dashboard/modules/operations/src/react/TaskDetailsDialog.tsx +9 -3
  87. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.tsx +13 -5
  88. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.test.ts +12 -4
  89. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.tsx +1 -4
  90. package/templates/.agents/dashboard/modules/operations/src/react/conversation-rows.ts +1 -2
  91. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +385 -4
  92. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +481 -61
  93. package/templates/.agents/dashboard/modules/operations/src/react/export/export-layout.test.ts +14 -1
  94. package/templates/.agents/dashboard/modules/operations/src/react/export/xlsx-runtime-e2e.test.ts +248 -0
  95. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.test.ts +4 -4
  96. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.ts +6 -4
  97. package/templates/.agents/dashboard/modules/operations/src/react/operations-global-filters.test.ts +4 -1
  98. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +1 -1
  99. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.status-cards.test.ts +1 -1
  100. package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +46 -6
  101. package/templates/.agents/dashboard/modules/operations/src/react/operations-view-order.test.ts +1 -4
  102. package/templates/.agents/dashboard/modules/operations/src/react/period-summary.test.ts +82 -6
  103. package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.test.ts +2 -1
  104. package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.ts +1 -1
  105. package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.test.ts +19 -7
  106. package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.ts +4 -1
  107. package/templates/.agents/dashboard/modules/operations/src/server/quick-daily-operations-projection.ts +11 -5
  108. package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
  109. package/templates/.agents/dashboard/packages/branding/package.json +1 -1
  110. package/templates/.agents/dashboard/packages/branding/src/index.ts +1 -1
  111. package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
  112. package/templates/.agents/dashboard/packages/contracts/src/index.ts +1 -0
  113. package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
  114. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +152 -149
  115. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs.metafile.json +7333 -0
  116. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +149 -146
  117. package/templates/.agents/dashboard/packages/local-runtime/package.json +1 -1
  118. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-dashboard.mjs +15 -7
  119. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +44 -13
  120. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +5 -2
  121. package/templates/.agents/dashboard/packages/local-runtime/scripts/source-size-runtime-scripts/sqlite-read-model-fixture.mjs +397 -0
  122. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-read-model.mjs +472 -145
  123. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-unix-launchers.mjs +15 -3
  124. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +129 -31
  125. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +153 -150
  126. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +6 -2
  127. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +3 -3
  128. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.test.ts +55 -16
  129. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.ts +23 -13
  130. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +19 -4
  131. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +7 -3
  132. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +132 -9
  133. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +126 -60
  134. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-metric-contract.ts +16 -2
  135. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +1861 -350
  136. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1218 -352
  137. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +119 -42
  138. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +45 -20
  139. package/templates/.agents/dashboard/packages/ui/package.json +1 -1
  140. package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
  141. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +7 -5
  142. package/templates/.agents/design/.zaip-managed.json +3 -3
  143. package/templates/.agents/design/apps/catalog/package.json +1 -1
  144. package/templates/.agents/design/packages/local-runtime/package.json +1 -1
  145. package/templates/.agents/growth/estrategia-bootstrap-90-dias.md +169 -0
  146. package/templates/.agents/growth/strategy.md +27 -0
  147. package/templates/.agents/marketing/estrategia-marketing-zaip-90-dias.md +197 -0
  148. package/templates/.agents/marketing/strategy.md +29 -0
  149. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  150. package/templates/.agents/requirements/comparative-audit.md +1 -1
  151. package/templates/.agents/requirements/context-engine.md +1 -1
  152. package/templates/.agents/requirements/cost-observability.md +1 -1
  153. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  154. package/templates/.agents/requirements/data-export.md +1 -1
  155. package/templates/.agents/requirements/design-system.md +1 -1
  156. package/templates/.agents/requirements/execution-engine.md +1 -1
  157. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  158. package/templates/.agents/requirements/model-orchestration.md +1 -1
  159. package/templates/.agents/requirements/public-distribution.md +1 -1
  160. package/templates/.agents/requirements/software-architecture.md +1 -1
  161. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  162. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  163. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  164. package/templates/.agents/runtime/VERSION +1 -1
  165. package/templates/.agents/runtime/bin/zaip.mjs +30 -29
  166. package/templates/.agents/runtime/checksums.sha256 +6 -6
  167. package/templates/.agents/runtime/manifest.json +1 -1
  168. package/templates/.agents/runtime/pnpm-lock.sha256 +1 -1
  169. package/templates/.agents/runtime/provenance.json +5 -5
  170. package/templates/.agents/runtime/sbom.cdx.json +3 -3
  171. package/templates/.agents/sales/playbook.md +32 -0
  172. package/templates/.agents/sales/roteiro-comercial-zaip.md +252 -0
  173. package/templates/.agents/skills/zaip-aios/SKILL.md +1 -1
  174. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +9 -2
  175. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +39 -0
  176. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +1 -1
  177. package/templates/.agents/skills/zaip-skill-enforce-architecture/SKILL.md +2 -0
  178. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +6 -0
  179. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +6 -0
  180. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +39 -82
  181. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +13 -8
  182. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +44 -14
  183. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.mjs +19 -9
  184. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +29 -5
  185. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/source-size-telemetry/model-identity-reconciliation.mjs +77 -0
  186. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +6 -0
  187. package/templates/.agents/skills/zaip-skill-sqlite-read-model/scripts/ensure-node.mjs +28 -5
  188. package/templates/.agents/zaip-aios/INDEX.md +10 -0
  189. package/templates/.agents/zaip-aios/README.md +2 -1
  190. package/templates/.agents/zaip-aios/VERSION +1 -1
  191. package/templates/.agents/zaip-aios/guides/18-catalogo-de-agentes.md +76 -0
  192. package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +72 -0
  193. package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +330 -0
  194. package/templates/.agents/zaip-aios/guides/21-telemetria-monitor-custos.md +52 -0
  195. package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +69 -0
  196. package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +483 -0
  197. package/templates/.agents/zaip-aios/guides/24-calculo-de-custos-openai.md +85 -0
  198. package/templates/.agents/zaip-aios/guides/25-fontes-de-precos-openai.md +18 -0
  199. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +44 -0
  200. package/templates/.agents/zaip-aios/guides/27-telemetria-git-native.md +167 -0
  201. package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +144 -0
  202. package/templates/.agents/zaip-aios/guides/assets/diagrams/lifecycles-task-spec-prd.png +0 -0
  203. package/templates/.agents/zaip-aios/guides/assets/diagrams/prd-lifecycle.png +0 -0
  204. package/templates/.agents/zaip-aios/guides/assets/diagrams/spec-lifecycle.png +0 -0
  205. package/templates/.agents/zaip-aios/guides/assets/diagrams/task-lifecycle.png +0 -0
  206. package/templates/template-manifest.json +313 -177
  207. package/templates/.agents/dashboard/apps/frontend/dist/assets/DashboardHelpDialog-Cs6dzDmW.js +0 -1505
  208. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-QE1k9yZB.css +0 -1
  209. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-_Pf5DA_A.js +0 -3
  210. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BVlz3DLZ.js +0 -1
  211. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Cf9ctPVt.js +0 -68
  212. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-8A4DcNXM.js +0 -2
@@ -2,9 +2,9 @@ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import {
5
+ PROJECT_CONFIG_PATH,
5
6
  parseProjectConfig,
6
7
  parseYaml,
7
- PROJECT_CONFIG_PATH,
8
8
  serializeYaml,
9
9
  WorkspaceFileSystem,
10
10
  } from "@zaip-ai/core";
@@ -123,6 +123,10 @@ async function fixtureProject() {
123
123
  }
124
124
 
125
125
  describe("project settings persistence", () => {
126
+ it("uses the canonical lowercase architecture artifact path", () => {
127
+ expect(SOFTWARE_ARCHITECTURE_PATH).toBe(".agents/architecture/software-architecture.md");
128
+ });
129
+
126
130
  it("reads, validates and atomically persists the complete project config", async () => {
127
131
  const { configPath, filesystem } = await fixtureProject();
128
132
  const current = await readProjectSettings(filesystem);
@@ -385,7 +389,7 @@ describe("project settings persistence", () => {
385
389
  }),
386
390
  expect.objectContaining({
387
391
  id: "brandbook",
388
- artifact: ".agents/brand/brandbook.md",
392
+ artifact: ".agents/branding/brandbook.md",
389
393
  available: false,
390
394
  }),
391
395
  ]),
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  PROJECT_CONFIG_PATH,
3
3
  type ProjectConfig,
4
- parseProjectConfig,
5
4
  parseAgentCatalog,
5
+ parseProjectConfig,
6
6
  parseYaml,
7
7
  serializeYaml,
8
8
  type WorkspaceFileSystem,
@@ -12,7 +12,7 @@ const AGENT_CATALOG_PATH = ".agents/config/agents/catalog.yaml";
12
12
  const CODEX_CONFIG_PATH = ".codex/config.toml";
13
13
  const FRAMEWORK_LOCK_PATH = ".agents/config/framework.lock.yaml";
14
14
  const SQLITE_READ_MODEL_PATH = ".local/dashboard-sqlite/read-model.sqlite";
15
- export const SOFTWARE_ARCHITECTURE_PATH = "docs/SOFTWARE-ARCHITECTURE.md";
15
+ export const SOFTWARE_ARCHITECTURE_PATH = ".agents/architecture/software-architecture.md";
16
16
 
17
17
  export type ProjectOverview = {
18
18
  framework_version: string | null;
@@ -48,7 +48,7 @@ const projectOverviewArtifacts: Array<Omit<ProjectOverview["areas"][number], "av
48
48
  artifact: SOFTWARE_ARCHITECTURE_PATH,
49
49
  },
50
50
  { id: "design_system", label: "Design system", artifact: ".agents/design/DESIGN.md" },
51
- { id: "brandbook", label: "Brandbook", artifact: ".agents/brand/brandbook.md" },
51
+ { id: "brandbook", label: "Brandbook", artifact: ".agents/branding/brandbook.md" },
52
52
  {
53
53
  id: "marketing_strategy",
54
54
  label: "Estratégia de Marketing",
@@ -17,14 +17,13 @@ describe("boardPage", () => {
17
17
  prds: [prd],
18
18
  specs: [spec],
19
19
  } as unknown as Parameters<typeof boardPage>[0];
20
- const page = boardPage(
21
- projection,
22
- "executing",
23
- 0,
24
- 10,
25
- [],
26
- { query: "", status: "", risk: "", origin: "", kind: "" },
27
- );
20
+ const page = boardPage(projection, "executing", 0, 10, [], {
21
+ query: "",
22
+ status: "",
23
+ risk: "",
24
+ origin: "",
25
+ kind: "",
26
+ });
28
27
 
29
28
  expect(page.tasks[0]).toBe(task);
30
29
  expect(page.prds[0]).toBe(prd);
@@ -42,17 +41,57 @@ describe("boardPage", () => {
42
41
  const projection = { changes: [], tasks: [task], prds: [], specs: [] } as unknown as Parameters<
43
42
  typeof boardPage
44
43
  >[0];
45
- const page = boardPage(
46
- projection,
47
- "executing",
48
- 0,
49
- 10,
50
- [],
51
- { query: "", status: "", risk: "", origin: "", kind: "" },
52
- );
44
+ const page = boardPage(projection, "executing", 0, 10, [], {
45
+ query: "",
46
+ status: "",
47
+ risk: "",
48
+ origin: "",
49
+ kind: "",
50
+ });
53
51
  expect(page.tasks[0]).toBe(task);
54
52
  });
55
53
 
54
+ it("separates standalone Tasks from Tasks linked to a SPEC", () => {
55
+ const standalone = {
56
+ id: "TASK-STANDALONE",
57
+ title: "Standalone",
58
+ status: "executing",
59
+ spec_id: null,
60
+ updated_at: "2026-08-08T10:00:00Z",
61
+ };
62
+ const linked = {
63
+ id: "TASK-LINKED",
64
+ title: "Linked",
65
+ status: "executing",
66
+ spec_id: "SPEC-1",
67
+ updated_at: "2026-08-08T10:00:00Z",
68
+ };
69
+ const projection = {
70
+ changes: [],
71
+ tasks: [standalone, linked],
72
+ prds: [],
73
+ specs: [],
74
+ } as unknown as Parameters<typeof boardPage>[0];
75
+
76
+ const standalonePage = boardPage(projection, "executing", 0, 10, [], {
77
+ query: "",
78
+ status: "",
79
+ risk: "",
80
+ origin: "",
81
+ kind: "standalone-task",
82
+ });
83
+ const linkedPage = boardPage(projection, "executing", 0, 10, [], {
84
+ query: "",
85
+ status: "",
86
+ risk: "",
87
+ origin: "",
88
+ kind: "task",
89
+ });
90
+
91
+ expect(standalonePage.tasks).toEqual([standalone]);
92
+ expect(linkedPage.tasks).toEqual([linked]);
93
+ expect(standalonePage.totals_by_kind).toMatchObject({ task: 1, "standalone-task": 1 });
94
+ });
56
95
  });
57
96
 
58
97
  function macro(id: string, kind: "prd" | "spec", taskIds: string[]) {
@@ -7,7 +7,13 @@ import type { Context } from "hono";
7
7
  import type { buildCanonicalOperationsProjection } from "./server-projections.js";
8
8
 
9
9
  type BoardStage = WorkStage;
10
- export type BoardClassification = "lite" | "standard" | "critical" | "empty" | "structured" | "rework";
10
+ export type BoardClassification =
11
+ | "lite"
12
+ | "standard"
13
+ | "critical"
14
+ | "empty"
15
+ | "structured"
16
+ | "rework";
11
17
 
12
18
  interface BoardFilters {
13
19
  query: string;
@@ -110,7 +116,11 @@ export function boardPage(
110
116
  const stageItems =
111
117
  filters.kind === ""
112
118
  ? allStageItems
113
- : allStageItems.filter((item) => item.kind === filters.kind);
119
+ : filters.kind === "standalone-task"
120
+ ? allStageItems.filter((item) => item.kind === "task" && item.row.spec_id === null)
121
+ : filters.kind === "task"
122
+ ? allStageItems.filter((item) => item.kind === "task" && item.row.spec_id !== null)
123
+ : allStageItems.filter((item) => item.kind === filters.kind);
114
124
  const page = stageItems.slice(offset, offset + limit);
115
125
  const ids = new Set(page.map((item) => item.row.id));
116
126
  return {
@@ -119,19 +129,17 @@ export function boardPage(
119
129
  total: stageItems.length,
120
130
  totals_by_kind: {
121
131
  all: allStageItems.length,
122
- task: allStageItems.filter((item) => item.kind === "task").length,
132
+ task: allStageItems.filter((item) => item.kind === "task" && item.row.spec_id !== null)
133
+ .length,
134
+ "standalone-task": allStageItems.filter(
135
+ (item) => item.kind === "task" && item.row.spec_id === null,
136
+ ).length,
123
137
  prd: allStageItems.filter((item) => item.kind === "prd").length,
124
138
  spec: allStageItems.filter((item) => item.kind === "spec").length,
125
139
  },
126
- prds: page
127
- .filter((item) => item.kind === "prd")
128
- .map((item) => item.row),
129
- specs: page
130
- .filter((item) => item.kind === "spec")
131
- .map((item) => item.row),
132
- tasks: page
133
- .filter((item) => item.kind === "task")
134
- .map((item) => item.row),
140
+ prds: page.filter((item) => item.kind === "prd").map((item) => item.row),
141
+ specs: page.filter((item) => item.kind === "spec").map((item) => item.row),
142
+ tasks: page.filter((item) => item.kind === "task").map((item) => item.row),
135
143
  changes: projection.changes.filter((change) =>
136
144
  change.task_ids.some((taskId) => ids.has(taskId)),
137
145
  ),
@@ -173,7 +181,9 @@ export function classificationPage(
173
181
  tasks: kind === "task" ? page : [],
174
182
  changes:
175
183
  kind === "task"
176
- ? projection.changes.filter((change) => page.some((task) => change.task_ids.includes(task.id)))
184
+ ? projection.changes.filter((change) =>
185
+ page.some((task) => change.task_ids.includes(task.id)),
186
+ )
177
187
  : [],
178
188
  };
179
189
  }
@@ -45,7 +45,7 @@ describe("dashboard backend snapshot", () => {
45
45
  [
46
46
  "lock_version: 1",
47
47
  "framework:",
48
- " version: 1.0.8",
48
+ " version: 1.3.0",
49
49
  " distribution_sha256: null",
50
50
  "layout:",
51
51
  " version: 3",
@@ -593,12 +593,27 @@ describe("Cost Monitor daily summary route", () => {
593
593
  it("keeps the Daily first-fold response to its displayed-column allowlist", () => {
594
594
  const source = readFileSync(new URL("./sqlite-read-model.ts", import.meta.url), "utf8");
595
595
  const dailyBranch = source.slice(
596
- source.indexOf('if (filteredCostGroup !== undefined && temporalCostGroup && !hasDimensionalCostFilter)'),
597
- source.indexOf('if (filteredCostGroup !== undefined && (!temporalCostGroup || hasDimensionalCostFilter))'),
596
+ source.indexOf(
597
+ "if (filteredCostGroup !== undefined && temporalCostGroup && !hasDimensionalCostFilter)",
598
+ ),
599
+ source.indexOf(
600
+ "if (filteredCostGroup !== undefined && (!temporalCostGroup || hasDimensionalCostFilter))",
601
+ ),
598
602
  );
599
603
  expect(dailyBranch).not.toContain("costMonitorFactualDurationByDay(");
600
604
  expect(dailyBranch).toContain("Daily first fold is an allowlisted table contract");
601
- for (const field of ["prd_count", "spec_count", "task_count", "total_run_count", "validation_count", "evidence_count", "total_duration", "duration_known", "total_tokens", "total_cost_brl"])
605
+ for (const field of [
606
+ "prd_count",
607
+ "spec_count",
608
+ "task_count",
609
+ "total_run_count",
610
+ "validation_count",
611
+ "evidence_count",
612
+ "total_duration",
613
+ "duration_known",
614
+ "total_tokens",
615
+ "total_cost_brl",
616
+ ])
602
617
  expect(dailyBranch).toContain(`${field}:`);
603
618
  expect(dailyBranch).not.toContain("...row,");
604
619
  });
@@ -51,7 +51,10 @@ export function githubRepositoryFromRemote(remote: string): string | null {
51
51
  }
52
52
 
53
53
  export function dashboardWorkspaceKey(workspaceRoot: string): string {
54
- return createHash("sha256").update(`${resolve(workspaceRoot)}:dashboard-sqlite`).digest("hex").slice(0, 16);
54
+ return createHash("sha256")
55
+ .update(`${resolve(workspaceRoot)}:dashboard-sqlite`)
56
+ .digest("hex")
57
+ .slice(0, 16);
55
58
  }
56
59
 
57
60
  export interface ReconciliationItem {
@@ -104,11 +107,12 @@ export function reconciliationResult(value: unknown): ReconciliationResult {
104
107
  const record = entity as Record<string, unknown>;
105
108
  if (
106
109
  item.status === "closed" &&
107
- record.status === "completed" &&
110
+ ["completed", "blocked", "cancelled", "failed"].includes(record.status as string) &&
108
111
  typeof record.id === "string"
109
112
  ) {
110
113
  reconciled.add(record.id);
111
- } else if (record.status === "blocked") {
114
+ }
115
+ if (record.status === "blocked") {
112
116
  remember(blocked, record.id, record.status_reason ?? reason);
113
117
  } else if (item.status === "deferred") {
114
118
  remember(notReconciled, record.id, reason);
@@ -12,6 +12,7 @@ import {
12
12
  openWorkIds,
13
13
  projectionPeriodFilter,
14
14
  reconciliationResult,
15
+ SqliteReadModelFileSystem,
15
16
  } from "./server.js";
16
17
  import { invocationSummary } from "./server.test-fixtures.js";
17
18
  import { classificationPage } from "./server-board.js";
@@ -21,6 +22,7 @@ import {
21
22
  mergeLifecycleRunPage,
22
23
  runTimestampInRange,
23
24
  } from "./server-projections.js";
25
+ import { rebuildSqliteReadModel } from "./sqlite-read-model.js";
24
26
 
25
27
  const workspaceRoot = resolve(import.meta.dirname, "../../../../..");
26
28
  describe("dashboard backend", () => {
@@ -252,7 +254,11 @@ describe("dashboard backend", () => {
252
254
  });
253
255
  });
254
256
  it("permits canonical open-work reconciliation while serving a read-only SQLite snapshot", async () => {
255
- const reconcile = vi.fn(async () => ({ reconciled_ids: ["TASK-RECONCILED"], blocked: [], not_reconciled: [] }));
257
+ const reconcile = vi.fn(async () => ({
258
+ reconciled_ids: ["TASK-RECONCILED"],
259
+ blocked: [],
260
+ not_reconciled: [],
261
+ }));
256
262
  const refresh = vi.fn(async () => undefined);
257
263
  const canonicalFilesystem = new WorkspaceFileSystem(workspaceRoot);
258
264
  const app = createDashboardApp(workspaceRoot, "test-session", undefined, {
@@ -275,6 +281,106 @@ describe("dashboard backend", () => {
275
281
  const rejected = await app.request("/api/v2/reconcile-open-work", { method: "POST" });
276
282
  expect(rejected.status).toBe(401);
277
283
  }, 130_000);
284
+ it("rebuilds a stale immutable projection without reconciling lifecycle work", async () => {
285
+ const fixtureRoot = await mkdtemp(join(tmpdir(), "zaip-dashboard-stale-projection-"));
286
+ const taskIndexPath = join(
287
+ fixtureRoot,
288
+ ".agents",
289
+ "records",
290
+ "lifecycle",
291
+ "indexes",
292
+ "task.json",
293
+ );
294
+ const taskRecordPath = join(
295
+ fixtureRoot,
296
+ ".agents",
297
+ "records",
298
+ "tasks",
299
+ "2026",
300
+ "08",
301
+ "TASK-STALE-PROJECTION.md",
302
+ );
303
+ await mkdir(join(fixtureRoot, ".agents", "config"), { recursive: true });
304
+ await mkdir(join(fixtureRoot, ".agents", "records", "lifecycle", "indexes"), {
305
+ recursive: true,
306
+ });
307
+ await mkdir(join(fixtureRoot, ".agents", "records", "tasks", "2026", "08"), {
308
+ recursive: true,
309
+ });
310
+ await writeFile(
311
+ join(fixtureRoot, ".agents", "config", "project.yaml"),
312
+ "schema_version: 1\nproject:\n id: stale-projection\n name: stale-projection\n timezone: UTC\nstack:\n id: zaip-aios-stack\n overrides: {}\nclients:\n codex: true\n claude: true\ntracking:\n enabled: true\n currency: BRL\n storage_mode: tracked_sanitized\n",
313
+ );
314
+ await writeFile(
315
+ join(fixtureRoot, ".agents", "config", "framework.lock.yaml"),
316
+ "lock_version: 1\nframework:\n version: 1.3.0\n distribution_sha256: null\nlayout:\n version: 3\nresolved_profile:\n id: null\n sha256: null\nmanaged_files: {}\n",
317
+ );
318
+ const task = (status: "executing" | "completed") => ({
319
+ schema_version: 1,
320
+ entities: {
321
+ "TASK-STALE-PROJECTION": {
322
+ kind: "task",
323
+ id: "TASK-STALE-PROJECTION",
324
+ title: "Stale projection",
325
+ status,
326
+ risk: "standard",
327
+ priority: "medium",
328
+ updated_at: "2026-08-22T12:00:00.000Z",
329
+ relations: [],
330
+ usage: null,
331
+ data: { created_at: "2026-08-22T12:00:00.000Z" },
332
+ },
333
+ },
334
+ });
335
+ await writeFile(taskIndexPath, `${JSON.stringify(task("executing"))}\n`);
336
+ const taskRecord = (status: "executing" | "completed") =>
337
+ `---\nid: TASK-STALE-PROJECTION\ntitle: Stale projection\nstatus: ${status}\nrisk: standard\ncreated_at: 2026-08-22T12:00:00.000Z\n---\n# Stale projection\n`;
338
+ await writeFile(taskRecordPath, taskRecord("executing"));
339
+ await rebuildSqliteReadModel(fixtureRoot);
340
+ const filesystem = new SqliteReadModelFileSystem(fixtureRoot);
341
+ const reconcile = vi.fn(async () => ({ reconciled_ids: [], blocked: [], not_reconciled: [] }));
342
+ try {
343
+ const app = createDashboardApp(fixtureRoot, "test-session", undefined, {
344
+ canonicalFilesystem: new WorkspaceFileSystem(fixtureRoot),
345
+ filesystem,
346
+ reconcileOpenWork: reconcile,
347
+ });
348
+ const before = await app.request("/api/v2/operations?scope=quick", {
349
+ headers: { "x-zaip-session": "test-session" },
350
+ });
351
+ const beforePayload = (await before.json()) as {
352
+ summary: { overview_metrics: { task_status_counts: Record<string, number> } };
353
+ };
354
+ expect(beforePayload.summary.overview_metrics.task_status_counts.executing).toBe(1);
355
+
356
+ await writeFile(taskIndexPath, `${JSON.stringify(task("completed"))}\n`);
357
+ await writeFile(taskRecordPath, taskRecord("completed"));
358
+ const previousSeedPreference = process.env.ZAIP_SQLITE_PREFER_LAST_VALID_SEED;
359
+ process.env.ZAIP_SQLITE_PREFER_LAST_VALID_SEED = "1";
360
+ const refreshed = await app.request("/api/v2/refresh-projection", {
361
+ method: "POST",
362
+ headers: { "x-zaip-session": "test-session" },
363
+ });
364
+ if (previousSeedPreference === undefined)
365
+ delete process.env.ZAIP_SQLITE_PREFER_LAST_VALID_SEED;
366
+ else process.env.ZAIP_SQLITE_PREFER_LAST_VALID_SEED = previousSeedPreference;
367
+
368
+ expect(refreshed.status, await refreshed.clone().text()).toBe(200);
369
+ const payload = (await refreshed.json()) as {
370
+ operations: {
371
+ summary: { overview_metrics: { task_status_counts: Record<string, number> } };
372
+ };
373
+ };
374
+ expect(payload.operations.summary.overview_metrics.task_status_counts.executing ?? 0).toBe(0);
375
+ expect(payload.operations.summary.overview_metrics.task_status_counts.completed).toBe(1);
376
+ expect(reconcile).not.toHaveBeenCalled();
377
+ const rejected = await app.request("/api/v2/refresh-projection", { method: "POST" });
378
+ expect(rejected.status).toBe(401);
379
+ } finally {
380
+ filesystem.close();
381
+ await rm(fixtureRoot, { recursive: true, force: true });
382
+ }
383
+ }, 130_000);
278
384
  it("persists Kanban priority canonically while a SQLite snapshot blocks other settings writes", async () => {
279
385
  const canonicalRoot = await mkdtemp(join(tmpdir(), "zaip-dashboard-priority-"));
280
386
  try {
@@ -298,11 +404,21 @@ describe("dashboard backend", () => {
298
404
  };
299
405
  expect(beforeBoard.tasks.length).toBeGreaterThan(1);
300
406
  const concurrent = await Promise.all([
301
- app.request("/api/v2/operations?scope=board&stage=completed&from=2000-01-01&to=2099-12-31&offset=0&limit=10", { headers: { "x-zaip-session": "test-session" } }),
302
- app.request("/api/v2/operations?scope=board&stage=blocked&from=2000-01-01&to=2099-12-31&offset=0&limit=10", { headers: { "x-zaip-session": "test-session" } }),
407
+ app.request(
408
+ "/api/v2/operations?scope=board&stage=completed&from=2000-01-01&to=2099-12-31&offset=0&limit=10",
409
+ { headers: { "x-zaip-session": "test-session" } },
410
+ ),
411
+ app.request(
412
+ "/api/v2/operations?scope=board&stage=blocked&from=2000-01-01&to=2099-12-31&offset=0&limit=10",
413
+ { headers: { "x-zaip-session": "test-session" } },
414
+ ),
303
415
  ]);
304
416
  expect(concurrent.every((response) => response.status === 200)).toBe(true);
305
- expect(priorityReads.mock.calls.filter(([path]) => path === ".agents/dashboard/work-priority.json")).toHaveLength(1);
417
+ expect(
418
+ priorityReads.mock.calls.filter(
419
+ ([path]) => path === ".agents/dashboard/work-priority.json",
420
+ ),
421
+ ).toHaveLength(1);
306
422
  const priority = { item_ids: [beforeBoard.tasks.at(-1)!.id], updated_at: null };
307
423
  const persisted = await app.request("/api/v2/work-priority", {
308
424
  method: "PUT",
@@ -327,7 +443,10 @@ describe("dashboard backend", () => {
327
443
  expect(after.status, await after.clone().text()).toBe(200);
328
444
  const afterBoard = (await after.json()) as typeof beforeBoard;
329
445
  expect(afterBoard.tasks[0]?.id).toBe(priority.item_ids[0]);
330
- expect(priorityReads.mock.calls.filter(([path]) => path === ".agents/dashboard/work-priority.json").length).toBe(readsAfterPut + 1);
446
+ expect(
447
+ priorityReads.mock.calls.filter(([path]) => path === ".agents/dashboard/work-priority.json")
448
+ .length,
449
+ ).toBe(readsAfterPut + 1);
331
450
  const blocked = await app.request("/api/v2/pricing", {
332
451
  method: "PUT",
333
452
  headers: { "content-type": "application/json", "x-zaip-session": "test-session" },
@@ -414,7 +533,9 @@ describe("dashboard backend", () => {
414
533
  };
415
534
  expect(payload.tasks).toEqual([]);
416
535
  expect(payload.prds).toEqual([]);
417
- expect(payload.specs.every((spec) => spec.task_count === 0 && spec.rework_run_count === 0)).toBe(true);
536
+ expect(
537
+ payload.specs.every((spec) => spec.task_count === 0 && spec.rework_run_count === 0),
538
+ ).toBe(true);
418
539
  });
419
540
  it("serves a compact cost summary and paginated Run metrics from the manifest", async () => {
420
541
  const app = createDashboardApp(workspaceRoot, "test-session");
@@ -721,7 +842,7 @@ describe("dashboard backend", () => {
721
842
  unresolved_running_run_ids: ["RUN-ACTIVE"],
722
843
  }),
723
844
  ).toEqual({
724
- reconciled_ids: ["CHG-COMPLETED", "TASK-COMPLETED"],
845
+ reconciled_ids: ["CHG-COMPLETED", "TASK-BLOCKED", "TASK-COMPLETED"],
725
846
  blocked: [
726
847
  {
727
848
  id: "EXEC-ORPHANED",
@@ -830,8 +951,10 @@ describe("dashboard backend", () => {
830
951
  source.indexOf("function buildSqliteOperationsProjection"),
831
952
  source.indexOf("function nullableMetric"),
832
953
  );
833
- expect(projection).toContain("options.includePeriods === true ? filesystem.dailyPeriods(filter) : {}");
834
- expect(source).toContain("includePeriods: scope === \"detail\" || scope === \"overview\"");
954
+ expect(projection).toContain(
955
+ "options.includePeriods === true ? filesystem.dailyPeriods(filter) : {}",
956
+ );
957
+ expect(source).toContain('includePeriods: scope === "detail" || scope === "overview"');
835
958
  });
836
959
  it("rebuilds a fresh aggregate after an in-flight normal read instead of sharing its payload", async () => {
837
960
  const source = await import("node:fs/promises").then(({ readFile }) =>