@zaip-ai/cli 3.2.0 → 3.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.
- package/dist/services/adapters.d.ts +1 -1
- package/dist/services/adapters.d.ts.map +1 -1
- package/dist/services/adapters.js +2 -1
- package/dist/services/adapters.js.map +1 -1
- package/dist/services/logger.js +24 -8
- package/dist/services/logger.js.map +1 -1
- package/dist/services/logger.test.js +21 -4
- package/dist/services/logger.test.js.map +1 -1
- package/package.json +2 -2
- package/templates/.agents/README-ENG.md +2 -1
- package/templates/.agents/README.md +2 -1
- package/templates/.agents/config/agents/catalog.yaml +1 -0
- package/templates/.agents/config/agents/discovery.json +2 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-builder.toml +2 -2
- package/templates/.agents/dashboard/.zaip-managed.json +60 -52
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-CEDZjl4V.js → CostCalculationGuidePage-BBFDrn64.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-BzaUTZDx.js → DashboardHelpDialog-Cd-qo9w7.js} +4 -3
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-21cCJik-.js → PricingSettingsDialog-J7L5m8lk.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-BAvLPAee.js → ProjectSettingsPage-BpsAFy0g.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-wqW52uCx.js → TraceabilityWorkspace-BzVtGLWS.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{cost-monitor-06__WZco.js → cost-monitor--iPcnZdx.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-BkAm8-h4.js → dashboard-localization-jvdDfNKs.js} +2 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DEmo6Tn2.js → icons-vendor-CWzf_RiP.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{index-DqZKJS8y.js → index-DfdlGa7c.js} +2 -2
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-CadYsm83.js → react-vendor-CtHdbZk3.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-BzKK8kK3.js → traceability-BKrDH2TR.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +5 -5
- package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
- package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
- package/templates/.agents/dashboard/modules/operations/package.json +1 -1
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +4 -1
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +5 -0
- package/templates/.agents/dashboard/modules/operations/src/react/export/xlsx-runtime-e2e.test.ts +55 -31
- package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
- package/templates/.agents/dashboard/packages/branding/package.json +1 -1
- package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
- package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +186 -166
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +135 -115
- package/templates/.agents/dashboard/packages/local-runtime/package.json +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +187 -167
- package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.test.ts +19 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-read-projections.ts +3 -3
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-cache.test.ts +94 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-cache.ts +32 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-force-canonical.test.ts +58 -3
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +2 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-counts-every-root-execution-day-returns.test.ts +13 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-keeps-orphan-telemetry-out-kanban-without.test.ts +2 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-schema.ts +15 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +2 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-schema-dashboard-views.ts +20 -2
- package/templates/.agents/dashboard/packages/ui/package.json +1 -1
- package/templates/.agents/dashboard/scripts/qualify-macro-overview.mjs +50 -0
- package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
- package/templates/.agents/design/.zaip-managed.json +3 -3
- package/templates/.agents/design/apps/catalog/package.json +1 -1
- package/templates/.agents/design/packages/local-runtime/package.json +1 -1
- package/templates/.agents/requirements/autonomous-assurance.md +1 -1
- package/templates/.agents/requirements/cloud-hostinger-autonomy.md +1 -1
- package/templates/.agents/requirements/comparative-audit.md +1 -1
- package/templates/.agents/requirements/context-engine.md +1 -1
- package/templates/.agents/requirements/cost-observability.md +1 -1
- package/templates/.agents/requirements/dashboard-launch.md +1 -1
- package/templates/.agents/requirements/data-export.md +1 -1
- package/templates/.agents/requirements/design-system.md +1 -1
- package/templates/.agents/requirements/execution-engine.md +1 -1
- package/templates/.agents/requirements/kanban-dashboard.md +1 -1
- package/templates/.agents/requirements/model-orchestration.md +1 -1
- package/templates/.agents/requirements/public-distribution.md +11 -3
- package/templates/.agents/requirements/software-architecture.md +1 -1
- package/templates/.agents/requirements/strategic-improvements.md +1 -1
- package/templates/.agents/requirements/work-lifecycle.md +1 -1
- package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
- package/templates/.agents/runtime/VERSION +1 -1
- package/templates/.agents/runtime/bin/zaip.mjs +13 -12
- package/templates/.agents/runtime/checksums.sha256 +5 -5
- package/templates/.agents/runtime/manifest.json +1 -1
- package/templates/.agents/runtime/provenance.json +4 -4
- package/templates/.agents/runtime/sbom.cdx.json +1 -1
- package/templates/.agents/skills/zaip-aios/SKILL.md +0 -1
- package/templates/.agents/skills/zaip-skill-author-skills/scripts/audit-skill-catalog.mjs +1 -0
- package/templates/.agents/skills/zaip-skill-config/SKILL.md +1 -1
- package/templates/.agents/skills/zaip-skill-github-setup/SKILL.md +2 -2
- package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +4 -0
- package/templates/.agents/skills/zaip-skill-setup/SKILL.md +35 -13
- package/templates/.agents/skills/zaip-skill-setup/agents/openai.yaml +2 -2
- package/templates/.agents/skills/zaip-skill-update-version-doc/SKILL.md +39 -0
- package/templates/.agents/skills/zaip-skill-update-version-doc/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-update-version-doc/scripts/update-version-doc.mjs +21 -0
- package/templates/.agents/skills/zaip-skill-update-version-doc/scripts/update-version-doc.test.mjs +32 -0
- package/templates/.agents/zaip-aios/README.md +2 -1
- package/templates/.agents/zaip-aios/VERSION +1 -1
- package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +7 -3
- package/templates/template-manifest.json +120 -96
package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.test.ts
CHANGED
|
@@ -41,4 +41,23 @@ describe("periodMacroMetric", () => {
|
|
|
41
41
|
[expectedStage]: 1,
|
|
42
42
|
});
|
|
43
43
|
});
|
|
44
|
+
|
|
45
|
+
it("keeps the canonical Task and rework classification for the filtered macro population", () => {
|
|
46
|
+
const specs = periodMacroMetric(
|
|
47
|
+
[
|
|
48
|
+
{ id: "SPEC-STRUCTURED", status: "active", task_count: 2, rework_run_count: 0 },
|
|
49
|
+
{ id: "SPEC-EMPTY", status: "blocked", task_count: 0, rework_run_count: 0 },
|
|
50
|
+
],
|
|
51
|
+
"spec",
|
|
52
|
+
);
|
|
53
|
+
const prds = periodMacroMetric(
|
|
54
|
+
[{ id: "PRD-REWORK", status: "delivered", task_count: 3, rework_run_count: 1 }],
|
|
55
|
+
"prd",
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect(specs.status_counts).toEqual({ executing: 1, blocked: 1 });
|
|
59
|
+
expect(specs.classification_counts).toEqual({ empty: 1, structured: 1, rework: 0 });
|
|
60
|
+
expect(prds.status_counts).toEqual({ completed: 1 });
|
|
61
|
+
expect(prds.classification_counts).toEqual({ empty: 0, structured: 0, rework: 1 });
|
|
62
|
+
});
|
|
44
63
|
});
|
|
@@ -308,11 +308,11 @@ export function buildSqliteOperationsProjection(
|
|
|
308
308
|
},
|
|
309
309
|
task_count: taskCount,
|
|
310
310
|
completed_prd_count:
|
|
311
|
-
allTime?.aggregate.completed_prd_count ??
|
|
312
|
-
prds.filter((row) => row.status === "completed").length,
|
|
311
|
+
allTime?.aggregate.completed_prd_count ?? macroMetrics.prds.status_counts.completed ?? 0,
|
|
313
312
|
completed_spec_count:
|
|
314
313
|
allTime?.aggregate.completed_spec_count ??
|
|
315
|
-
specs.
|
|
314
|
+
macroMetrics.specs.status_counts.completed ??
|
|
315
|
+
0,
|
|
316
316
|
completed_task_count: allTime?.aggregate.completed_task_count ?? statuses.completed ?? 0,
|
|
317
317
|
task_status_counts: statuses,
|
|
318
318
|
task_risk_counts: risks,
|
package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-cache.test.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
hasReconciledMacroDistributions,
|
|
5
|
+
readFirstFoldAllTime,
|
|
6
|
+
} from "./sqlite-first-fold-cache.js";
|
|
7
|
+
import type { SqliteFirstFoldAllTime } from "./sqlite-read-model-first-fold-types.js";
|
|
8
|
+
|
|
9
|
+
function cachedFirstFold(
|
|
10
|
+
specs: { entityCount: number; statusCount: number; classificationCount: number },
|
|
11
|
+
prds: { entityCount: number; statusCount: number; classificationCount: number },
|
|
12
|
+
) {
|
|
13
|
+
const macro = (item: {
|
|
14
|
+
entityCount: number;
|
|
15
|
+
statusCount: number;
|
|
16
|
+
classificationCount: number;
|
|
17
|
+
}) => ({
|
|
18
|
+
entity_count: item.entityCount,
|
|
19
|
+
status_counts: { executing: item.statusCount },
|
|
20
|
+
classification_counts: { structured: item.classificationCount },
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
macro_metrics: { specs: macro(specs), prds: macro(prds) },
|
|
24
|
+
} as unknown as SqliteFirstFoldAllTime;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe("first-fold all-time cache", () => {
|
|
28
|
+
it("rejects a legacy macro cache that would render zero panels beside populated cards", () => {
|
|
29
|
+
expect(
|
|
30
|
+
hasReconciledMacroDistributions(
|
|
31
|
+
cachedFirstFold(
|
|
32
|
+
{ entityCount: 66, statusCount: 0, classificationCount: 0 },
|
|
33
|
+
{ entityCount: 16, statusCount: 0, classificationCount: 0 },
|
|
34
|
+
),
|
|
35
|
+
),
|
|
36
|
+
).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("accepts fully reconciled SPEC and PRD panels", () => {
|
|
40
|
+
expect(
|
|
41
|
+
hasReconciledMacroDistributions(
|
|
42
|
+
cachedFirstFold(
|
|
43
|
+
{ entityCount: 66, statusCount: 66, classificationCount: 66 },
|
|
44
|
+
{ entityCount: 16, statusCount: 16, classificationCount: 16 },
|
|
45
|
+
),
|
|
46
|
+
),
|
|
47
|
+
).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("rejects malformed legacy count maps before summing them", () => {
|
|
51
|
+
const legacy = cachedFirstFold(
|
|
52
|
+
{ entityCount: 1, statusCount: 1, classificationCount: 1 },
|
|
53
|
+
{ entityCount: 1, statusCount: 1, classificationCount: 1 },
|
|
54
|
+
) as unknown as { macro_metrics: { specs: { status_counts: unknown } } };
|
|
55
|
+
legacy.macro_metrics.specs.status_counts = undefined;
|
|
56
|
+
|
|
57
|
+
expect(hasReconciledMacroDistributions(legacy as unknown as SqliteFirstFoldAllTime)).toBe(
|
|
58
|
+
false,
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("recomputes a persisted legacy payload whose macro panels are missing", () => {
|
|
63
|
+
const database = new DatabaseSync(":memory:");
|
|
64
|
+
try {
|
|
65
|
+
database.exec(`
|
|
66
|
+
CREATE TABLE zaip_aios_activities (payload_type TEXT, payload_json TEXT, recorded_at TEXT);
|
|
67
|
+
INSERT INTO zaip_aios_activities VALUES ('first_fold_all_time_cache', '{"aggregate":{}}', '2026-09-15T00:00:00.000Z');
|
|
68
|
+
CREATE VIEW zaip_vw_first_fold_overview_all_time AS SELECT 0 AS task_count, 0 AS completed_task_count, 1 AS spec_count, 0 AS completed_spec_count, 1 AS prd_count, 0 AS completed_prd_count, 0 AS total_run_count, 0 AS agent_invocation_count, NULL AS llm_call_count, NULL AS total_duration_ms, NULL AS total_tokens, NULL AS total_cost_usd, NULL AS total_cost_brl, 0 AS execution_day_count, NULL AS latest_task_created_at, NULL AS start_day, NULL AS end_day;
|
|
69
|
+
CREATE VIEW zaip_vw_first_fold_task_status_all_time AS SELECT 'draft' AS status, 0 AS task_count WHERE 0;
|
|
70
|
+
CREATE VIEW zaip_vw_first_fold_task_risk_all_time AS SELECT 'standard' AS risk, 0 AS task_count WHERE 0;
|
|
71
|
+
CREATE VIEW zaip_vw_first_fold_top_tasks_all_time AS SELECT 1 AS rank WHERE 0;
|
|
72
|
+
CREATE VIEW zaip_vw_first_fold_task_metrics_all_time AS SELECT NULL AS average_duration_ms_per_task, NULL AS average_tokens_per_task, NULL AS average_cost_usd_per_task, NULL AS average_cost_brl_per_task, NULL AS latest_task_created_at;
|
|
73
|
+
CREATE VIEW zaip_vw_first_fold_spec_status_all_time AS SELECT 'executing' AS stage, 1 AS entity_count;
|
|
74
|
+
CREATE VIEW zaip_vw_first_fold_prd_status_all_time AS SELECT 'executing' AS stage, 1 AS entity_count;
|
|
75
|
+
CREATE VIEW zaip_vw_first_fold_spec_classification_all_time AS SELECT 'structured' AS classification, 1 AS entity_count;
|
|
76
|
+
CREATE VIEW zaip_vw_first_fold_prd_classification_all_time AS SELECT 'structured' AS classification, 1 AS entity_count;
|
|
77
|
+
CREATE VIEW zaip_vw_first_fold_spec_metrics_all_time AS SELECT 1 AS entity_count, 0 AS total_run_count, NULL AS total_duration_ms, NULL AS total_tokens, NULL AS total_cost_usd, NULL AS total_cost_brl, NULL AS average_duration_ms, NULL AS average_tokens, NULL AS average_cost_usd, NULL AS average_cost_brl, NULL AS latest_created_at;
|
|
78
|
+
CREATE VIEW zaip_vw_first_fold_prd_metrics_all_time AS SELECT 1 AS entity_count, 0 AS total_run_count, NULL AS total_duration_ms, NULL AS total_tokens, NULL AS total_cost_usd, NULL AS total_cost_brl, NULL AS average_duration_ms, NULL AS average_tokens, NULL AS average_cost_usd, NULL AS average_cost_brl, NULL AS latest_created_at;
|
|
79
|
+
CREATE VIEW zaip_vw_first_fold_top_specs_all_time AS SELECT 1 AS rank WHERE 0;
|
|
80
|
+
CREATE VIEW zaip_vw_first_fold_top_prds_all_time AS SELECT 1 AS rank WHERE 0;
|
|
81
|
+
`);
|
|
82
|
+
const rebuilt = readFirstFoldAllTime(database, () => 0);
|
|
83
|
+
|
|
84
|
+
expect(rebuilt.macro_metrics.specs).toMatchObject({
|
|
85
|
+
entity_count: 1,
|
|
86
|
+
status_counts: { executing: 1 },
|
|
87
|
+
classification_counts: { structured: 1 },
|
|
88
|
+
});
|
|
89
|
+
expect(rebuilt.macro_metrics.prds.entity_count).toBe(1);
|
|
90
|
+
} finally {
|
|
91
|
+
database.close();
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -24,13 +24,44 @@ function persisted<T>(database: DatabaseSync, payloadType: string): T | null {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/** Reject legacy first-fold payloads that have card totals but no matching macro panels. */
|
|
28
|
+
export function hasReconciledMacroDistributions(value: SqliteFirstFoldAllTime): boolean {
|
|
29
|
+
return (["specs", "prds"] as const).every((kind) => {
|
|
30
|
+
const metric = value.macro_metrics?.[kind];
|
|
31
|
+
if (
|
|
32
|
+
metric === null ||
|
|
33
|
+
metric === undefined ||
|
|
34
|
+
!Number.isSafeInteger(metric.entity_count) ||
|
|
35
|
+
metric.entity_count < 0 ||
|
|
36
|
+
!isCountMap(metric.status_counts) ||
|
|
37
|
+
!isCountMap(metric.classification_counts)
|
|
38
|
+
)
|
|
39
|
+
return false;
|
|
40
|
+
const sum = (counts: Record<string, number>) =>
|
|
41
|
+
Object.values(counts).reduce((total, count) => total + count, 0);
|
|
42
|
+
return (
|
|
43
|
+
sum(metric.status_counts) === metric.entity_count &&
|
|
44
|
+
sum(metric.classification_counts) === metric.entity_count
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isCountMap(value: unknown): value is Record<string, number> {
|
|
50
|
+
return (
|
|
51
|
+
value !== null &&
|
|
52
|
+
typeof value === "object" &&
|
|
53
|
+
!Array.isArray(value) &&
|
|
54
|
+
Object.values(value).every((count) => Number.isSafeInteger(count) && count >= 0)
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
27
58
|
export function readFirstFoldAllTime(
|
|
28
59
|
database: DatabaseSync,
|
|
29
60
|
dailyAggregateCount: () => number,
|
|
30
61
|
viewport?: SqliteFirstFoldViewport,
|
|
31
62
|
): SqliteFirstFoldAllTime {
|
|
32
63
|
const cached = persisted<SqliteFirstFoldAllTime>(database, "first_fold_all_time_cache");
|
|
33
|
-
if (cached !== null) return cached;
|
|
64
|
+
if (cached !== null && hasReconciledMacroDistributions(cached)) return cached;
|
|
34
65
|
const timed = <T>(phase: string, action: () => T) => measureFirstFoldQuery(phase, action);
|
|
35
66
|
const row =
|
|
36
67
|
viewport === undefined
|
package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-force-canonical.test.ts
CHANGED
|
@@ -2,7 +2,9 @@ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { DatabaseSync } from "node:sqlite";
|
|
4
4
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import {
|
|
5
|
+
import { buildSqliteOperationsProjection } from "./server-read-projections.js";
|
|
6
|
+
import { rebuildSqliteReadModel, SqliteReadModelFileSystem } from "./sqlite-read-model.js";
|
|
7
|
+
import { readModelSchemaVersion } from "./sqlite-read-model-schema.js";
|
|
6
8
|
|
|
7
9
|
const roots: string[] = [];
|
|
8
10
|
|
|
@@ -22,6 +24,37 @@ afterEach(async () => {
|
|
|
22
24
|
});
|
|
23
25
|
|
|
24
26
|
describe("forceCanonical SQLite refresh", () => {
|
|
27
|
+
it("keeps filtered completed cards consistent with legacy delivered macro statuses", async () => {
|
|
28
|
+
const root = await fixture();
|
|
29
|
+
for (const kind of ["spec", "prd"]) {
|
|
30
|
+
const directory = join(root, `.agents/records/${kind}s/2026/08`);
|
|
31
|
+
await mkdir(directory, { recursive: true });
|
|
32
|
+
await writeFile(
|
|
33
|
+
join(directory, `${kind.toUpperCase()}-DELIVERED.md`),
|
|
34
|
+
`---\nid: ${kind.toUpperCase()}-DELIVERED\ntitle: Delivered macro\nstatus: delivered\ncreated_at: 2026-08-03T12:00:00.000Z\n---\n`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
await rebuildSqliteReadModel(root);
|
|
38
|
+
const filesystem = new SqliteReadModelFileSystem(root);
|
|
39
|
+
try {
|
|
40
|
+
const projection = buildSqliteOperationsProjection(
|
|
41
|
+
filesystem,
|
|
42
|
+
{ version: 3 } as Parameters<typeof buildSqliteOperationsProjection>[1],
|
|
43
|
+
{ from: "2026-08-01", to: "2026-08-31" },
|
|
44
|
+
{},
|
|
45
|
+
);
|
|
46
|
+
expect(projection.summary.overview_metrics).toMatchObject({
|
|
47
|
+
completed_spec_count: 1,
|
|
48
|
+
completed_prd_count: 1,
|
|
49
|
+
macro_metrics: {
|
|
50
|
+
specs: { status_counts: { completed: 1 } },
|
|
51
|
+
prds: { status_counts: { completed: 1 } },
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
} finally {
|
|
55
|
+
filesystem.close();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
25
58
|
it("bypasses a healthy generation and verifies canonical sources again", async () => {
|
|
26
59
|
const root = await fixture();
|
|
27
60
|
vi.spyOn(Date, "now").mockReturnValue(Date.now() + 5_000);
|
|
@@ -37,14 +70,20 @@ describe("forceCanonical SQLite refresh", () => {
|
|
|
37
70
|
});
|
|
38
71
|
});
|
|
39
72
|
|
|
40
|
-
it("
|
|
73
|
+
it("upgrades an old physical projection when the canonical sources are unchanged", async () => {
|
|
41
74
|
const root = await fixture();
|
|
42
75
|
const initial = await rebuildSqliteReadModel(root);
|
|
43
76
|
const database = new DatabaseSync(initial.path);
|
|
44
77
|
try {
|
|
78
|
+
// Simulate the pre-80 cache that predates macro classification fields.
|
|
79
|
+
// The source fingerprint remains valid, so only the schema contract can
|
|
80
|
+
// cause the next ordinary refresh to rebuild this disposable projection.
|
|
81
|
+
database.exec(
|
|
82
|
+
"DROP TABLE operations_work_projection; CREATE TABLE operations_work_projection (id TEXT);",
|
|
83
|
+
);
|
|
45
84
|
database
|
|
46
85
|
.prepare(
|
|
47
|
-
"UPDATE zaip_aios_activities SET payload_json=json_set(payload_json, '$.value', '
|
|
86
|
+
"UPDATE zaip_aios_activities SET payload_json=json_set(payload_json, '$.value', '79') WHERE payload_type='metadata' AND activity_key='metadata:schema_version'",
|
|
48
87
|
)
|
|
49
88
|
.run();
|
|
50
89
|
} finally {
|
|
@@ -54,5 +93,21 @@ describe("forceCanonical SQLite refresh", () => {
|
|
|
54
93
|
cache: "rebuilt",
|
|
55
94
|
path: initial.path,
|
|
56
95
|
});
|
|
96
|
+
const upgraded = new DatabaseSync(initial.path, { readOnly: true });
|
|
97
|
+
try {
|
|
98
|
+
expect(upgraded.prepare("PRAGMA table_info(operations_work_projection)").all()).toEqual(
|
|
99
|
+
expect.arrayContaining([
|
|
100
|
+
expect.objectContaining({ name: "task_count" }),
|
|
101
|
+
expect.objectContaining({ name: "rework_run_count" }),
|
|
102
|
+
]),
|
|
103
|
+
);
|
|
104
|
+
expect(
|
|
105
|
+
upgraded
|
|
106
|
+
.prepare("SELECT value FROM zaip_vw_read_model_metadata WHERE key='schema_version'")
|
|
107
|
+
.get(),
|
|
108
|
+
).toEqual({ value: readModelSchemaVersion });
|
|
109
|
+
} finally {
|
|
110
|
+
upgraded.close();
|
|
111
|
+
}
|
|
57
112
|
});
|
|
58
113
|
});
|
package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts
CHANGED
|
@@ -22,6 +22,8 @@ export function materializeOperationsWorkProjection(database: DatabaseSync): voi
|
|
|
22
22
|
JOIN resolved canonical ON canonical.entity_kind=summary.entity_kind AND canonical.id=summary.id
|
|
23
23
|
LEFT JOIN changes change ON change.id=canonical.resolved_change_id`;
|
|
24
24
|
database.exec(`
|
|
25
|
+
-- Schema compatibility is checked before incremental refresh. Preserve
|
|
26
|
+
-- this table and its indexes; only replace the projected rows here.
|
|
25
27
|
CREATE TABLE IF NOT EXISTS operations_work_projection AS ${select} LIMIT 0;
|
|
26
28
|
DELETE FROM operations_work_projection;
|
|
27
29
|
INSERT INTO operations_work_projection ${select};
|
|
@@ -262,6 +262,19 @@ describe("SQLite dashboard read model", () => {
|
|
|
262
262
|
total_cost_brl: null,
|
|
263
263
|
}),
|
|
264
264
|
);
|
|
265
|
+
// Period-scoped macro cards are read from operations_work_projection.
|
|
266
|
+
// Keep structural classification fields in that physical projection; a
|
|
267
|
+
// missing column would make every selected SPEC/PRD appear empty.
|
|
268
|
+
expect(
|
|
269
|
+
filesystem.canonicalWorkPage("spec", { from: null, to: null }, 0, 50).rows,
|
|
270
|
+
).toContainEqual(
|
|
271
|
+
expect.objectContaining({ id: "SPEC-NO-RUN", task_count: 0, rework_run_count: 0 }),
|
|
272
|
+
);
|
|
273
|
+
expect(
|
|
274
|
+
filesystem.canonicalWorkPage("prd", { from: null, to: null }, 0, 50).rows,
|
|
275
|
+
).toContainEqual(
|
|
276
|
+
expect.objectContaining({ id: "PRD-NO-RUN", task_count: 0, rework_run_count: 0 }),
|
|
277
|
+
);
|
|
265
278
|
const allTime = filesystem.firstFoldAllTime();
|
|
266
279
|
expectPersistedFirstFoldCaches(root, allTime, filesystem.firstFoldBoardAllTime());
|
|
267
280
|
expect(allTime.aggregate).toMatchObject({
|
|
@@ -4,6 +4,7 @@ import { join, resolve } from "node:path";
|
|
|
4
4
|
import { DatabaseSync } from "node:sqlite";
|
|
5
5
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
6
6
|
import { rebuildSqliteReadModel, SqliteReadModelFileSystem } from "./sqlite-read-model.js";
|
|
7
|
+
import { readModelSchemaVersion } from "./sqlite-read-model-schema.js";
|
|
7
8
|
|
|
8
9
|
const roots: string[] = [];
|
|
9
10
|
const sqliteTestTempRoot = join(
|
|
@@ -210,7 +211,7 @@ describe("SQLite dashboard read model", () => {
|
|
|
210
211
|
snapshot
|
|
211
212
|
.prepare("SELECT value FROM zaip_vw_read_model_metadata WHERE key='schema_version'")
|
|
212
213
|
.get(),
|
|
213
|
-
).toEqual({ value:
|
|
214
|
+
).toEqual({ value: readModelSchemaVersion });
|
|
214
215
|
} finally {
|
|
215
216
|
snapshot.close();
|
|
216
217
|
}
|
|
@@ -4,9 +4,9 @@ import { DatabaseSync } from "node:sqlite";
|
|
|
4
4
|
import { hasRequiredFirstFoldPayloads } from "./sqlite-first-fold-persistence.js";
|
|
5
5
|
import type { SqliteReadModelSourceCheckpoint } from "./sqlite-read-model-source-fingerprint.js";
|
|
6
6
|
export const dbRelativePath = ".local/sqlite/zaip-aios-dashboard.sqlite";
|
|
7
|
-
// Schema
|
|
7
|
+
// Schema 80 requires the persisted macro classification fields used by period views.
|
|
8
8
|
// Old snapshots must rebuild even when their canonical hashes still match.
|
|
9
|
-
export const readModelSchemaVersion = "
|
|
9
|
+
export const readModelSchemaVersion = "80";
|
|
10
10
|
export const requiredPersistedTables = [
|
|
11
11
|
"zaip_aios_activities",
|
|
12
12
|
"zaip_dim_llm_model",
|
|
@@ -151,6 +151,19 @@ export function validateSqliteReadModelSchema(path: string): {
|
|
|
151
151
|
throw new Error(
|
|
152
152
|
`SQLite schema ${readModelSchemaVersion} requires the complete persisted table contract; found ${tables.join(", ") || "none"}`,
|
|
153
153
|
);
|
|
154
|
+
const projectionColumns = database
|
|
155
|
+
.prepare("PRAGMA table_info(operations_work_projection)")
|
|
156
|
+
.all() as Array<{ name?: unknown }>;
|
|
157
|
+
const projectionColumnNames = new Set(
|
|
158
|
+
projectionColumns.flatMap((column) => (typeof column.name === "string" ? [column.name] : [])),
|
|
159
|
+
);
|
|
160
|
+
const missingProjectionColumns = ["task_count", "rework_run_count"].filter(
|
|
161
|
+
(column) => !projectionColumnNames.has(column),
|
|
162
|
+
);
|
|
163
|
+
if (missingProjectionColumns.length > 0)
|
|
164
|
+
throw new Error(
|
|
165
|
+
`SQLite schema ${readModelSchemaVersion} requires operations_work_projection columns: ${missingProjectionColumns.join(", ")}`,
|
|
166
|
+
);
|
|
154
167
|
const names = new Set(objects.map((object) => object.name));
|
|
155
168
|
const missingViews = requiredViews.filter((view) => !names.has(view));
|
|
156
169
|
if (missingViews.length > 0)
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
SqliteReadModelFileSystem,
|
|
9
9
|
sourceFingerprint,
|
|
10
10
|
} from "./sqlite-read-model.js";
|
|
11
|
+
import { readModelSchemaVersion } from "./sqlite-read-model-schema.js";
|
|
11
12
|
|
|
12
13
|
const roots: string[] = [];
|
|
13
14
|
const sqliteTestTempRoot = join(
|
|
@@ -177,7 +178,7 @@ describe("SQLite dashboard read model", () => {
|
|
|
177
178
|
metadata
|
|
178
179
|
.prepare("SELECT value FROM zaip_vw_read_model_metadata WHERE key='schema_version'")
|
|
179
180
|
.get(),
|
|
180
|
-
).toEqual({ value:
|
|
181
|
+
).toEqual({ value: readModelSchemaVersion });
|
|
181
182
|
} finally {
|
|
182
183
|
metadata.close();
|
|
183
184
|
}
|
package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-schema-dashboard-views.ts
CHANGED
|
@@ -49,13 +49,31 @@ export const dashboardViewSql = ` CREATE VIEW vw_operations_work_list AS
|
|
|
49
49
|
), durations AS (
|
|
50
50
|
SELECT entity_kind, entity_id, ROUND(SUM(duration_ms)) AS total_duration
|
|
51
51
|
FROM entity_activity GROUP BY entity_kind, entity_id
|
|
52
|
+
), macro_classification AS (
|
|
53
|
+
-- These are structural macro facts. The narrowed-period first
|
|
54
|
+
-- fold filters the selected macros by activity, then retains their
|
|
55
|
+
-- canonical Task/rework classification instead of treating the
|
|
56
|
+
-- compact work-list row as an empty delivery.
|
|
57
|
+
SELECT 'spec' AS entity_kind, spec_id AS entity_id,
|
|
58
|
+
COUNT(DISTINCT task_id) AS task_count,
|
|
59
|
+
SUM(CASE WHEN work_type='rework' AND run_role='root' THEN 1 ELSE 0 END) AS rework_run_count
|
|
60
|
+
FROM activities WHERE spec_id IS NOT NULL GROUP BY spec_id
|
|
61
|
+
UNION ALL
|
|
62
|
+
SELECT 'prd' AS entity_kind, prd_id AS entity_id,
|
|
63
|
+
COUNT(DISTINCT task_id) AS task_count,
|
|
64
|
+
SUM(CASE WHEN work_type='rework' AND run_role='root' THEN 1 ELSE 0 END) AS rework_run_count
|
|
65
|
+
FROM activities WHERE prd_id IS NOT NULL GROUP BY prd_id
|
|
52
66
|
)
|
|
53
67
|
SELECT w.entity_kind, w.id, w.title, w.status, w.risk, w.occurred_at, w.occurred_day_sp,
|
|
54
68
|
f.invocation_count, f.llm_call_count, d.total_duration,
|
|
55
|
-
f.total_tokens, f.total_cost_usd, f.total_cost_brl
|
|
69
|
+
f.total_tokens, f.total_cost_usd, f.total_cost_brl,
|
|
70
|
+
COALESCE(classification.task_count, 0) AS task_count,
|
|
71
|
+
COALESCE(classification.rework_run_count, 0) AS rework_run_count
|
|
56
72
|
FROM work_items w
|
|
57
73
|
LEFT JOIN financial f ON f.entity_kind=w.entity_kind AND f.entity_id=w.id
|
|
58
|
-
LEFT JOIN durations d ON d.entity_kind=w.entity_kind AND d.entity_id=w.id
|
|
74
|
+
LEFT JOIN durations d ON d.entity_kind=w.entity_kind AND d.entity_id=w.id
|
|
75
|
+
LEFT JOIN macro_classification classification
|
|
76
|
+
ON classification.entity_kind=w.entity_kind AND classification.entity_id=w.id;
|
|
59
77
|
-- Kanban is a lifecycle surface: it lists only canonical records.
|
|
60
78
|
-- Telemetry-only identifiers remain Cost Monitor facts but cannot
|
|
61
79
|
-- create a synthetic operational card.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
|
|
6
|
+
const root = resolve(import.meta.dirname, "../../..");
|
|
7
|
+
const mode = process.argv[2];
|
|
8
|
+
assert(["test", "check", "integration"].includes(mode), "Expected test|check|integration");
|
|
9
|
+
function run(args, cwd = root) {
|
|
10
|
+
const result = spawnSync(process.execPath, args, {
|
|
11
|
+
cwd,
|
|
12
|
+
windowsHide: true,
|
|
13
|
+
stdio: "inherit",
|
|
14
|
+
timeout: 300_000,
|
|
15
|
+
env: { ...process.env, CI: "true" },
|
|
16
|
+
});
|
|
17
|
+
if (result.error) throw result.error;
|
|
18
|
+
assert.equal(result.status, 0, `Check failed: ${args.join(" ")}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!existsSync(join(root, "node_modules/vitest/vitest.mjs"))) {
|
|
22
|
+
run([
|
|
23
|
+
join(dirname(process.execPath), "node_modules/corepack/dist/pnpm.js"),
|
|
24
|
+
"install",
|
|
25
|
+
"--offline",
|
|
26
|
+
"--frozen-lockfile",
|
|
27
|
+
"--ignore-scripts",
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
const runtime = ".agents/dashboard/packages/local-runtime";
|
|
31
|
+
const operations = ".agents/dashboard/modules/operations";
|
|
32
|
+
if (mode === "check") {
|
|
33
|
+
for (const directory of [runtime, operations]) {
|
|
34
|
+
run([join(root, "node_modules/typescript/bin/tsc"), "--noEmit"], join(root, directory));
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
const tests =
|
|
38
|
+
mode === "test"
|
|
39
|
+
? [
|
|
40
|
+
`${runtime}/src/sqlite-first-fold-cache.test.ts`,
|
|
41
|
+
`${runtime}/src/server-period-macro-metric.test.ts`,
|
|
42
|
+
`${operations}/src/react/OperationsOverview.test.ts`,
|
|
43
|
+
]
|
|
44
|
+
: [
|
|
45
|
+
`${runtime}/src/sqlite-force-canonical.test.ts`,
|
|
46
|
+
`${runtime}/src/sqlite-read-model-counts-every-root-execution-day-returns.test.ts`,
|
|
47
|
+
`${runtime}/src/server-overview.test.ts`,
|
|
48
|
+
];
|
|
49
|
+
run([join(root, "node_modules/vitest/vitest.mjs"), "run", ...tests]);
|
|
50
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"files": [
|
|
5
5
|
{
|
|
6
6
|
"path": "DESIGN.md",
|
|
@@ -252,7 +252,7 @@
|
|
|
252
252
|
},
|
|
253
253
|
{
|
|
254
254
|
"path": "apps/catalog/package.json",
|
|
255
|
-
"sha256": "
|
|
255
|
+
"sha256": "a5f6cdd1f87cd6476ddfee45685be8daa2769ac1275ae201a725aac53f11739c"
|
|
256
256
|
},
|
|
257
257
|
{
|
|
258
258
|
"path": "apps/catalog/public/logo_novo_azul.png",
|
|
@@ -352,7 +352,7 @@
|
|
|
352
352
|
},
|
|
353
353
|
{
|
|
354
354
|
"path": "packages/local-runtime/package.json",
|
|
355
|
-
"sha256": "
|
|
355
|
+
"sha256": "7afa7584db17c5e4e7584da707b0d7cbb38affa1940a1d989ee4d267df80d33d"
|
|
356
356
|
},
|
|
357
357
|
{
|
|
358
358
|
"path": "packages/local-runtime/runtime.mjs",
|