@zaip-ai/cli 1.3.4 → 1.3.5

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 (52) hide show
  1. package/package.json +1 -1
  2. package/templates/.agents/dashboard/.zaip-managed.json +44 -44
  3. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-BZWjXQB-.js → CostCalculationGuidePage-D1mpyYwh.js} +1 -1
  4. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-D8qqeIPE.js → DashboardHelpDialog-CKO47D5s.js} +1 -1
  5. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-Cm12_A0v.js → PricingSettingsDialog-BuSlCbyZ.js} +1 -1
  6. package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-DxO9F8B4.js → ProjectSettingsPage-CUHFkAhZ.js} +1 -1
  7. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-_x0twOcS.js → TraceabilityWorkspace-BmASq2gW.js} +1 -1
  8. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Csj6LiAt.js +68 -0
  9. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-CR4Fbf3g.js → icons-vendor-VyTTl_ph.js} +1 -1
  10. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DIwDvCu5.js +2 -0
  11. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-hPPfVGQv.css +1 -0
  12. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-DRJQbKnp.js → react-vendor-CTC6vtln.js} +1 -1
  13. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-CMrDYqjR.js → traceability-9Mtvbwrg.js} +1 -1
  14. package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
  15. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +32 -13
  16. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +13 -3
  17. package/templates/.agents/dashboard/apps/frontend/src/dashboard-capability-nav.css +11 -0
  18. package/templates/.agents/dashboard/apps/frontend/src/operations-period-contract.test.ts +14 -0
  19. package/templates/.agents/dashboard/apps/frontend/src/operations-period.test.ts +3 -6
  20. package/templates/.agents/dashboard/apps/frontend/src/operations-period.ts +3 -2
  21. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +2 -1
  22. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +12 -4
  23. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +67 -24
  24. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +19 -4
  25. package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.test.ts +25 -0
  26. package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.ts +29 -0
  27. package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +3 -2
  28. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +12 -2
  29. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-loading.spec.ts +1 -1
  30. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-pagination.spec.ts +4 -0
  31. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-period-transition.spec.ts +6 -0
  32. package/templates/.agents/dashboard/tests/e2e/specs/dashboard-performance-matrix.spec.ts +4 -0
  33. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +40 -18
  34. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  35. package/templates/.agents/requirements/comparative-audit.md +1 -1
  36. package/templates/.agents/requirements/context-engine.md +1 -1
  37. package/templates/.agents/requirements/cost-observability.md +1 -1
  38. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  39. package/templates/.agents/requirements/data-export.md +1 -1
  40. package/templates/.agents/requirements/design-system.md +1 -1
  41. package/templates/.agents/requirements/execution-engine.md +1 -1
  42. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  43. package/templates/.agents/requirements/model-orchestration.md +1 -1
  44. package/templates/.agents/requirements/public-distribution.md +1 -1
  45. package/templates/.agents/requirements/software-architecture.md +1 -1
  46. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  47. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  48. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  49. package/templates/template-manifest.json +61 -61
  50. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Cpp8xFij.js +0 -68
  51. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-B31GMG5O.css +0 -1
  52. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-ncC1rxap.js +0 -2
@@ -537,7 +537,8 @@ export function formatExecutionAverageHint(
537
537
  totalDays: number,
538
538
  locale: OperationsLocale,
539
539
  ): string {
540
- if (totalDurationMs <= 0 || totalTasks <= 0 || totalDays <= 0) {
540
+ if (totalTasks <= 0) return "0";
541
+ if (totalDurationMs <= 0 || totalDays <= 0) {
541
542
  return locale === "pt-BR"
542
543
  ? "Médias de execução indisponíveis"
543
544
  : "Execution averages unavailable";
@@ -12,6 +12,7 @@ import {
12
12
  findLatestCreatedAt,
13
13
  findLatestTaskCreatedAt,
14
14
  formatExecutionAverageHint,
15
+ formatSummaryMetricValue,
15
16
  formatTokenThroughputHint,
16
17
  macroClassificationStats,
17
18
  macroStatusDistribution,
@@ -278,11 +279,12 @@ describe("execution average summary", () => {
278
279
  );
279
280
  });
280
281
 
281
- it("localizes English and preserves unavailable coverage", () => {
282
+ it("localizes English and shows zero for an empty population", () => {
282
283
  expect(formatExecutionAverageHint(3_600_000, 3, 1, "en-US")).toBe(
283
284
  "20 min / task (1 h / day) (1 day)",
284
285
  );
285
- expect(formatExecutionAverageHint(0, 0, 0, "pt-BR")).toBe("Médias de execução indisponíveis");
286
+ expect(formatExecutionAverageHint(0, 0, 0, "pt-BR")).toBe("0");
287
+ expect(formatExecutionAverageHint(0, 0, 0, "en-US")).toBe("0");
286
288
  });
287
289
  });
288
290
 
@@ -351,14 +353,20 @@ describe("overviewMetricAverages", () => {
351
353
  });
352
354
  });
353
355
 
354
- it("passes unknown totals through as unavailable instead of coercing them to zero", () => {
356
+ it("keeps unknown totals null internally and renders zero only for an empty population", () => {
355
357
  const averages = overviewMetricAverages({ durationMs: null, tokens: null, costBrl: null }, 3);
356
358
  expect(averages).toEqual({ durationMs: null, tokens: null, costBrl: null });
357
359
  const source = readOverviewSource();
358
360
  expect(source).toContain(
359
361
  "durationMs: totalDurationMetric, tokens: totalTokensMetric, costBrl: totalCostMetric",
360
362
  );
361
- expect(source).toContain("formatEntityDuration(averageDuration, locale)");
363
+ expect(formatSummaryMetricValue(null, 0, (value) => String(value), "pt-BR")).toBe("0");
364
+ expect(formatSummaryMetricValue(null, 0, (value) => String(value), "en-US")).toBe("0");
365
+ expect(formatSummaryMetricValue(null, 1, (value) => String(value), "pt-BR")).toBe(
366
+ "Indisponível",
367
+ );
368
+ expect(formatSummaryMetricValue(42, 0, (value) => String(value), "pt-BR")).toBe("42");
369
+ expect(source).toContain("formatSummaryMetricValue(\n averageDuration,");
362
370
  expect(source).not.toContain("executionMetrics.durationMs ?? 0");
363
371
  });
364
372
  });
@@ -98,6 +98,20 @@ function formatEntityDuration(value: number | null, locale: OperationsLocale): s
98
98
  if (value !== null) return formatDurationMs(value, locale);
99
99
  return locale === "pt-BR" ? "Indisponível" : "Unavailable";
100
100
  }
101
+
102
+ /**
103
+ * Keeps missing source facts as null while making a genuinely empty selected
104
+ * population readable as the factual numeric zero in the overview.
105
+ */
106
+ export function formatSummaryMetricValue(
107
+ value: number | null,
108
+ populationCount: number | null,
109
+ formatKnownValue: (knownValue: number) => string,
110
+ locale: OperationsLocale,
111
+ ): string {
112
+ if (value !== null) return formatKnownValue(value);
113
+ return populationCount === 0 ? "0" : formatEntityDuration(null, locale);
114
+ }
101
115
  export function OperationsOverview({
102
116
  currency,
103
117
  data,
@@ -399,18 +413,32 @@ export function OperationsOverview({
399
413
  : findLatestCreatedAt(tasks)
400
414
  : (selectedMacroAggregate?.latest_created_at ?? findLatestCreatedAt(selectedMacros));
401
415
  const metricStats = [
402
- [text.averageTime, formatEntityDuration(averageDuration, locale)],
416
+ [
417
+ text.averageTime,
418
+ formatSummaryMetricValue(
419
+ averageDuration,
420
+ selectedCount,
421
+ (value) => formatDurationMs(value, locale),
422
+ locale,
423
+ ),
424
+ ],
403
425
  [
404
426
  text.averageTokens,
405
- averageTokens === null
406
- ? formatEntityDuration(null, locale)
407
- : formatCompactNumber(averageTokens, locale),
427
+ formatSummaryMetricValue(
428
+ averageTokens,
429
+ selectedCount,
430
+ (value) => formatCompactNumber(value, locale),
431
+ locale,
432
+ ),
408
433
  ],
409
434
  [
410
435
  text.averageCost,
411
- averageCost === null
412
- ? formatEntityDuration(null, locale)
413
- : formatCurrency(averageCost, currency, locale),
436
+ formatSummaryMetricValue(
437
+ averageCost,
438
+ selectedCount,
439
+ (value) => formatCurrency(value, currency, locale),
440
+ locale,
441
+ ),
414
442
  ],
415
443
  ] as const;
416
444
  const aggregateTopTasks: EntityMetric[] = (overviewMetrics?.top_tasks ?? []).map((metric) => ({
@@ -580,14 +608,23 @@ export function OperationsOverview({
580
608
  ? `${text.totalExecutionTime} de ${copy.shortPlural}`
581
609
  : `${copy.shortPlural} ${text.totalExecutionTime.toLowerCase()}`
582
610
  }
583
- value={formatEntityDuration(totalDurationMetric, locale)}
611
+ value={formatSummaryMetricValue(
612
+ totalDurationMetric,
613
+ metricPopulationCount,
614
+ (value) => formatDurationMs(value, locale),
615
+ locale,
616
+ )}
584
617
  />
585
618
  <Kpi
586
619
  hint={
587
620
  tokensPerTask === null || tokensPerHour === null
588
- ? locale === "pt-BR"
589
- ? "Tokens por tarefa e por hora indisponíveis"
590
- : "Tokens per task and hour unavailable"
621
+ ? metricPopulationCount === 0
622
+ ? locale === "pt-BR"
623
+ ? `0 / ${metricUnit} (0 / hora)`
624
+ : `0 / ${metricUnit} (0 / hour)`
625
+ : locale === "pt-BR"
626
+ ? "Tokens por tarefa e por hora indisponíveis"
627
+ : "Tokens per task and hour unavailable"
591
628
  : locale === "pt-BR"
592
629
  ? `${formatCompactNumber(tokensPerTask, locale)} / ${metricUnit} (${formatCompactNumber(tokensPerHour, locale, 0)} / hora)`
593
630
  : `${formatCompactNumber(tokensPerTask, locale)} / ${metricUnit} (${formatCompactNumber(tokensPerHour, locale, 0)} / hour)`
@@ -601,18 +638,23 @@ export function OperationsOverview({
601
638
  ? `Tokens gastos por ${copy.shortPlural}`
602
639
  : `${copy.shortPlural} tokens spent`
603
640
  }
604
- value={
605
- totalTokensMetric === null
606
- ? formatEntityDuration(null, locale)
607
- : formatCompactNumber(totalTokensMetric, locale)
608
- }
641
+ value={formatSummaryMetricValue(
642
+ totalTokensMetric,
643
+ metricPopulationCount,
644
+ (value) => formatCompactNumber(value, locale),
645
+ locale,
646
+ )}
609
647
  />
610
648
  <Kpi
611
649
  hint={
612
650
  hourlyCost === null || costPerTask === null
613
- ? locale === "pt-BR"
614
- ? "Custo por tarefa e por hora indispon\u00edvel"
615
- : "Cost per task and hour unavailable"
651
+ ? metricPopulationCount === 0
652
+ ? locale === "pt-BR"
653
+ ? `${formatRoundedCurrency(0, currency, locale)} / ${metricUnit} (${formatRoundedCurrency(0, currency, locale)} / hora)`
654
+ : `${formatRoundedCurrency(0, currency, locale)} / ${metricUnit} (${formatRoundedCurrency(0, currency, locale)} / hour)`
655
+ : locale === "pt-BR"
656
+ ? "Custo por tarefa e por hora indispon\u00edvel"
657
+ : "Cost per task and hour unavailable"
616
658
  : locale === "pt-BR"
617
659
  ? `${formatRoundedCurrency(costPerTask, currency, locale)} / ${metricUnit} (${formatRoundedCurrency(hourlyCost, currency, locale)} / hora)`
618
660
  : `${formatRoundedCurrency(costPerTask, currency, locale)} / ${metricUnit} (${formatRoundedCurrency(hourlyCost, currency, locale)} / hour)`
@@ -626,11 +668,12 @@ export function OperationsOverview({
626
668
  ? `Custo de ${copy.shortPlural}`
627
669
  : `${copy.shortPlural} cost`
628
670
  }
629
- value={
630
- totalCostMetric === null
631
- ? formatEntityDuration(null, locale)
632
- : formatRoundedCurrency(totalCostMetric, currency, locale)
633
- }
671
+ value={formatSummaryMetricValue(
672
+ totalCostMetric,
673
+ metricPopulationCount,
674
+ (value) => formatRoundedCurrency(value, currency, locale),
675
+ locale,
676
+ )}
634
677
  />
635
678
  </div>
636
679
 
@@ -25,6 +25,7 @@ export {
25
25
 
26
26
  import {
27
27
  dateRangeForPreset,
28
+ factualCostDateRange,
28
29
  initialOperationsDateRange,
29
30
  isDateInOperationPreset,
30
31
  isRunInOperationPreset,
@@ -130,10 +131,13 @@ export function OperationsPage({
130
131
  const [filterLoading, setFilterLoading] = useState(false);
131
132
  const filterLoadingTimer = useRef<number | null>(null);
132
133
  const searchInputRef = useRef<HTMLInputElement>(null);
133
- const defaultDateRange = useMemo(
134
- () => initialOperationsDateRange(data.tasks, Object.keys(data.summary.periods?.daily ?? {})),
135
- [data.summary.periods, data.tasks],
136
- );
134
+ const defaultDateRange = useMemo(() => {
135
+ const operationsRange = initialOperationsDateRange(
136
+ data.tasks,
137
+ Object.keys(data.summary.periods?.daily ?? {}),
138
+ );
139
+ return factualCostDateRange(factualCostPeriods?.day, operationsRange);
140
+ }, [data.summary.periods, data.tasks, factualCostPeriods?.day]);
137
141
  const initialDateRange = useMemo(() => dateRangeForPreset("this-month"), []);
138
142
  const [localDateRange, setLocalDateRange] = useState(() => initialDateRange);
139
143
  const [localDatePreset, setLocalDatePreset] = useState<OperationDatePreset>("all");
@@ -156,6 +160,17 @@ export function OperationsPage({
156
160
  },
157
161
  [isPeriodControlled, onDateChange, onPeriodChange],
158
162
  );
163
+ // A controlled parent initially owns the range selected before the factual
164
+ // Cost summary arrives. Once it does, synchronize only All Time's displayed
165
+ // coverage range; requestPeriodForOperations still omits both bounds.
166
+ useEffect(() => {
167
+ if (
168
+ datePreset !== "all" ||
169
+ (dateRange.start === defaultDateRange.start && dateRange.end === defaultDateRange.end)
170
+ )
171
+ return;
172
+ updatePeriod("all", defaultDateRange);
173
+ }, [datePreset, dateRange.end, dateRange.start, defaultDateRange, updatePeriod]);
159
174
  useEffect(() => {
160
175
  if (!isPeriodControlled && datePreset === "all") setLocalDateRange(defaultDateRange);
161
176
  }, [datePreset, defaultDateRange, isPeriodControlled]);
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import {
3
3
  dateRangeForPreset,
4
+ factualCostDateRange,
4
5
  initialOperationsDateRange,
5
6
  initialTaskDateRange,
6
7
  isDateInOperationPreset,
@@ -34,6 +35,30 @@ describe("operation date ranges", () => {
34
35
  });
35
36
  });
36
37
 
38
+ it("uses the first factual Cost Monitor day for All Time while ending today", () => {
39
+ expect(
40
+ factualCostDateRange(
41
+ [
42
+ { key: "2026-06-01", estimated_brl: null, estimated_usd: null },
43
+ { key: "2026-06-02", estimated_brl: 0, estimated_usd: 0 },
44
+ { key: "2026-06-03", estimated_brl: 1.25, estimated_usd: 0.24 },
45
+ ],
46
+ { start: "2026-07-10", end: "2026-07-14" },
47
+ today,
48
+ ),
49
+ ).toEqual({ start: "2026-06-02", end: "2026-07-14" });
50
+ });
51
+
52
+ it("keeps the Operations range when no factual Cost Monitor day is available", () => {
53
+ expect(
54
+ factualCostDateRange(
55
+ [{ key: "2026-06-01", estimated_brl: null, estimated_usd: null }],
56
+ { start: "2026-07-10", end: "2026-07-14" },
57
+ today,
58
+ ),
59
+ ).toEqual({ start: "2026-07-10", end: "2026-07-14" });
60
+ });
61
+
37
62
  it.each([
38
63
  ["today", "2026-07-14", "2026-07-14"],
39
64
  ["yesterday", "2026-07-13", "2026-07-13"],
@@ -17,6 +17,35 @@ export interface OperationDateRange {
17
17
  end: string;
18
18
  }
19
19
 
20
+ /** A daily Cost Monitor group with a factual monetary measurement. */
21
+ export interface FactualCostDay {
22
+ key: string;
23
+ estimated_brl: number | null;
24
+ estimated_usd: number | null;
25
+ }
26
+
27
+ /**
28
+ * All Time is unbounded for requests, but its disabled date controls should
29
+ * describe the actual Cost Monitor coverage rather than an Operations record.
30
+ */
31
+ export function factualCostDateRange(
32
+ days: readonly FactualCostDay[] | undefined,
33
+ fallback: OperationDateRange,
34
+ today = new Date(),
35
+ ): OperationDateRange {
36
+ const firstMeasuredDay = days
37
+ ?.map((day) => day.key)
38
+ .filter(
39
+ (key, index, keys) =>
40
+ /^\d{4}-\d{2}-\d{2}$/.test(key) &&
41
+ (days[index]!.estimated_brl !== null || days[index]!.estimated_usd !== null),
42
+ )
43
+ .sort()[0];
44
+ return firstMeasuredDay === undefined
45
+ ? fallback
46
+ : { start: firstMeasuredDay, end: toDateInputValue(today) };
47
+ }
48
+
20
49
  export function initialTaskDateRange(
21
50
  tasks: readonly { created_at: string }[],
22
51
  today = new Date(),
@@ -36,7 +36,7 @@ describe("Operations persisted aggregate display", () => {
36
36
  expect(totals).toMatchObject({ duration_ms: null, tokens: 0, cost_brl: 0 });
37
37
  });
38
38
 
39
- it("preserves unavailable aggregate totals instead of rendering coerced zeroes", () => {
39
+ it("preserves unavailable aggregate totals and renders zero only for an empty population", () => {
40
40
  const overview = readFileSync(new URL("OperationsOverview.tsx", import.meta.url), "utf8");
41
41
 
42
42
  expect(overview).toContain("overviewMetrics?.duration_ms ??");
@@ -48,7 +48,8 @@ describe("Operations persisted aggregate display", () => {
48
48
  expect(overview).toContain(
49
49
  "durationMs: totalDurationMetric, tokens: totalTokensMetric, costBrl: totalCostMetric",
50
50
  );
51
- expect(overview).toContain("formatEntityDuration(averageDuration, locale)");
51
+ expect(overview).toContain("formatSummaryMetricValue(\n averageDuration,");
52
+ expect(overview).toContain('populationCount === 0 ? "0"');
52
53
  expect(overview).not.toContain("allPhaseMetrics.tokens ?? 0");
53
54
  expect(overview).not.toContain("executionMetrics.durationMs ?? 0");
54
55
  });
@@ -50,6 +50,15 @@ describe("overview period filter", () => {
50
50
  expect(shouldRenderOperationsContent(true)).toBe(false);
51
51
  });
52
52
 
53
+ it("synchronizes the controlled All Time display after factual cost coverage arrives", () => {
54
+ const source = readFileSync(new URL("OperationsPage.tsx", import.meta.url), "utf8");
55
+
56
+ expect(source).toContain('datePreset !== "all"');
57
+ expect(source).toContain("dateRange.start === defaultDateRange.start");
58
+ expect(source).toContain('updatePeriod("all", defaultDateRange);');
59
+ expect(source).toContain("requestPeriodForOperations still omits both bounds");
60
+ });
61
+
53
62
  it("derives all big-number KPIs from the period-filtered global aggregate", () => {
54
63
  const source = readFileSync(new URL("OperationsOverview.tsx", import.meta.url), "utf8");
55
64
  const helpers = readFileSync(
@@ -81,8 +90,9 @@ describe("overview period filter", () => {
81
90
 
82
91
  expect(source).toContain('currency: "BRL" | "USD"');
83
92
  expect(source).toContain("const selectedCurrencyCost =");
84
- expect(source).toContain("formatCurrency(averageCost, currency, locale)");
85
- expect(source).toContain("formatRoundedCurrency(totalCostMetric, currency, locale)");
93
+ expect(source).toContain("formatCurrency(value, currency, locale)");
94
+ expect(source).toContain("formatSummaryMetricValue(\n totalCostMetric,");
95
+ expect(source).toContain("formatRoundedCurrency(value, currency, locale)");
86
96
  expect(source).toContain("const topEntitiesCost =");
87
97
  expect(topPanel).toContain("formatCurrency(topCost, currency, locale)");
88
98
  expect(periods).toContain("resolveRunCost?: ((runId: string) => number | null)");
@@ -126,7 +126,7 @@ test("loads every Cost Monitor tab before returning to Operation", async ({ page
126
126
 
127
127
  async function expectGlobalMonthPreset(page: import("@playwright/test").Page) {
128
128
  await expect(
129
- page.locator(".dashboard-global-controls").getByRole("combobox", { name: "Período" }),
129
+ page.locator(".dashboard-global-controls").getByLabel("Período"),
130
130
  ).toHaveValue("this-month");
131
131
  }
132
132
 
@@ -156,6 +156,10 @@ async function openAllTimeCostMonitor(page: Page) {
156
156
  );
157
157
  await page.goto("/?token=e2e-token");
158
158
  await operations;
159
+ await page
160
+ .getByRole("navigation", { name: "Áreas do dashboard" })
161
+ .getByRole("button", { name: /Operação/ })
162
+ .click();
159
163
  const period = page
160
164
  .locator(".dashboard-global-controls")
161
165
  .getByRole("combobox", { name: "Período" });
@@ -25,6 +25,12 @@ test("loads Cost Monitor within five seconds when the user enters after choosing
25
25
  }) => {
26
26
  await openDashboard(page);
27
27
 
28
+ await page
29
+ .getByRole("navigation", { name: "Áreas do dashboard" })
30
+ .getByRole("button", { name: /Operação/ })
31
+ .click();
32
+ await expect(periodControl(page)).toBeVisible();
33
+
28
34
  await periodControl(page).selectOption("all");
29
35
  await expect(page.locator(".period-transition-overlay")).toHaveCount(0, { timeout: 60_000 });
30
36
 
@@ -174,6 +174,10 @@ async function openDashboard(page: Page) {
174
174
  }
175
175
 
176
176
  async function selectPeriod(page: Page, period: "this-month" | "all") {
177
+ await page
178
+ .getByRole("navigation", { name: "Áreas do dashboard" })
179
+ .getByRole("button", { name: /Operação/ })
180
+ .click();
177
181
  const control = page
178
182
  .locator(".dashboard-global-controls")
179
183
  .getByRole("combobox", { name: "Período" });
@@ -163,10 +163,12 @@ test("exports Kanban, Lista and Cost Monitor through a save picker while the das
163
163
  });
164
164
 
165
165
  test("renders the modern read-only dashboard and meets accessibility gates", async ({ page }) => {
166
- await openDashboard(page);
166
+ const initialResponse = await openDashboard(page);
167
167
  const globalPeriod = page.getByRole("combobox", { name: /Per/ }).first();
168
- await expect(globalPeriod).toHaveValue("this-month");
169
- await selectPopulatedAllTime(page);
168
+ await expect(globalPeriod).toHaveValue("all");
169
+ const initialUrl = new URL(initialResponse.url());
170
+ expect(initialUrl.searchParams.has("from")).toBe(false);
171
+ expect(initialUrl.searchParams.has("to")).toBe(false);
170
172
  await expect(page.getByRole("navigation", { name: "Dashboard Zaip AIOS menu" })).toBeVisible();
171
173
  const capabilityNav = page.getByRole("navigation", { name: "Áreas do dashboard" });
172
174
  const globalControls = page.locator(".dashboard-global-controls");
@@ -182,6 +184,13 @@ test("renders the modern read-only dashboard and meets accessibility gates", asy
182
184
  buttons.map((button) => button.getBoundingClientRect().top),
183
185
  );
184
186
  expect(new Set(buttonBoxes.map((top) => Math.round(top))).size).toBe(1);
187
+ await expect(globalControls.getByRole("textbox", { name: "Busca" })).toBeHidden();
188
+ await expect(globalControls.getByLabel("Data inicial")).toBeHidden();
189
+ await expect(globalControls.getByLabel("Data final")).toBeHidden();
190
+ await expect(globalControls.getByRole("combobox", { name: "Período" })).toBeHidden();
191
+ await expect(page.getByLabel("Kanban de trabalho")).toHaveCount(0);
192
+ await expect(page.locator(".operations-task-card")).toHaveCount(0);
193
+ await selectOperations(page);
185
194
  const searchInput = globalControls.getByRole("textbox", { name: "Busca" });
186
195
  await expect(searchInput).toBeVisible();
187
196
  await expect(globalControls.getByRole("button", { name: "Limpar busca" })).toHaveCount(0);
@@ -207,9 +216,6 @@ test("renders the modern read-only dashboard and meets accessibility gates", asy
207
216
  elements.map((element) => Math.round(element.getBoundingClientRect().bottom)),
208
217
  );
209
218
  expect(new Set(controlRowBottoms).size).toBe(1);
210
- await expect(page.getByLabel("Kanban de trabalho")).toHaveCount(0);
211
- await expect(page.locator(".operations-task-card")).toHaveCount(0);
212
- await selectOperations(page);
213
219
  await expect(page.getByLabel("Kanban de trabalho")).toBeVisible();
214
220
  await expect(page.locator(".operations-risk.is-standard").first()).toBeVisible();
215
221
  const visibleCards = page.locator(".operations-task-card:visible");
@@ -266,6 +272,13 @@ test("renders the modern read-only dashboard and meets accessibility gates", asy
266
272
 
267
273
  const results = await new AxeBuilder({ page }).analyze();
268
274
  expect(results.violations).toEqual([]);
275
+
276
+ await capabilityNav.getByRole("button", { name: /Monitor de Custos/ }).click();
277
+ await expect(page.locator(".cost-tabs")).toBeVisible();
278
+ await expect(searchInput).toBeVisible();
279
+ await expect(startDate).toBeVisible();
280
+ await expect(endDate).toBeVisible();
281
+ await expect(period).toBeVisible();
269
282
  });
270
283
 
271
284
  test("loads a quick Operations summary without full snapshot hydration", async ({ page }) => {
@@ -456,9 +469,11 @@ test("groups macro work in All and reveals descendant Tasks only in Tasks", asyn
456
469
  test("shows the observed all-time macro duration consistently for SPEC and PRD", async ({
457
470
  page,
458
471
  }) => {
459
- await openDashboard(page);
460
- await expect(page.getByRole("combobox", { name: /Per/ }).first()).toHaveValue("this-month");
461
- const response = await selectPopulatedAllTime(page);
472
+ const response = await openDashboard(page);
473
+ await expect(page.getByRole("combobox", { name: /Per/ }).first()).toHaveValue("all");
474
+ const responseUrl = new URL(response.url());
475
+ expect(responseUrl.searchParams.has("from")).toBe(false);
476
+ expect(responseUrl.searchParams.has("to")).toBe(false);
462
477
  const projection = (await response.json()) as {
463
478
  summary: {
464
479
  overview_metrics?: {
@@ -526,20 +541,26 @@ test("loads monthly overview quickly and keeps PRD and SPEC chart totals reconci
526
541
  page,
527
542
  }) => {
528
543
  const openedAt = Date.now();
529
- await openDashboard(page);
544
+ const initialQuickResponse = await openDashboard(page);
530
545
  expect(Date.now() - openedAt).toBeLessThan(3_000);
546
+ await selectOperations(page);
531
547
 
532
548
  const period = page.getByRole("combobox", { name: /Per/ }).first();
533
549
  for (const preset of ["all", "this-month"] as const) {
534
- const response = page.waitForResponse(
535
- (candidate) =>
536
- candidate.status() === 200 &&
537
- candidate.url().includes("/api/v2/operations") &&
538
- candidate.url().includes("scope=quick"),
539
- );
540
550
  const selectedAt = Date.now();
541
- await period.selectOption(preset);
542
- const quickResponse = await response;
551
+ const quickResponse =
552
+ preset === "all"
553
+ ? initialQuickResponse
554
+ : await (async () => {
555
+ const response = page.waitForResponse(
556
+ (candidate) =>
557
+ candidate.status() === 200 &&
558
+ candidate.url().includes("/api/v2/operations") &&
559
+ candidate.url().includes("scope=quick"),
560
+ );
561
+ await period.selectOption(preset);
562
+ return response;
563
+ })();
543
564
  const quickUrl = new URL(quickResponse.url());
544
565
  const dateControls = page.locator(".dashboard-global-controls");
545
566
  const expectedFrom =
@@ -671,6 +692,7 @@ async function selectPopulatedAllTime(page: import("@playwright/test").Page) {
671
692
  const url = new URL(candidate.url());
672
693
  return !url.searchParams.has("from") && !url.searchParams.has("to");
673
694
  });
695
+ await selectOperations(page);
674
696
  await page.getByRole("combobox", { name: /Per/ }).first().selectOption("all");
675
697
  return response;
676
698
  }
@@ -6,7 +6,7 @@ title: Garantia adaptativa e execução autônoma
6
6
  status: active
7
7
  version: 3
8
8
  source_change: null
9
- source_release: "1.3.4"
9
+ source_release: "1.3.5"
10
10
  ---
11
11
 
12
12
  # Garantia adaptativa e execução autônoma
@@ -6,7 +6,7 @@ title: Auditoria comparativa e plano de evolução
6
6
  status: active
7
7
  version: 1
8
8
  source_change: null
9
- source_release: "1.3.4"
9
+ source_release: "1.3.5"
10
10
  ---
11
11
 
12
12
  # Auditoria comparativa e plano de evolução
@@ -6,7 +6,7 @@ title: Contexto mínimo, seguro e navegável
6
6
  status: active
7
7
  version: 8
8
8
  source_change: null
9
- source_release: "1.3.4"
9
+ source_release: "1.3.5"
10
10
  ---
11
11
 
12
12
  # Context engine
@@ -6,7 +6,7 @@ title: Tokens, custos e cobertura
6
6
  status: active
7
7
  version: 10
8
8
  source_change: null
9
- source_release: "1.3.4"
9
+ source_release: "1.3.5"
10
10
  ---
11
11
 
12
12
  # Cost observability
@@ -6,7 +6,7 @@ title: Inicialização e hidratação confiável do dashboard
6
6
  status: active
7
7
  version: 1
8
8
  source_change: null
9
- source_release: "1.3.4"
9
+ source_release: "1.3.5"
10
10
  ---
11
11
 
12
12
  # Inicialização e hidratação confiável do dashboard
@@ -6,7 +6,7 @@ title: Exportação contextual de dados para Excel
6
6
  status: proposed
7
7
  version: 2
8
8
  source_change: null
9
- source_release: "1.3.4"
9
+ source_release: "1.3.5"
10
10
  ---
11
11
 
12
12
  # Exportação contextual de dados
@@ -6,7 +6,7 @@ title: Contrato visual e catálogo executável
6
6
  status: active
7
7
  version: 3
8
8
  source_change: null
9
- source_release: "1.3.4"
9
+ source_release: "1.3.5"
10
10
  ---
11
11
 
12
12
  # Design system
@@ -6,7 +6,7 @@ title: Preparação, materialização e DAG
6
6
  status: active
7
7
  version: 11
8
8
  source_change: null
9
- source_release: "1.3.4"
9
+ source_release: "1.3.5"
10
10
  ---
11
11
 
12
12
  # Execution engine
@@ -6,7 +6,7 @@ title: Projeções operacionais e analíticas
6
6
  status: active
7
7
  version: 14
8
8
  source_change: null
9
- source_release: "1.3.4"
9
+ source_release: "1.3.5"
10
10
  ---
11
11
 
12
12
  # Kanban and dashboard
@@ -6,7 +6,7 @@ title: Seleção automática e observável de modelo e effort
6
6
  status: active
7
7
  version: 7
8
8
  source_change: null
9
- source_release: "1.3.4"
9
+ source_release: "1.3.5"
10
10
  ---
11
11
 
12
12
  # Orquestração de modelos
@@ -6,7 +6,7 @@ title: Distribuição pública e ativação
6
6
  status: active
7
7
  version: 3
8
8
  source_change: null
9
- source_release: "1.3.4"
9
+ source_release: "1.3.5"
10
10
  ---
11
11
 
12
12
  # Distribuição pública e ativação
@@ -6,7 +6,7 @@ title: Layout modular e limites de código-fonte
6
6
  status: active
7
7
  version: 5
8
8
  source_change: null
9
- source_release: "1.3.4"
9
+ source_release: "1.3.5"
10
10
  ---
11
11
 
12
12
  # Software architecture