cctally 1.91.0 → 1.92.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 (37) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/bin/_cctally_cache.py +863 -74
  3. package/bin/_cctally_config.py +57 -0
  4. package/bin/_cctally_core.py +39 -8
  5. package/bin/_cctally_dashboard.py +146 -5
  6. package/bin/_cctally_dashboard_conversation.py +164 -18
  7. package/bin/_cctally_dashboard_envelope.py +2 -0
  8. package/bin/_cctally_db.py +372 -10
  9. package/bin/_cctally_doctor.py +18 -1
  10. package/bin/_cctally_journal.py +535 -13
  11. package/bin/_cctally_journal_repair.py +6 -0
  12. package/bin/_cctally_parser.py +6 -0
  13. package/bin/_cctally_quota.py +171 -55
  14. package/bin/_cctally_record.py +13 -1
  15. package/bin/_cctally_rederive.py +4 -0
  16. package/bin/_cctally_store.py +311 -6
  17. package/bin/_cctally_transcript.py +32 -2
  18. package/bin/_lib_cache_report.py +8 -3
  19. package/bin/_lib_codex_conversation.py +851 -81
  20. package/bin/_lib_codex_conversation_query.py +2005 -95
  21. package/bin/_lib_codex_find_projection.py +370 -0
  22. package/bin/_lib_codex_harness_preamble.py +176 -0
  23. package/bin/_lib_codex_hooks.py +5 -3
  24. package/bin/_lib_codex_js_scan.py +254 -0
  25. package/bin/_lib_codex_landmarks.py +309 -0
  26. package/bin/_lib_codex_title_clean.py +116 -0
  27. package/bin/_lib_conversation_dispatch.py +153 -21
  28. package/bin/_lib_conversation_watch.py +4 -2
  29. package/bin/_lib_doctor.py +64 -0
  30. package/bin/_lib_quota_alert_axes.py +31 -34
  31. package/bin/_lib_stats_damage.py +523 -0
  32. package/bin/cctally +5 -0
  33. package/dashboard/static/assets/index-BEzzJtUd.js +97 -0
  34. package/dashboard/static/assets/{index-Dwirao3Y.css → index-DnWdv8um.css} +1 -1
  35. package/dashboard/static/dashboard.html +2 -2
  36. package/package.json +7 -1
  37. package/dashboard/static/assets/index-CILAoEja.js +0 -90
@@ -332,6 +332,9 @@ def _apply(requested, initial_preview):
332
332
  try:
333
333
  _call_rebuild_error_hook()
334
334
  rebuild = _journal.rebuild_stats_index(
335
+ context=_journal.RebuildContext(
336
+ trigger="journal-repair-acknowledge"
337
+ ),
335
338
  high_water=audit_high_water,
336
339
  update_quota_cache=False,
337
340
  before_swap=lambda: _call_crash_hook(
@@ -377,6 +380,9 @@ def _apply(requested, initial_preview):
377
380
  try:
378
381
  _call_rebuild_error_hook()
379
382
  rebuild = _journal.rebuild_stats_index(
383
+ context=_journal.RebuildContext(
384
+ trigger="journal-repair-recovery"
385
+ ),
380
386
  high_water=recovery_high_water,
381
387
  update_quota_cache=False,
382
388
  before_swap=lambda: _call_crash_hook(
@@ -2799,6 +2799,9 @@ def _build_transcript_parser(subparsers, name, *, help_text, xref=None):
2799
2799
  help="Codex service tier for per-turn cost (default: auto). Applies only "
2800
2800
  "to Codex (v1.) conversations; an explicit value on any other ref "
2801
2801
  "is a usage error")
2802
+ t_export.add_argument(
2803
+ "--account", metavar="REF", default=None,
2804
+ help="Restrict the export to one account (label, email, or key prefix)")
2802
2805
  t_export.add_argument(
2803
2806
  "-o", "--output", metavar="PATH", default=None,
2804
2807
  help="Write to PATH instead of stdout (same exact bytes)")
@@ -2811,6 +2814,9 @@ def _build_transcript_parser(subparsers, name, *, help_text, xref=None):
2811
2814
  t_search.add_argument(
2812
2815
  "--source", choices=("claude", "codex"), default="claude",
2813
2816
  help="Which provider's conversations to search (default: claude)")
2817
+ t_search.add_argument(
2818
+ "--account", metavar="REF", default=None,
2819
+ help="Restrict search to one account (label, email, or key prefix)")
2814
2820
  t_search.add_argument(
2815
2821
  "--kind",
2816
2822
  choices=("all", "prompts", "assistant", "tools", "thinking",
@@ -333,7 +333,8 @@ def _ledger_state(stats_conn: sqlite3.Connection) -> dict | None:
333
333
  try:
334
334
  row = stats_conn.execute(
335
335
  "SELECT watermark_seq, interpretation_version, alerts_enabled, "
336
- " next_evaluation_at_utc, last_full_pass_at "
336
+ " next_evaluation_at_utc, last_full_pass_at, "
337
+ " next_evaluation_by_root_json "
337
338
  " FROM quota_projection_ledger_state WHERE source='codex'"
338
339
  ).fetchone()
339
340
  except sqlite3.Error:
@@ -341,17 +342,35 @@ def _ledger_state(stats_conn: sqlite3.Connection) -> dict | None:
341
342
  if row is None:
342
343
  return None
343
344
  try:
345
+ schedule_wire = json.loads(str(row[5]))
346
+ if not isinstance(schedule_wire, dict):
347
+ return None
348
+ schedule: dict[str, str] = {}
349
+ for root_key, captured_at in schedule_wire.items():
350
+ if not isinstance(root_key, str) or not root_key:
351
+ return None
352
+ parsed = _parse_utc(str(captured_at), "next_evaluation_by_root_json")
353
+ schedule[root_key] = _utc_iso(parsed)
354
+ scalar_boundary = None if row[3] is None else str(row[3])
355
+ if schedule:
356
+ if scalar_boundary is None:
357
+ return None
358
+ parsed_scalar = _utc_iso(_parse_utc(
359
+ scalar_boundary, "next_evaluation_at_utc"))
360
+ if parsed_scalar != min(schedule.values()):
361
+ return None
344
362
  return {
345
363
  "watermark": int(row[0]),
346
364
  "interpretation_version": int(row[1]),
347
365
  "alerts_enabled": (
348
366
  None if row[2] is None else bool(int(row[2]))),
349
367
  "next_evaluation_at": (
350
- None if row[3] is None else str(row[3])),
368
+ scalar_boundary),
351
369
  "last_full_pass_at": (
352
370
  None if row[4] is None else str(row[4])),
371
+ "next_evaluation_by_root": schedule,
353
372
  }
354
- except (TypeError, ValueError):
373
+ except (json.JSONDecodeError, TypeError, ValueError):
355
374
  return None
356
375
 
357
376
 
@@ -514,6 +533,7 @@ def _store_ledger_state(
514
533
  conn: sqlite3.Connection, *, watermark: int,
515
534
  alerts_enabled: "bool | None", next_evaluation_at: "str | None",
516
535
  last_full_pass_at: "str | None",
536
+ next_evaluation_by_root: Mapping[str, str],
517
537
  ) -> None:
518
538
  """Stamp the consumed range, the non-dirtiness alert axes and the deadline.
519
539
 
@@ -524,18 +544,23 @@ def _store_ledger_state(
524
544
  conn.execute(
525
545
  """INSERT INTO quota_projection_ledger_state
526
546
  (source, watermark_seq, interpretation_version, alerts_enabled,
527
- next_evaluation_at_utc, last_full_pass_at)
528
- VALUES ('codex',?,?,?,?,?)
547
+ next_evaluation_at_utc, last_full_pass_at,
548
+ next_evaluation_by_root_json)
549
+ VALUES ('codex',?,?,?,?,?,?)
529
550
  ON CONFLICT(source) DO UPDATE SET
530
551
  watermark_seq=excluded.watermark_seq,
531
552
  interpretation_version=excluded.interpretation_version,
532
553
  alerts_enabled=excluded.alerts_enabled,
533
554
  next_evaluation_at_utc=excluded.next_evaluation_at_utc,
534
- last_full_pass_at=excluded.last_full_pass_at""",
555
+ last_full_pass_at=excluded.last_full_pass_at,
556
+ next_evaluation_by_root_json=excluded.next_evaluation_by_root_json""",
535
557
  (
536
558
  int(watermark), _CODEX_QUOTA_INTERPRETATION_VERSION,
537
559
  None if alerts_enabled is None else int(bool(alerts_enabled)),
538
560
  next_evaluation_at, last_full_pass_at,
561
+ json.dumps(
562
+ dict(sorted(next_evaluation_by_root.items())),
563
+ sort_keys=True, separators=(",", ":")),
539
564
  ),
540
565
  )
541
566
 
@@ -639,7 +664,7 @@ def _armed_identities(
639
664
  def _resolve_alert_scope(
640
665
  stats_conn: sqlite3.Connection, *, ledger_scope: str, now: dt.datetime,
641
666
  ledger_state: "dict | None", global_enabled: bool, quota_enabled: bool,
642
- rules, config, defer_scheduled: bool = False,
667
+ rules, config, eligible_roots: set[str], defer_scheduled: bool = False,
643
668
  ) -> _axes.AlertDirtyScope:
644
669
  """Feed the five-axis kernel from stats.db and the resolved configuration."""
645
670
  armed = _armed_identities(stats_conn)
@@ -657,12 +682,22 @@ def _resolve_alert_scope(
657
682
  quota_enabled=quota_enabled,
658
683
  )
659
684
  boundary = None
685
+ scheduled_roots: "frozenset[str] | None" = None
660
686
  if ledger_state is not None and ledger_state["next_evaluation_at"]:
661
687
  try:
662
688
  boundary = _parse_utc(
663
689
  ledger_state["next_evaluation_at"], "next_evaluation_at_utc")
664
690
  except (TypeError, ValueError):
665
691
  boundary = None
692
+ if ledger_state is not None:
693
+ schedule = ledger_state["next_evaluation_by_root"]
694
+ if schedule:
695
+ scheduled_roots = frozenset(
696
+ root_key for root_key, captured_at in schedule.items()
697
+ if root_key in eligible_roots
698
+ and now >= _parse_utc(
699
+ captured_at, "next_evaluation_by_root_json")
700
+ )
666
701
  return _axes.alert_dirty_scope(
667
702
  ledger_groups=(1,) if ledger_scope == _axes.SCOPE_GROUPS else (),
668
703
  stored_fingerprints=stored,
@@ -672,6 +707,7 @@ def _resolve_alert_scope(
672
707
  gate_after=bool(global_enabled and quota_enabled),
673
708
  now=now,
674
709
  next_evaluation_at=boundary,
710
+ scheduled_roots=scheduled_roots,
675
711
  defer_scheduled=defer_scheduled,
676
712
  )
677
713
 
@@ -681,7 +717,7 @@ def _blocks_missing_reverse_map(stats_conn: sqlite3.Connection) -> bool:
681
717
 
682
718
  A scoped sweep matches on ``physical_group_key``, so a NULL there would
683
719
  silently escape it and the stale block would survive indefinitely. The
684
- epoch rebuild (1005 for the column, 1006 as shipped) stamps every row, and
720
+ epoch rebuild (1005 introduced the column; current epoch 1007) stamps every row, and
685
721
  this is the guard that turns the
686
722
  one shape it cannot reach — a block written by an older binary against an
687
723
  already-current index — into a full pass rather than a missed one.
@@ -1909,7 +1945,10 @@ def _apply_quota_projection_rows(
1909
1945
  journal_terminal=None, holder=None, dirty_units=None,
1910
1946
  ledger_watermark=None, alerts_enabled=None,
1911
1947
  stored_next_evaluation_at=None, stored_last_full_pass_at=None,
1912
- stored_alerts_enabled=None, consume_alert_axes=True,
1948
+ stored_alerts_enabled=None, stored_next_evaluation_by_root=None,
1949
+ consume_alert_axes=True, reconcile_roots=None,
1950
+ schedule_evaluated_roots=frozenset(),
1951
+ prune_inactive_schedule=False,
1913
1952
  ):
1914
1953
  """Transaction-neutral quota projection apply (spec §5.3 "projection").
1915
1954
 
@@ -1924,17 +1963,16 @@ def _apply_quota_projection_rows(
1924
1963
  live caller; rebuild passes ``None``.
1925
1964
 
1926
1965
  ``dirty_units`` (public #5) is the BOUNDED pass: a set of serialized loading
1927
- units whose complete current membership ``observations`` carries. ``None`` is
1928
- the whole-history pass — every other caller, the rebuild, an
1929
- interpretation-version bump and ``force_full``. It changes exactly two
1966
+ units whose complete current membership ``observations`` carries.
1967
+ ``reconcile_roots`` distinguishes a complete root-scoped pass from the
1968
+ genuine whole-history ``None``/``None`` shape. It changes exactly two
1930
1969
  things: the sweep is scoped to those units, and the root signature is
1931
1970
  composed from the stored per-group digests rather than recomputed from
1932
1971
  scratch. Everything else runs identically, which is what keeps the two paths
1933
1972
  from drifting.
1934
1973
 
1935
- ``consume_alert_axes`` says whether this pass may ADVANCE the two
1936
- non-dirtiness alert axes it stores (``alerts_enabled``,
1937
- ``next_evaluation_at``) or must carry the stored values through untouched,
1974
+ ``consume_alert_axes`` says whether this pass may advance the global
1975
+ non-dirtiness axes or must carry the stored values through untouched,
1938
1976
  the way ``last_full_pass_at`` is carried through by a bounded pass. False
1939
1977
  means "this pass did not do the work those axes exist to trigger", and there
1940
1978
  are two such passes. A REPORTING-ONLY pass (no alert-eligible roots — the
@@ -1944,8 +1982,9 @@ def _apply_quota_projection_rows(
1944
1982
  stamping the gate would retire a delivery-gate ENABLE with no arming row and
1945
1983
  no ``suppressed_backfill`` written, and the axis could never re-fire because
1946
1984
  ``gate_before`` now reads True. A hook tick that DEFERRED axis 4 is the
1947
- other. ``stored_alerts_enabled`` / ``stored_next_evaluation_at`` are what it
1948
- carries through.
1985
+ other. Scheduled ownership is narrower: ``schedule_evaluated_roots`` names
1986
+ only complete root histories this alert-eligible pass may replace. Group
1987
+ passes and reporting-only passes can merge new minima but retire none.
1949
1988
 
1950
1989
  ``ledger_watermark`` is stamped INSIDE this transaction. That is deliberate:
1951
1990
  ``run_stats_ingest`` is the sole stats writer, so advancing it after the
@@ -1954,7 +1993,10 @@ def _apply_quota_projection_rows(
1954
1993
  group is idempotent.
1955
1994
  """
1956
1995
  historic_roots = _historic_root_keys(conn)
1957
- roots_to_reconcile = active_roots | historic_roots
1996
+ roots_to_reconcile = (
1997
+ active_roots | historic_roots
1998
+ if reconcile_roots is None else set(reconcile_roots)
1999
+ )
1958
2000
  if not roots_to_reconcile:
1959
2001
  return
1960
2002
  generation = secrets.token_hex(16)
@@ -1995,12 +2037,11 @@ def _apply_quota_projection_rows(
1995
2037
  # retires the right rows — and the DELETE below removes what it no longer
1996
2038
  # names. A root with no blocks at all still stamps one `unattributed` row,
1997
2039
  # byte-stable with the prior behaviour.
1998
- signatures: dict[str, str] = {}
1999
- for root_key in sorted(active_roots):
2040
+ roots_to_stamp = active_roots & roots_to_reconcile
2041
+ for root_key in sorted(roots_to_stamp):
2000
2042
  accounts = _root_accounts(conn, root_key) or {_lib_accounts.UNATTRIBUTED}
2001
2043
  root_signature = _ledger.compose_root_signature(
2002
2044
  _root_group_pairs(conn, root_key))
2003
- signatures[root_key] = root_signature
2004
2045
  placeholders = ",".join("?" for _ in accounts)
2005
2046
  conn.execute(
2006
2047
  "DELETE FROM quota_projection_state WHERE source_root_key=? "
@@ -2019,6 +2060,15 @@ def _apply_quota_projection_rows(
2019
2060
  completed_at_utc=excluded.completed_at_utc""",
2020
2061
  (root_key, account_key, generation, root_signature, now_iso),
2021
2062
  )
2063
+ # The cache-side certificate remains whole-store even when this pass was
2064
+ # root-scoped. Root signatures are composable from the stored group digests,
2065
+ # so collecting every active root here is O(groups), not O(observations),
2066
+ # and prevents a scoped pass from erasing untouched roots from the proof.
2067
+ signatures = {
2068
+ root_key: _ledger.compose_root_signature(
2069
+ _root_group_pairs(conn, root_key))
2070
+ for root_key in sorted(active_roots)
2071
+ }
2022
2072
  # The state row is stamped even when ``ledger_watermark`` is ``None`` — a
2023
2073
  # cache too old to carry the change log, where ``_ledger_max_seq`` cannot
2024
2074
  # report a sequence. Guarding this whole block on it left such a store with
@@ -2045,16 +2095,61 @@ def _apply_quota_projection_rows(
2045
2095
  # Recording a NEWLY seen future capture is safe from any pass, so that side
2046
2096
  # stays unconditional; only RETIRING a matured instant is gated, because
2047
2097
  # that is the half that claims an evaluation happened.
2048
- boundary = _axes.next_evaluation_boundary(
2049
- capture_times=[
2050
- observation.captured_at for observation in observations],
2051
- now=now, stored=stored_boundary, retain_due=not consume_alert_axes,
2098
+ stored_schedule = dict(stored_next_evaluation_by_root or {})
2099
+ # Retire only roots whose COMPLETE history this alert-eligible pass
2100
+ # evaluated. A group-bounded pass may discover an earlier future capture,
2101
+ # but it cannot prove the absence of another capture in a clean group, so
2102
+ # it only merges minima and never removes a stored root deadline.
2103
+ for root_key in schedule_evaluated_roots:
2104
+ stored_schedule.pop(root_key, None)
2105
+ future_by_root: dict[str, dt.datetime] = {}
2106
+ for observation in observations:
2107
+ if observation.captured_at <= now:
2108
+ continue
2109
+ root_key = observation.identity.source_root_key
2110
+ current = future_by_root.get(root_key)
2111
+ if current is None or observation.captured_at < current:
2112
+ future_by_root[root_key] = observation.captured_at
2113
+ for root_key, captured_at in future_by_root.items():
2114
+ current_wire = stored_schedule.get(root_key)
2115
+ current = (
2116
+ None if current_wire is None
2117
+ else _parse_utc(current_wire, "next_evaluation_by_root_json")
2118
+ )
2119
+ if root_key in schedule_evaluated_roots or current is None or captured_at < current:
2120
+ stored_schedule[root_key] = _utc_iso(captured_at)
2121
+ # Inactive roots have no lifecycle owner and cannot dispatch. Keeping their
2122
+ # deadlines would create an unretirable scheduled axis.
2123
+ if prune_inactive_schedule:
2124
+ stored_schedule = {
2125
+ root_key: captured_at
2126
+ for root_key, captured_at in stored_schedule.items()
2127
+ if root_key in active_roots
2128
+ }
2129
+ schedule_boundary = min(stored_schedule.values(), default=None)
2130
+ # A scalar-only boundary is a legacy/fail-safe state with unknown ownership.
2131
+ # Keep the prior behavior until a qualifying whole-history pass can retire
2132
+ # it; epoch-1007 indexes normally never enter this branch.
2133
+ legacy_boundary = stored_boundary if not stored_next_evaluation_by_root else None
2134
+ boundary = (
2135
+ None if schedule_boundary is None
2136
+ else _parse_utc(schedule_boundary, "next_evaluation_by_root_json")
2052
2137
  )
2138
+ if legacy_boundary is not None:
2139
+ retained_legacy = _axes.next_evaluation_boundary(
2140
+ capture_times=(), now=now, stored=legacy_boundary,
2141
+ retain_due=not consume_alert_axes,
2142
+ )
2143
+ if retained_legacy is not None and (
2144
+ boundary is None or retained_legacy < boundary
2145
+ ):
2146
+ boundary = retained_legacy
2053
2147
  _store_ledger_state(
2054
2148
  conn, watermark=0 if ledger_watermark is None else ledger_watermark,
2055
2149
  alerts_enabled=(
2056
2150
  alerts_enabled if consume_alert_axes else stored_alerts_enabled),
2057
2151
  next_evaluation_at=None if boundary is None else _utc_iso(boundary),
2152
+ next_evaluation_by_root=stored_schedule,
2058
2153
  # Spec §2: EVERY full pass stamps the verification deadline,
2059
2154
  # whatever triggered it — the interval itself, a rebuild, an
2060
2155
  # interpretation bump, `force_full`, or a dirty-unit burst
@@ -2064,7 +2159,9 @@ def _apply_quota_projection_rows(
2064
2159
  # untouched; overwriting it would let an install that never
2065
2160
  # bursts postpone verification forever.
2066
2161
  last_full_pass_at=(
2067
- now_iso if dirty_units is None else stored_last_full_pass_at),
2162
+ now_iso
2163
+ if dirty_units is None and reconcile_roots is None
2164
+ else stored_last_full_pass_at),
2068
2165
  )
2069
2166
  if sink is not None:
2070
2167
  # Set-then-dispatch: all claims committed with the cycle before the
@@ -2078,7 +2175,7 @@ def _apply_quota_projection_rows(
2078
2175
  milestones_upserted=sum(len(percent_milestones(b)) for b in blocks),
2079
2176
  blocks_orphaned=blocks_orphaned,
2080
2177
  milestones_orphaned=milestones_orphaned,
2081
- roots_stamped=len(active_roots),
2178
+ roots_stamped=len(roots_to_stamp),
2082
2179
  alerts_dispatched=len(queued),
2083
2180
  )
2084
2181
 
@@ -2130,6 +2227,7 @@ def rematerialize_quota_projection_for_rebuild(stats_conn, *, now=None) -> None:
2130
2227
  # value — `gate_before is not True` makes the next alert-eligible pass
2131
2228
  # widen and do the activation.
2132
2229
  consume_alert_axes=True,
2230
+ prune_inactive_schedule=True,
2133
2231
  )
2134
2232
 
2135
2233
 
@@ -2260,12 +2358,10 @@ def reconcile_codex_quota_projection(
2260
2358
  delay it. Both fire on a config change the user just made, so the cost is
2261
2359
  bounded and attributable.
2262
2360
 
2263
- Axis 4 is the exception and is DEFERRED under ``"defer"``. It fires on wall
2264
- clock, not on a config change: a capture stamped in the future (clock skew
2265
- across a sleep/resume, an NTP correction) sets the boundary, and the first
2266
- tick after wall time passes it would otherwise run the whole-history load
2267
- and apply on the blocking path with nothing to have predicted it. A BOUNDED
2268
- tick carries the stored boundary through untouched instead.
2361
+ Axis 4 fires on wall clock. Epoch 1007 persists its owning roots, so a due
2362
+ hook tick performs complete passes only for the matured roots. A legacy or
2363
+ inconsistent scalar-only boundary still defers under ``"defer"`` because
2364
+ its only honest fallback is whole history.
2269
2365
 
2270
2366
  A tick that widened to whole-history for axis 2 or 3 anyway does retire it,
2271
2367
  because it did look: at every observation of every active root. Withholding
@@ -2274,11 +2370,8 @@ def reconcile_codex_quota_projection(
2274
2370
  declining both left the same scope standing and repeated the whole-history
2275
2371
  pass inline on every subsequent tick.
2276
2372
 
2277
- What is NOT closed: a hook-only install with a steady enabled gate,
2278
- unchanged rules and a quiet ledger never produces a qualifying pass, so a
2279
- matured boundary is retained indefinitely. Bounded in cost (the tick stays
2280
- bounded and fast; the window is re-evaluated the moment it goes
2281
- ledger-dirty) and under-alerting in direction, but open.
2373
+ A quiet owned boundary is therefore consumed without broadening the hook to
2374
+ unrelated roots; the following quiet tick returns to the zero-group path.
2282
2375
  """
2283
2376
  if full_pass not in ("inline", "defer"):
2284
2377
  raise ValueError(
@@ -2341,6 +2434,7 @@ def reconcile_codex_quota_projection(
2341
2434
  global_enabled=global_alerts_enabled,
2342
2435
  quota_enabled=quota_alerts_enabled,
2343
2436
  rules=_rules, config=_config,
2437
+ eligible_roots=alert_eligible_roots,
2344
2438
  defer_scheduled=(full_pass == "defer"),
2345
2439
  )
2346
2440
  finally:
@@ -2430,6 +2524,10 @@ def reconcile_codex_quota_projection(
2430
2524
  stale_reverse_map=stale_reverse_map,
2431
2525
  verification_due=verification_due,
2432
2526
  )
2527
+ # ``None`` means the genuine whole-history path. A set means complete
2528
+ # histories for only those roots — axis 2 and epoch-1007 axis 4 can both
2529
+ # be satisfied without scanning unrelated roots.
2530
+ reconcile_roots: "set[str] | None" = None
2433
2531
  if dirty_units is None and full_pass == "defer" and not force_full:
2434
2532
  # The rule, for every OTHER route into a whole-history pass: an
2435
2533
  # absent or freshly rebuilt projector state, an interpretation-
@@ -2465,25 +2563,28 @@ def reconcile_codex_quota_projection(
2465
2563
  # just made, so the widening is bounded and expected, and it stays
2466
2564
  # inline even under `defer`.
2467
2565
  #
2468
- # Axis 4 does NOT, and the honest statement about it is "reachable from
2469
- # the hook, and therefore deferred" rather than "unreachable". Every
2470
- # tick that clears the 15s lifecycle throttle carries eligible roots, so
2471
- # this branch is live on all of them; a future-clocked capture (clock
2472
- # skew across a sleep/resume, an NTP correction) would then put one
2473
- # unannounced whole-history load and apply on the blocking path the
2474
- # first time wall time passed the boundary. `_resolve_alert_scope`
2475
- # records it as `REASON_SCHEDULED_DEFERRED` instead, and the boundary is
2476
- # carried through below so the next pass that CAN afford the widening
2477
- # still sees it.
2478
- if (
2479
- dirty_units is not None
2480
- and alert_scope is not None
2481
- and alert_scope.widens(_axes.SCOPE_GROUPS)
2482
- ):
2483
- dirty_units = None
2566
+ # Axis 4 is root-scoped when epoch-1007 ownership is available. A
2567
+ # scalar-only legacy boundary still records `REASON_SCHEDULED_DEFERRED`
2568
+ # under `full_pass="defer"`, because whole history remains its only
2569
+ # honest fallback.
2570
+ if dirty_units is not None and alert_scope is not None:
2571
+ if alert_scope.scope == _axes.SCOPE_ALL:
2572
+ dirty_units = None
2573
+ elif alert_scope.scope == _axes.SCOPE_ROOTS:
2574
+ # Reconcile every alert-invalidated root in full. If physical
2575
+ # ledger work landed on the same tick, promote its roots too so
2576
+ # the watermark can advance without skipping those mutations.
2577
+ reconcile_roots = set(alert_scope.roots)
2578
+ reconcile_roots.update(
2579
+ str(group[0]) for group in dirty_units["raw_groups"])
2580
+ dirty_units = None
2484
2581
  if dirty_units is None:
2485
2582
  observations = load_codex_quota_observations(
2486
- source_root_keys=active_roots, cache_conn=cache,
2583
+ source_root_keys=(
2584
+ active_roots if reconcile_roots is None
2585
+ else active_roots & reconcile_roots
2586
+ ),
2587
+ cache_conn=cache,
2487
2588
  )
2488
2589
  else:
2489
2590
  observations = load_codex_quota_observations(
@@ -2539,6 +2640,12 @@ def reconcile_codex_quota_projection(
2539
2640
  or alert_scope is None
2540
2641
  or _axes.REASON_SCHEDULED_DEFERRED not in alert_scope.reasons
2541
2642
  )
2643
+ complete_roots = (
2644
+ (active_roots if reconcile_roots is None else active_roots & reconcile_roots)
2645
+ if dirty_units is None else set()
2646
+ )
2647
+ schedule_evaluated_roots = frozenset(
2648
+ complete_roots & alert_eligible_roots)
2542
2649
 
2543
2650
  # ── Apply phase (Task 7 Item 3) ─────────────────────────────────────────
2544
2651
  # The stats.db writes route through the single-flight ingest cycle instead
@@ -2581,7 +2688,16 @@ def reconcile_codex_quota_projection(
2581
2688
  stored_alerts_enabled=(
2582
2689
  None if ledger_state is None
2583
2690
  else ledger_state["alerts_enabled"]),
2691
+ stored_next_evaluation_by_root=(
2692
+ {} if ledger_state is None
2693
+ else ledger_state["next_evaluation_by_root"]),
2584
2694
  consume_alert_axes=consume_alert_axes,
2695
+ reconcile_roots=reconcile_roots,
2696
+ schedule_evaluated_roots=schedule_evaluated_roots,
2697
+ prune_inactive_schedule=(
2698
+ source_root_keys is None
2699
+ and dirty_units is None
2700
+ and reconcile_roots is None),
2585
2701
  )
2586
2702
 
2587
2703
  import _cctally_journal as _jr
@@ -4915,8 +4915,20 @@ def _cmd_hook_tick_codex(
4915
4915
  {}, conn=ictx.conn, alert_sink=ictx.pending_alerts,
4916
4916
  raise_errors=True) or 0)
4917
4917
 
4918
- _jr_codex.run_stats_ingest(
4918
+ budget_ingest = _jr_codex.run_stats_ingest(
4919
4919
  mode="authoritative", codex_apply=_codex_budget_leg)
4920
+ if not budget_ingest.ran:
4921
+ # A detached quota-verification worker can own the ingest lock
4922
+ # while applying its projection. Authoritative ingest reports
4923
+ # that bounded timeout as `ran=False`; acknowledging the root
4924
+ # here would throttle away the skipped budget evaluation. Keep
4925
+ # the lifecycle markers due so the first uncontended tick
4926
+ # retries the forward-only budget crossing.
4927
+ log_outcome(
4928
+ sync="contended", result="noop", projection=projection,
4929
+ backlog=int(getattr(stats, "backlog_files", 0) or 0),
4930
+ )
4931
+ return 0
4920
4932
  budget_alerts = _budget_holder["n"]
4921
4933
  if getattr(stats, "deferred_reason", None) == "replay_pending":
4922
4934
  # public #5: the budgeted tick declined a byte-zero replay, and on
@@ -779,6 +779,7 @@ def apply_db_rederive(
779
779
  try:
780
780
  _call_crash_hook("after-batch-commit")
781
781
  result = _journal.rebuild_stats_index(
782
+ context=_journal.RebuildContext(trigger="rederive-apply"),
782
783
  high_water=batch_high_water,
783
784
  update_quota_cache=False,
784
785
  before_swap=lambda: _call_crash_hook(
@@ -810,6 +811,9 @@ def apply_db_rederive(
810
811
  )
811
812
  try:
812
813
  result = _journal.rebuild_stats_index(
814
+ context=_journal.RebuildContext(
815
+ trigger="rederive-recovery"
816
+ ),
813
817
  high_water=preview.latest_completed_high_water,
814
818
  update_quota_cache=False,
815
819
  before_swap=lambda: _call_crash_hook(