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.
- package/CHANGELOG.md +47 -0
- package/README.md +4 -4
- package/bin/_cctally_account.py +925 -0
- package/bin/_cctally_cache.py +829 -31
- package/bin/_cctally_core.py +52 -0
- package/bin/_cctally_dashboard.py +960 -55
- package/bin/_cctally_dashboard_envelope.py +43 -23
- package/bin/_cctally_dashboard_share.py +50 -1
- package/bin/_cctally_dashboard_sources.py +1580 -131
- package/bin/_cctally_db.py +810 -34
- package/bin/_cctally_doctor.py +184 -1
- package/bin/_cctally_journal.py +732 -76
- package/bin/_cctally_parser.py +65 -0
- package/bin/_cctally_quota.py +896 -17
- package/bin/_cctally_rederive.py +157 -5
- package/bin/_cctally_source_analytics.py +60 -6
- package/bin/_cctally_tui.py +811 -67
- package/bin/_lib_aggregators.py +117 -11
- package/bin/_lib_alert_axes.py +8 -3
- package/bin/_lib_budget.py +60 -0
- package/bin/_lib_codex_window_attribution.py +259 -0
- package/bin/_lib_dashboard_sources.py +488 -19
- package/bin/_lib_doctor.py +71 -0
- package/bin/_lib_journal.py +212 -0
- package/bin/_lib_jsonl.py +6 -0
- package/bin/_lib_rederive.py +8 -0
- package/bin/_lib_snapshot_cache.py +248 -7
- package/bin/_lib_source_analytics.py +20 -2
- package/bin/cctally +25 -0
- package/dashboard/static/assets/index-Bcbm-DNP.js +97 -0
- package/dashboard/static/assets/index-hJP4wlIO.css +1 -0
- package/dashboard/static/dashboard.html +2 -2
- package/package.json +2 -1
- package/dashboard/static/assets/index-BSESoPIK.css +0 -1
- package/dashboard/static/assets/index-DgsMz5hA.js +0 -97
package/bin/_cctally_doctor.py
CHANGED
|
@@ -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,
|