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_core.py
CHANGED
|
@@ -888,6 +888,58 @@ def note_stats_maintenance_released() -> None:
|
|
|
888
888
|
_STATS_MAINTENANCE_HELD.set(max(0, _STATS_MAINTENANCE_HELD.get() - 1))
|
|
889
889
|
|
|
890
890
|
|
|
891
|
+
# === #500 §8.1 ordered-partial-release apply lock set =====================
|
|
892
|
+
#
|
|
893
|
+
# `_cctally_rederive.codex_attribution_apply_locks` takes the two cache writer
|
|
894
|
+
# flocks INSIDE the stats maintenance and ingest locks, and the repository
|
|
895
|
+
# lock-order law requires every cache write to be committed and unlocked before
|
|
896
|
+
# the stats transaction opens. The ordered partial release is what satisfies
|
|
897
|
+
# that law; this counter is what MAKES it a law rather than a convention, by
|
|
898
|
+
# letting `_run_stats_ingest_once(locks_held=True)` refuse while they are still
|
|
899
|
+
# held instead of quietly running the stats transaction underneath them.
|
|
900
|
+
#
|
|
901
|
+
# A ContextVar like the two above, but NOT for their reason, and the difference
|
|
902
|
+
# is worth stating because copying their rationale here inverts it. Those two
|
|
903
|
+
# are AUTHORIZATION counters: a global would let one sanctioned context
|
|
904
|
+
# authorize an unsanctioned one, so per-context isolation is what fails safe.
|
|
905
|
+
# This one is a REFUSAL guard, and for a refusal per-context isolation fails
|
|
906
|
+
# OPEN — a context that cannot see the hold does not refuse.
|
|
907
|
+
#
|
|
908
|
+
# What the ContextVar buys is therefore narrower and still worth having: the
|
|
909
|
+
# guard reports the state of the context that is about to open the stats
|
|
910
|
+
# transaction, so it can never be silenced by an unrelated dashboard thread that
|
|
911
|
+
# happens to hold the cache flocks for its own read, and it can never be left
|
|
912
|
+
# armed by one. It is a guard against the apply sequence's own steps running out
|
|
913
|
+
# of order, not a process-wide interlock.
|
|
914
|
+
#
|
|
915
|
+
# The cost of that choice: the guard is ADVISORY across a context boundary. A
|
|
916
|
+
# bare `os.fork()` inherits the value by copy and a thread spawned inside the
|
|
917
|
+
# `with` block starts from a copy of the spawning context, so neither child sees
|
|
918
|
+
# a later release, and a thread created BEFORE the acquisition never sees the
|
|
919
|
+
# hold at all. `codex_attribution_apply_locks` therefore forbids both inside its
|
|
920
|
+
# block; the flocks themselves remain the real mutual exclusion.
|
|
921
|
+
|
|
922
|
+
_ATTRIBUTION_APPLY_CACHE_FLOCKS_HELD = contextvars.ContextVar(
|
|
923
|
+
"cctally_attribution_apply_cache_flocks_held", default=0
|
|
924
|
+
)
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
def holds_attribution_apply_cache_flocks() -> bool:
|
|
928
|
+
"""True while this context still holds the #500 apply set's cache flocks."""
|
|
929
|
+
return _ATTRIBUTION_APPLY_CACHE_FLOCKS_HELD.get() > 0
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
def note_attribution_apply_cache_flocks_acquired() -> None:
|
|
933
|
+
_ATTRIBUTION_APPLY_CACHE_FLOCKS_HELD.set(
|
|
934
|
+
_ATTRIBUTION_APPLY_CACHE_FLOCKS_HELD.get() + 1)
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
def note_attribution_apply_cache_flocks_released() -> None:
|
|
938
|
+
"""Clamped at zero, for the reason the maintenance twin above states."""
|
|
939
|
+
_ATTRIBUTION_APPLY_CACHE_FLOCKS_HELD.set(
|
|
940
|
+
max(0, _ATTRIBUTION_APPLY_CACHE_FLOCKS_HELD.get() - 1))
|
|
941
|
+
|
|
942
|
+
|
|
891
943
|
# === Alerts validation cluster ======================================
|
|
892
944
|
|
|
893
945
|
|