cctally 1.112.0 → 1.114.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +4 -5
  3. package/bin/_cctally_cache.py +1683 -485
  4. package/bin/_cctally_core.py +223 -1
  5. package/bin/_cctally_dashboard.py +96 -18
  6. package/bin/_cctally_dashboard_perf.py +128 -9
  7. package/bin/_cctally_dashboard_sources.py +267 -204
  8. package/bin/_cctally_db.py +861 -18
  9. package/bin/_cctally_diagnosis_sources.py +22 -4
  10. package/bin/_cctally_doctor.py +136 -18
  11. package/bin/_cctally_five_hour.py +2 -2
  12. package/bin/_cctally_forecast.py +27 -6
  13. package/bin/_cctally_journal.py +67 -5
  14. package/bin/_cctally_milestone_history.py +82 -9
  15. package/bin/_cctally_percent_breakdown.py +3 -2
  16. package/bin/_cctally_quota.py +201 -33
  17. package/bin/_cctally_record.py +14 -0
  18. package/bin/_cctally_rederive.py +23 -9
  19. package/bin/_cctally_source_analytics.py +60 -15
  20. package/bin/_cctally_statusline.py +17 -0
  21. package/bin/_cctally_store.py +80 -5
  22. package/bin/_cctally_tui.py +4 -3
  23. package/bin/_cctally_weekrefs.py +128 -21
  24. package/bin/_lib_cache_report.py +35 -6
  25. package/bin/_lib_codex_conversation.py +32 -0
  26. package/bin/_lib_codex_conversation_query.py +164 -30
  27. package/bin/_lib_conversation_query.py +95 -56
  28. package/bin/_lib_conversation_retention.py +1605 -640
  29. package/bin/_lib_cost_provenance.py +53 -2
  30. package/bin/_lib_doctor.py +263 -69
  31. package/bin/_lib_fmt.py +11 -0
  32. package/bin/_lib_merge_gate.py +469 -51
  33. package/bin/_lib_perf.py +27 -1
  34. package/bin/_lib_pricing.py +209 -8
  35. package/bin/_lib_pricing_check.py +20 -4
  36. package/bin/_lib_reclaim_planner.py +792 -0
  37. package/bin/_lib_rederive.py +225 -77
  38. package/bin/_lib_snapshot_cache.py +21 -0
  39. package/bin/_lib_sqlite_close.py +81 -0
  40. package/bin/_lib_subscription_weeks.py +100 -17
  41. package/bin/_lib_tick_stats.py +217 -17
  42. package/bin/_lib_wal_checkpoint.py +1077 -0
  43. package/bin/_lib_write_budget.py +483 -0
  44. package/bin/_lib_write_io.py +703 -0
  45. package/bin/cctally +8 -0
  46. package/dashboard/static/assets/{ConversationsView-CEZ-BUyi.js → ConversationsView-Bc8LCOAZ.js} +1 -1
  47. package/dashboard/static/assets/{DoctorModal-CE1oH_Zy.js → DoctorModal-D--qVBwe.js} +1 -1
  48. package/dashboard/static/assets/ModalRoot-DLPgyAM_.js +1 -0
  49. package/dashboard/static/assets/{ProjectsDrillPanel-B3DZoCeD.js → ProjectsDrillPanel-z53a4v4M.js} +1 -1
  50. package/dashboard/static/assets/{SourceDetailModal-CHp9EgLP.js → SourceDetailModal-Bw0T8W-W.js} +1 -1
  51. package/dashboard/static/assets/{UpdateModal-5iKST6jo.js → UpdateModal-D5AKx4Yw.js} +1 -1
  52. package/dashboard/static/assets/index-BKITk1CT.css +1 -0
  53. package/dashboard/static/assets/{index-Cm0VTxJC.js → index-BXDOzSng.js} +3 -3
  54. package/dashboard/static/assets/{outlineNavigation-DkXGL8J6.js → outlineNavigation-JObY7cUY.js} +1 -1
  55. package/dashboard/static/dashboard.html +2 -2
  56. package/package.json +6 -1
  57. package/dashboard/static/assets/ModalRoot-D5Y8IFUG.js +0 -1
  58. package/dashboard/static/assets/index-Dt4tqf43.css +0 -1
@@ -817,22 +817,75 @@ def _load_codex_cycles(stats_conn, root_keys, *, include_orphaned=False,
817
817
  if not roots:
818
818
  return []
819
819
  placeholders = ",".join("?" for _ in roots)
820
- orphan_clause = "" if include_orphaned else "AND orphaned_at IS NULL "
821
- # `quota_window_blocks.account_key` is NOT NULL DEFAULT 'unattributed'.
822
- account_clause = "" if account_key is None else "AND account_key=? "
823
- account_params = () if account_key is None else (account_key,)
824
820
  # BEFORE the SQL, per #496 S5b section 4.7.
825
821
  assert_projection_readable(stats_conn)
822
+ # #901 (I1/G1, Amendment 1 item 2, 1b and 1c): stream every weekly block
823
+ # in reset order through an epoch-1017 partial expression index. Unpinned,
824
+ # the planner seeks a root-leading index and sorts the whole weekly
825
+ # history through a temp b-tree (measured for every root/account/orphan
826
+ # variant).
827
+ #
828
+ # The ORDER BY names the tie order explicitly: `_canonicalize_codex_
829
+ # cluster` keeps the first maximal member, so rows tied on one reset
830
+ # second must arrive in the order the pre-#901 sorter received them, and
831
+ # that order came from the legacy plan, which differs by root count
832
+ # (Amendment 1c, measured on SQLite 3.53.4). With two or more roots the
833
+ # legacy plan scanned `idx_quota_blocks_active`, whose order includes
834
+ # orphaned_at; with exactly one it seeked the UNIQUE autoindex, which has
835
+ # no orphaned_at (its one exception, an account-scoped live-only read, has
836
+ # a constant orphaned_at, so the two orders agree there). The branch
837
+ # counts the NORMALIZED roots above, so duplicate or empty arguments take
838
+ # the single-root order, exactly as they took the single-root plan.
839
+ #
840
+ # Each branch pins its own index only when that index exists; on an
841
+ # older index the statement stays unpinned with ordinary predicates and
842
+ # the same explicit order. Under the pin, the root, account and orphan
843
+ # predicates carry a unary `+`: SQLite treats a column that a WHERE term
844
+ # fixes but the scan does not seek as satisfying its ORDER BY term and
845
+ # then cannot match that index column, so the later ORDER BY terms fell
846
+ # back to a tie-run sorter ("USE TEMP B-TREE FOR LAST n TERMS OF ORDER
847
+ # BY", measured). Limitation: a unary `+` keeps the operand's value but
848
+ # removes its column affinity. That is equivalent here ONLY because these
849
+ # comparisons are TEXT against TEXT: the root keys are filtered to `str`
850
+ # above, `account_key` is a TEXT key bound as a string, and `IS NULL`
851
+ # involves no affinity. It is not an equivalence for numeric parameters or
852
+ # any other affinity-dependent comparison; a caller binding a non-string
853
+ # account key would need the unpinned predicate.
854
+ if len(roots) == 1:
855
+ order_index = "idx_quota_blocks_weekly_single_root_order"
856
+ order_by = (
857
+ "ORDER BY unixepoch(resets_at_utc) DESC, source_root_key ASC, "
858
+ "account_key ASC, logical_limit_key ASC, observed_slot ASC, "
859
+ "resets_at_utc ASC")
860
+ else:
861
+ order_index = "idx_quota_blocks_weekly_reset_order"
862
+ order_by = (
863
+ "ORDER BY unixepoch(resets_at_utc) DESC, source_root_key ASC, "
864
+ "account_key ASC, orphaned_at ASC, logical_limit_key ASC, "
865
+ "observed_slot ASC, resets_at_utc ASC")
866
+ pinned = stats_conn.execute(
867
+ "SELECT 1 FROM sqlite_master WHERE type='index' AND name=?",
868
+ (order_index,),
869
+ ).fetchone() is not None
870
+ order_hint = f"INDEXED BY {order_index} " if pinned else ""
871
+ plus = "+" if pinned else ""
872
+ orphan_clause = (
873
+ "" if include_orphaned else f"AND {plus}orphaned_at IS NULL ")
874
+ # `quota_window_blocks.account_key` is NOT NULL DEFAULT 'unattributed'.
875
+ account_clause = (
876
+ "" if account_key is None else f"AND {plus}account_key=? ")
877
+ account_params = () if account_key is None else (account_key,)
826
878
  rows = stats_conn.execute(
827
879
  "SELECT source_root_key, logical_limit_key, observed_slot, "
828
880
  " window_minutes, limit_id, limit_name, account_key, "
829
881
  " resets_at_utc, nominal_start_at_utc, current_percent "
830
882
  "FROM quota_window_blocks "
883
+ f"{order_hint}"
831
884
  "WHERE source='codex' AND window_minutes=10080 "
832
885
  f"{orphan_clause}"
833
886
  f"{account_clause}"
834
- f"AND source_root_key IN ({placeholders}) "
835
- "ORDER BY unixepoch(resets_at_utc) DESC",
887
+ f"AND {plus}source_root_key IN ({placeholders}) "
888
+ f"{order_by}",
836
889
  (*account_params, *roots),
837
890
  ).fetchall()
838
891
 
@@ -952,10 +1005,20 @@ def _codex_five_hour_rows(stats_conn, cyc, *, include_orphaned=False) -> list:
952
1005
  The widening is one-directional on purpose: an unattributed CYCLE never
953
1006
  picks up another account's identified 5h rows, which would violate #341's
954
1007
  never-combine rule.
1008
+
1009
+ #901 Amendment 19 T1 (spec I1): the rows are returned whole, so they are
1010
+ ordered here, where ``ORDER BY unixepoch(nominal_start_at_utc)`` sorted
1011
+ them through a temp b-tree. Each physical 5h block surfaces as many
1012
+ jittered rows, so one weekly cycle is not a bound. The key is SQLite's own
1013
+ ``unixepoch()`` of each distinct start spelling (a table-free scalar
1014
+ query, memoized), so it is the old key exactly, and the rows keep the
1015
+ caller's row factory. ``list.sort`` is stable over the same scan SQLite's
1016
+ in-memory sorter consumed, so rows tied on one start second keep their
1017
+ order. The WHERE clause admits no NULL start.
955
1018
  """
956
1019
  orphan_clause = "" if include_orphaned else "AND orphaned_at IS NULL "
957
1020
  assert_projection_readable(stats_conn)
958
- return stats_conn.execute(
1021
+ rows = stats_conn.execute(
959
1022
  "SELECT source_root_key, logical_limit_key, observed_slot, "
960
1023
  " window_minutes, limit_id, limit_name, account_key, "
961
1024
  " resets_at_utc, nominal_start_at_utc, current_percent "
@@ -964,12 +1027,22 @@ def _codex_five_hour_rows(stats_conn, cyc, *, include_orphaned=False) -> list:
964
1027
  f"{orphan_clause}"
965
1028
  "AND source_root_key=? AND account_key IN (?,?) "
966
1029
  "AND unixepoch(nominal_start_at_utc) < unixepoch(?) "
967
- "AND unixepoch(resets_at_utc) > unixepoch(?) "
968
- "ORDER BY unixepoch(nominal_start_at_utc) ASC",
1030
+ "AND unixepoch(resets_at_utc) > unixepoch(?)",
969
1031
  (cyc.root, cyc.account_key, UNATTRIBUTED,
970
1032
  cyc.end.astimezone(UTC).isoformat(),
971
1033
  cyc.start.astimezone(UTC).isoformat()),
972
1034
  ).fetchall()
1035
+ epochs: dict = {}
1036
+
1037
+ def start_epoch(row):
1038
+ text = row[8]
1039
+ if text not in epochs:
1040
+ epochs[text] = stats_conn.execute(
1041
+ "SELECT unixepoch(?)", (text,)).fetchone()[0]
1042
+ return epochs[text]
1043
+
1044
+ rows.sort(key=start_epoch)
1045
+ return rows
973
1046
 
974
1047
 
975
1048
  def _codex_five_hour_clusters(stats_conn, cyc, *, include_orphaned=False) -> list:
@@ -262,7 +262,8 @@ def observation_gap_marginal_cell(marginal_value: Any, *, withheld: bool) -> str
262
262
  table already says what the code means.
263
263
  """
264
264
  if marginal_value is not None:
265
- return f"${float(marginal_value):.6f}"
265
+ c = _cctally()
266
+ return c._fmt_usd_accounting(float(marginal_value), 6)
266
267
  return OBSERVATION_GAP_CAUSE if withheld else "n/a"
267
268
 
268
269
 
@@ -315,7 +316,7 @@ def _render_percent_breakdown_terminal(
315
316
  rows.append([
316
317
  str(idx),
317
318
  f"{percent}%",
318
- f"${cumulative:.6f}",
319
+ c._fmt_usd_accounting(cumulative, 6),
319
320
  c.observation_gap_marginal_cell(
320
321
  marginal_value, withheld=(idx - 1) in withheld),
321
322
  f"{float(five_hour_value):.0f}%" if five_hour_value is not None else "n/a",
@@ -378,7 +378,14 @@ PROJECTION_DYNAMIC_READ_SITES: "dict[str, int]" = {
378
378
  # so no `PROJECTION_DYNAMIC_READ_ACTIONS` classification applies.
379
379
  "_lib_codex_conversation_query.py": 1,
380
380
  "_lib_conversation_query.py": 1,
381
- "_lib_conversation_retention.py": 2,
381
+ # Four, not two, since #901's paced retention (`f2077468a`, spec §5.4):
382
+ # `group_row_count` counts the rows a deletion's reservation charges with
383
+ # `SELECT COUNT(*) FROM {table}` in its two shapes (by identity key, and by
384
+ # source path for a NULL-identity group). `{table}` comes from
385
+ # `_GROUP_TABLES`, a hardcoded pair (`conversation_messages`,
386
+ # `codex_conversation_events`); neither is a quota projection table, so no
387
+ # `PROJECTION_DYNAMIC_READ_ACTIONS` classification applies.
388
+ "_lib_conversation_retention.py": 4,
382
389
  # Five: `_target_has_cursor_gap` selects one of the two accounting source
383
390
  # tables, `_conversation_source_paths` and `_conversation_target_risk`
384
391
  # select one of the two transcript source tables, and #769 S6's
@@ -1307,6 +1314,107 @@ def _codex_quota_required_text_present(values: Iterable[object]) -> bool:
1307
1314
  value is None or not str(value).strip() for value in values)
1308
1315
 
1309
1316
 
1317
+ # ── #901 W1: reading the maintained raw-partition maxima (spec §5.1, T1) ─────
1318
+
1319
+ #: The objects the latest-only read and the general loader's plan pins depend
1320
+ #: on, probed together once per load.
1321
+ _CODEX_QUOTA_READ_OBJECTS = (
1322
+ "codex_quota_partition_latest", "idx_qws_partition_capture",
1323
+ "trg_qws_latest_ins", "trg_qws_latest_del", "trg_qws_latest_upd",
1324
+ "idx_qws_codex_load_order", "idx_qws_physical_group",
1325
+ )
1326
+ _CODEX_QUOTA_SUMMARY_OBJECTS = frozenset(_CODEX_QUOTA_READ_OBJECTS[:5])
1327
+
1328
+ #: The latest path's SELECT list over the summary join, aliased so every name
1329
+ #: the interpretation loop reads resolves exactly as it does on the window path.
1330
+ _CODEX_QUOTA_LATEST_SELECT = ", ".join(
1331
+ f"q.{column} AS {column}" for column in (
1332
+ "source", "source_root_key", "source_path", "line_offset",
1333
+ "captured_at_utc", "observed_slot", "logical_limit_key", "limit_id",
1334
+ "limit_name", "window_minutes", "used_percent", "resets_at_utc",
1335
+ "plan_type", "individual_limit_json", "reached_type",
1336
+ "observed_model", "account_key", "canonical_resets_at_utc",
1337
+ ))
1338
+
1339
+
1340
+ def _codex_quota_read_objects(conn: sqlite3.Connection) -> "frozenset[str]":
1341
+ """Which of the read-path objects this store carries (one probe)."""
1342
+ placeholders = ",".join("?" for _ in _CODEX_QUOTA_READ_OBJECTS)
1343
+ return frozenset(
1344
+ str(row[0]) for row in conn.execute(
1345
+ f"SELECT name FROM sqlite_master WHERE name IN ({placeholders})",
1346
+ _CODEX_QUOTA_READ_OBJECTS)
1347
+ )
1348
+
1349
+
1350
+ def _codex_quota_latest_summary_ready(read_objects: "frozenset[str]") -> bool:
1351
+ """True when the summary and everything that maintains it are present.
1352
+
1353
+ The five objects are created and bootstrapped in one transaction
1354
+ (``_cctally_db._apply_codex_quota_latest_summary``), and SQLite drops a
1355
+ table's triggers and indexes only together with the table, so their joint
1356
+ presence means the summary has been maintained since its bootstrap.
1357
+ Anything less — a read-only open of a store migration 047 has not reached,
1358
+ or a legacy shape — keeps the window query, which selects the same rows.
1359
+ """
1360
+ return _CODEX_QUOTA_SUMMARY_OBJECTS <= read_objects
1361
+
1362
+
1363
+ def _sqlite_order_value(value) -> tuple:
1364
+ """Rank a stored value the way SQLite's BINARY ORDER BY ranks it.
1365
+
1366
+ NULL first, then numbers (integers and reals compared numerically), then
1367
+ text in code-point order (which is UTF-8 byte order), then blobs.
1368
+ """
1369
+ if value is None:
1370
+ return (0, 0)
1371
+ if isinstance(value, (int, float)):
1372
+ return (1, value)
1373
+ if isinstance(value, str):
1374
+ return (2, value)
1375
+ return (3, bytes(value))
1376
+
1377
+
1378
+ def _codex_quota_latest_rows(
1379
+ conn: sqlite3.Connection, requested: "set[str] | None",
1380
+ ) -> "list[sqlite3.Row]":
1381
+ """Every valid physical row tied at its raw partition's maximum second.
1382
+
1383
+ One scan of the summary (one row per retained raw partition) and one
1384
+ ``idx_qws_partition_capture`` seek per partition. ``CROSS JOIN`` fixes the
1385
+ summary as the outer loop and ``INDEXED BY`` the inner seek, so the plan
1386
+ cannot drift into walking the snapshots. The window path's ORDER BY
1387
+ (``source_root_key, captured_at_utc, resets_at_utc, source_path,
1388
+ line_offset``) is reproduced here in Python over this bounded set, so the
1389
+ rows reach the interpretation loop in exactly the order they did before.
1390
+ """
1391
+ import _cctally_db
1392
+
1393
+ on = " AND ".join(
1394
+ f"{expr} = s.{key}" for key, expr in zip(
1395
+ _cctally_db._CODEX_QUOTA_LATEST_KEY_COLUMNS,
1396
+ _cctally_db._codex_quota_latest_key_exprs("q.")))
1397
+ sql = (
1398
+ f"SELECT {_CODEX_QUOTA_LATEST_SELECT}"
1399
+ " FROM codex_quota_partition_latest AS s"
1400
+ " CROSS JOIN quota_window_snapshots AS q"
1401
+ " INDEXED BY idx_qws_partition_capture"
1402
+ f" ON {on} AND unixepoch(q.captured_at_utc) = s.latest_capture_epoch"
1403
+ f" WHERE {_cctally_db._codex_quota_latest_validity_sql('q.')}"
1404
+ )
1405
+ params: "list[object]" = []
1406
+ if requested is not None:
1407
+ sql += (" AND s.k_source_root_key IN ("
1408
+ + ",".join("quote(?)" for _ in requested) + ")")
1409
+ params.extend(sorted(requested))
1410
+ rows = conn.execute(sql, params).fetchall()
1411
+ rows.sort(key=lambda row: tuple(
1412
+ _sqlite_order_value(row[name]) for name in (
1413
+ "source_root_key", "captured_at_utc", "resets_at_utc",
1414
+ "source_path", "line_offset")))
1415
+ return rows
1416
+
1417
+
1310
1418
  # --------------------------------------------------------------------------
1311
1419
  # #500 §6.4 — the fold-time operator attribution overlay
1312
1420
  # --------------------------------------------------------------------------
@@ -1449,6 +1557,56 @@ def _codex_attribution_stored_limit_keys(logical_limit_key: str) -> "list[str]":
1449
1557
  )
1450
1558
 
1451
1559
 
1560
+ def _codex_shard_axis_filter(axes: tuple) -> "tuple[str, tuple]":
1561
+ """The WHERE clause and parameters selecting one attribution axis shard.
1562
+
1563
+ Rows are STORED under whichever length and key spelling the provider sent,
1564
+ so the filter enumerates every equivalent rather than snapping in SQL —
1565
+ which it could not do and still seek the index.
1566
+ """
1567
+ root_key, limit_key, slot, window_minutes = axes
1568
+ limit_keys = _codex_attribution_stored_limit_keys(limit_key)
1569
+ minutes = sorted({
1570
+ int(value)
1571
+ for value in codex_snap_equivalent_window_minutes(window_minutes)
1572
+ })
1573
+ axis_clause = (
1574
+ " WHERE source='codex' AND source_root_key = ?"
1575
+ " AND logical_limit_key IN ("
1576
+ + ",".join("?" * len(limit_keys)) + ")"
1577
+ " AND observed_slot = ?"
1578
+ " AND window_minutes IN (" + ",".join("?" * len(minutes)) + ")"
1579
+ )
1580
+ return axis_clause, (root_key, *limit_keys, slot, *minutes)
1581
+
1582
+
1583
+ def _codex_witness_anchors(
1584
+ conn: sqlite3.Connection, anchor_expr: str, axis_clause: str,
1585
+ axis_params: tuple, witnesses: "Sequence[str]",
1586
+ ) -> "set[str]":
1587
+ """Pass 1: the canonical anchors of the groups a witness reset reaches.
1588
+
1589
+ #901 Amendment 19 T1 (spec I1): the set is built in Python from the
1590
+ streamed rows. ``SELECT DISTINCT`` sorted the anchors of every capture
1591
+ carrying a witness reset through a temp b-tree — a window group's whole
1592
+ activity — and one attribution axis shard is not a bound. The result is
1593
+ the same set; the Python set holds one entry per distinct anchor.
1594
+ """
1595
+ pass_one = (
1596
+ f"SELECT {anchor_expr} AS anchor"
1597
+ " FROM quota_window_snapshots"
1598
+ + axis_clause
1599
+ + " AND unixepoch(resets_at_utc) IN ("
1600
+ + ",".join("unixepoch(?)" for _ in witnesses) + ")"
1601
+ )
1602
+ anchors: "set[str]" = set()
1603
+ for row in conn.execute(pass_one, (*axis_params, *witnesses)):
1604
+ if row[0] is None or not str(row[0]).strip():
1605
+ continue
1606
+ anchors.add(str(row[0]))
1607
+ return anchors
1608
+
1609
+
1452
1610
  def _load_codex_window_group_evidence(
1453
1611
  conn: sqlite3.Connection, assertions: "Sequence[Mapping[str, object]]",
1454
1612
  ) -> "tuple[_wa.WindowGroup, ...]":
@@ -1557,36 +1715,10 @@ def _load_codex_window_group_evidence(
1557
1715
  witness_texts: "dict[str, str]" = {}
1558
1716
  model_scoped: "dict[tuple, bool]" = {}
1559
1717
  for axes, shard_witnesses in sorted(shards.items()):
1560
- root_key, limit_key, slot, window_minutes = axes
1561
- # Rows are STORED under whichever length and key spelling the provider
1562
- # sent, so the filter enumerates every equivalent rather than snapping
1563
- # in SQL — which it could not do and still seek the index.
1564
- limit_keys = _codex_attribution_stored_limit_keys(limit_key)
1565
- minutes = sorted({
1566
- int(value)
1567
- for value in codex_snap_equivalent_window_minutes(window_minutes)
1568
- })
1569
- witnesses = sorted(shard_witnesses)
1570
- axis_clause = (
1571
- " WHERE source='codex' AND source_root_key = ?"
1572
- " AND logical_limit_key IN ("
1573
- + ",".join("?" * len(limit_keys)) + ")"
1574
- " AND observed_slot = ?"
1575
- " AND window_minutes IN (" + ",".join("?" * len(minutes)) + ")"
1576
- )
1577
- axis_params = (root_key, *limit_keys, slot, *minutes)
1578
- anchors: "set[str]" = set()
1579
- pass_one = (
1580
- f"SELECT DISTINCT {anchor_expr} AS anchor"
1581
- " FROM quota_window_snapshots"
1582
- + axis_clause
1583
- + " AND unixepoch(resets_at_utc) IN ("
1584
- + ",".join("unixepoch(?)" for _ in witnesses) + ")"
1585
- )
1586
- for row in conn.execute(pass_one, (*axis_params, *witnesses)):
1587
- if row[0] is None or not str(row[0]).strip():
1588
- continue
1589
- anchors.add(str(row[0]))
1718
+ axis_clause, axis_params = _codex_shard_axis_filter(axes)
1719
+ anchors = _codex_witness_anchors(
1720
+ conn, anchor_expr, axis_clause, axis_params,
1721
+ sorted(shard_witnesses))
1590
1722
  if not anchors:
1591
1723
  continue
1592
1724
  # ONE SHARD PER GROUP, for the reason the loader states at its own group
@@ -1983,6 +2115,16 @@ def load_codex_quota_observations(
1983
2115
  and therefore the same verdict as the full load, on a store where the full
1984
2116
  load meant interpreting 266,337 rows to answer a question about 608 of them.
1985
2117
 
2118
+ #901 W1 (spec §5.1, T1): on a store carrying cache migration 047's objects
2119
+ the candidate rows come from ``codex_quota_partition_latest``, a
2120
+ trigger-maintained table holding every raw partition's maximum whole-second
2121
+ capture, joined back to every physical row tied at that second through
2122
+ ``idx_qws_partition_capture``. The window query described below stays as the
2123
+ fallback for a store without those objects (a read-only open before the
2124
+ migration ran, or a legacy shape); both select exactly the same rows, and
2125
+ tests/test_901_partition_latest_maintenance.py holds the summary to the
2126
+ frozen pre-#901 window query after every writer class.
2127
+
1986
2128
  SQL narrows to the rows that can possibly win, and Python still decides. The
1987
2129
  partition is the RAW spelling of every column the interpretation reads
1988
2130
  (root, limit key, slot, window minutes, limit id and name, observed model,
@@ -2106,6 +2248,8 @@ def load_codex_quota_observations(
2106
2248
  "canonical_resets_at_utc" if has_anchor
2107
2249
  else "NULL AS canonical_resets_at_utc"
2108
2250
  )
2251
+ # #901: one probe for the summary and the two plan-pin indexes.
2252
+ read_objects = _codex_quota_read_objects(conn)
2109
2253
  # Public #5: the model is read from THIS table alone. There used to be a
2110
2254
  # COALESCE onto the nearest preceding `codex_session_entries.model` at
2111
2255
  # or before the snapshot's byte offset, for rows written before the
@@ -2129,9 +2273,26 @@ def load_codex_quota_observations(
2129
2273
  model_expr=model_expr, account_expr=account_expr,
2130
2274
  anchor_expr=anchor_expr,
2131
2275
  )
2276
+ # #901 W3 (spec §5.1, T1/T3): pin the plan. Unpinned, the planner sorts
2277
+ # an unbounded or multi-root load through a temp b-tree via the
2278
+ # `source` autoindex, and the load-order index hijacks the projector's
2279
+ # per-group shards into a whole-root walk (both measured). A bounded
2280
+ # `max_rows` read keeps its plan: its sorter holds at most `max_rows`
2281
+ # rows. Each pin is applied only when its index exists, so a store
2282
+ # without them keeps today's SQL.
2283
+ if group_filter is not None:
2284
+ table_hint = (
2285
+ " INDEXED BY idx_qws_physical_group"
2286
+ if "idx_qws_physical_group" in read_objects else "")
2287
+ elif physical_signatures is None and max_rows is not None:
2288
+ table_hint = ""
2289
+ else:
2290
+ table_hint = (
2291
+ " INDEXED BY idx_qws_codex_load_order"
2292
+ if "idx_qws_codex_load_order" in read_objects else "")
2132
2293
  sql = f"""
2133
2294
  SELECT {select_list}
2134
- FROM quota_window_snapshots
2295
+ FROM quota_window_snapshots{table_hint}
2135
2296
  WHERE source='codex' AND source_root_key IS NOT NULL
2136
2297
  """
2137
2298
  params: list[object] = []
@@ -2271,9 +2432,16 @@ def load_codex_quota_observations(
2271
2432
  shard_sql = f"{head}{clause}{order_by}"
2272
2433
  for group in group_filter:
2273
2434
  shards.append((shard_sql, (*params, *group)))
2435
+ if latest_per_identity and _codex_quota_latest_summary_ready(
2436
+ read_objects):
2437
+ # #901 W1 (spec §5.1, T1): the maintained summary yields exactly
2438
+ # the rows the window query above selects, without sorting history.
2439
+ row_source = _codex_quota_latest_rows(conn, requested)
2440
+ else:
2441
+ row_source = _iter_shard_rows(conn, shards)
2274
2442
  result: list[QuotaObservation] = []
2275
2443
  signature_tuples: dict[str, dict[str, list[tuple[object, ...]]]] = {}
2276
- for row in _iter_shard_rows(conn, shards):
2444
+ for row in row_source:
2277
2445
  if not _codex_quota_required_text_present(
2278
2446
  row[name] for name in _CODEX_QUOTA_REQUIRED_TEXT
2279
2447
  ):
@@ -6596,6 +6596,16 @@ def _record_dashboard_activity(provider: str, transcript_path: str) -> None:
6596
6596
  pass
6597
6597
 
6598
6598
 
6599
+ def _finalize_wal_checkpoints() -> None:
6600
+ """The W9 finalizer before a detached worker's ``os._exit`` (#901 Q14)."""
6601
+ try:
6602
+ import _lib_wal_checkpoint
6603
+
6604
+ _lib_wal_checkpoint.finalize()
6605
+ except Exception: # noqa: BLE001 — a hook must exit 0 whatever happens
6606
+ pass
6607
+
6608
+
6599
6609
  def cmd_hook_tick(args: argparse.Namespace) -> int:
6600
6610
  """Per-fire hook runtime (Section 3 of onboarding spec).
6601
6611
 
@@ -6831,6 +6841,10 @@ def cmd_hook_tick(args: argparse.Namespace) -> int:
6831
6841
  # Forked child: skip Python's atexit / argparse / cleanup paths
6832
6842
  # (they may try to flush already-redirected stdio handles).
6833
6843
  if forked:
6844
+ # #901 W9 (Q14): `os._exit` skips `atexit`, so the checkpoint
6845
+ # policy's finalizer runs here: one PASSIVE per armed store, then
6846
+ # the keepers close (the last close drains the WAL).
6847
+ _finalize_wal_checkpoints()
6834
6848
  os._exit(0)
6835
6849
  return 0
6836
6850
  # --explain mapping (Section 3 of spec)
@@ -90,6 +90,13 @@ def _fingerprint(value) -> str:
90
90
  return "sha256:" + hashlib.sha256(_canonical_bytes(value)).hexdigest()
91
91
 
92
92
 
93
+ #: #874: the only tables the cache contract names. Inspecting any other table
94
+ #: is not the planner's business, and on a virtual table whose module this
95
+ #: SQLite build lacks (a legacy FTS5 index on a build without FTS5) even
96
+ #: `PRAGMA table_info` raises before the contract could type the gap.
97
+ _CONTRACT_TABLES = ("session_entries", "session_files", "codex_session_entries")
98
+
99
+
93
100
  def _cache_contract(cache_conn: sqlite3.Connection) -> dict[str, set[str]]:
94
101
  names = {
95
102
  row[0] for row in cache_conn.execute(
@@ -99,12 +106,18 @@ def _cache_contract(cache_conn: sqlite3.Connection) -> dict[str, set[str]]:
99
106
  name: {
100
107
  row[1] for row in cache_conn.execute(f"PRAGMA table_info({name})")
101
108
  }
102
- for name in names
109
+ for name in _CONTRACT_TABLES
110
+ if name in names
103
111
  }
104
112
 
105
113
 
106
114
  def _cache_fingerprint(cache_conn: sqlite3.Connection, *, include_codex=False) -> str:
107
- """Hash every cost-bearing Claude cache row and its project metadata."""
115
+ """Hash every cost-bearing Claude cache row and its project metadata.
116
+
117
+ The caller must have validated the cache contract first, including
118
+ ``validate_codex_cache_contract`` when ``include_codex`` is set (#874):
119
+ a missing column here is a raw sqlite error, not a typed data gap.
120
+ """
108
121
  entry_columns = (
109
122
  "source_path", "line_offset", "timestamp_utc", "model", "input_tokens",
110
123
  "output_tokens", "cache_create_tokens", "cache_read_tokens",
@@ -123,10 +136,6 @@ def _cache_fingerprint(cache_conn: sqlite3.Connection, *, include_codex=False) -
123
136
  ]
124
137
  basis = {"sessionEntries": entries, "sessionFiles": files}
125
138
  if include_codex:
126
- if "codex_session_entries" not in _cache_contract(cache_conn):
127
- raise _lib_rederive.RederiveDataGap(
128
- "missing cache.db table codex_session_entries"
129
- )
130
139
  basis["codexEntries"] = [
131
140
  list(row) for row in cache_conn.execute(
132
141
  "SELECT source_path,line_offset,timestamp_utc,session_id,model,"
@@ -1088,15 +1097,20 @@ def plan_claude_usage(
1088
1097
  + ",".join(report.unclassified_evt_kinds)
1089
1098
  + " op=" + ",".join(report.unclassified_op_kinds)
1090
1099
  )
1091
- _lib_rederive.validate_claude_cache_contract(_cache_contract(cache_conn))
1092
- raw_records = _rederivable_raw_records(records)
1093
- _validate_cache_rows(cache_conn, raw_records)
1100
+ tables = _cache_contract(cache_conn)
1101
+ _lib_rederive.validate_claude_cache_contract(tables)
1094
1102
  codex_pricing_records = any(
1095
1103
  (record.get("payload") or {}).get("vendor") == "codex"
1096
1104
  and (record.get("payload") or {}).get("kind") == "budget"
1097
1105
  and (record.get("payload") or {}).get("_pricing")
1098
1106
  for record in records
1099
1107
  )
1108
+ if codex_pricing_records:
1109
+ # #874: the Codex fingerprint and re-pricing read the Codex cache only
1110
+ # here, so its contract is required only here, and before any reader.
1111
+ _lib_rederive.validate_codex_cache_contract(tables)
1112
+ raw_records = _rederivable_raw_records(records)
1113
+ _validate_cache_rows(cache_conn, raw_records)
1100
1114
  cache_fingerprint = _cache_fingerprint(
1101
1115
  cache_conn, include_codex=codex_pricing_records,
1102
1116
  )
@@ -153,18 +153,45 @@ _QUALIFIED_CODEX_ENTRIES_SQL = """
153
153
 
154
154
 
155
155
  def _qualified_codex_path_entries_sql(identity_count: int) -> str:
156
- """Return the qualified read constrained to physical cache identities."""
156
+ """Return the qualified read constrained to physical cache identities.
157
+
158
+ Unordered: ``_load_qualified_codex_rows`` orders its rows (#901
159
+ Amendment 19 T1). Seeking ``idx_codex_entries_root_path`` cannot also
160
+ deliver the window read's order, so the old ``ORDER BY`` sorted every
161
+ entry of the dirty rollouts through a temp b-tree, and one rollout file is
162
+ not a bound.
163
+ """
157
164
  predicates = " OR ".join(
158
165
  "(entries.source_root_key = ? AND entries.source_path = ?)"
159
166
  for _ in range(identity_count)
160
167
  )
161
- return _QUALIFIED_CODEX_ENTRIES_SQL.replace(
168
+ head, order_by, _tail = _QUALIFIED_CODEX_ENTRIES_SQL.partition(
169
+ " ORDER BY entries.timestamp_utc ASC")
170
+ if not order_by:
171
+ raise RuntimeError(
172
+ "_QUALIFIED_CODEX_ENTRIES_SQL no longer carries its ORDER BY")
173
+ return head.replace(
162
174
  "INDEXED BY idx_codex_entries_ts_root_conversation",
163
175
  "INDEXED BY idx_codex_entries_root_path",
164
- ).replace(
165
- " ORDER BY entries.timestamp_utc ASC",
166
- f" AND ({predicates})\n ORDER BY entries.timestamp_utc ASC",
167
- )
176
+ ) + f" AND ({predicates})\n"
177
+
178
+
179
+ def _qualified_codex_row_order(row) -> tuple:
180
+ """The window read's ``ORDER BY entries.timestamp_utc,
181
+ entries.source_root_key, entries.conversation_key, entries.id`` as a Python
182
+ key: NULL first, text by UTF-8 bytes (SQLite's BINARY collation). ``id``
183
+ is unique, so the order is total."""
184
+ def collate(value):
185
+ if value is None:
186
+ return (0, b"")
187
+ if isinstance(value, str):
188
+ return (2, value.encode("utf-8"))
189
+ if isinstance(value, (bytes, bytearray, memoryview)):
190
+ return (3, bytes(value))
191
+ return (1, value)
192
+
193
+ return (collate(row[0]), collate(row[3]), collate(row[4]),
194
+ collate(row[16]))
168
195
 
169
196
 
170
197
  #: The thread leg of the decode inventory (§4.3 item 1). ``{threads}`` is the
@@ -1184,6 +1211,32 @@ def _require_joined_metadata(row: sqlite3.Row) -> tuple[str, str]:
1184
1211
  return root_key, conversation_key
1185
1212
 
1186
1213
 
1214
+ def _load_qualified_codex_rows(
1215
+ conn: sqlite3.Connection, start: dt.datetime, end: dt.datetime,
1216
+ identities: "tuple[tuple[str, str], ...] | None",
1217
+ ) -> tuple:
1218
+ """The qualified accounting rows of ``[start, end)``, in
1219
+ ``(timestamp_utc, source_root_key, conversation_key, id)`` order: the whole
1220
+ window, or only the given dirty ``(root, path)`` identities.
1221
+
1222
+ The window read streams in that order from
1223
+ ``idx_codex_entries_ts_root_conversation``. The dirty-path read seeks
1224
+ ``idx_codex_entries_root_path`` instead and is ordered here, in Python, over
1225
+ the rows it returns whole anyway (#901 Amendment 19 T1); the rows keep the
1226
+ connection's row factory.
1227
+ """
1228
+ params: tuple[object, ...] = (
1229
+ start.astimezone(UTC).isoformat(), end.astimezone(UTC).isoformat(),
1230
+ *(value for identity in (identities or ()) for value in identity),
1231
+ )
1232
+ if identities is None:
1233
+ return tuple(conn.execute(_QUALIFIED_CODEX_ENTRIES_SQL, params))
1234
+ rows = conn.execute(
1235
+ _qualified_codex_path_entries_sql(len(identities)), params).fetchall()
1236
+ rows.sort(key=_qualified_codex_row_order)
1237
+ return tuple(rows)
1238
+
1239
+
1187
1240
  def load_qualified_codex_entries(
1188
1241
  start: dt.datetime,
1189
1242
  end: dt.datetime,
@@ -1241,15 +1294,7 @@ def load_qualified_codex_entries(
1241
1294
  if stats.lock_contended:
1242
1295
  raise QualifiedMetadataUnavailable("Codex qualified project metadata is unavailable")
1243
1296
  conn.row_factory = sqlite3.Row
1244
- sql = (
1245
- _QUALIFIED_CODEX_ENTRIES_SQL if identities is None
1246
- else _qualified_codex_path_entries_sql(len(identities))
1247
- )
1248
- params: tuple[object, ...] = (
1249
- start.astimezone(UTC).isoformat(), end.astimezone(UTC).isoformat(),
1250
- *(value for identity in (identities or ()) for value in identity),
1251
- )
1252
- rows = tuple(conn.execute(sql, params))
1297
+ rows = _load_qualified_codex_rows(conn, start, end, identities)
1253
1298
  # §4.3: one inventory read per build. The caller supplies the build's
1254
1299
  # own inventory when it has one; its alias leg is unbounded, which is a
1255
1300
  # SUPERSET of the `paths=identities` leg this read would have loaded,