@zaip-ai/cli 4.2.0 → 4.4.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 (178) hide show
  1. package/dist/commands/init.d.ts.map +1 -1
  2. package/dist/commands/init.js +3 -1
  3. package/dist/commands/init.js.map +1 -1
  4. package/dist/services/adapters.d.ts +2 -2
  5. package/dist/services/adapters.d.ts.map +1 -1
  6. package/dist/services/adapters.js +24 -70
  7. package/dist/services/adapters.js.map +1 -1
  8. package/dist/services/adapters.test.js +5 -1
  9. package/dist/services/adapters.test.js.map +1 -1
  10. package/dist/services/agent-adapters.d.ts.map +1 -1
  11. package/dist/services/agent-adapters.js +3 -7
  12. package/dist/services/agent-adapters.js.map +1 -1
  13. package/dist/services/agent-adapters.test.js +43 -11
  14. package/dist/services/agent-adapters.test.js.map +1 -1
  15. package/dist/services/codex-config-overlay.d.ts +6 -0
  16. package/dist/services/codex-config-overlay.d.ts.map +1 -0
  17. package/dist/services/codex-config-overlay.js +161 -0
  18. package/dist/services/codex-config-overlay.js.map +1 -0
  19. package/dist/services/codex-config-template.js +2 -2
  20. package/dist/services/codex-conversation-lifecycle-hook.d.ts.map +1 -1
  21. package/dist/services/codex-conversation-lifecycle-hook.js +38 -14
  22. package/dist/services/codex-conversation-lifecycle-hook.js.map +1 -1
  23. package/dist/services/codex-hook-activation.d.ts +4 -0
  24. package/dist/services/codex-hook-activation.d.ts.map +1 -0
  25. package/dist/services/codex-hook-activation.js +64 -0
  26. package/dist/services/codex-hook-activation.js.map +1 -0
  27. package/dist/services/codex-model-policy.d.ts +4 -0
  28. package/dist/services/codex-model-policy.d.ts.map +1 -0
  29. package/dist/services/codex-model-policy.js +24 -0
  30. package/dist/services/codex-model-policy.js.map +1 -0
  31. package/dist/services/codex-native-tool-authority-template.d.ts +2 -0
  32. package/dist/services/codex-native-tool-authority-template.d.ts.map +1 -0
  33. package/dist/services/codex-native-tool-authority-template.js +89 -0
  34. package/dist/services/codex-native-tool-authority-template.js.map +1 -0
  35. package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
  36. package/dist/services/codex-pre-tool-hook-template.js +59 -86
  37. package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
  38. package/dist/services/codex-pre-tool-hook-template.test.js +200 -1
  39. package/dist/services/codex-pre-tool-hook-template.test.js.map +1 -1
  40. package/dist/services/codex-pre-tool-support-template.d.ts.map +1 -1
  41. package/dist/services/codex-pre-tool-support-template.js +303 -12
  42. package/dist/services/codex-pre-tool-support-template.js.map +1 -1
  43. package/dist/services/codex-subagent-hook-template.d.ts +1 -1
  44. package/dist/services/codex-subagent-hook-template.d.ts.map +1 -1
  45. package/dist/services/codex-subagent-hook-template.js +11 -4
  46. package/dist/services/codex-subagent-hook-template.js.map +1 -1
  47. package/dist/services/codex-validation-runtime-template.d.ts +3 -0
  48. package/dist/services/codex-validation-runtime-template.d.ts.map +1 -0
  49. package/dist/services/codex-validation-runtime-template.js +18 -0
  50. package/dist/services/codex-validation-runtime-template.js.map +1 -0
  51. package/dist/services/codex-windows-hook-command.js +1 -1
  52. package/dist/services/codex-windows-hook-command.js.map +1 -1
  53. package/dist/services/codex-windows-hook-command.test.js +22 -0
  54. package/dist/services/codex-windows-hook-command.test.js.map +1 -1
  55. package/dist/services/global-codex-config-fixtures.d.ts +12 -0
  56. package/dist/services/global-codex-config-fixtures.d.ts.map +1 -0
  57. package/dist/services/global-codex-config-fixtures.js +38 -0
  58. package/dist/services/global-codex-config-fixtures.js.map +1 -0
  59. package/dist/services/global-codex-config-migration.test.d.ts +2 -0
  60. package/dist/services/global-codex-config-migration.test.d.ts.map +1 -0
  61. package/dist/services/global-codex-config-migration.test.js +301 -0
  62. package/dist/services/global-codex-config-migration.test.js.map +1 -0
  63. package/dist/services/global-codex-config-transaction.d.ts +15 -0
  64. package/dist/services/global-codex-config-transaction.d.ts.map +1 -0
  65. package/dist/services/global-codex-config-transaction.js +89 -0
  66. package/dist/services/global-codex-config-transaction.js.map +1 -0
  67. package/dist/services/global-codex-config-transaction.test.d.ts +2 -0
  68. package/dist/services/global-codex-config-transaction.test.d.ts.map +1 -0
  69. package/dist/services/global-codex-config-transaction.test.js +145 -0
  70. package/dist/services/global-codex-config-transaction.test.js.map +1 -0
  71. package/dist/services/global-codex-config.d.ts +10 -11
  72. package/dist/services/global-codex-config.d.ts.map +1 -1
  73. package/dist/services/global-codex-config.js +225 -107
  74. package/dist/services/global-codex-config.js.map +1 -1
  75. package/dist/services/global-codex-config.test.js +6 -6
  76. package/dist/services/global-codex-config.test.js.map +1 -1
  77. package/dist/services/hook-readiness.d.ts +7 -0
  78. package/dist/services/hook-readiness.d.ts.map +1 -0
  79. package/dist/services/hook-readiness.js +17 -0
  80. package/dist/services/hook-readiness.js.map +1 -0
  81. package/dist/services/install-agents-template.d.ts.map +1 -1
  82. package/dist/services/install-agents-template.js +12 -18
  83. package/dist/services/install-agents-template.js.map +1 -1
  84. package/dist/services/logger.d.ts +2 -0
  85. package/dist/services/logger.d.ts.map +1 -1
  86. package/dist/services/logger.js +15 -1
  87. package/dist/services/logger.js.map +1 -1
  88. package/dist/services/logger.test.js +3 -0
  89. package/dist/services/logger.test.js.map +1 -1
  90. package/dist/services/managed-adapter.d.ts +6 -0
  91. package/dist/services/managed-adapter.d.ts.map +1 -0
  92. package/dist/services/managed-adapter.js +63 -0
  93. package/dist/services/managed-adapter.js.map +1 -0
  94. package/dist/services/render-framework-lock.d.ts +6 -0
  95. package/dist/services/render-framework-lock.d.ts.map +1 -0
  96. package/dist/services/render-framework-lock.js +20 -0
  97. package/dist/services/render-framework-lock.js.map +1 -0
  98. package/dist/types.d.ts +2 -0
  99. package/dist/types.d.ts.map +1 -1
  100. package/package.json +1 -1
  101. package/templates/.agents/README.md +8 -0
  102. package/templates/.agents/config/agents/discovery.json +1 -1
  103. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +1 -2
  104. package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +1 -2
  105. package/templates/.agents/config/clients/codex/agents/zaip-agent-builder.toml +1 -2
  106. package/templates/.agents/config/clients/codex/agents/zaip-agent-cloud-engineer.toml +1 -2
  107. package/templates/.agents/config/clients/codex/agents/zaip-agent-data-analyst.toml +1 -2
  108. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +1 -2
  109. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +1 -2
  110. package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +1 -2
  111. package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +1 -2
  112. package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +1 -2
  113. package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +1 -2
  114. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -2
  115. package/templates/.agents/config/clients/codex/agents/zaip-agent-refactoring-specialist.toml +1 -2
  116. package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +1 -2
  117. package/templates/.agents/config/clients/codex/agents/zaip-agent-task-executor.toml +1 -2
  118. package/templates/.agents/config/clients/codex/agents/zaip-agent-website-developer.toml +1 -2
  119. package/templates/.agents/config/clients/codex/agents/zaip-agent-writer.toml +1 -2
  120. package/templates/.agents/dashboard/.zaip-managed.json +40 -32
  121. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-DlN5EMmD.js → DashboardHelpDialog-CkQ_y8kg.js} +60 -1
  122. package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-BRdoeTWk.js → ProjectSettingsPage-doIMhei7.js} +1 -1
  123. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-B_5u4Xnp.js → TraceabilityWorkspace-C_4ftjyB.js} +1 -1
  124. package/templates/.agents/dashboard/apps/frontend/dist/assets/{index-C4rLXrz_.js → index-Rbmpz4UO.js} +2 -2
  125. package/templates/.agents/dashboard/apps/frontend/dist/index.html +1 -1
  126. package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
  127. package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
  128. package/templates/.agents/dashboard/modules/operations/package.json +1 -1
  129. package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
  130. package/templates/.agents/dashboard/packages/branding/package.json +1 -1
  131. package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
  132. package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
  133. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +177 -177
  134. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +135 -135
  135. package/templates/.agents/dashboard/packages/local-runtime/package.json +1 -1
  136. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +177 -177
  137. package/templates/.agents/dashboard/packages/local-runtime/src/server-operation-detail.test.ts +106 -0
  138. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +0 -13
  139. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-counts-every-root-execution-day-returns.test.ts +17 -230
  140. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-counts-fixture.test-support.ts +239 -0
  141. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.test.mjs +28 -8
  142. package/templates/.agents/dashboard/packages/ui/package.json +1 -1
  143. package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
  144. package/templates/.agents/design/.zaip-managed.json +3 -3
  145. package/templates/.agents/design/apps/catalog/package.json +1 -1
  146. package/templates/.agents/design/packages/local-runtime/package.json +1 -1
  147. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  148. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +1 -1
  149. package/templates/.agents/requirements/comparative-audit.md +1 -1
  150. package/templates/.agents/requirements/context-engine.md +1 -1
  151. package/templates/.agents/requirements/cost-observability.md +1 -1
  152. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  153. package/templates/.agents/requirements/data-export.md +1 -1
  154. package/templates/.agents/requirements/design-system.md +1 -1
  155. package/templates/.agents/requirements/execution-engine.md +1 -1
  156. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  157. package/templates/.agents/requirements/model-orchestration.md +1 -1
  158. package/templates/.agents/requirements/public-distribution.md +1 -1
  159. package/templates/.agents/requirements/software-architecture.md +1 -1
  160. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  161. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  162. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  163. package/templates/.agents/rules/astra-diagnostic.md +1 -1
  164. package/templates/.agents/runtime/VERSION +1 -1
  165. package/templates/.agents/runtime/bin/zaip.mjs +654 -199
  166. package/templates/.agents/runtime/checksums.sha256 +5 -5
  167. package/templates/.agents/runtime/manifest.json +1 -1
  168. package/templates/.agents/runtime/provenance.json +4 -4
  169. package/templates/.agents/runtime/sbom.cdx.json +1 -1
  170. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +1 -1
  171. package/templates/.agents/skills/zaip-skill-execution/references/correction-cycle.md +26 -0
  172. package/templates/.agents/skills/zaip-skill-execution/references/runtime-governance.md +2 -0
  173. package/templates/.agents/skills/zaip-skill-release-qualification/references/local-preparation.md +31 -1
  174. package/templates/.agents/skills/zaip-skill-setup/SKILL.md +26 -1
  175. package/templates/.agents/skills/zaip-skill-task/references/commit-policy.md +8 -1
  176. package/templates/.agents/zaip-aios/VERSION +1 -1
  177. package/templates/.agents/zaip-aios/guides/14-troubleshooting.md +52 -1
  178. package/templates/template-manifest.json +95 -87
@@ -0,0 +1,106 @@
1
+ import { rm } from "node:fs/promises";
2
+ import { serializeMarkdown, WorkspaceFileSystem } from "@zaip-ai/core";
3
+ import { LifecycleManifestStore } from "@zaip-ai/lifecycle-manifests";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
5
+ import { createDashboardApp } from "./server.js";
6
+ import * as readProjections from "./server-read-projections.js";
7
+ import { rebuildSqliteReadModel, SqliteReadModelFileSystem } from "./sqlite-read-model.js";
8
+ import { createCountsFixture } from "./sqlite-read-model-counts-fixture.test-support.js";
9
+
10
+ const roots: string[] = [];
11
+ const readers: SqliteReadModelFileSystem[] = [];
12
+ const taskId = "TASK-2026-08-03-00-00-DETAIL-TEST";
13
+ afterEach(async () => {
14
+ vi.restoreAllMocks();
15
+ for (const reader of readers.splice(0)) reader.close();
16
+ for (const root of roots.splice(0)) await rm(root, { recursive: true, force: true });
17
+ });
18
+
19
+ describe("bounded Operations detail transport", () => {
20
+ it.each([
21
+ "canonical",
22
+ "sqlite",
23
+ ] as const)("serves %s operation detail without building the cost snapshot", async (mode) => {
24
+ // Real synthetic records, never the growing history of the checkout.
25
+ const { root } = await createCountsFixture(roots);
26
+ let filesystem: WorkspaceFileSystem = new WorkspaceFileSystem(root);
27
+ const task = {
28
+ schema_version: 2,
29
+ id: taskId,
30
+ revision: 1,
31
+ change_id: "CHG-2026-08-03-00-00-DETAIL-TEST",
32
+ title: "Synthetic detail task",
33
+ short_name: "detail-test",
34
+ objective: "Verify the isolated detail transport",
35
+ status: "draft",
36
+ status_reason: null,
37
+ attempt_count: 0,
38
+ risk: "standard",
39
+ priority: "medium",
40
+ owner: null,
41
+ requirement_ids: [],
42
+ acceptance_ids: [],
43
+ depends_on: [],
44
+ write_scopes: [],
45
+ created_at: "2026-08-03T12:00:00.000Z",
46
+ updated_at: "2026-08-03T12:00:00.000Z",
47
+ started_at: null,
48
+ finished_at: null,
49
+ result: null,
50
+ };
51
+ await filesystem.writeTextAtomic(
52
+ `.agents/records/tasks/2026/08/${taskId}.md`,
53
+ serializeMarkdown({ metadata: task, body: "# Synthetic detail task\n" }),
54
+ );
55
+ await new LifecycleManifestStore(filesystem).replace([
56
+ {
57
+ schema_version: 1,
58
+ event_id: "fixture:detail-task",
59
+ occurred_at: task.created_at,
60
+ operation: "upsert",
61
+ entity: {
62
+ kind: "task",
63
+ id: taskId,
64
+ title: task.title,
65
+ status: task.status,
66
+ risk: task.risk,
67
+ priority: task.priority,
68
+ updated_at: task.updated_at,
69
+ relations: [],
70
+ usage: null,
71
+ data: task,
72
+ },
73
+ },
74
+ ]);
75
+ if (mode === "sqlite") {
76
+ await rebuildSqliteReadModel(root);
77
+ const reader = new SqliteReadModelFileSystem(root);
78
+ readers.push(reader);
79
+ filesystem = reader;
80
+ }
81
+ const snapshot = vi
82
+ .spyOn(readProjections, "dashboardSnapshot")
83
+ .mockRejectedValue(new Error("Operations detail must not build the cost snapshot"));
84
+ const app = createDashboardApp(root, "test-session", undefined, { filesystem });
85
+ const response = await app.request("/api/v2/operations?scope=detail", {
86
+ headers: { "x-zaip-session": "test-session" },
87
+ });
88
+ expect(response.status, await response.clone().text()).toBe(200);
89
+ expect(response.headers.get("X-ZAIP-Snapshot-Cache")).toBeNull();
90
+ const payload = (await response.json()) as { tasks: unknown[]; runs: unknown[] };
91
+ expect(payload).toMatchObject({
92
+ source: ".agents/records",
93
+ tasks: expect.any(Array),
94
+ runs: expect.any(Array),
95
+ });
96
+ expect(payload.tasks).toEqual(
97
+ expect.arrayContaining([expect.objectContaining({ id: taskId })]),
98
+ );
99
+ if (mode === "canonical") {
100
+ expect(payload.runs).toEqual(
101
+ expect.arrayContaining([expect.objectContaining({ id: "RUN-R1" })]),
102
+ );
103
+ }
104
+ expect(snapshot).not.toHaveBeenCalled();
105
+ });
106
+ });
@@ -602,19 +602,6 @@ describe("dashboard backend", () => {
602
602
  expect(page.runs.length).toBeLessThanOrEqual(1);
603
603
  expect(page.runs.every((run) => run.phase === "execution")).toBe(true);
604
604
  });
605
- it("serves operation detail without building the cost snapshot", async () => {
606
- const app = createDashboardApp(workspaceRoot, "test-session");
607
- const response = await app.request("/api/v2/operations?scope=detail", {
608
- headers: { "x-zaip-session": "test-session" },
609
- });
610
- expect(response.status).toBe(200);
611
- expect(response.headers.get("X-ZAIP-Snapshot-Cache")).toBeNull();
612
- await expect(response.json()).resolves.toMatchObject({
613
- source: ".agents/records",
614
- tasks: expect.any(Array),
615
- runs: expect.any(Array),
616
- });
617
- }, 60_000);
618
605
  it("serves only allowlisted canonical agent portraits", async () => {
619
606
  const app = createDashboardApp(workspaceRoot, "test-session");
620
607
  const agentIds = [
@@ -1,21 +1,16 @@
1
- import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
2
- import { join, resolve } from "node:path";
1
+ import { writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
3
  import { afterEach, describe, expect, it, vi } from "vitest";
4
4
  import { createDashboardApp, projectAllTimeTopTasks } from "./server.js";
5
5
  import { rebuildSqliteReadModel, SqliteReadModelFileSystem } from "./sqlite-read-model.js";
6
6
  import { expectPersistedFirstFoldCaches } from "./sqlite-read-model-cache.test-support.js";
7
+ import {
8
+ addCanonicalDayRecords,
9
+ createCountsFixture,
10
+ } from "./sqlite-read-model-counts-fixture.test-support.js";
7
11
 
8
12
  const roots: string[] = [];
9
13
  const readers = new Set<SqliteReadModelFileSystem>();
10
- const sqliteTestTempRoot = join(
11
- resolve(import.meta.dirname, "../../../../../.."),
12
- ".tmp",
13
- "sqlite-read-model-tests",
14
- );
15
- async function createSqliteTestRoot(): Promise<string> {
16
- await mkdir(sqliteTestTempRoot, { recursive: true });
17
- return mkdtemp(join(sqliteTestTempRoot, "fixture-"));
18
- }
19
14
  afterEach(async () => {
20
15
  for (const reader of readers) reader.close();
21
16
  readers.clear();
@@ -31,192 +26,7 @@ afterEach(async () => {
31
26
  });
32
27
  describe("SQLite dashboard read model", () => {
33
28
  it("counts every root execution day and returns task duration leaders", async () => {
34
- const root = await createSqliteTestRoot();
35
- roots.push(root);
36
- await mkdir(join(root, ".agents", "records", "runs", "2026", "08"), { recursive: true });
37
- for (const kind of ["tasks", "specs", "prds"])
38
- await mkdir(join(root, ".agents", "records", kind, "2026", "08"), { recursive: true });
39
- await mkdir(
40
- join(root, ".agents", "records", "changes", "2026", "08", "CHG-ART", "artifacts", "evidence"),
41
- { recursive: true },
42
- );
43
- await mkdir(
44
- join(
45
- root,
46
- ".agents",
47
- "records",
48
- "changes",
49
- "2026",
50
- "08",
51
- "CHG-ART",
52
- "artifacts",
53
- "validations",
54
- ),
55
- { recursive: true },
56
- );
57
- await mkdir(join(root, ".agents", "config"), { recursive: true });
58
- await writeFile(
59
- join(root, ".agents", "config", "project.yaml"),
60
- "schema_version: 1\nzaip:\n enabled: true\nproject:\n id: sqlite-test\n name: sqlite-test\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",
61
- "utf8",
62
- );
63
- await writeFile(
64
- join(root, ".agents", "config", "framework.lock.yaml"),
65
- "lock_version: 1\nframework:\n version: 2.5.0\n distribution_sha256: null\nlayout:\n version: 3\nresolved_profile:\n id: null\n sha256: null\nmanaged_files: {}\n",
66
- "utf8",
67
- );
68
- const run = (
69
- id: string,
70
- day: string,
71
- task: string,
72
- duration: number,
73
- time = "12:00:00.000Z",
74
- spec: string | null = null,
75
- prd: string | null = null,
76
- ) => [
77
- {
78
- schema_version: 2,
79
- event_id: `${id}:1`,
80
- run_id: id,
81
- sequence: 1,
82
- occurred_at: `${day}T${time}`,
83
- type: "run.started",
84
- attribution_status: "attributed",
85
- phase: "execution",
86
- run_role: "orchestrator",
87
- run_purpose: "execution",
88
- requires_finalizer: false,
89
- execution_kind: "delivery",
90
- execution_eligibility: ["delivery"],
91
- entrypoint: "task",
92
- work_type: "new",
93
- storage_mode: "tracked_sanitized",
94
- client: "codex",
95
- agent: "agent",
96
- agent_name: "agent",
97
- provider: "openai",
98
- model: "gpt-5",
99
- reasoning_effort: null,
100
- client_thread_id: id,
101
- client_turn_id: id,
102
- conversation_id: `CONV-${id}`,
103
- execution_id: `EXEC-${id}`,
104
- task_id: task,
105
- spec_id: spec,
106
- prd_id: prd,
107
- change_id: null,
108
- parent_run_id: null,
109
- continuation_of_run_id: null,
110
- retry_of_run_id: null,
111
- agent_invocation_id: `AINV-${id}`,
112
- root_agent_invocation_id: `AINV-${id}`,
113
- parent_agent_invocation_id: null,
114
- invocation_kind: "orchestrator",
115
- delegation_depth: 0,
116
- },
117
- {
118
- schema_version: 2,
119
- event_id: `${id}:2`,
120
- run_id: id,
121
- sequence: 2,
122
- occurred_at: `${day}T12:00:01.000Z`,
123
- type: "usage.recorded",
124
- phase: "execution",
125
- activity_kind: "model_usage",
126
- classification_source: "explicit",
127
- tokens: {
128
- input: 1,
129
- cached_input: 0,
130
- output: 1,
131
- reasoning: 0,
132
- total: 2,
133
- source: "provider_reported",
134
- },
135
- cost: { usd: 1, brl: 5, fx_rate: 5 },
136
- provider: "openai",
137
- model: "gpt-5",
138
- source_segment_key: id,
139
- model_call_id: `MCALL-${id}`,
140
- runtime: { reasoning_effort: null, service_tier: null },
141
- supersedes_event_id: null,
142
- duration_ms: null,
143
- duration_provenance: null,
144
- },
145
- {
146
- schema_version: 2,
147
- event_id: `${id}:3`,
148
- run_id: id,
149
- sequence: 3,
150
- occurred_at: `${day}T12:00:02.000Z`,
151
- type: "run.finished",
152
- phase: "execution",
153
- status: "completed",
154
- duration_ms: duration,
155
- duration_provenance: "observed",
156
- },
157
- ];
158
- for (const [id, records] of [
159
- [
160
- "RUN-R1",
161
- run("RUN-R1", "2026-08-01", "TASK-A", 600_000, "12:00:00.000Z", "SPEC-A", "PRD-A"),
162
- ],
163
- [
164
- "RUN-R2",
165
- run("RUN-R2", "2026-08-02", "TASK-B", 1_200_000, "12:00:00.000Z", "SPEC-B", "PRD-A"),
166
- ],
167
- [
168
- "RUN-R3",
169
- run("RUN-R3", "2026-08-02", "TASK-B", 1_200_000, "12:10:00.000Z", "SPEC-B", "PRD-A"),
170
- ],
171
- ] as const) {
172
- await writeFile(
173
- join(root, ".agents", "records", "runs", "2026", "08", `${id}.jsonl`),
174
- `${records.map((record) => JSON.stringify(record)).join("\n")}\n`,
175
- "utf8",
176
- );
177
- }
178
- await writeFile(
179
- join(
180
- root,
181
- ".agents",
182
- "records",
183
- "changes",
184
- "2026",
185
- "08",
186
- "CHG-ART",
187
- "artifacts",
188
- "evidence",
189
- "EVD-1.md",
190
- ),
191
- "---\nid: EVD-1\nobserved_at: 2026-08-01T12:00:00.000Z\n---\n# Evidence\n",
192
- "utf8",
193
- );
194
- await writeFile(
195
- join(
196
- root,
197
- ".agents",
198
- "records",
199
- "changes",
200
- "2026",
201
- "08",
202
- "CHG-ART",
203
- "artifacts",
204
- "validations",
205
- "VAL-1.md",
206
- ),
207
- "---\nid: VAL-1\nexecuted_at: 2026-08-02T12:00:00.000Z\n---\n# Validation\n",
208
- "utf8",
209
- );
210
- for (const [kind, id] of [
211
- ["tasks", "TASK-NO-RUN"],
212
- ["specs", "SPEC-NO-RUN"],
213
- ["prds", "PRD-NO-RUN"],
214
- ] as const)
215
- await writeFile(
216
- join(root, ".agents", "records", kind, "2026", "08", `${id}.md`),
217
- `---\nid: ${id}\ntitle: ${id}\nstatus: draft\ncreated_at: 2026-08-03T12:00:00.000Z\n---\n# ${id}\n`,
218
- "utf8",
219
- );
29
+ const { root } = await createCountsFixture(roots);
220
30
  await rebuildSqliteReadModel(root);
221
31
  const filesystem = new SqliteReadModelFileSystem(root);
222
32
  readers.add(filesystem);
@@ -668,43 +478,15 @@ describe("SQLite dashboard read model", () => {
668
478
  expect(filesystem.firstFoldAllTime()).toBe(allTime);
669
479
  filesystem.close();
670
480
  readers.delete(filesystem);
481
+ });
671
482
 
483
+ it("uses the canonical macro day independently of invocation days", async () => {
484
+ const { root, run } = await createCountsFixture(roots);
672
485
  // Period membership follows the canonical macro day, not an invocation
673
486
  // that happened on another day. Search accepts either the macro ID or
674
487
  // title, and totals are still calculated before its one-row page.
675
- await writeFile(
676
- join(root, ".agents", "records", "specs", "2026", "08", "SPEC-CANONICAL-DAY.md"),
677
- "---\nid: SPEC-CANONICAL-DAY\ntitle: Macro by canonical title\nstatus: draft\ncreated_at: 2026-08-03T12:00:00.000Z\n---\n# Macro\n",
678
- "utf8",
679
- );
680
- await writeFile(
681
- join(root, ".agents", "records", "runs", "2026", "08", "RUN-CANONICAL-DAY.jsonl"),
682
- `${run(
683
- "RUN-CANONICAL-DAY",
684
- "2026-08-02",
685
- "TASK-CANONICAL-DAY",
686
- 1_000,
687
- "12:30:00.000Z",
688
- "SPEC-CANONICAL-DAY",
689
- )
690
- .map((record) => JSON.stringify(record))
691
- .join("\n")}\n`,
692
- "utf8",
693
- );
694
- await writeFile(
695
- join(root, ".agents", "records", "runs", "2026", "08", "RUN-CANONICAL-DAY-LATE.jsonl"),
696
- `${run(
697
- "RUN-CANONICAL-DAY-LATE",
698
- "2026-08-04",
699
- "TASK-CANONICAL-DAY-LATE",
700
- 1_000,
701
- "12:30:00.000Z",
702
- "SPEC-CANONICAL-DAY",
703
- )
704
- .map((record) => JSON.stringify(record))
705
- .join("\n")}\n`,
706
- "utf8",
707
- );
488
+ await rebuildSqliteReadModel(root);
489
+ await addCanonicalDayRecords(root, run);
708
490
  await rebuildSqliteReadModel(root);
709
491
  const canonicalDayFilesystem = new SqliteReadModelFileSystem(root);
710
492
  readers.add(canonicalDayFilesystem);
@@ -737,7 +519,12 @@ describe("SQLite dashboard read model", () => {
737
519
  ).toMatchObject({ total_count: 0, rows: [] });
738
520
  canonicalDayFilesystem.close();
739
521
  readers.delete(canonicalDayFilesystem);
522
+ });
740
523
 
524
+ it("keeps all 270 matching task subtotals while paging 50 rows", async () => {
525
+ const { root, run } = await createCountsFixture(roots);
526
+ await addCanonicalDayRecords(root, run);
527
+ await rebuildSqliteReadModel(root);
741
528
  // A large result set keeps its subtotal for all 270 matching Tasks while
742
529
  // the page transports only 50 records.
743
530
  for (let index = 0; index < 270; index += 1) {
@@ -0,0 +1,239 @@
1
+ import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
3
+
4
+ const sqliteTestTempRoot = join(
5
+ resolve(import.meta.dirname, "../../../../../.."),
6
+ ".tmp",
7
+ "sqlite-read-model-tests",
8
+ );
9
+ async function createSqliteTestRoot(): Promise<string> {
10
+ await mkdir(sqliteTestTempRoot, { recursive: true });
11
+ return mkdtemp(join(sqliteTestTempRoot, "fixture-"));
12
+ }
13
+
14
+ // Each scenario owns a fresh canonical dataset; no inter-test mutation order.
15
+ export async function createCountsFixture(roots: string[]) {
16
+ const root = await createSqliteTestRoot();
17
+ roots.push(root);
18
+ await mkdir(join(root, ".agents", "records", "runs", "2026", "08"), { recursive: true });
19
+ for (const kind of ["tasks", "specs", "prds"])
20
+ await mkdir(join(root, ".agents", "records", kind, "2026", "08"), { recursive: true });
21
+ await mkdir(
22
+ join(root, ".agents", "records", "changes", "2026", "08", "CHG-ART", "artifacts", "evidence"),
23
+ { recursive: true },
24
+ );
25
+ await mkdir(
26
+ join(
27
+ root,
28
+ ".agents",
29
+ "records",
30
+ "changes",
31
+ "2026",
32
+ "08",
33
+ "CHG-ART",
34
+ "artifacts",
35
+ "validations",
36
+ ),
37
+ { recursive: true },
38
+ );
39
+ await mkdir(join(root, ".agents", "config"), { recursive: true });
40
+ await writeFile(
41
+ join(root, ".agents", "config", "project.yaml"),
42
+ "schema_version: 1\nzaip:\n enabled: true\nproject:\n id: sqlite-test\n name: sqlite-test\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",
43
+ "utf8",
44
+ );
45
+ await writeFile(
46
+ join(root, ".agents", "config", "framework.lock.yaml"),
47
+ "lock_version: 1\nframework:\n version: 2.5.0\n distribution_sha256: null\nlayout:\n version: 3\nresolved_profile:\n id: null\n sha256: null\nmanaged_files: {}\n",
48
+ "utf8",
49
+ );
50
+ const run = (
51
+ id: string,
52
+ day: string,
53
+ task: string,
54
+ duration: number,
55
+ time = "12:00:00.000Z",
56
+ spec: string | null = null,
57
+ prd: string | null = null,
58
+ ) => [
59
+ {
60
+ schema_version: 2,
61
+ event_id: `${id}:1`,
62
+ run_id: id,
63
+ sequence: 1,
64
+ occurred_at: `${day}T${time}`,
65
+ type: "run.started",
66
+ attribution_status: "attributed",
67
+ phase: "execution",
68
+ run_role: "orchestrator",
69
+ run_purpose: "execution",
70
+ requires_finalizer: false,
71
+ execution_kind: "delivery",
72
+ execution_eligibility: ["delivery"],
73
+ entrypoint: "task",
74
+ work_type: "new",
75
+ storage_mode: "tracked_sanitized",
76
+ client: "codex",
77
+ agent: "agent",
78
+ agent_name: "agent",
79
+ provider: "openai",
80
+ model: "gpt-5",
81
+ reasoning_effort: null,
82
+ client_thread_id: id,
83
+ client_turn_id: id,
84
+ conversation_id: `CONV-${id}`,
85
+ execution_id: `EXEC-${id}`,
86
+ task_id: task,
87
+ spec_id: spec,
88
+ prd_id: prd,
89
+ change_id: null,
90
+ parent_run_id: null,
91
+ continuation_of_run_id: null,
92
+ retry_of_run_id: null,
93
+ agent_invocation_id: `AINV-${id}`,
94
+ root_agent_invocation_id: `AINV-${id}`,
95
+ parent_agent_invocation_id: null,
96
+ invocation_kind: "orchestrator",
97
+ delegation_depth: 0,
98
+ },
99
+ {
100
+ schema_version: 2,
101
+ event_id: `${id}:2`,
102
+ run_id: id,
103
+ sequence: 2,
104
+ occurred_at: `${day}T12:00:01.000Z`,
105
+ type: "usage.recorded",
106
+ phase: "execution",
107
+ activity_kind: "model_usage",
108
+ classification_source: "explicit",
109
+ tokens: {
110
+ input: 1,
111
+ cached_input: 0,
112
+ output: 1,
113
+ reasoning: 0,
114
+ total: 2,
115
+ source: "provider_reported",
116
+ },
117
+ cost: { usd: 1, brl: 5, fx_rate: 5 },
118
+ provider: "openai",
119
+ model: "gpt-5",
120
+ source_segment_key: id,
121
+ model_call_id: `MCALL-${id}`,
122
+ runtime: { reasoning_effort: null, service_tier: null },
123
+ supersedes_event_id: null,
124
+ duration_ms: null,
125
+ duration_provenance: null,
126
+ },
127
+ {
128
+ schema_version: 2,
129
+ event_id: `${id}:3`,
130
+ run_id: id,
131
+ sequence: 3,
132
+ occurred_at: `${day}T12:00:02.000Z`,
133
+ type: "run.finished",
134
+ phase: "execution",
135
+ status: "completed",
136
+ duration_ms: duration,
137
+ duration_provenance: "observed",
138
+ },
139
+ ];
140
+ for (const [id, records] of [
141
+ ["RUN-R1", run("RUN-R1", "2026-08-01", "TASK-A", 600_000, "12:00:00.000Z", "SPEC-A", "PRD-A")],
142
+ [
143
+ "RUN-R2",
144
+ run("RUN-R2", "2026-08-02", "TASK-B", 1_200_000, "12:00:00.000Z", "SPEC-B", "PRD-A"),
145
+ ],
146
+ [
147
+ "RUN-R3",
148
+ run("RUN-R3", "2026-08-02", "TASK-B", 1_200_000, "12:10:00.000Z", "SPEC-B", "PRD-A"),
149
+ ],
150
+ ] as const) {
151
+ await writeFile(
152
+ join(root, ".agents", "records", "runs", "2026", "08", `${id}.jsonl`),
153
+ `${records.map((record) => JSON.stringify(record)).join("\n")}\n`,
154
+ "utf8",
155
+ );
156
+ }
157
+ await writeFile(
158
+ join(
159
+ root,
160
+ ".agents",
161
+ "records",
162
+ "changes",
163
+ "2026",
164
+ "08",
165
+ "CHG-ART",
166
+ "artifacts",
167
+ "evidence",
168
+ "EVD-1.md",
169
+ ),
170
+ "---\nid: EVD-1\nobserved_at: 2026-08-01T12:00:00.000Z\n---\n# Evidence\n",
171
+ "utf8",
172
+ );
173
+ await writeFile(
174
+ join(
175
+ root,
176
+ ".agents",
177
+ "records",
178
+ "changes",
179
+ "2026",
180
+ "08",
181
+ "CHG-ART",
182
+ "artifacts",
183
+ "validations",
184
+ "VAL-1.md",
185
+ ),
186
+ "---\nid: VAL-1\nexecuted_at: 2026-08-02T12:00:00.000Z\n---\n# Validation\n",
187
+ "utf8",
188
+ );
189
+ for (const [kind, id] of [
190
+ ["tasks", "TASK-NO-RUN"],
191
+ ["specs", "SPEC-NO-RUN"],
192
+ ["prds", "PRD-NO-RUN"],
193
+ ] as const)
194
+ await writeFile(
195
+ join(root, ".agents", "records", kind, "2026", "08", `${id}.md`),
196
+ `---\nid: ${id}\ntitle: ${id}\nstatus: draft\ncreated_at: 2026-08-03T12:00:00.000Z\n---\n# ${id}\n`,
197
+ "utf8",
198
+ );
199
+ return { root, run };
200
+ }
201
+
202
+ export async function addCanonicalDayRecords(
203
+ root: string,
204
+ run: Awaited<ReturnType<typeof createCountsFixture>>["run"],
205
+ ) {
206
+ await writeFile(
207
+ join(root, ".agents", "records", "specs", "2026", "08", "SPEC-CANONICAL-DAY.md"),
208
+ "---\nid: SPEC-CANONICAL-DAY\ntitle: Macro by canonical title\nstatus: draft\ncreated_at: 2026-08-03T12:00:00.000Z\n---\n# Macro\n",
209
+ "utf8",
210
+ );
211
+ await writeFile(
212
+ join(root, ".agents", "records", "runs", "2026", "08", "RUN-CANONICAL-DAY.jsonl"),
213
+ `${run(
214
+ "RUN-CANONICAL-DAY",
215
+ "2026-08-02",
216
+ "TASK-CANONICAL-DAY",
217
+ 1_000,
218
+ "12:30:00.000Z",
219
+ "SPEC-CANONICAL-DAY",
220
+ )
221
+ .map((record) => JSON.stringify(record))
222
+ .join("\n")}\n`,
223
+ "utf8",
224
+ );
225
+ await writeFile(
226
+ join(root, ".agents", "records", "runs", "2026", "08", "RUN-CANONICAL-DAY-LATE.jsonl"),
227
+ `${run(
228
+ "RUN-CANONICAL-DAY-LATE",
229
+ "2026-08-04",
230
+ "TASK-CANONICAL-DAY-LATE",
231
+ 1_000,
232
+ "12:30:00.000Z",
233
+ "SPEC-CANONICAL-DAY",
234
+ )
235
+ .map((record) => JSON.stringify(record))
236
+ .join("\n")}\n`,
237
+ "utf8",
238
+ );
239
+ }