cctally 1.97.0 → 1.99.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.
@@ -646,6 +646,175 @@ def _codex_quota_verify_activity_24h(*, now_utc: "dt.datetime") -> dict:
646
646
  return counts
647
647
 
648
648
 
649
+ #: How much journal tail the `accounts.codex_window_attribution` leg will read
650
+ #: to decide whether an attribution op is sitting unconsumed. Past this, the
651
+ #: condition is left unreported: `doctor` is a read-only health report, not an
652
+ #: ingest, and the state it would have reported self-heals on the next sync.
653
+ _WINDOW_ATTRIBUTION_TAIL_BUDGET_BYTES = 8 * 1024 * 1024
654
+
655
+ #: The same discipline on the same leg's OTHER unbounded read. Every distinct
656
+ #: `(source_root_key, source_path)` in `codex_session_entries` costs a path
657
+ #: canonicalization and one or two indexed probes, and a store carrying an
658
+ #: assertion pays that on every `doctor` run. Past the cap the condition is
659
+ #: reported as NOT CHECKED rather than partially counted, because a partial
660
+ #: count of a whole-store condition reads exactly like a complete one.
661
+ _WINDOW_ATTRIBUTION_BASELINE_FILE_CAP = 5_000
662
+
663
+
664
+ def _unconsumed_window_attribution_ops(cursor, high_water) -> bool:
665
+ """Whether the journal tail after `cursor` holds an attribution op."""
666
+ import _cctally_journal
667
+ import _lib_journal
668
+
669
+ if (_lib_journal.segment_sort_key(str(cursor[0])), int(cursor[1])) >= (
670
+ _lib_journal.segment_sort_key(str(high_water[0])),
671
+ int(high_water[1])):
672
+ return False
673
+ kinds = (b'"codex_window_attribution"',
674
+ b'"codex_window_attribution_retract"')
675
+ scanned = 0
676
+ try:
677
+ for _segment, _offset, raw in _cctally_journal.iter_range(
678
+ cursor, high_water):
679
+ scanned += len(raw)
680
+ if scanned > _WINDOW_ATTRIBUTION_TAIL_BUDGET_BYTES:
681
+ return False
682
+ if any(kind in raw for kind in kinds):
683
+ return True
684
+ except Exception:
685
+ return False
686
+ return False
687
+
688
+
689
+ def _gather_codex_window_attribution_state() -> "dict | None":
690
+ """Read-only state for the ``accounts.codex_window_attribution`` leg (#500 §9).
691
+
692
+ ``None`` means "this store cannot be inspected" — no cache.db, or a cache
693
+ too old to carry the derived table — which the kernel renders as OK rather
694
+ than as a finding, exactly as every other pre-feature cache shape degrades.
695
+
696
+ Four conditions the spec names, plus one the Task-2 review added. The added
697
+ one is an OBSERVABILITY check, not a health claim in its own right:
698
+ ``_CodexFileBaselineResolver`` recovers a file identity by re-canonicalizing
699
+ the stored ``source_path``, so a rollout whose path spelling changed after
700
+ ingest reads as "no per-file decision" and is INDISTINGUISHABLE from a
701
+ genuine absence. Counting the files whose derived identity has neither an
702
+ incarnation row nor any account row is what makes that condition visible
703
+ instead of silent, and it is computed only when this store carries an
704
+ assertion at all — the restore path is the only thing it can affect.
705
+ """
706
+ if not _cctally_core.CACHE_DB_PATH.exists():
707
+ return None
708
+ try:
709
+ conn = sqlite3.connect(
710
+ f"file:{_cctally_core.CACHE_DB_PATH}?mode=ro", uri=True)
711
+ except sqlite3.Error:
712
+ return None
713
+ try:
714
+ import _cctally_cache
715
+ import _cctally_journal
716
+ import _cctally_quota
717
+ import _lib_codex_window_attribution as _wa
718
+ import _lib_journal
719
+
720
+ try:
721
+ active = _cctally_cache.load_active_window_attributions(conn)
722
+ retracted = _cctally_cache.load_active_window_attributions(
723
+ conn, retracted_only=True)
724
+ except sqlite3.DatabaseError:
725
+ return None
726
+ state = {
727
+ "active": len(active), "retracted": len(retracted),
728
+ "dormant": 0, "split": 0, "conflicting": 0, "model_scoped": 0,
729
+ "cursor_behind": False, "unrecoverable_baselines": 0,
730
+ "baselines_checked": True,
731
+ }
732
+ # The cursor check runs BEFORE the empty-table early return, and that
733
+ # ordering is the whole point of it: the state it exists to report is a
734
+ # journal holding attribution records the derived index has not
735
+ # consumed, which is exactly a store whose table is EMPTY. Returning
736
+ # early on an empty table would make the leg blind to the condition it
737
+ # names.
738
+ #
739
+ # The predicate is "an unconsumed attribution op EXISTS", never "the
740
+ # cursor is behind the journal high-water". The attribution replay
741
+ # cursor advances only inside a Codex sync while the high-water advances
742
+ # on ALL journal traffic, so the positional comparison reports every
743
+ # store that has appended anything since its last sync — measured
744
+ # against this feature's own test fixture, which appends two
745
+ # `account_observe` ops after its syncs and would report a stale index
746
+ # with no attribution record in existence. It is the same trap §8.3
747
+ # names for the projection certificate.
748
+ #
749
+ # A cursor of `None` reads as "not behind": it means no replay has ever
750
+ # run, which is true of every store until its first Codex sync after
751
+ # this feature shipped, and that sync replays from byte zero.
752
+ #
753
+ # The tail scan is BUDGETED. On a store whose Codex sync has not run in
754
+ # a long time the tail is the whole journal — 1.7 GB on the maintainer's
755
+ # store — and `doctor` must not read that. Past the budget the condition
756
+ # is left unreported rather than guessed at, because it self-heals on
757
+ # the next sync either way.
758
+ cursor = _cctally_cache.load_codex_window_attribution_cursor(conn)
759
+ high_water = _cctally_journal.journal_high_water()
760
+ if cursor is not None and high_water is not None:
761
+ state["cursor_behind"] = _unconsumed_window_attribution_ops(
762
+ cursor, high_water)
763
+ if not active and not retracted:
764
+ return state
765
+
766
+ resolutions, _ownership = _cctally_quota.resolve_codex_window_attributions(
767
+ conn)
768
+ for resolution in resolutions:
769
+ if resolution.outcome == _wa.DORMANT:
770
+ state["dormant"] += 1
771
+ elif resolution.outcome == _wa.SPLIT:
772
+ state["split"] += 1
773
+ elif resolution.outcome in (_wa.SUPPRESSED_NATIVE,
774
+ _wa.SUPPRESSED_CONFLICT):
775
+ state["conflicting"] += 1
776
+ elif resolution.outcome == _wa.SUPPRESSED_MODEL_SCOPED:
777
+ state["model_scoped"] += 1
778
+
779
+ try:
780
+ files = conn.execute(
781
+ "SELECT DISTINCT source_root_key, source_path "
782
+ " FROM codex_session_entries "
783
+ " WHERE source_root_key IS NOT NULL AND source_path IS NOT NULL"
784
+ " LIMIT ?", (_WINDOW_ATTRIBUTION_BASELINE_FILE_CAP + 1,)
785
+ ).fetchall()
786
+ except sqlite3.DatabaseError:
787
+ files = []
788
+ if len(files) > _WINDOW_ATTRIBUTION_BASELINE_FILE_CAP:
789
+ # Declined whole rather than truncated: see the cap's own note.
790
+ state["baselines_checked"] = False
791
+ files = []
792
+ for root_key, source_path in files:
793
+ try:
794
+ identity = _cctally_cache.codex_file_key_for_entry_path(
795
+ str(root_key), str(source_path))
796
+ except (ValueError, OSError):
797
+ state["unrecoverable_baselines"] += 1
798
+ continue
799
+ try:
800
+ known = conn.execute(
801
+ "SELECT 1 FROM codex_file_incarnations "
802
+ " WHERE file_identity = ? LIMIT 1", (identity,)
803
+ ).fetchone() or conn.execute(
804
+ "SELECT 1 FROM codex_file_accounts "
805
+ " WHERE file_identity = ? LIMIT 1", (identity,)
806
+ ).fetchone()
807
+ except sqlite3.DatabaseError:
808
+ known = None
809
+ if not known:
810
+ state["unrecoverable_baselines"] += 1
811
+ return state
812
+ except Exception:
813
+ return None
814
+ finally:
815
+ conn.close()
816
+
817
+
649
818
  def _gather_accounts_state(now_utc: "dt.datetime") -> dict:
650
819
  """Best-effort account-attribution state for the doctor `accounts.*` legs
651
820
  (#341). Never raises: identity + registry reads are read-only and each guard
@@ -1311,8 +1480,19 @@ def _doctor_gather_state_impl(
1311
1480
  # inspector supplies the exact owned-hook state without exposing paths.
1312
1481
  codex_quota_windows: list[dict] = []
1313
1482
  try:
1483
+ # #566 §5.1 item 5: the population is unchanged — all history, every
1484
+ # root, no row cap — but the read returns each identity's latest
1485
+ # physical capture instead of every retained row, because that is the
1486
+ # only thing this probe consumes. On the maintainer's store the former
1487
+ # shape interpreted 266,337 rows to answer a question about 608 windows
1488
+ # and cost about 2.7s of every dashboard build. Nothing here may bound
1489
+ # the range: doing so would drop old and inactive-root identities and
1490
+ # silently change `window_count`, the responsible identity and the
1491
+ # WARN/OK verdict.
1314
1492
  observations = (
1315
- c._cctally_quota.load_codex_quota_observations()
1493
+ c._cctally_quota.load_codex_quota_observations(
1494
+ latest_per_identity=True,
1495
+ )
1316
1496
  if _cache_probe_allowed
1317
1497
  else ()
1318
1498
  )
@@ -1995,6 +2175,9 @@ def _doctor_gather_state_impl(
1995
2175
  )
1996
2176
  accounts_state = _gather_accounts_state(now_utc)
1997
2177
  accounts_state["codex_null_reset_anchors"] = codex_null_reset_anchors
2178
+ accounts_state["codex_window_attribution"] = (
2179
+ _gather_codex_window_attribution_state()
2180
+ if _cache_probe_allowed else None)
1998
2181
 
1999
2182
  return _lib_doctor.DoctorState(
2000
2183
  symlink_state=symlink_state,