cctally 1.83.0 → 1.84.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.
@@ -299,6 +299,7 @@ def resolve_codex_cycle_detail_identity(
299
299
  *,
300
300
  source_root_keys: Iterable[str],
301
301
  now_utc: dt.datetime,
302
+ account_key: str | None = None,
302
303
  ):
303
304
  """The live-cycle identity for a per-request Codex cycle-DETAIL read (#373).
304
305
 
@@ -314,6 +315,21 @@ def resolve_codex_cycle_detail_identity(
314
315
  the live boundary itself is resolved from the active roots' observations,
315
316
  exactly as the source build does.
316
317
 
318
+ ``account_key`` (#416 QA sweep) picks the LIVE boundary belonging to the
319
+ account the route is focused on. ``_resolve_codex_weekly_cycle`` returns one
320
+ boundary per account and this function used ``cycles[0]`` — the first
321
+ account by sorted key — unconditionally, while ``build_codex_cycle_detail``
322
+ was already given the account predicate. So a focused read enumerated
323
+ account B's cycles and judged them against account A's reset: no candidate
324
+ falls inside ``CODEX_CYCLE_JITTER_FLOOR_SECONDS`` of a foreign boundary, so
325
+ ``_select_live_physical_cycle`` returned ``None`` and B's own live cycle
326
+ lost both its ``is_current`` flag and the §7.4 no-clip guard — the exact
327
+ index/detail disagreement #373 closed, re-opened one account over.
328
+
329
+ An account with no live weekly cycle resolves to NO boundary rather than a
330
+ sibling's: an unarmed guard is today's honest degrade, a foreign boundary is
331
+ a wrong answer. ``None`` keeps the merged representative and is byte-stable.
332
+
317
333
  Degrades to a bare-roots identity — today's behaviour — whenever no live
318
334
  cycle resolves. The clip guard stays unarmed on that identity by design
319
335
  (``_boundary_has_live_reset``), so the detail keeps clipping as it did
@@ -348,7 +364,22 @@ def resolve_codex_cycle_detail_identity(
348
364
  return identity
349
365
  if not cycles:
350
366
  return identity
351
- boundary = cycles[0]
367
+ if account_key is None:
368
+ boundary = cycles[0]
369
+ else:
370
+ boundary = next(
371
+ (
372
+ cyc for cyc in cycles
373
+ if (
374
+ cyc.quota_identity.account_key
375
+ if cyc.quota_identity is not None
376
+ else _lib_accounts.UNATTRIBUTED
377
+ ) == account_key
378
+ ),
379
+ None,
380
+ )
381
+ if boundary is None:
382
+ return identity
352
383
  identity.resets_at = boundary.resets_at
353
384
  identity.quota_identity = boundary.quota_identity
354
385
  return identity
@@ -450,6 +481,7 @@ def _codex_weekly_periods(
450
481
  *,
451
482
  source_root_keys: Iterable[str],
452
483
  active_cycle: CodexCycleBoundary | None,
484
+ account_key: str | None = None,
453
485
  ) -> tuple[CodexWeeklyPeriod, ...]:
454
486
  """Read durable 10,080-minute boundaries and clip early re-anchors.
455
487
 
@@ -457,6 +489,24 @@ def _codex_weekly_periods(
457
489
  the prior seven-day deadline. Sorting those nominal starts and ending the
458
490
  prior segment at the next start preserves the actual quota-cycle boundary
459
491
  without double-counting the overlapping nominal windows.
492
+
493
+ ``account_key`` (#416 Slice 3A review B1) scopes the read to ONE account.
494
+ ``quota_window_blocks`` is ``UNIQUE(source, source_root_key, account_key,
495
+ logical_limit_key, observed_slot, window_minutes, resets_at_utc)``, so two
496
+ accounts on one root genuinely produce two weekly rows; without the
497
+ predicate the jitter merge below pools their ``current_percent`` values and
498
+ ``max(...)`` hands the focused account the OTHER account's percentage —
499
+ the never-combine violation D6 forbids — while ``end_at = min(resets_at,
500
+ next_start)`` clips one account's week at the other's start. ``None`` keeps
501
+ the merged "All accounts" read, which is byte-stable and is what the parent
502
+ still uses.
503
+
504
+ The predicate is strict equality, deliberately NOT the one-directional
505
+ ``(account, unattributed)`` widening ``_codex_five_hour_rows`` uses: that
506
+ widening produces a LISTING whose members each keep their own percentage,
507
+ whereas the merge here ADOPTS a pooled percentage onto one account's row.
508
+ Unattributed weekly boundaries are already rendered by the ``unattributed``
509
+ child, which is a first-class scope after D1.
460
510
  """
461
511
  roots = tuple(sorted({
462
512
  root for root in source_root_keys if isinstance(root, str) and root
@@ -464,6 +514,10 @@ def _codex_weekly_periods(
464
514
  if not roots:
465
515
  return ()
466
516
  placeholders = ",".join("?" for _ in roots)
517
+ # `quota_window_blocks.account_key` is `NOT NULL DEFAULT 'unattributed'`,
518
+ # so the sentinel needs no NULL branch here (unlike the cache tables).
519
+ account_predicate = "" if account_key is None else "AND account_key = ? "
520
+ account_params: tuple = () if account_key is None else (account_key,)
467
521
  try:
468
522
  rows = stats_conn.execute(
469
523
  "SELECT source_root_key, logical_limit_key, limit_name, resets_at_utc, "
@@ -471,9 +525,10 @@ def _codex_weekly_periods(
471
525
  "FROM quota_window_blocks "
472
526
  "WHERE source='codex' AND window_minutes=10080 "
473
527
  f"AND source_root_key IN ({placeholders}) AND orphaned_at IS NULL "
528
+ f"{account_predicate}"
474
529
  "ORDER BY nominal_start_at_utc DESC, resets_at_utc DESC, source_root_key "
475
530
  "LIMIT ?",
476
- (*roots, SOURCE_HISTORY_LIMIT),
531
+ (*roots, *account_params, SOURCE_HISTORY_LIMIT),
477
532
  ).fetchall()
478
533
  except sqlite3.Error:
479
534
  rows = ()
@@ -1294,6 +1349,8 @@ def _quota_wire(
1294
1349
  cycle: CodexCycleBoundary | None = None,
1295
1350
  now_utc: dt.datetime | None = None,
1296
1351
  display_tz_name: str | None = None,
1352
+ account_key: str | None = None,
1353
+ decorated: bool = False,
1297
1354
  ) -> tuple[dict[str, object], ...]:
1298
1355
  """Build current-cycle Codex 5-hour activity rows from durable windows.
1299
1356
 
@@ -1301,13 +1358,33 @@ def _quota_wire(
1301
1358
  tokens, and model splits come from root-qualified accounting inside each
1302
1359
  half-open 300-minute interval. Weekly quota summaries are deliberately not
1303
1360
  activity blocks and never enter this wire.
1361
+
1362
+ #416 spec §5.2 (review F9): blocks were filtered by `source_root_key` and
1363
+ time ONLY, against a single `cycle` that is `cycles_all[0]` — the FIRST
1364
+ account's. Two accounts sharing one physical root therefore saw each other's
1365
+ 5h blocks. `account_key` scopes both the durable block row and the
1366
+ accounting inside it to the block identity's account; `None` keeps the
1367
+ merged read, which is byte-stable. `decorated` (R8) serializes the block's
1368
+ own account so the client can label it; below two REAL accounts no key is
1369
+ added at all.
1370
+
1371
+ The account predicate here is STRICT and deliberately diverges from
1372
+ `_codex_five_hour_rows`, which widens the same table (#416 closeout F2).
1373
+ Both reads are selection reads, but the rule turns on the stamping
1374
+ mechanism, not on the verb: `_codex_five_hour_rows` asks "is this block
1375
+ inside the focused CYCLE" — a different physical-window group than the
1376
+ weekly key it is scoped by, so a still-`unattributed` 5h block genuinely
1377
+ belongs and must be admitted. This is a LISTING of the block rows
1378
+ themselves, keyed by the very column it filters, so widening would render
1379
+ one unattributed block twice, once under each real account.
1304
1380
  """
1305
1381
  if cycle is None or now_utc is None:
1306
1382
  return ()
1307
1383
  try:
1308
1384
  rows = stats_conn.execute(
1309
1385
  "SELECT source_root_key, logical_limit_key, observed_slot, window_minutes, "
1310
- "limit_name, resets_at_utc, nominal_start_at_utc, current_percent, orphaned_at "
1386
+ "limit_name, resets_at_utc, nominal_start_at_utc, current_percent, orphaned_at, "
1387
+ "account_key "
1311
1388
  "FROM quota_window_blocks WHERE source='codex' AND window_minutes=300 "
1312
1389
  "ORDER BY resets_at_utc DESC, source_root_key, logical_limit_key, observed_slot "
1313
1390
  "LIMIT ?",
@@ -1323,7 +1400,11 @@ def _quota_wire(
1323
1400
  for (
1324
1401
  root_key, logical_limit_key, observed_slot, window_minutes,
1325
1402
  _limit_name, resets_at_raw, nominal_start_raw, current_percent, orphaned_at,
1403
+ block_account,
1326
1404
  ) in rows:
1405
+ block_account = str(block_account or _lib_accounts.UNATTRIBUTED)
1406
+ if account_key is not None and block_account != account_key:
1407
+ continue
1327
1408
  if orphaned_at is not None or str(root_key) not in cycle.source_root_keys:
1328
1409
  continue
1329
1410
  try:
@@ -1337,7 +1418,14 @@ def _quota_wire(
1337
1418
  resets_at = resets_at.astimezone(UTC)
1338
1419
  if resets_at <= cycle.start_at or start_at >= cycle.resets_at:
1339
1420
  continue
1340
- physical_key = (str(root_key), start_at, resets_at)
1421
+ # The account joins the physical dedup key only under decoration: two
1422
+ # accounts sharing one physical 5h window are two windows (never-combine
1423
+ # extends to accounts), but a <=1-real-account install must keep exactly
1424
+ # today's key so its wire is byte-identical.
1425
+ physical_key = (
1426
+ (str(root_key), start_at, resets_at, block_account) if decorated
1427
+ else (str(root_key), start_at, resets_at)
1428
+ )
1341
1429
  if physical_key in seen_windows:
1342
1430
  continue
1343
1431
  seen_windows.add(physical_key)
@@ -1378,6 +1466,10 @@ def _quota_wire(
1378
1466
  observed_slot, window_minutes, resets_at_raw,
1379
1467
  ),
1380
1468
  "source": "codex",
1469
+ # R8: snake_case to match the sibling `quota.history[].account_key`
1470
+ # rows in this same subtree (#341 Task 4), NOT the camelCase
1471
+ # `accounts[].accountKey` hero-card surface.
1472
+ **({"account_key": block_account} if decorated else {}),
1381
1473
  "label": c.format_display_dt(
1382
1474
  start_at, display_tz, fmt="%H:%M %b %d", suffix=True,
1383
1475
  ),
@@ -1394,11 +1486,13 @@ def _quota_wire(
1394
1486
  return tuple(wired)
1395
1487
 
1396
1488
 
1397
- def _budget_wire(stats_conn: sqlite3.Connection) -> tuple[dict[str, object], ...]:
1489
+ def _budget_wire(
1490
+ stats_conn: sqlite3.Connection, *, decorated: bool = False,
1491
+ ) -> tuple[dict[str, object], ...]:
1398
1492
  try:
1399
1493
  rows = stats_conn.execute(
1400
1494
  "SELECT period_start_at, period, threshold, budget_usd, spent_usd, "
1401
- "consumption_pct FROM budget_milestones WHERE vendor='codex' "
1495
+ "consumption_pct, account_key FROM budget_milestones WHERE vendor='codex' "
1402
1496
  "ORDER BY period_start_at DESC, threshold DESC LIMIT ?",
1403
1497
  (SOURCE_HISTORY_LIMIT,),
1404
1498
  ).fetchall()
@@ -1411,13 +1505,21 @@ def _budget_wire(stats_conn: sqlite3.Connection) -> tuple[dict[str, object], ...
1411
1505
  "budget_usd": budget_usd,
1412
1506
  "spent_usd": spent_usd,
1413
1507
  "consumption_pct": consumption_pct,
1414
- } for period_start_at, period, threshold, budget_usd, spent_usd, consumption_pct in rows)
1508
+ # R8 (#416 §5.5): the per-account ladder needs the key to scope a child;
1509
+ # below two REAL accounts nothing is added.
1510
+ **({"account_key": str(account_key or _CODEX_VENDOR_WIDE_ACCOUNT)}
1511
+ if decorated else {}),
1512
+ } for period_start_at, period, threshold, budget_usd, spent_usd,
1513
+ consumption_pct, account_key in rows)
1415
1514
 
1416
1515
 
1417
- def _projected_budget_wire(stats_conn: sqlite3.Connection) -> tuple[dict[str, object], ...]:
1516
+ def _projected_budget_wire(
1517
+ stats_conn: sqlite3.Connection, *, decorated: bool = False,
1518
+ ) -> tuple[dict[str, object], ...]:
1418
1519
  try:
1419
1520
  rows = stats_conn.execute(
1420
- "SELECT period, threshold, projected_value, denominator, crossed_at_utc, alerted_at "
1521
+ "SELECT period, threshold, projected_value, denominator, crossed_at_utc, "
1522
+ "alerted_at, account_key "
1421
1523
  "FROM projected_milestones WHERE metric='codex_budget_usd' "
1422
1524
  "ORDER BY crossed_at_utc DESC, threshold DESC LIMIT ?",
1423
1525
  (SOURCE_HISTORY_LIMIT,),
@@ -1431,7 +1533,10 @@ def _projected_budget_wire(stats_conn: sqlite3.Connection) -> tuple[dict[str, ob
1431
1533
  "denominator": denominator,
1432
1534
  "crossed_at": crossed_at,
1433
1535
  "alerted_at": alerted_at,
1434
- } for period, threshold, projected_value, denominator, crossed_at, alerted_at in rows)
1536
+ **({"account_key": str(account_key or _CODEX_VENDOR_WIDE_ACCOUNT)}
1537
+ if decorated else {}),
1538
+ } for period, threshold, projected_value, denominator, crossed_at, alerted_at,
1539
+ account_key in rows)
1435
1540
 
1436
1541
 
1437
1542
  def _configured_codex_budget_status(
@@ -1439,6 +1544,7 @@ def _configured_codex_budget_status(
1439
1544
  entries: Iterable[object],
1440
1545
  *,
1441
1546
  cost_events: tuple[tuple[dt.datetime, float], ...] | None = None,
1547
+ account_key: str | None = None,
1442
1548
  ) -> dict[str, object] | None:
1443
1549
  """Compute the live configured Codex budget from the coordinated entries.
1444
1550
 
@@ -1446,10 +1552,23 @@ def _configured_codex_budget_status(
1446
1552
  This reuses the CLI's calendar-window and ``BudgetInputs``/status kernels
1447
1553
  while deliberately keeping the accounting read on the caller-owned cache
1448
1554
  snapshot.
1555
+
1556
+ ``account_key`` (#416 §5.5) scopes the status to ONE account: the target is
1557
+ that account's own configured budget from ``budget.codex.accounts`` — never a
1558
+ share of the vendor amount, which would be an invented number. An account
1559
+ with no configured budget therefore has no budget status at all (``None``),
1560
+ exactly as an unconfigured vendor does; the merged vendor status stays on the
1561
+ parent. ``None`` keeps the merged behaviour and is byte-stable.
1449
1562
  """
1450
1563
  config = context.codex_budget
1451
1564
  if config is None:
1452
1565
  return None
1566
+ amount_usd = config.get("amount_usd")
1567
+ if account_key is not None:
1568
+ per_account = config.get("accounts")
1569
+ if not isinstance(per_account, Mapping) or account_key not in per_account:
1570
+ return None
1571
+ amount_usd = per_account[account_key]
1453
1572
  c = sys.modules["cctally"]
1454
1573
  period, start_at, end_at = _configured_codex_budget_window(context)
1455
1574
 
@@ -1462,7 +1581,7 @@ def _configured_codex_budget_status(
1462
1581
 
1463
1582
  recent_start = max(start_at, context.now_utc - dt.timedelta(hours=24))
1464
1583
  inputs = c.BudgetInputs(
1465
- target_usd=float(config["amount_usd"]),
1584
+ target_usd=float(amount_usd),
1466
1585
  spent_usd=_sum_cost(start_at, context.now_utc),
1467
1586
  recent_24h_usd=_sum_cost(recent_start, context.now_utc),
1468
1587
  week_start_at=start_at,
@@ -1512,13 +1631,52 @@ def _configured_codex_budget_window(
1512
1631
  return period, start_at.astimezone(UTC), end_at.astimezone(UTC)
1513
1632
 
1514
1633
 
1634
+ def _codex_account_admits(scope_key: str | None, row_key: object) -> bool:
1635
+ """Whether an account-scoped quota read admits ``row_key`` (#416 B2).
1636
+
1637
+ One-directional widening, identical to ``_codex_five_hour_rows``
1638
+ (``bin/_cctally_milestone_history.py``): a REAL account admits its own rows
1639
+ plus the ``unattributed`` sentinel, because
1640
+ ``adopt_unidentified_observations`` resolves attribution PER physical-window
1641
+ group — a decorated install can legitimately carry the weekly window under a
1642
+ real account while its 5h windows are still unattributed, and strict
1643
+ equality would then correlate nothing. The widening is one-directional on
1644
+ purpose: an ``unattributed`` scope never picks up another account's
1645
+ identified rows, so no REAL account's number ever reaches another's row.
1646
+
1647
+ ``scope_key is None`` is the merged read and admits everything.
1648
+ """
1649
+ if scope_key is None:
1650
+ return True
1651
+ key = str(row_key or _lib_accounts.UNATTRIBUTED)
1652
+ if scope_key == _lib_accounts.UNATTRIBUTED:
1653
+ return key == _lib_accounts.UNATTRIBUTED
1654
+ return key in (scope_key, _lib_accounts.UNATTRIBUTED)
1655
+
1656
+
1515
1657
  def _quota_read_model(
1516
1658
  context: DashboardReadContext,
1517
1659
  observations: Iterable[object],
1518
1660
  *,
1519
1661
  accounting_entries: Iterable[object] = (),
1662
+ account_key: str | None = None,
1520
1663
  ) -> dict[str, object]:
1521
- """Use S2's pure history/block/forecast kernels over cache evidence."""
1664
+ """Use S2's pure history/block/forecast kernels over cache evidence.
1665
+
1666
+ ``account_key`` (#416 Slice 3A review B2/F4) scopes the two reads this
1667
+ function reaches that the observation partition does NOT already cover: the
1668
+ durable milestone breakdown (``codex_quota_breakdown``, whose accounting and
1669
+ block-start reads filter by root and time only) and the 5h correlation load
1670
+ below. ``None`` keeps the merged parent read, byte-stable.
1671
+
1672
+ The key passed on is POST-fold — it names a registry account or an
1673
+ ``obs_partition`` bucket, and ``load_codex_quota_observations`` applies
1674
+ ``adopt_unidentified_observations`` before returning — while
1675
+ ``codex_quota_breakdown``'s block-start boundary reads the PRE-fold
1676
+ ``quota_window_snapshots``. That read widens (#416 closeout F1); its
1677
+ accounting read stays strict so the children keep partitioning the parent's
1678
+ spend. Neither is elected here — the kernel settles both.
1679
+ """
1522
1680
  quota_observations = tuple(observations)
1523
1681
  cost_entries = tuple(accounting_entries)
1524
1682
  histories = build_history(quota_observations)
@@ -1628,6 +1786,7 @@ def _quota_read_model(
1628
1786
  speed=context.speed,
1629
1787
  cache_conn=context.cache_conn,
1630
1788
  stats_conn=context.stats_conn,
1789
+ account_key=account_key,
1631
1790
  )
1632
1791
  except sqlite3.Error:
1633
1792
  # Older or partially migrated stores retain the bounded
@@ -1636,6 +1795,9 @@ def _quota_read_model(
1636
1795
  canonical_rows = ()
1637
1796
  if canonical_rows:
1638
1797
  try:
1798
+ # #416 Slice 3A review F4: this load is bounded by root, slot
1799
+ # and `limit_id` only, so under focus the crossing was annotated
1800
+ # with whichever ACCOUNT's 5h observation happened to sort last.
1639
1801
  correlated_five_hour = tuple(
1640
1802
  observation
1641
1803
  for observation in load_codex_quota_observations(
@@ -1646,6 +1808,8 @@ def _quota_read_model(
1646
1808
  if observation.identity.window_minutes == 300
1647
1809
  and observation.identity.observed_slot == identity.observed_slot
1648
1810
  and observation.identity.limit_id == identity.limit_id
1811
+ and _codex_account_admits(
1812
+ account_key, observation.identity.account_key)
1649
1813
  )
1650
1814
  except sqlite3.Error:
1651
1815
  correlated_five_hour = ()
@@ -2038,12 +2202,30 @@ def refresh_codex_source_clock(
2038
2202
  return state if refreshed_state == state else refreshed_state
2039
2203
 
2040
2204
 
2041
- def _alerts_wire(stats_conn: sqlite3.Connection) -> tuple[dict[str, object], ...]:
2042
- """Return only safe, source-owned Codex alert context in newest-first order."""
2205
+ def _alerts_wire(
2206
+ stats_conn: sqlite3.Connection, *, decorated: bool = False,
2207
+ ) -> tuple[dict[str, object], ...]:
2208
+ """Return only safe, source-owned Codex alert context in newest-first order.
2209
+
2210
+ #416 spec §5.4 (review F14): the underlying tables all carry an account key —
2211
+ including the vendor-wide ``*`` rows — but this wire neither selected nor
2212
+ emitted it, so removing the `alerts-unfiltered-note` disclaimer badge without
2213
+ this would silently show one account another's alerts. `account_key` is now
2214
+ selected on all three legs and serialized under decoration (R8: below two
2215
+ REAL accounts no key is added, so the envelope is byte-identical).
2216
+
2217
+ Vendor-wide ``*`` rows keep that literal key rather than being dropped or
2218
+ reassigned: a vendor-wide budget crossing is not attributable to one account,
2219
+ so it stays visible under focus and the client labels it as vendor-wide.
2220
+ """
2043
2221
  rows: list[dict[str, object]] = []
2222
+
2223
+ def _account(value: object) -> dict[str, object]:
2224
+ return {"account_key": str(value or _CODEX_VENDOR_WIDE_ACCOUNT)} if decorated else {}
2225
+
2044
2226
  try:
2045
- for period, threshold, consumption_pct, crossed_at in stats_conn.execute(
2046
- "SELECT period, threshold, consumption_pct, crossed_at_utc "
2227
+ for period, threshold, consumption_pct, crossed_at, account_key in stats_conn.execute(
2228
+ "SELECT period, threshold, consumption_pct, crossed_at_utc, account_key "
2047
2229
  "FROM budget_milestones WHERE vendor='codex' AND alerted_at IS NOT NULL "
2048
2230
  "ORDER BY crossed_at_utc DESC, threshold DESC LIMIT ?",
2049
2231
  (SOURCE_HISTORY_LIMIT,),
@@ -2053,9 +2235,10 @@ def _alerts_wire(stats_conn: sqlite3.Connection) -> tuple[dict[str, object], ...
2053
2235
  "source": "codex",
2054
2236
  "axis": "codex_budget", "period": period, "threshold": threshold,
2055
2237
  "value": consumption_pct, "created_at": crossed_at,
2238
+ **_account(account_key),
2056
2239
  })
2057
- for period, threshold, projected_value, crossed_at in stats_conn.execute(
2058
- "SELECT period, threshold, projected_value, crossed_at_utc "
2240
+ for period, threshold, projected_value, crossed_at, account_key in stats_conn.execute(
2241
+ "SELECT period, threshold, projected_value, crossed_at_utc, account_key "
2059
2242
  "FROM projected_milestones WHERE metric='codex_budget_usd' AND alerted_at IS NOT NULL "
2060
2243
  "ORDER BY crossed_at_utc DESC, threshold DESC LIMIT ?",
2061
2244
  (SOURCE_HISTORY_LIMIT,),
@@ -2065,10 +2248,12 @@ def _alerts_wire(stats_conn: sqlite3.Connection) -> tuple[dict[str, object], ...
2065
2248
  "source": "codex",
2066
2249
  "axis": "projected", "period": period, "threshold": threshold,
2067
2250
  "value": projected_value, "created_at": crossed_at,
2251
+ **_account(account_key),
2068
2252
  })
2069
- for root_key, logical_key, observed_slot, window_minutes, resets_at, threshold, severity, created_at in stats_conn.execute(
2253
+ for (root_key, logical_key, observed_slot, window_minutes, resets_at,
2254
+ threshold, severity, created_at, account_key) in stats_conn.execute(
2070
2255
  "SELECT source_root_key, logical_limit_key, observed_slot, window_minutes, resets_at_utc, "
2071
- "threshold, severity, created_at_utc FROM quota_threshold_events "
2256
+ "threshold, severity, created_at_utc, account_key FROM quota_threshold_events "
2072
2257
  "WHERE source='codex' AND disposition='alerted' AND orphaned_at IS NULL "
2073
2258
  "ORDER BY created_at_utc DESC, source_root_key, logical_limit_key, observed_slot, threshold "
2074
2259
  "LIMIT ?",
@@ -2082,6 +2267,7 @@ def _alerts_wire(stats_conn: sqlite3.Connection) -> tuple[dict[str, object], ...
2082
2267
  "source": "codex",
2083
2268
  "axis": "quota", "threshold": threshold, "severity": severity,
2084
2269
  "created_at": created_at,
2270
+ **_account(account_key),
2085
2271
  })
2086
2272
  except sqlite3.Error:
2087
2273
  return ()
@@ -2276,12 +2462,18 @@ def _build_codex_native_weekly_view(
2276
2462
  now_utc: dt.datetime,
2277
2463
  display_tz_name: str | None,
2278
2464
  speed: str,
2465
+ account_key: str | None = None,
2279
2466
  ) -> CodexWeeklyView:
2280
- """Aggregate Codex cost into observed native quota-cycle segments."""
2467
+ """Aggregate Codex cost into observed native quota-cycle segments.
2468
+
2469
+ ``account_key`` scopes the durable boundary read to one account (#416
2470
+ Slice 3A review B1); ``None`` is the merged parent read and is byte-stable.
2471
+ """
2281
2472
  periods = _codex_weekly_periods(
2282
2473
  stats_conn,
2283
2474
  source_root_keys=source_root_keys,
2284
2475
  active_cycle=active_cycle,
2476
+ account_key=account_key,
2285
2477
  )
2286
2478
  converted: list[CodexEntry] = []
2287
2479
  bucket_by_entry: dict[int, str] = {}
@@ -2416,6 +2608,11 @@ def _codex_accounts_wire(
2416
2608
  or _lib_accounts.UNATTRIBUTED in five_hour
2417
2609
  ):
2418
2610
  ordered_keys.append(_lib_accounts.UNATTRIBUTED)
2611
+ # #416 §6: population-aware labels, so two Codex accounts that auto-label to
2612
+ # one email do not render two identical chips. Collision-only (D5): a
2613
+ # non-colliding label is untouched.
2614
+ _codex_label_map = _cctally_account.display_label_map(
2615
+ context.stats_conn, "codex")
2419
2616
 
2420
2617
  def _totals(rows: tuple[object, ...]) -> dict[str, object]:
2421
2618
  entries = _codex_entries_from_accounting(rows)
@@ -2457,7 +2654,7 @@ def _codex_accounts_wire(
2457
2654
  totals = _totals(rows)
2458
2655
  card: dict[str, object] = {
2459
2656
  "accountKey": key,
2460
- "label": _cctally_account.account_label(context.stats_conn, key),
2657
+ "label": _codex_label_map.get(key) or _cctally_account.account_label(context.stats_conn, key),
2461
2658
  "plan": plan_by_key.get(key),
2462
2659
  "active": key in active_keys,
2463
2660
  "weeklyPercent": (
@@ -2486,6 +2683,270 @@ def _codex_accounts_wire(
2486
2683
  return accounts_wire, hero_cycles_wire
2487
2684
 
2488
2685
 
2686
+ def _codex_partition_by_account(
2687
+ entries: Iterable[object],
2688
+ ) -> dict[str, tuple[object, ...]]:
2689
+ """Partition already-loaded Codex accounting rows by their stamped account.
2690
+
2691
+ #416 spec §5.2/§5.3 (review F9/F10). The account axis is added by splitting
2692
+ rows that are ALREADY in memory and re-running the SHIPPED builders per
2693
+ partition — not by threading an account through `CodexEntry`,
2694
+ `QualifiedCodexEntry`'s grouping keys, or the shared aggregator kernels. Two
2695
+ consequences, both load-bearing:
2696
+
2697
+ * the merged parent is byte-identical BY CONSTRUCTION, because the parent's
2698
+ code path is literally unchanged; and
2699
+ * `_aggregate_codex_buckets` accumulates in encounter order and preserves
2700
+ first-seen model order plus merged `model_breakdowns`, so ENCOUNTER ORDER
2701
+ IS PRESERVED within each partition here. Sorting, or partitioning through
2702
+ a set, would move a bucket's `models` order for free.
2703
+
2704
+ `NULL ≡ unattributed` — a row with no stamp lands in the reserved sentinel
2705
+ bucket, which stays selectable because after D1 it holds the bulk of Codex
2706
+ history.
2707
+ """
2708
+ buckets: dict[str, list[object]] = {}
2709
+ for entry in entries:
2710
+ key = str(
2711
+ getattr(entry, "account_key", "") or _lib_accounts.UNATTRIBUTED)
2712
+ buckets.setdefault(key, []).append(entry)
2713
+ return {key: tuple(values) for key, values in buckets.items()}
2714
+
2715
+
2716
+ def _codex_account_scopes_wire(
2717
+ context: DashboardReadContext,
2718
+ *,
2719
+ account_keys: Iterable[str],
2720
+ quota_observations: Iterable[object],
2721
+ cycle_by_account: Mapping[str, "CodexCycleBoundary"],
2722
+ visible_accounting_entries: Iterable[object],
2723
+ active_roots: Iterable[str],
2724
+ accounting_end: dt.datetime,
2725
+ metadata_incomplete: bool,
2726
+ conversation_metadata: Mapping[tuple[str, str], Mapping[str, object]],
2727
+ alerts: Iterable[Mapping[str, object]],
2728
+ budget_milestones: Iterable[Mapping[str, object]],
2729
+ projected_budget_milestones: Iterable[Mapping[str, object]],
2730
+ budget_cost_events_by_account: Mapping[str, tuple[tuple[dt.datetime, float], ...]],
2731
+ private_session_labels: dict[str, str],
2732
+ hero_failure: bool = False,
2733
+ ) -> dict[str, dict[str, object]]:
2734
+ """The per-account CHILDREN of the merged Codex read model (spec §5.3).
2735
+
2736
+ Caller must gate on `provider_is_decorated(stats_conn, "codex")` — this
2737
+ builds nothing for a <=1-real-account install, so the whole surface is ABSENT
2738
+ rather than present-and-empty and the envelope stays byte-identical (R8).
2739
+
2740
+ Each child mirrors the parent's own key shape (`periods` / `sessions` /
2741
+ `projects` / `cache_report` / `budget` / `quota` / `alerts`) so one client
2742
+ selector can return a structurally identical object for "All accounts" (the
2743
+ parent) and for a focused account (its child). Nothing is summed on the
2744
+ client: §5.3 established that scalar summation cannot reconstruct `models` /
2745
+ `model_breakdowns` and that weekly `used_pct` / `dollar_per_pct` are not
2746
+ additive at all.
2747
+
2748
+ `is_empty` is the explicit empty state from §6 and acceptance criterion 2 —
2749
+ an account with no evidence renders blank rather than the PREVIOUS account's
2750
+ numbers, which is the literal reported symptom.
2751
+
2752
+ Every read a child reaches that is NOT already covered by the in-memory
2753
+ partition is account-scoped explicitly (#416 Slice 3A review B1/B2/B3):
2754
+ `_codex_weekly_periods`, `_quota_wire`, `codex_quota_breakdown` (its block
2755
+ boundary AND its accounting), the 5h correlation load, and the cycle index.
2756
+ A read that filters by root, time or slot but not by account is the defect
2757
+ CLASS this section exists to close — `quota_window_blocks` and
2758
+ `quota_window_snapshots` both carry two rows when two accounts share one
2759
+ physical root.
2760
+ """
2761
+ visible = tuple(visible_accounting_entries)
2762
+ observations = tuple(quota_observations)
2763
+ partition = _codex_partition_by_account(visible)
2764
+ obs_partition: dict[str, list[object]] = {}
2765
+ for observation in observations:
2766
+ obs_partition.setdefault(
2767
+ observation.identity.account_key, []).append(observation)
2768
+ alert_rows = tuple(alerts)
2769
+ budget_rows = tuple(budget_milestones)
2770
+ projected_rows = tuple(projected_budget_milestones)
2771
+ roots = tuple(active_roots)
2772
+
2773
+ def _for_account(key: str) -> dict[str, object]:
2774
+ rows = partition.get(key, ())
2775
+ account_observations = tuple(obs_partition.get(key, ()))
2776
+ entries = _codex_entries_from_accounting(rows)
2777
+ cycle = cycle_by_account.get(key)
2778
+ sessions_view = (
2779
+ build_rooted_codex_session_view(
2780
+ rows, now_utc=context.now_utc,
2781
+ tz_name=context.display_tz_name, speed=context.speed,
2782
+ )
2783
+ if metadata_incomplete else build_codex_session_view(
2784
+ entries, now_utc=context.now_utc,
2785
+ tz_name=context.display_tz_name, speed=context.speed,
2786
+ )
2787
+ )
2788
+ quota = _quota_read_model(
2789
+ context, account_observations, accounting_entries=rows,
2790
+ account_key=key,
2791
+ )
2792
+ # #416 Slice 3A review B3. The parent sets `quota.cycle_index` and the
2793
+ # client reads `codex.quota.cycle_index`, so a child without the key
2794
+ # forces the client into a fallback — and the tempting one (reuse the
2795
+ # parent's) would render account A's milestone HISTORY on account B's
2796
+ # hero. The index is derivable per account from what the child already
2797
+ # has (its own `CodexCycleBoundary` plus `stats_conn`), so it is built
2798
+ # genuinely rather than declared parent-only. No cycle => `()`, an
2799
+ # honest empty state, never another account's ledger.
2800
+ cycle_index: tuple = ()
2801
+ if cycle is not None and not hero_failure:
2802
+ try:
2803
+ cycle_index = tuple(
2804
+ sys.modules["cctally"].build_codex_cycle_index(
2805
+ context.stats_conn, identity=cycle,
2806
+ now_utc=context.now_utc, account_key=key,
2807
+ )
2808
+ )
2809
+ except sqlite3.Error:
2810
+ cycle_index = ()
2811
+ quota = {
2812
+ **quota,
2813
+ "blocks": _quota_wire(
2814
+ context.stats_conn, accounting_entries=rows, cycle=cycle,
2815
+ now_utc=context.now_utc,
2816
+ display_tz_name=context.display_tz_name,
2817
+ account_key=key, decorated=True,
2818
+ ),
2819
+ "cycle_index": cycle_index,
2820
+ }
2821
+ return {
2822
+ # An account is empty when it owns neither accounting rows nor quota
2823
+ # evidence. Both axes matter: a brand-new account can carry a live
2824
+ # quota window with no spend yet, and a retired one the reverse.
2825
+ "is_empty": not rows and not account_observations,
2826
+ "periods": {
2827
+ "daily": _period_wire(build_codex_daily_view(
2828
+ entries, now_utc=context.now_utc,
2829
+ tz_name=context.display_tz_name, speed=context.speed,
2830
+ )),
2831
+ "monthly": _period_wire(build_codex_monthly_view(
2832
+ entries, now_utc=context.now_utc,
2833
+ tz_name=context.display_tz_name, speed=context.speed,
2834
+ )),
2835
+ "weekly": _period_wire(_build_codex_native_weekly_view(
2836
+ context.stats_conn, rows, source_root_keys=roots,
2837
+ active_cycle=cycle, now_utc=context.now_utc,
2838
+ display_tz_name=context.display_tz_name, speed=context.speed,
2839
+ account_key=key,
2840
+ )),
2841
+ },
2842
+ "sessions": _session_wire(
2843
+ sessions_view, metadata=conversation_metadata,
2844
+ private_labels=private_session_labels,
2845
+ ),
2846
+ "projects": (
2847
+ _partial_projects_wire(rows, conversation_metadata)
2848
+ if metadata_incomplete else _projects_wire(
2849
+ context, account_observations, rows,
2850
+ accounting_end=accounting_end,
2851
+ )
2852
+ ),
2853
+ "cache_report": _codex_cache_report_wire(
2854
+ rows, metadata=conversation_metadata, now_utc=context.now_utc,
2855
+ display_tz_name=context.display_tz_name, speed=context.speed,
2856
+ anomaly_threshold_pp=context.cache_report_anomaly_threshold_pp,
2857
+ ),
2858
+ "budget": {
2859
+ "status": _configured_codex_budget_status(
2860
+ context, rows,
2861
+ cost_events=budget_cost_events_by_account.get(key, ()),
2862
+ account_key=key,
2863
+ ),
2864
+ "milestones": tuple(_codex_account_scoped_rows(budget_rows, key)),
2865
+ "projected": tuple(_codex_account_scoped_rows(projected_rows, key)),
2866
+ },
2867
+ "quota": quota,
2868
+ "alerts": {
2869
+ "rows": tuple(_codex_account_scoped_rows(alert_rows, key)),
2870
+ "actual_thresholds": context.codex_quota_actual_thresholds,
2871
+ "projected_thresholds": context.codex_quota_projected_thresholds,
2872
+ },
2873
+ }
2874
+
2875
+ # #416 Slice 3A review B4. The requested key set comes from the stats
2876
+ # `accounts` REGISTRY (the hero cards) while the two partitions above key
2877
+ # off the DATA (`codex_session_entries.account_key` and each observation's
2878
+ # identity). Cache/stats drift — a stats rebuild that has not re-registered
2879
+ # an account, or a key stamped by a newer binary — therefore left rows in a
2880
+ # bucket with NO scope, so the union of the children was silently LESS than
2881
+ # the parent with no warning. Residual data keys become scopes of their own:
2882
+ # nothing is folded into `unattributed` (that would misattribute a KNOWN
2883
+ # key, which D1 forbids) and nothing is dropped. Such a scope has no hero
2884
+ # card, so it is simply not chip-selectable until the registry catches up —
2885
+ # a safe degrade, never a silent loss.
2886
+ #
2887
+ # #416 closeout F2: the durable projection is the THIRD axis. Those two
2888
+ # partitions cover only the rows this build loaded, and both loads are
2889
+ # bounded (`DASHBOARD_QUOTA_RECENT_DAYS` / `DASHBOARD_QUOTA_OBSERVATION_-
2890
+ # LIMIT`, and the visible accounting window) while `quota_window_blocks`
2891
+ # retains its account stamp indefinitely. A key that survives only there —
2892
+ # an older cycle, or a cache pruned behind a retained projection — was in no
2893
+ # bucket at all, which is B4's own failure on the axis B4 missed.
2894
+ ordered_keys = list(dict.fromkeys(str(key) for key in account_keys))
2895
+ residual_keys = sorted(
2896
+ (set(partition) | set(obs_partition) | _codex_block_account_keys(
2897
+ context.stats_conn, roots)) - set(ordered_keys)
2898
+ )
2899
+ return {key: _for_account(key) for key in ordered_keys + residual_keys}
2900
+
2901
+
2902
+ def _codex_block_account_keys(
2903
+ stats_conn: sqlite3.Connection, roots: Iterable[str],
2904
+ ) -> set[str]:
2905
+ """Every account stamped on a retained Codex block over ``roots`` (#416 F2).
2906
+
2907
+ Scoped to the child-visible cycle roots, so an unrelated root's account
2908
+ cannot manufacture a scope. `quota_window_blocks.account_key` is NOT NULL
2909
+ DEFAULT `unattributed`, and the `or UNATTRIBUTED` is belt-and-suspenders for
2910
+ a store written before that default landed. A read failure degrades to the
2911
+ two in-memory axes rather than failing the whole child build.
2912
+ """
2913
+ root_keys = tuple(dict.fromkeys(str(root) for root in roots))
2914
+ if not root_keys:
2915
+ return set()
2916
+ placeholders = ",".join("?" for _ in root_keys)
2917
+ try:
2918
+ return {
2919
+ str(row[0] or _lib_accounts.UNATTRIBUTED)
2920
+ for row in stats_conn.execute(
2921
+ "SELECT DISTINCT account_key FROM quota_window_blocks "
2922
+ "WHERE source='codex' AND orphaned_at IS NULL "
2923
+ f"AND source_root_key IN ({placeholders})",
2924
+ root_keys,
2925
+ )
2926
+ }
2927
+ except sqlite3.Error:
2928
+ return set()
2929
+
2930
+
2931
+ def _codex_account_scoped_rows(
2932
+ rows: Iterable[Mapping[str, object]], account_key: str,
2933
+ ) -> list[Mapping[str, object]]:
2934
+ """Rows this account owns, PLUS the vendor-wide ``*`` rows (spec §5.4).
2935
+
2936
+ A vendor-wide budget crossing is not attributable to one account, so hiding
2937
+ it under focus would silently drop a real alert. It stays visible and keeps
2938
+ its ``account_key == "*"`` so the client can label it as vendor-wide rather
2939
+ than as this account's.
2940
+ """
2941
+ return [
2942
+ row for row in rows
2943
+ if row.get("account_key") in (account_key, _CODEX_VENDOR_WIDE_ACCOUNT)
2944
+ ]
2945
+
2946
+
2947
+ _CODEX_VENDOR_WIDE_ACCOUNT = "*"
2948
+
2949
+
2489
2950
  def _claude_accounts_wire(
2490
2951
  stats_conn: sqlite3.Connection,
2491
2952
  *,
@@ -2527,6 +2988,8 @@ def _claude_accounts_wire(
2527
2988
  ).fetchone()
2528
2989
  return float(row[0]) if row is not None and row[0] is not None else 0.0
2529
2990
 
2991
+ _claude_label_map = _cctally_account.display_label_map(stats_conn, "claude")
2992
+
2530
2993
  # Include the unattributed bucket last iff it retained any snapshot.
2531
2994
  unattr_usage = _latest_usage(_lib_accounts.UNATTRIBUTED)
2532
2995
  unattr_cost = stats_conn.execute(
@@ -2545,7 +3008,7 @@ def _claude_accounts_wire(
2545
3008
  resets_at = usage[2] if usage is not None else None
2546
3009
  card: dict[str, object] = {
2547
3010
  "accountKey": key,
2548
- "label": _cctally_account.account_label(stats_conn, key),
3011
+ "label": _claude_label_map.get(key) or _cctally_account.account_label(stats_conn, key),
2549
3012
  "plan": plan_by_key.get(key),
2550
3013
  "active": key in active_keys,
2551
3014
  "weeklyPercent": None if is_unattributed else weekly_pct,
@@ -2715,12 +3178,24 @@ def build_codex_source_state(
2715
3178
  quota_observations,
2716
3179
  accounting_entries=visible_accounting_entries,
2717
3180
  )
3181
+ # R8 gate, resolved ONCE and threaded (#341 Task 4 / #416 §5.8). Every
3182
+ # per-account decoration below — block/alert/budget `account_key`, the
3183
+ # `accounts[]` cards, `hero.cycles[]`, and the `account_scopes` children —
3184
+ # hangs off this single boolean, so a <=1-real-account install is provably
3185
+ # byte-identical by construction rather than by golden observation.
3186
+ try:
3187
+ import _cctally_account
3188
+ _codex_decorated = _cctally_account.provider_is_decorated(
3189
+ context.stats_conn, "codex")
3190
+ except Exception:
3191
+ _codex_decorated = False
2718
3192
  quota_blocks = _quota_wire(
2719
3193
  context.stats_conn,
2720
3194
  accounting_entries=visible_accounting_entries,
2721
3195
  cycle=cycle,
2722
3196
  now_utc=context.now_utc,
2723
3197
  display_tz_name=context.display_tz_name,
3198
+ decorated=_codex_decorated,
2724
3199
  )
2725
3200
  # Hero-modal historical-milestone navigation index (spec §1c, §3). Built
2726
3201
  # here on the non-idle codex source rebuild (idle ticks reuse the stored
@@ -2737,8 +3212,9 @@ def build_codex_source_state(
2737
3212
  except sqlite3.Error:
2738
3213
  cycle_index = ()
2739
3214
  quota = {**quota, "blocks": quota_blocks, "cycle_index": cycle_index}
2740
- budget_rows = _budget_wire(context.stats_conn)
2741
- projected_budget_rows = _projected_budget_wire(context.stats_conn)
3215
+ budget_rows = _budget_wire(context.stats_conn, decorated=_codex_decorated)
3216
+ projected_budget_rows = _projected_budget_wire(
3217
+ context.stats_conn, decorated=_codex_decorated)
2742
3218
  budget_cost_events = _codex_budget_cost_events(context, budget_entries)
2743
3219
  configured_budget = _configured_codex_budget_status(
2744
3220
  context, budget_entries, cost_events=budget_cost_events,
@@ -2761,7 +3237,16 @@ def build_codex_source_state(
2761
3237
  accounting_end=accounting_end,
2762
3238
  )
2763
3239
  )
2764
- alerts = _alerts_wire(context.stats_conn)
3240
+ alerts = _alerts_wire(context.stats_conn, decorated=_codex_decorated)
3241
+ # Built here, BEFORE the children, so the parent's session wire is the first
3242
+ # writer into `private_session_labels` and the children (strict subsets of
3243
+ # the parent's rows) can only ever re-derive the same entries.
3244
+ private_session_labels: dict[str, str] = {}
3245
+ sessions_wire = _session_wire(
3246
+ sessions,
3247
+ metadata=conversation_metadata,
3248
+ private_labels=private_session_labels,
3249
+ )
2765
3250
  availability = (
2766
3251
  "partial" if metadata_incomplete or hero_failure
2767
3252
  else ("ok" if (entries or quota_blocks or budget_rows) else "empty")
@@ -2796,12 +3281,7 @@ def build_codex_source_state(
2796
3281
  # projection (client-side chip filter); the hero renders per-account cards.
2797
3282
  accounts_wire: list[dict[str, object]] = []
2798
3283
  hero_cycles_wire: list[dict[str, object]] = []
2799
- try:
2800
- import _cctally_account
2801
- _codex_decorated = _cctally_account.provider_is_decorated(
2802
- context.stats_conn, "codex")
2803
- except Exception:
2804
- _codex_decorated = False
3284
+ account_scopes: dict[str, dict[str, object]] = {}
2805
3285
  if _codex_decorated:
2806
3286
  try:
2807
3287
  accounts_wire, hero_cycles_wire = _codex_accounts_wire(
@@ -2811,12 +3291,134 @@ def build_codex_source_state(
2811
3291
  accounting_start=accounting_start,
2812
3292
  accounting_end=accounting_end,
2813
3293
  )
3294
+ # #416 §5.3: the per-account CHILDREN beside the merged parent. The
3295
+ # scope set is exactly the card set, so every chip the client can
3296
+ # focus resolves to a scope (an account with no evidence gets an
3297
+ # explicit `is_empty` child, never a missing key the client would
3298
+ # have to fall back from).
3299
+ cycle_by_account: dict[str, CodexCycleBoundary] = {}
3300
+ for cyc in cycles_all:
3301
+ cycle_by_account.setdefault(
3302
+ (
3303
+ cyc.quota_identity.account_key
3304
+ if cyc.quota_identity is not None
3305
+ else _lib_accounts.UNATTRIBUTED
3306
+ ),
3307
+ cyc,
3308
+ )
3309
+ # Budget cost events are frozen per account over the CONFIGURED
3310
+ # budget window, which can start before `range_start` — so they come
3311
+ # from the full `accounting_entries`, not the visible slice.
3312
+ budget_events_by_account = {
3313
+ key: _codex_budget_cost_events(context, rows)
3314
+ for key, rows in _codex_partition_by_account(
3315
+ accounting_entries).items()
3316
+ } if context.codex_budget is not None else {}
3317
+ account_scopes = _codex_account_scopes_wire(
3318
+ context,
3319
+ account_keys=[str(card["accountKey"]) for card in accounts_wire],
3320
+ quota_observations=quota_observations,
3321
+ cycle_by_account=cycle_by_account,
3322
+ visible_accounting_entries=visible_accounting_entries,
3323
+ active_roots=active_roots,
3324
+ accounting_end=accounting_end,
3325
+ metadata_incomplete=metadata_incomplete,
3326
+ conversation_metadata=conversation_metadata,
3327
+ alerts=alerts,
3328
+ budget_milestones=budget_rows,
3329
+ projected_budget_milestones=projected_budget_rows,
3330
+ budget_cost_events_by_account=budget_events_by_account,
3331
+ private_session_labels=private_session_labels,
3332
+ hero_failure=hero_failure,
3333
+ )
3334
+ # #416 QA P1-A — the "All accounts" Blocks panel is the UNION of
3335
+ # every account's 5-hour blocks. `_quota_wire` filters
3336
+ # `str(root_key) not in cycle.source_root_keys` against a single
3337
+ # `cycle` that is `cycles_all[0]`, so in the production shape (one
3338
+ # Codex root per account) every SIBLING account's live block is
3339
+ # dropped: the merged panel read "1 blocks · $0.86" while focusing
3340
+ # the sibling revealed a second live block the merged view never
3341
+ # showed. That is an UNDERCOUNT, not a misattribution — the strict
3342
+ # account predicate `_quota_wire` applies under focus is correct and
3343
+ # is untouched; the defect is on the separate CYCLE-ROOT axis.
3344
+ #
3345
+ # The merge is STRICTLY ADDITIVE over today's parent read: every
3346
+ # child's rows are unioned in, and every row the representative-cycle
3347
+ # read already produced is kept. Taking the children ALONE was the
3348
+ # obvious construction (it mirrors P0-A's sum-of-the-cards, and it
3349
+ # makes the merged list incapable of disagreeing with the chip the
3350
+ # operator focuses next) but it LOSES rows: a child whose account has
3351
+ # no live cycle passes `cycle=None` to `_quota_wire`, which returns
3352
+ # `()` — so an account whose key survives only in
3353
+ # `quota_window_blocks` (the third stamping axis; see
3354
+ # `test_a_block_only_account_key_still_gets_a_scope`) vanished from
3355
+ # the merged view that used to list it. Electing a sibling's cycle to
3356
+ # bound it instead would be the very defect this fixes, so the parent
3357
+ # keeps its own rows and gains the siblings'.
3358
+ #
3359
+ # Dedup is on `(key, account_key)`: the opaque block key is built
3360
+ # from root/limit/slot/window/reset and deliberately excludes the
3361
+ # account, so two accounts sharing one physical root are two rows
3362
+ # with one key — never-combine extends to accounts.
3363
+ #
3364
+ # Each row keeps its own `current_percent` and its own `account_key`,
3365
+ # so this is a LISTING of independent windows, never a blend (D6).
3366
+ # Ordering mirrors `_quota_wire`'s own `resets_at DESC` with the
3367
+ # opaque key as the deterministic tie-break.
3368
+ by_identity: dict[tuple[str, str], dict[str, object]] = {}
3369
+ for block in (
3370
+ *(
3371
+ row
3372
+ for scope in account_scopes.values()
3373
+ for row in scope["quota"]["blocks"]
3374
+ ),
3375
+ *quota["blocks"],
3376
+ ):
3377
+ by_identity.setdefault(
3378
+ (str(block["key"]), str(block.get("account_key", ""))), block)
3379
+ merged_blocks = tuple(sorted(
3380
+ by_identity.values(),
3381
+ key=lambda row: (
3382
+ str(row["resets_at"]),
3383
+ str(row["key"]),
3384
+ str(row.get("account_key", "")),
3385
+ ),
3386
+ reverse=True,
3387
+ ))
3388
+ quota = {**quota, "blocks": merged_blocks}
2814
3389
  except (sqlite3.Error, QualifiedMetadataUnavailable):
2815
3390
  # A per-account wire failure must never fail the whole source build;
2816
3391
  # degrade to the byte-stable undecorated shape.
2817
3392
  accounts_wire = []
2818
3393
  hero_cycles_wire = []
2819
- private_session_labels: dict[str, str] = {}
3394
+ account_scopes = {}
3395
+ # #416 QA P0-A — the "All accounts" headline is the MERGED spend and tokens
3396
+ # (spec §6, decision D6). Everything above resolves the hero from ONE
3397
+ # representative cycle (`cycles_all[0]` plus that cycle's own
3398
+ # `source_root_keys`), which in the production shape — one Codex root per
3399
+ # account — cannot see a sibling's spend at all: the headline then reads as
3400
+ # a live total while being byte-identical to a single card sitting directly
3401
+ # beneath it.
3402
+ #
3403
+ # Spend and tokens are the ONLY axes D6 lets "All accounts" merge; the
3404
+ # percentage, reset, forecast and $/1% stay per-account and the client
3405
+ # blanks them with a pointer to the cards. The merge is a SUM OF THE CARDS
3406
+ # rather than a fresh query, so the headline can never disagree with the
3407
+ # strip it sits above (an account without a live cycle contributes exactly
3408
+ # what its own card shows, over the accounting range — the card's documented
3409
+ # fallback). Gated on `_codex_decorated`, so a <=1-real-account install
3410
+ # keeps the single-cycle hero byte-for-byte (R8); gated on `hero_failure`,
3411
+ # so an unavailable hero stays unavailable rather than gaining totals the
3412
+ # rest of the envelope says are absent.
3413
+ if _codex_decorated and accounts_wire and not hero_failure:
3414
+ cycle_cost_usd = stable_sum(
3415
+ float(card["spendUsd"]) for card in accounts_wire)
3416
+ hero_input = sum(int(card["inputTokens"]) for card in accounts_wire)
3417
+ hero_cached = sum(int(card["cachedInputTokens"]) for card in accounts_wire)
3418
+ hero_output = sum(int(card["outputTokens"]) for card in accounts_wire)
3419
+ hero_reasoning = sum(
3420
+ int(card["reasoningOutputTokens"]) for card in accounts_wire)
3421
+ hero_total = sum(int(card["totalTokens"]) for card in accounts_wire)
2820
3422
  return SourceDashboardState(
2821
3423
  source="codex",
2822
3424
  availability=availability,
@@ -2883,16 +3485,17 @@ def build_codex_source_state(
2883
3485
  **({"cycles": hero_cycles_wire} if _codex_decorated else {}),
2884
3486
  },
2885
3487
  **({"accounts": accounts_wire} if _codex_decorated else {}),
3488
+ # #416 §5.3 — the per-account children. Present ONLY under
3489
+ # decoration; the merged parent below is untouched by their
3490
+ # existence, which is what makes acceptance criterion 7 true by
3491
+ # construction rather than by careful re-derivation.
3492
+ **({"account_scopes": account_scopes} if _codex_decorated else {}),
2886
3493
  "periods": {
2887
3494
  "daily": _period_wire(daily),
2888
3495
  "monthly": _period_wire(monthly),
2889
3496
  "weekly": _period_wire(weekly),
2890
3497
  },
2891
- "sessions": _session_wire(
2892
- sessions,
2893
- metadata=conversation_metadata,
2894
- private_labels=private_session_labels,
2895
- ),
3498
+ "sessions": sessions_wire,
2896
3499
  "quota": quota,
2897
3500
  "budget": {
2898
3501
  "status": configured_budget,