cctally 1.113.0 → 1.115.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 -6
- package/bin/_cctally_account.py +32 -8
- package/bin/_cctally_cache.py +6035 -722
- package/bin/_cctally_completion.py +1287 -0
- package/bin/_cctally_config.py +37 -0
- package/bin/_cctally_core.py +400 -2
- package/bin/_cctally_dashboard.py +98 -20
- package/bin/_cctally_dashboard_conversation.py +292 -23
- package/bin/_cctally_dashboard_envelope.py +16 -3
- package/bin/_cctally_dashboard_perf.py +128 -9
- package/bin/_cctally_dashboard_sources.py +285 -213
- package/bin/_cctally_db.py +1177 -23
- package/bin/_cctally_diagnosis_sources.py +22 -4
- package/bin/_cctally_doctor.py +572 -45
- package/bin/_cctally_five_hour.py +84 -33
- package/bin/_cctally_forecast.py +108 -15
- package/bin/_cctally_journal.py +3950 -116
- package/bin/_cctally_journal_repair.py +101 -12
- package/bin/_cctally_milestone_history.py +82 -9
- package/bin/_cctally_parser.py +42 -0
- package/bin/_cctally_percent_breakdown.py +3 -2
- package/bin/_cctally_project.py +8 -2
- package/bin/_cctally_quarantine.py +652 -0
- package/bin/_cctally_quota.py +227 -37
- package/bin/_cctally_quota_model.py +360 -83
- package/bin/_cctally_record.py +691 -149
- package/bin/_cctally_rederive.py +320 -41
- package/bin/_cctally_refresh.py +84 -13
- package/bin/_cctally_source_analytics.py +83 -17
- package/bin/_cctally_statusline.py +6154 -517
- package/bin/_cctally_store.py +367 -53
- package/bin/_cctally_transcript.py +8 -7
- package/bin/_cctally_tui.py +61 -56
- package/bin/_cctally_weekrefs.py +128 -21
- package/bin/_lib_account_binding.py +152 -0
- package/bin/_lib_account_protocol.py +386 -0
- package/bin/_lib_aggregators.py +4 -0
- package/bin/_lib_cache_coverage.py +13 -1
- package/bin/_lib_cache_report.py +35 -6
- package/bin/_lib_cache_writer_lock.py +19 -3
- package/bin/_lib_clone_copy.py +142 -0
- package/bin/_lib_codex_conversation.py +32 -0
- package/bin/_lib_codex_conversation_query.py +297 -49
- package/bin/_lib_codex_conversation_watch.py +59 -14
- package/bin/_lib_codex_drain_admission.py +214 -0
- package/bin/_lib_codex_metadata.py +18 -1
- package/bin/_lib_codex_publication_barrier.py +277 -0
- package/bin/_lib_codex_rollout_container.py +502 -0
- package/bin/_lib_conversation_dispatch.py +3 -0
- package/bin/_lib_conversation_query.py +106 -59
- package/bin/_lib_conversation_retention.py +1605 -640
- package/bin/_lib_cost_provenance.py +53 -2
- package/bin/_lib_detector_access_registry.py +3817 -0
- package/bin/_lib_doctor.py +714 -69
- package/bin/_lib_five_hour.py +126 -0
- package/bin/_lib_fmt.py +11 -0
- package/bin/_lib_ingest_frontier.py +414 -20
- package/bin/_lib_journal.py +141 -10
- package/bin/_lib_jsonl.py +5 -4
- package/bin/_lib_legacy_spool.py +126 -0
- package/bin/_lib_legacy_writer_evidence.py +136 -0
- package/bin/_lib_merge_gate.py +231 -86
- package/bin/_lib_meter_rate_change.py +10 -0
- package/bin/_lib_outcome_quarantine.py +690 -0
- package/bin/_lib_perf.py +27 -1
- package/bin/_lib_pricing.py +199 -9
- package/bin/_lib_pricing_check.py +20 -4
- package/bin/_lib_quarantine.py +154 -0
- package/bin/_lib_quota_calibration.py +53 -8
- package/bin/_lib_quota_copy.py +2 -1
- package/bin/_lib_quota_model.py +288 -46
- package/bin/_lib_reclaim_planner.py +792 -0
- package/bin/_lib_rederive.py +319 -82
- package/bin/_lib_segment_summary.py +35 -2
- package/bin/_lib_snapshot_cache.py +21 -0
- package/bin/_lib_sqlite_close.py +81 -0
- package/bin/_lib_statusline_candidates.py +292 -23
- package/bin/_lib_subscription_weeks.py +100 -17
- package/bin/_lib_tick_stats.py +217 -17
- package/bin/_lib_wal_checkpoint.py +1077 -0
- package/bin/_lib_write_budget.py +483 -0
- package/bin/_lib_write_io.py +703 -0
- package/bin/cctally +31 -1
- package/dashboard/static/assets/{ConversationsView-CEZ-BUyi.js → ConversationsView-Bc8LCOAZ.js} +1 -1
- package/dashboard/static/assets/{DoctorModal-CE1oH_Zy.js → DoctorModal-D--qVBwe.js} +1 -1
- package/dashboard/static/assets/ModalRoot-DLPgyAM_.js +1 -0
- package/dashboard/static/assets/{ProjectsDrillPanel-B3DZoCeD.js → ProjectsDrillPanel-z53a4v4M.js} +1 -1
- package/dashboard/static/assets/{SourceDetailModal-CHp9EgLP.js → SourceDetailModal-Bw0T8W-W.js} +1 -1
- package/dashboard/static/assets/{UpdateModal-5iKST6jo.js → UpdateModal-D5AKx4Yw.js} +1 -1
- package/dashboard/static/assets/index-BKITk1CT.css +1 -0
- package/dashboard/static/assets/{index-Cm0VTxJC.js → index-BXDOzSng.js} +3 -3
- package/dashboard/static/assets/{outlineNavigation-DkXGL8J6.js → outlineNavigation-JObY7cUY.js} +1 -1
- package/dashboard/static/dashboard.html +2 -2
- package/package.json +19 -1
- package/dashboard/static/assets/ModalRoot-D5Y8IFUG.js +0 -1
- package/dashboard/static/assets/index-Dt4tqf43.css +0 -1
|
@@ -86,7 +86,10 @@ from _lib_codex_pools import (
|
|
|
86
86
|
from _lib_codex_conversation import _display_title as _codex_display_title
|
|
87
87
|
from _lib_fmt import stable_sum
|
|
88
88
|
from _lib_aggregators import _aggregate_codex_buckets, codex_path_scope
|
|
89
|
-
from _lib_five_hour import
|
|
89
|
+
from _lib_five_hour import (
|
|
90
|
+
_FIVE_HOUR_JITTER_FLOOR_SECONDS,
|
|
91
|
+
latest_five_hour_reading,
|
|
92
|
+
)
|
|
90
93
|
from _lib_source_analytics import (
|
|
91
94
|
QualifiedCodexEntry,
|
|
92
95
|
assign_collision_safe_project_labels,
|
|
@@ -2865,23 +2868,57 @@ def _codex_cache_report_wire(
|
|
|
2865
2868
|
)
|
|
2866
2869
|
|
|
2867
2870
|
|
|
2868
|
-
#: Per-file terminal thread aliases,
|
|
2869
|
-
#:
|
|
2870
|
-
#:
|
|
2871
|
-
#:
|
|
2872
|
-
#: join
|
|
2873
|
-
#:
|
|
2871
|
+
#: Per-file terminal thread aliases, each with its file's first accounting
|
|
2872
|
+
#: instant.
|
|
2873
|
+
#:
|
|
2874
|
+
#: #901 W2 (spec §5.2, T2). The pre-#901 form LEFT JOINed every accounting row
|
|
2875
|
+
#: and GROUPed the join back to one row per file, which needed two temp b-trees
|
|
2876
|
+
#: over the joined history (168 MB of temp-file writes per Codex-active build on
|
|
2877
|
+
#: the measured store). ``codex_session_files.path`` is the primary key, so the
|
|
2878
|
+
#: grouping only ever collapsed one file's joined rows; a correlated MIN per
|
|
2879
|
+
#: file answers the same question with one covering seek of
|
|
2880
|
+
#: ``idx_codex_entries_root_path_time``, and ``idx_codex_files_alias_recent``
|
|
2881
|
+
#: (partial on the same filter, in the final order) removes the sorter. A file
|
|
2882
|
+
#: with no accounting row still yields NULL, and the root qualification is
|
|
2883
|
+
#: unchanged. Module-level so the plan tests assert THIS text rather than a
|
|
2884
|
+
#: copy that can drift from it.
|
|
2874
2885
|
_CODEX_FILE_ALIAS_SQL = (
|
|
2875
2886
|
"SELECT f.source_root_key, f.path, f.last_native_thread_id, "
|
|
2876
|
-
"f.last_session_id,
|
|
2887
|
+
"f.last_session_id, "
|
|
2888
|
+
"(SELECT MIN(e.timestamp_utc) FROM codex_session_entries AS e "
|
|
2889
|
+
"WHERE e.source_root_key=f.source_root_key AND e.source_path=f.path) "
|
|
2877
2890
|
"FROM codex_session_files AS f "
|
|
2878
|
-
"LEFT JOIN codex_session_entries AS e "
|
|
2879
|
-
"ON e.source_root_key=f.source_root_key AND e.source_path=f.path "
|
|
2880
2891
|
"WHERE f.last_native_thread_id IS NOT NULL AND f.last_native_thread_id != '' "
|
|
2881
|
-
"GROUP BY f.source_root_key, f.path, f.last_native_thread_id, f.last_session_id "
|
|
2882
2892
|
"ORDER BY f.last_ingested_at DESC, f.path DESC"
|
|
2883
2893
|
)
|
|
2884
2894
|
|
|
2895
|
+
#: Conversation threads with their accounting session and first accounting
|
|
2896
|
+
#: instant (#901 W2, spec §5.2, T2).
|
|
2897
|
+
#:
|
|
2898
|
+
#: Two INDEPENDENT correlated lookups replace the pre-#901 ``WITH accounting``
|
|
2899
|
+
#: CTE, which grouped every accounting row (materialized, then probed through
|
|
2900
|
+
#: an automatic index, then sorted). The accounting session is the row with the
|
|
2901
|
+
#: minimum ``id`` (``idx_codex_entries_root_path``, whose trailing rowid makes it
|
|
2902
|
+
#: a seek) and ``started_at`` is the minimum ``timestamp_utc``
|
|
2903
|
+
#: (``idx_codex_entries_root_path_time``); they need not be the same row, which
|
|
2904
|
+
#: is exactly what the CTE's two independent minima computed.
|
|
2905
|
+
#: ``idx_codex_threads_recent`` serves the final order. A thread with no
|
|
2906
|
+
#: accounting rows still yields NULL for both.
|
|
2907
|
+
_CODEX_CONVERSATION_METADATA_CORE_SQL = (
|
|
2908
|
+
"SELECT t.source_root_key, t.source_path, t.native_thread_id, "
|
|
2909
|
+
"(SELECT e.session_id FROM codex_session_entries AS e WHERE e.id = ("
|
|
2910
|
+
"SELECT MIN(f.id) FROM codex_session_entries AS f "
|
|
2911
|
+
"WHERE f.source_root_key=t.source_root_key AND f.source_path=t.source_path"
|
|
2912
|
+
")) AS accounting_session_id, "
|
|
2913
|
+
"CAST(t.cwd AS BLOB) AS cwd_blob, "
|
|
2914
|
+
"CAST(t.git_json AS BLOB) AS git_json_blob, "
|
|
2915
|
+
"(SELECT MIN(s.timestamp_utc) FROM codex_session_entries AS s "
|
|
2916
|
+
"WHERE s.source_root_key=t.source_root_key AND s.source_path=t.source_path"
|
|
2917
|
+
") AS started_at, t.last_seen_utc "
|
|
2918
|
+
"FROM codex_conversation_threads AS t "
|
|
2919
|
+
"ORDER BY t.last_seen_utc DESC, t.conversation_key DESC"
|
|
2920
|
+
)
|
|
2921
|
+
|
|
2885
2922
|
|
|
2886
2923
|
@dataclass(frozen=True)
|
|
2887
2924
|
class _CodexConversationMetadataRead:
|
|
@@ -3015,23 +3052,7 @@ def _codex_conversation_metadata(
|
|
|
3015
3052
|
metadata: dict[tuple[str, str], dict[str, object]] = {}
|
|
3016
3053
|
try:
|
|
3017
3054
|
core_rows = tuple(cache_conn.execute(
|
|
3018
|
-
|
|
3019
|
-
" SELECT source_root_key, source_path, MIN(id) AS first_id,"
|
|
3020
|
-
" MIN(timestamp_utc) AS started_at"
|
|
3021
|
-
" FROM codex_session_entries"
|
|
3022
|
-
" GROUP BY source_root_key, source_path"
|
|
3023
|
-
") "
|
|
3024
|
-
"SELECT t.source_root_key, t.source_path, t.native_thread_id, "
|
|
3025
|
-
"e.session_id AS accounting_session_id, "
|
|
3026
|
-
"CAST(t.cwd AS BLOB) AS cwd_blob, "
|
|
3027
|
-
"CAST(t.git_json AS BLOB) AS git_json_blob, "
|
|
3028
|
-
"a.started_at, t.last_seen_utc "
|
|
3029
|
-
"FROM codex_conversation_threads AS t "
|
|
3030
|
-
"LEFT JOIN accounting AS a "
|
|
3031
|
-
"ON a.source_root_key=t.source_root_key AND a.source_path=t.source_path "
|
|
3032
|
-
"LEFT JOIN codex_session_entries AS e ON e.id=a.first_id "
|
|
3033
|
-
"ORDER BY t.last_seen_utc DESC, t.conversation_key DESC"
|
|
3034
|
-
))
|
|
3055
|
+
_CODEX_CONVERSATION_METADATA_CORE_SQL))
|
|
3035
3056
|
from _cctally_cache import _codex_conversation_project_attribution
|
|
3036
3057
|
|
|
3037
3058
|
def _attribution(root_key, cwd_blob, git_json_blob):
|
|
@@ -4535,6 +4556,28 @@ _CODEX_PROJECT_LABEL_CACHE: dict[object, dict[str, object]] = _ObservedDict()
|
|
|
4535
4556
|
_CODEX_PROJECT_LABEL_ALGORITHM_VERSION = "collision-safe-labels-v1"
|
|
4536
4557
|
|
|
4537
4558
|
|
|
4559
|
+
def _codex_visible_population_name(
|
|
4560
|
+
signature: tuple | None,
|
|
4561
|
+
token: int | None,
|
|
4562
|
+
visible_start: dt.datetime,
|
|
4563
|
+
) -> tuple | None:
|
|
4564
|
+
"""#872: the exact in-process name of one VISIBLE Codex population.
|
|
4565
|
+
|
|
4566
|
+
The carrier signature alone repeats across a cleared state (its
|
|
4567
|
+
generation leg restarts at one, #857), and it names the CARRIER window,
|
|
4568
|
+
which a configured budget widens below the visible lower bound. The
|
|
4569
|
+
never-reused provenance token closes the first gap and the visible lower
|
|
4570
|
+
bound the second. `None` — no signature (no ledger) or no token (metadata
|
|
4571
|
+
incomplete) — names nothing, so it can be neither a hit nor a base.
|
|
4572
|
+
"""
|
|
4573
|
+
if signature is None or token is None:
|
|
4574
|
+
return None
|
|
4575
|
+
return (
|
|
4576
|
+
*signature, ("provenance", int(token)),
|
|
4577
|
+
("visible-start", visible_start),
|
|
4578
|
+
)
|
|
4579
|
+
|
|
4580
|
+
|
|
4538
4581
|
def _codex_project_label_signature(base: tuple | None) -> tuple | None:
|
|
4539
4582
|
"""Extend one accounting population name with the labelling algorithm's.
|
|
4540
4583
|
|
|
@@ -4554,6 +4597,9 @@ def _codex_project_label_signature(base: tuple | None) -> tuple | None:
|
|
|
4554
4597
|
def _codex_project_population_signature() -> tuple | None:
|
|
4555
4598
|
"""The identity the project and label caches compare instead of scanning.
|
|
4556
4599
|
|
|
4600
|
+
Test-facing since #872: a build names its population from its OWN capture
|
|
4601
|
+
(`_codex_visible_population_name`), never from this process-wide reading.
|
|
4602
|
+
|
|
4557
4603
|
`None` means the accounting carrier could not establish an identity, and
|
|
4558
4604
|
every consumer must then prove its hit the old way rather than assume one.
|
|
4559
4605
|
"""
|
|
@@ -4722,124 +4768,41 @@ def _cached_projects_wire(
|
|
|
4722
4768
|
semantic_signature: object,
|
|
4723
4769
|
population_signature: tuple | None = None,
|
|
4724
4770
|
) -> dict[str, object]:
|
|
4725
|
-
"""
|
|
4726
|
-
|
|
4727
|
-
The
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4771
|
+
"""Reuse the project wire only under a name that identifies the population.
|
|
4772
|
+
|
|
4773
|
+
The name is `(semantic_signature, population_signature)`, where the
|
|
4774
|
+
population half is the visible population's #872 name (provenance token
|
|
4775
|
+
and visible start included). An equal name therefore means an unchanged
|
|
4776
|
+
population, so a hit returns the retained value as it stands. A miss
|
|
4777
|
+
rebuilds through `_projects_wire`, whose label cache advances by the delta
|
|
4778
|
+
when G1 has kept the layer coherent. `None` names nothing: no reuse.
|
|
4779
|
+
|
|
4780
|
+
The former group splice applied a delta onto retained groups under a
|
|
4781
|
+
matching key and read labels from the label cache's state. With a sound
|
|
4782
|
+
name it was reachable only through a reused name or the signature-less
|
|
4783
|
+
pair-set key, and there it raised or double-counted (#872 D3).
|
|
4732
4784
|
"""
|
|
4733
4785
|
values = tuple(entries)
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
population_key: object = population_signature
|
|
4738
|
-
else:
|
|
4739
|
-
population_key = frozenset(
|
|
4740
|
-
(str(entry.project_key), str(entry.project_label))
|
|
4741
|
-
for entry in values
|
|
4742
|
-
)
|
|
4743
|
-
# `entries` is already the complete half-open population for the advancing
|
|
4744
|
-
# upper bound. A moving wall clock is therefore not an aggregation
|
|
4745
|
-
# semantic: `build_cached_codex_accounting` emits newly-visible rows in the
|
|
4746
|
-
# delta when its upper bound advances. Keeping `accounting_end` here made
|
|
4747
|
-
# every live dirty tick discard every project group before that delta could
|
|
4748
|
-
# be spliced.
|
|
4749
|
-
signature = (semantic_signature, population_key, context.range_start)
|
|
4750
|
-
state = _CODEX_PROJECT_WIRE_CACHE.get(cache_key)
|
|
4751
|
-
if state is None or state[0] != signature:
|
|
4752
|
-
value = _projects_wire(
|
|
4786
|
+
if population_signature is None:
|
|
4787
|
+
_CODEX_PROJECT_WIRE_CACHE.pop(cache_key, None)
|
|
4788
|
+
return _projects_wire(
|
|
4753
4789
|
context, quota_observations, values,
|
|
4754
4790
|
accounting_end=accounting_end, cache_key=cache_key,
|
|
4755
|
-
population_signature=
|
|
4791
|
+
population_signature=None,
|
|
4756
4792
|
changed_old=changed_old, changed_new=changed_new,
|
|
4757
4793
|
)
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
).append(entry)
|
|
4763
|
-
_CODEX_PROJECT_WIRE_CACHE[cache_key] = (
|
|
4764
|
-
signature, value,
|
|
4765
|
-
{key: tuple(group) for key, group in groups.items()},
|
|
4766
|
-
)
|
|
4767
|
-
return value
|
|
4768
|
-
|
|
4769
|
-
_CODEX_PROJECT_WIRE_CACHE.touch(cache_key)
|
|
4770
|
-
affected = {
|
|
4771
|
-
(str(entry.source_root_key), str(entry.project_key))
|
|
4772
|
-
for entry in (*changed_old, *changed_new)
|
|
4773
|
-
}
|
|
4774
|
-
if not affected:
|
|
4794
|
+
signature = (semantic_signature, population_signature)
|
|
4795
|
+
state = _CODEX_PROJECT_WIRE_CACHE.get(cache_key)
|
|
4796
|
+
if state is not None and state[0] == signature:
|
|
4797
|
+
_CODEX_PROJECT_WIRE_CACHE.touch(cache_key)
|
|
4775
4798
|
return state[1]
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
}
|
|
4782
|
-
groups = dict(state[2])
|
|
4783
|
-
for key in affected:
|
|
4784
|
-
groups[key] = tuple(
|
|
4785
|
-
entry for entry in groups.get(key, ())
|
|
4786
|
-
if int(getattr(entry, "cache_entry_id", 0) or 0) not in old_ids
|
|
4787
|
-
)
|
|
4788
|
-
for entry in changed_new:
|
|
4789
|
-
key = (str(entry.source_root_key), str(entry.project_key))
|
|
4790
|
-
groups[key] = (*groups.get(key, ()), entry)
|
|
4791
|
-
for key in affected:
|
|
4792
|
-
if groups.get(key):
|
|
4793
|
-
groups[key] = tuple(sorted(
|
|
4794
|
-
groups[key], key=_codex_incremental_entry_order,
|
|
4795
|
-
))
|
|
4796
|
-
else:
|
|
4797
|
-
groups.pop(key, None)
|
|
4798
|
-
partial_entries = tuple(
|
|
4799
|
-
replace(entry, display_label=labels[str(entry.project_key)])
|
|
4800
|
-
for key in sorted(affected) for entry in groups.get(key, ())
|
|
4801
|
-
)
|
|
4802
|
-
result = build_codex_project_result(
|
|
4803
|
-
partial_entries,
|
|
4804
|
-
range_start=context.range_start,
|
|
4805
|
-
range_end=accounting_end,
|
|
4806
|
-
as_of=context.now_utc,
|
|
4799
|
+
value = _projects_wire(
|
|
4800
|
+
context, quota_observations, values,
|
|
4801
|
+
accounting_end=accounting_end, cache_key=cache_key,
|
|
4802
|
+
population_signature=population_signature,
|
|
4803
|
+
changed_old=changed_old, changed_new=changed_new,
|
|
4807
4804
|
)
|
|
4808
|
-
|
|
4809
|
-
"key": dashboard_resource_key("project", "codex", row.project_key),
|
|
4810
|
-
"source": "codex",
|
|
4811
|
-
"label": row.display_label,
|
|
4812
|
-
"session_count": row.session_count,
|
|
4813
|
-
"first_seen": row.first_seen.astimezone(UTC).isoformat(),
|
|
4814
|
-
"last_seen": row.last_seen.astimezone(UTC).isoformat(),
|
|
4815
|
-
"cost_usd": row.totals.cost_usd,
|
|
4816
|
-
"input_tokens": row.totals.input_tokens,
|
|
4817
|
-
"cached_input_tokens": row.totals.cached_input_tokens,
|
|
4818
|
-
"output_tokens": row.totals.output_tokens,
|
|
4819
|
-
"reasoning_output_tokens": row.totals.reasoning_output_tokens,
|
|
4820
|
-
"total_tokens": row.totals.total_tokens,
|
|
4821
|
-
} for row in result.data.projects)
|
|
4822
|
-
affected_keys = {
|
|
4823
|
-
dashboard_resource_key("project", "codex", project_key)
|
|
4824
|
-
for _root_key, project_key in affected
|
|
4825
|
-
}
|
|
4826
|
-
rows = tuple(sorted(
|
|
4827
|
-
(
|
|
4828
|
-
*(row for row in state[1]["rows"] if row["key"] not in affected_keys),
|
|
4829
|
-
*partial_rows,
|
|
4830
|
-
),
|
|
4831
|
-
key=lambda row: (
|
|
4832
|
-
float(row["cost_usd"]), str(row["label"]), str(row["key"]),
|
|
4833
|
-
),
|
|
4834
|
-
reverse=True,
|
|
4835
|
-
))
|
|
4836
|
-
value = {
|
|
4837
|
-
"rows": rows,
|
|
4838
|
-
"total_cost_usd": stable_sum(
|
|
4839
|
-
float(row["cost_usd"]) for row in rows),
|
|
4840
|
-
"total_tokens": sum(int(row["total_tokens"]) for row in rows),
|
|
4841
|
-
}
|
|
4842
|
-
_CODEX_PROJECT_WIRE_CACHE[cache_key] = (signature, value, groups)
|
|
4805
|
+
_CODEX_PROJECT_WIRE_CACHE[cache_key] = (signature, value)
|
|
4843
4806
|
return value
|
|
4844
4807
|
|
|
4845
4808
|
|
|
@@ -5648,6 +5611,12 @@ def _codex_fold_visible_rows(
|
|
|
5648
5611
|
|
|
5649
5612
|
|
|
5650
5613
|
_CODEX_VISIBLE_POPULATION_MAX_BYTES = 128 * 1024 * 1024
|
|
5614
|
+
#: #872 (review F7): every per-account visible version comes from this
|
|
5615
|
+
#: allocator, which nothing rewinds. `_CODEX_ACCOUNT_CARD_TOTALS_CACHE` keys
|
|
5616
|
+
#: card totals on these versions, and the two caches are evicted
|
|
5617
|
+
#: independently; a reconstruction that restarted at one would re-issue a
|
|
5618
|
+
#: version a surviving card-total entry still holds and serve its old spend.
|
|
5619
|
+
_CODEX_VISIBLE_ACCOUNT_VERSIONS = itertools.count(1)
|
|
5651
5620
|
_CODEX_VISIBLE_POPULATION_CACHE: dict[str, object] = _ObservedDict()
|
|
5652
5621
|
_CODEX_ACCOUNT_CARD_TOTALS_MAX_BYTES = 256 * 1024
|
|
5653
5622
|
_CODEX_ACCOUNT_CARD_TOTAL_ESTIMATED_BYTES = 1024
|
|
@@ -5780,7 +5749,9 @@ def _cached_codex_visible_rows(
|
|
|
5780
5749
|
"entries_by_id": entry_by_id,
|
|
5781
5750
|
"rows_by_account": dict(rows_by_account),
|
|
5782
5751
|
"entries_by_account": dict(entries_by_account),
|
|
5783
|
-
"account_versions": {
|
|
5752
|
+
"account_versions": {
|
|
5753
|
+
key: next(_CODEX_VISIBLE_ACCOUNT_VERSIONS)
|
|
5754
|
+
for key in rows_by_account},
|
|
5784
5755
|
"estimated_bytes": estimated_bytes,
|
|
5785
5756
|
"entry_count": len(rows),
|
|
5786
5757
|
"account_count": len(rows_by_account),
|
|
@@ -5848,7 +5819,7 @@ def _cached_codex_visible_rows(
|
|
|
5848
5819
|
else:
|
|
5849
5820
|
rows_by_account.pop(key, None)
|
|
5850
5821
|
entries_by_account.pop(key, None)
|
|
5851
|
-
account_versions[key] =
|
|
5822
|
+
account_versions[key] = next(_CODEX_VISIBLE_ACCOUNT_VERSIONS)
|
|
5852
5823
|
|
|
5853
5824
|
try:
|
|
5854
5825
|
entries = tuple(
|
|
@@ -5895,13 +5866,59 @@ _CODEX_ACCOUNT_SCOPE_CACHE: dict[
|
|
|
5895
5866
|
] = _ObservedDict()
|
|
5896
5867
|
|
|
5897
5868
|
|
|
5898
|
-
|
|
5899
|
-
|
|
5869
|
+
@dataclass(frozen=True)
|
|
5870
|
+
class _CodexDerivedCoherence:
|
|
5871
|
+
"""#872: the population every delta-advanced derived Codex cache reflects.
|
|
5872
|
+
|
|
5873
|
+
`name` is the visible-population name the whole derived layer was last
|
|
5874
|
+
advanced to, or `None` when that cannot be stated; `account_keys` is
|
|
5875
|
+
every account whose per-account derived state was built since the last
|
|
5876
|
+
discard. ONE immutable value under ONE key, so the two are written,
|
|
5877
|
+
journaled, charged and evicted together and can never disagree.
|
|
5878
|
+
"""
|
|
5879
|
+
|
|
5880
|
+
name: tuple | None
|
|
5881
|
+
account_keys: frozenset
|
|
5882
|
+
|
|
5883
|
+
|
|
5884
|
+
_CODEX_DERIVED_COHERENCE_KEY = "state"
|
|
5885
|
+
_CODEX_DERIVED_COHERENCE: dict[str, _CodexDerivedCoherence] = _ObservedDict()
|
|
5886
|
+
|
|
5887
|
+
|
|
5888
|
+
def _codex_derived_coherence() -> _CodexDerivedCoherence:
|
|
5889
|
+
"""The current record; an absent or evicted slot names nothing."""
|
|
5890
|
+
record = _CODEX_DERIVED_COHERENCE.get(_CODEX_DERIVED_COHERENCE_KEY)
|
|
5891
|
+
if isinstance(record, _CodexDerivedCoherence):
|
|
5892
|
+
return record
|
|
5893
|
+
return _CodexDerivedCoherence(None, frozenset())
|
|
5894
|
+
|
|
5895
|
+
|
|
5896
|
+
def reset_codex_account_scope_cache(*, keep_counters: bool = False) -> None:
|
|
5897
|
+
"""Discard every delta-advanced derived Codex cache and the #872 record.
|
|
5898
|
+
|
|
5899
|
+
The complete derived reset: a test/process reset, the metadata-incomplete
|
|
5900
|
+
fold's reset, and — with `keep_counters=True` — #872's G1 discard, which
|
|
5901
|
+
runs in production on every base mismatch. G1 must not zero the
|
|
5902
|
+
cumulative fallback counters `bin/cctally-snapshot-measure` reads (the
|
|
5903
|
+
card-totals fallbacks and the visible population's `fallback_count`), so
|
|
5904
|
+
it keeps them; the caches themselves are discarded either way.
|
|
5905
|
+
"""
|
|
5900
5906
|
global _CODEX_ACCOUNT_CARD_TOTALS_FALLBACKS
|
|
5907
|
+
visible_fallbacks = int(
|
|
5908
|
+
_CODEX_VISIBLE_POPULATION_CACHE.get("fallback_count", 0) or 0)
|
|
5901
5909
|
_CODEX_ACCOUNT_SCOPE_CACHE.clear()
|
|
5902
5910
|
_CODEX_VISIBLE_POPULATION_CACHE.clear()
|
|
5911
|
+
if keep_counters and visible_fallbacks:
|
|
5912
|
+
# The over-size shape `_cached_codex_visible_rows` already writes:
|
|
5913
|
+
# counters only, nothing reusable.
|
|
5914
|
+
_CODEX_VISIBLE_POPULATION_CACHE.update({
|
|
5915
|
+
"fallback_count": visible_fallbacks,
|
|
5916
|
+
"estimated_bytes": 0,
|
|
5917
|
+
"entry_count": 0,
|
|
5918
|
+
})
|
|
5903
5919
|
_CODEX_ACCOUNT_CARD_TOTALS_CACHE.clear()
|
|
5904
|
-
|
|
5920
|
+
if not keep_counters:
|
|
5921
|
+
_CODEX_ACCOUNT_CARD_TOTALS_FALLBACKS = 0
|
|
5905
5922
|
_CODEX_ENTRY_ADAPTER_CACHE.clear()
|
|
5906
5923
|
_CODEX_PROJECT_LABEL_CACHE.clear()
|
|
5907
5924
|
_CODEX_PERIOD_VIEW_CACHE.clear()
|
|
@@ -5909,6 +5926,7 @@ def reset_codex_account_scope_cache() -> None:
|
|
|
5909
5926
|
_CODEX_CACHE_REPORT_ROWS.clear()
|
|
5910
5927
|
_CODEX_SESSION_VIEW_CACHE.clear()
|
|
5911
5928
|
_CODEX_PROJECT_WIRE_CACHE.clear()
|
|
5929
|
+
_CODEX_DERIVED_COHERENCE.clear()
|
|
5912
5930
|
|
|
5913
5931
|
|
|
5914
5932
|
def _codex_account_scopes_wire(
|
|
@@ -6313,6 +6331,11 @@ def _claude_accounts_wire(
|
|
|
6313
6331
|
# load-bearing, which is the same reason every other weekly consumer
|
|
6314
6332
|
# excludes held rows rather than trusting it.
|
|
6315
6333
|
#
|
|
6334
|
+
# The five-hour figure is the account's newest five-hour READING
|
|
6335
|
+
# (#825/#832 spec 4.7 revision 7, the one shared selection): a retired
|
|
6336
|
+
# replica or a weekly-only reading can be the newest row and carries
|
|
6337
|
+
# no five-hour value, so "the latest row" is not the latest reading.
|
|
6338
|
+
#
|
|
6316
6339
|
# None is still returned only when the account retained NO row at all,
|
|
6317
6340
|
# because the caller uses that to decide whether the unattributed
|
|
6318
6341
|
# bucket appears.
|
|
@@ -6323,17 +6346,18 @@ def _claude_accounts_wire(
|
|
|
6323
6346
|
"ORDER BY captured_at_utc DESC LIMIT 1",
|
|
6324
6347
|
(key,),
|
|
6325
6348
|
).fetchone()
|
|
6326
|
-
five_hour = stats_conn
|
|
6327
|
-
"SELECT five_hour_percent FROM weekly_usage_snapshots "
|
|
6328
|
-
"WHERE account_key=? "
|
|
6329
|
-
"ORDER BY captured_at_utc DESC LIMIT 1",
|
|
6330
|
-
(key,),
|
|
6331
|
-
).fetchone()
|
|
6349
|
+
five_hour = latest_five_hour_reading(stats_conn, account_key=key)
|
|
6332
6350
|
if weekly is None and five_hour is None:
|
|
6333
|
-
|
|
6351
|
+
retained = stats_conn.execute(
|
|
6352
|
+
"SELECT 1 FROM weekly_usage_snapshots WHERE account_key=? "
|
|
6353
|
+
"LIMIT 1",
|
|
6354
|
+
(key,),
|
|
6355
|
+
).fetchone()
|
|
6356
|
+
if retained is None:
|
|
6357
|
+
return None
|
|
6334
6358
|
return (
|
|
6335
6359
|
None if weekly is None else weekly[0],
|
|
6336
|
-
None if five_hour is None else five_hour
|
|
6360
|
+
None if five_hour is None else five_hour.percent,
|
|
6337
6361
|
None if weekly is None else weekly[1],
|
|
6338
6362
|
)
|
|
6339
6363
|
|
|
@@ -6494,6 +6518,7 @@ def _codex_source_caches() -> tuple[dict, ...]:
|
|
|
6494
6518
|
_CODEX_ACCOUNT_CARD_TOTALS_CACHE,
|
|
6495
6519
|
_CODEX_WEEKLY_VIEW_CACHE,
|
|
6496
6520
|
_CODEX_ACCOUNT_SCOPE_CACHE,
|
|
6521
|
+
_CODEX_DERIVED_COHERENCE,
|
|
6497
6522
|
)
|
|
6498
6523
|
|
|
6499
6524
|
|
|
@@ -6525,7 +6550,7 @@ def _codex_source_caches() -> tuple[dict, ...]:
|
|
|
6525
6550
|
# incremental byte estimate of its own, and a second charge computed here would
|
|
6526
6551
|
# be the same number derived twice. Those readers are named beside the owner.
|
|
6527
6552
|
#
|
|
6528
|
-
# WHAT THE TOTAL IS, AND WHAT IT IS NOT. It is the sum of
|
|
6553
|
+
# WHAT THE TOTAL IS, AND WHAT IT IS NOT. It is the sum of twelve DECLARED
|
|
6529
6554
|
# CHARGES. It is not a measurement of process memory, and
|
|
6530
6555
|
# `_CODEX_SOURCE_ACCELERATOR_MAX_BYTES` is not a cap on process memory: it is a
|
|
6531
6556
|
# budget over these charges, and the charges depart from the retained graph in
|
|
@@ -6553,8 +6578,8 @@ def _codex_source_caches() -> tuple[dict, ...]:
|
|
|
6553
6578
|
# `tests/test_source_cache_ownership.py` holds the WALKED owners to a two-sided
|
|
6554
6579
|
# band against one walk of their own cache — the upper half is what caught the
|
|
6555
6580
|
# adapter walking its row's strings once per row — while asserting only the
|
|
6556
|
-
# upper bound for the
|
|
6557
|
-
# closed-form set is exactly the
|
|
6581
|
+
# upper bound for the three closed-form owners and asserting that the
|
|
6582
|
+
# closed-form set is exactly the three named here.
|
|
6558
6583
|
|
|
6559
6584
|
|
|
6560
6585
|
def _is_codex_accounting_row(obj) -> bool:
|
|
@@ -6626,23 +6651,13 @@ def _charge_project_label_entry(_key, value) -> int:
|
|
|
6626
6651
|
|
|
6627
6652
|
|
|
6628
6653
|
def _charge_project_wire_entry(_key, value) -> int:
|
|
6629
|
-
"""`(signature, wire
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
"""
|
|
6637
|
-
signature, wire, groups = value[0], value[1], value[2]
|
|
6638
|
-
total = sys.getsizeof(value)
|
|
6639
|
-
total += retained_size_bytes(
|
|
6640
|
-
signature, borrowed=_is_borrowed_codex_payload)
|
|
6641
|
-
total += retained_size_bytes(wire, borrowed=_is_borrowed_codex_payload)
|
|
6642
|
-
total += sys.getsizeof(groups)
|
|
6643
|
-
for key, group in groups.items():
|
|
6644
|
-
total += sys.getsizeof(key) + sys.getsizeof(group)
|
|
6645
|
-
return total
|
|
6654
|
+
"""`(signature, wire)` — both owned; borrowed payloads are excluded."""
|
|
6655
|
+
signature, wire = value[0], value[1]
|
|
6656
|
+
return (
|
|
6657
|
+
sys.getsizeof(value)
|
|
6658
|
+
+ retained_size_bytes(signature, borrowed=_is_borrowed_codex_payload)
|
|
6659
|
+
+ retained_size_bytes(wire, borrowed=_is_borrowed_codex_payload)
|
|
6660
|
+
)
|
|
6646
6661
|
|
|
6647
6662
|
|
|
6648
6663
|
#: How faithfully each owner's declared charge tracks what one walk of that
|
|
@@ -6652,9 +6667,9 @@ def _charge_project_wire_entry(_key, value) -> int:
|
|
|
6652
6667
|
#: formula that DELIBERATELY UNDERSTATES, with no stated bound on the gap; the
|
|
6653
6668
|
#: value beside the name is why that trade was taken.
|
|
6654
6669
|
#:
|
|
6655
|
-
#: The budget is compared against a sum that includes the
|
|
6670
|
+
#: The budget is compared against a sum that includes the three closed-form
|
|
6656
6671
|
#: owners, so the budget is a heuristic over declared charges rather than a cap
|
|
6657
|
-
#: on memory. Adding a
|
|
6672
|
+
#: on memory. Adding a fourth closed-form owner without adding it here is a
|
|
6658
6673
|
#: test failure, which is the only reason this constant exists.
|
|
6659
6674
|
CODEX_SOURCE_CHARGE_FIDELITY = MappingProxyType({
|
|
6660
6675
|
"quota_observations": "walked",
|
|
@@ -6666,10 +6681,7 @@ CODEX_SOURCE_CHARGE_FIDELITY = MappingProxyType({
|
|
|
6666
6681
|
"string each, because a labelled copy is `replace(row, "
|
|
6667
6682
|
"display_label=...)` and every other field is the borrowed row's own "
|
|
6668
6683
|
"object"),
|
|
6669
|
-
"project_wire":
|
|
6670
|
-
"closed-form: the group containers, never the borrowed rows inside "
|
|
6671
|
-
"them, because descending into them makes an ordinary tick "
|
|
6672
|
-
"proportional to the whole population"),
|
|
6684
|
+
"project_wire": "walked",
|
|
6673
6685
|
"entry_adapters": (
|
|
6674
6686
|
"closed-form: two object shells and one float, because the adapted "
|
|
6675
6687
|
"entry shares every string with the row beside it and walking it "
|
|
@@ -6681,6 +6693,7 @@ CODEX_SOURCE_CHARGE_FIDELITY = MappingProxyType({
|
|
|
6681
6693
|
"account_card_totals": "walked",
|
|
6682
6694
|
"weekly_views": "walked",
|
|
6683
6695
|
"account_scopes": "walked",
|
|
6696
|
+
"derived_coherence": "walked",
|
|
6684
6697
|
})
|
|
6685
6698
|
|
|
6686
6699
|
|
|
@@ -6834,8 +6847,9 @@ def _declare_codex_source_cache_owners() -> tuple[_CodexCacheOwner, ...]:
|
|
|
6834
6847
|
_CodexCacheOwner(
|
|
6835
6848
|
"project_wire", _CODEX_PROJECT_WIRE_CACHE,
|
|
6836
6849
|
_charge_project_wire_entry, None,
|
|
6837
|
-
owns="the rendered project wire and the
|
|
6838
|
-
|
|
6850
|
+
owns="the rendered project wire and the population name it was "
|
|
6851
|
+
"built for (#872 removed the retained group splice)",
|
|
6852
|
+
borrows="nothing; borrowed payloads are excluded from its charge",
|
|
6839
6853
|
),
|
|
6840
6854
|
_CodexCacheOwner(
|
|
6841
6855
|
"entry_adapters", _CODEX_ENTRY_ADAPTER_CACHE,
|
|
@@ -6872,6 +6886,13 @@ def _declare_codex_source_cache_owners() -> tuple[_CodexCacheOwner, ...]:
|
|
|
6872
6886
|
owns="the finalized per-account scope mappings",
|
|
6873
6887
|
borrows="accounting rows and adapted entries",
|
|
6874
6888
|
),
|
|
6889
|
+
_CodexCacheOwner(
|
|
6890
|
+
"derived_coherence", _CODEX_DERIVED_COHERENCE,
|
|
6891
|
+
_charge_borrowing_payloads, None,
|
|
6892
|
+
owns="the one immutable record naming the population every "
|
|
6893
|
+
"delta-advanced derived cache reflects",
|
|
6894
|
+
borrows="nothing",
|
|
6895
|
+
),
|
|
6875
6896
|
)
|
|
6876
6897
|
caches = _codex_source_caches()
|
|
6877
6898
|
if tuple(owner.cache for owner in owners) != caches:
|
|
@@ -6888,10 +6909,10 @@ _CODEX_SOURCE_CACHE_OWNERS = _declare_codex_source_cache_owners()
|
|
|
6888
6909
|
|
|
6889
6910
|
|
|
6890
6911
|
def codex_source_retained_bytes() -> int:
|
|
6891
|
-
"""The
|
|
6912
|
+
"""The twelve Codex source caches' DECLARED CHARGES, summed.
|
|
6892
6913
|
|
|
6893
6914
|
Read it as "what these caches say they cost", not as "how much memory
|
|
6894
|
-
these caches hold".
|
|
6915
|
+
these caches hold". THREE of the twelve are charged in closed form and
|
|
6895
6916
|
understate without a stated bound — see `CODEX_SOURCE_CHARGE_FIDELITY` —
|
|
6896
6917
|
so this figure is the input to a heuristic budget rather than a
|
|
6897
6918
|
measurement of the retained graph.
|
|
@@ -6907,8 +6928,8 @@ def codex_source_retained_bytes() -> int:
|
|
|
6907
6928
|
|
|
6908
6929
|
|
|
6909
6930
|
#: The budget the declared charges are held to. NOT a bound on process memory:
|
|
6910
|
-
#: it is compared against `codex_source_retained_bytes()`,
|
|
6911
|
-
#:
|
|
6931
|
+
#: it is compared against `codex_source_retained_bytes()`, three of whose
|
|
6932
|
+
#: twelve terms are closed-form under-charges with no stated bound on the gap.
|
|
6912
6933
|
#: See `CODEX_SOURCE_CHARGE_FIDELITY`.
|
|
6913
6934
|
_CODEX_SOURCE_ACCELERATOR_MAX_BYTES = 768 * 1024 * 1024
|
|
6914
6935
|
_CODEX_SOURCE_ACCELERATOR_MAX_ENTRIES = 500_000
|
|
@@ -6995,15 +7016,16 @@ def _evict_codex_source_buckets(caches) -> tuple[int, bool]:
|
|
|
6995
7016
|
|
|
6996
7017
|
Returns `(evicted_entries, saw_an_indivisible_oversized_bucket)`.
|
|
6997
7018
|
|
|
6998
|
-
|
|
7019
|
+
Eleven of the twelve caches are keyed by stable semantic units —
|
|
6999
7020
|
`("account", key)`, `("parent",)`, a project identity, a session identity,
|
|
7000
|
-
a physical row id
|
|
7001
|
-
|
|
7021
|
+
a physical row id, the #872 coherence record's single key — so those keys
|
|
7022
|
+
ARE the buckets and no new partition is needed. The visible population is
|
|
7023
|
+
declared indivisible and is evicted whole; see
|
|
7002
7024
|
`CODEX_VISIBLE_POPULATION_INDIVISIBLE_REASON`.
|
|
7003
7025
|
|
|
7004
7026
|
Cold buckets go first and everything else stays resident, which is what
|
|
7005
7027
|
makes an above-cap steady state rebuild only what it lost. Clearing all
|
|
7006
|
-
|
|
7028
|
+
the caches, which is what this replaced, made a population permanently
|
|
7007
7029
|
above a cap rebuild and discard on every generation: measured on the sealed
|
|
7008
7030
|
#786 v3 `current` corpus, one run cleared 222,932 entries at a stroke.
|
|
7009
7031
|
|
|
@@ -7022,7 +7044,7 @@ def _evict_codex_source_buckets(caches) -> tuple[int, bool]:
|
|
|
7022
7044
|
# budget leaves the next build one entry over it again, and every tick then
|
|
7023
7045
|
# pays the candidate build below over every resident key — 222,932 of them
|
|
7024
7046
|
# on the measurement corpus. Freeing headroom instead lets the early return
|
|
7025
|
-
# above answer the next several ticks for
|
|
7047
|
+
# above answer the next several ticks for twelve integer reads.
|
|
7026
7048
|
if count >= int(_CODEX_SOURCE_ACCELERATOR_LOW_WATER_MIN_ENTRIES):
|
|
7027
7049
|
low_water = min(
|
|
7028
7050
|
budget, int(budget * _CODEX_SOURCE_ACCELERATOR_LOW_WATER))
|
|
@@ -7058,7 +7080,7 @@ def _evict_codex_source_buckets(caches) -> tuple[int, bool]:
|
|
|
7058
7080
|
# proportional to the whole population. Together they mean the
|
|
7059
7081
|
# owner with the largest under-charge is also the last one this
|
|
7060
7082
|
# pass will ever reach, so freeing memory by lowering the budget
|
|
7061
|
-
# acts on the other
|
|
7083
|
+
# acts on the other eleven owners first.
|
|
7062
7084
|
candidates.append((
|
|
7063
7085
|
max(recency for recency, _key, _bytes in entries),
|
|
7064
7086
|
index, position, _CACHE_MISSING, owner.retained_bytes(),
|
|
@@ -7102,7 +7124,7 @@ def _evict_codex_source_buckets(caches) -> tuple[int, bool]:
|
|
|
7102
7124
|
continue
|
|
7103
7125
|
evicted += removed
|
|
7104
7126
|
count -= removed
|
|
7105
|
-
# Re-read ONE owner rather than summing all
|
|
7127
|
+
# Re-read ONE owner rather than summing all twelve after every single
|
|
7106
7128
|
# eviction. A charged owner's total moves by exactly this bucket, and a
|
|
7107
7129
|
# self-reported one answers for itself; either way the arithmetic below
|
|
7108
7130
|
# is checked against a full re-read once, after the loop.
|
|
@@ -7127,17 +7149,17 @@ def _evict_codex_source_buckets(caches) -> tuple[int, bool]:
|
|
|
7127
7149
|
def enforce_codex_source_accelerator_bounds() -> None:
|
|
7128
7150
|
"""Apply byte and entry admission from the incrementally maintained total.
|
|
7129
7151
|
|
|
7130
|
-
The byte total is a sum of
|
|
7152
|
+
The byte total is a sum of twelve integers each owner keeps current, so
|
|
7131
7153
|
admission is a read. It used to be a background traversal of two full
|
|
7132
7154
|
container copies of the whole cache set, once per generation, in a worker
|
|
7133
7155
|
limited to a quarter of one core — which on the measurement corpus never
|
|
7134
7156
|
finished a generation at all, so the caches were admitted against a byte
|
|
7135
7157
|
figure that stayed at zero until the walk finally returned the over-budget
|
|
7136
|
-
sentinel and cleared
|
|
7158
|
+
sentinel and cleared every cache at once.
|
|
7137
7159
|
|
|
7138
7160
|
Publication is unconditional now rather than gated on the dirty flag,
|
|
7139
7161
|
because there is nothing left to schedule: an unchanged generation costs
|
|
7140
|
-
the same
|
|
7162
|
+
the same twelve reads and publishes the same number.
|
|
7141
7163
|
"""
|
|
7142
7164
|
caches = _codex_source_caches()
|
|
7143
7165
|
global _CODEX_SOURCE_ACCELERATOR_ESTIMATED_BYTES
|
|
@@ -7271,7 +7293,7 @@ _CODEX_SOURCE_PUBLISH_LOCK = threading.Lock()
|
|
|
7271
7293
|
|
|
7272
7294
|
|
|
7273
7295
|
class _CodexSourceBuildOverlay:
|
|
7274
|
-
"""One build's tentative generation over the
|
|
7296
|
+
"""One build's tentative generation over the twelve source caches.
|
|
7275
7297
|
|
|
7276
7298
|
THE JOURNAL IS THE OVERLAY. Rather than hold changed keys in a separate
|
|
7277
7299
|
mapping every read would have to consult, each mutation records the key's
|
|
@@ -7463,6 +7485,11 @@ class _CodexAccountingCapture:
|
|
|
7463
7485
|
#: qualified accounting read: such a generation names no population, and
|
|
7464
7486
|
#: borrowing the process's current token would name one it never read.
|
|
7465
7487
|
provenance_token: int | None = None
|
|
7488
|
+
#: #872: the name of the population this capture's delta is relative to,
|
|
7489
|
+
#: from the accounting result (`None` on a cold result and whenever the
|
|
7490
|
+
#: capture bypassed the qualified read, exactly like the fields above).
|
|
7491
|
+
base_population_signature: tuple | None = None
|
|
7492
|
+
base_provenance_token: int | None = None
|
|
7466
7493
|
|
|
7467
7494
|
|
|
7468
7495
|
#: The detail routes' horizon: one year ending at the generation instant. The
|
|
@@ -7556,6 +7583,9 @@ def _capture_codex_accounting(
|
|
|
7556
7583
|
population_signature: tuple | None = None
|
|
7557
7584
|
# #857: set only by a qualified read that returned; every bypass keeps it.
|
|
7558
7585
|
provenance_token: int | None = None
|
|
7586
|
+
# #872: the delta's base, under the same rule.
|
|
7587
|
+
base_population_signature: tuple | None = None
|
|
7588
|
+
base_provenance_token: int | None = None
|
|
7559
7589
|
|
|
7560
7590
|
def fallback_entries() -> tuple[object, ...]:
|
|
7561
7591
|
nonlocal metadata_read_failed, failed_legs
|
|
@@ -7625,6 +7655,9 @@ def _capture_codex_accounting(
|
|
|
7625
7655
|
changed_new = cached_accounting.changed_new
|
|
7626
7656
|
population_signature = cached_accounting.population_signature
|
|
7627
7657
|
provenance_token = cached_accounting.provenance_token
|
|
7658
|
+
base_population_signature = (
|
|
7659
|
+
cached_accounting.base_population_signature)
|
|
7660
|
+
base_provenance_token = cached_accounting.base_provenance_token
|
|
7628
7661
|
entries: tuple[object, ...] = qualified_entries
|
|
7629
7662
|
except QualifiedMetadataUnavailable as exc:
|
|
7630
7663
|
_lib_log.get_logger("dashboard").warning(
|
|
@@ -7671,6 +7704,10 @@ def _capture_codex_accounting(
|
|
|
7671
7704
|
None if metadata_incomplete else population_signature),
|
|
7672
7705
|
provenance_token=(
|
|
7673
7706
|
None if metadata_incomplete else provenance_token),
|
|
7707
|
+
base_population_signature=(
|
|
7708
|
+
None if metadata_incomplete else base_population_signature),
|
|
7709
|
+
base_provenance_token=(
|
|
7710
|
+
None if metadata_incomplete else base_provenance_token),
|
|
7674
7711
|
)
|
|
7675
7712
|
|
|
7676
7713
|
|
|
@@ -7797,7 +7834,7 @@ def _drop_codex_source_generation(
|
|
|
7797
7834
|
builds and replaying it would overwrite whatever the other build
|
|
7798
7835
|
legitimately published.
|
|
7799
7836
|
|
|
7800
|
-
ALL
|
|
7837
|
+
ALL TWELVE CACHES GO, not only the ones this build wrote to, and that is a
|
|
7801
7838
|
correctness requirement rather than caution. Ten of them are fed by the
|
|
7802
7839
|
accounting carrier's DELTA lists, not by its population: a build hands
|
|
7803
7840
|
`_cached_codex_period_view` and its siblings `changed_old` and
|
|
@@ -8320,6 +8357,23 @@ def _build_codex_source_state(
|
|
|
8320
8357
|
accounting_changed_new = accounting_capture.changed_new
|
|
8321
8358
|
accounting_entries = accounting_capture.entries
|
|
8322
8359
|
metadata_incomplete = accounting_capture.metadata_incomplete
|
|
8360
|
+
# #872 G1: a delta reaches the derived layer only while the layer's
|
|
8361
|
+
# coherence record names the delta's BASE. A cold carrier result has no
|
|
8362
|
+
# base, and a record that names anything else — an overflow-cleared or
|
|
8363
|
+
# reset carrier, a moved visible start, a skipped generation, a vanished
|
|
8364
|
+
# account — means the retained state is not what the delta is relative
|
|
8365
|
+
# to. Every delta-advanced derived cache is then discarded, and each
|
|
8366
|
+
# consumer derives from the full population through its own cold path.
|
|
8367
|
+
derived_base_name = _codex_visible_population_name(
|
|
8368
|
+
accounting_capture.base_population_signature,
|
|
8369
|
+
accounting_capture.base_provenance_token, context.range_start)
|
|
8370
|
+
derived_current_name = _codex_visible_population_name(
|
|
8371
|
+
accounting_capture.population_signature,
|
|
8372
|
+
accounting_capture.provenance_token, context.range_start)
|
|
8373
|
+
prior_coherence = _codex_derived_coherence()
|
|
8374
|
+
if derived_base_name is None or prior_coherence.name != derived_base_name:
|
|
8375
|
+
reset_codex_account_scope_cache(keep_counters=True)
|
|
8376
|
+
prior_coherence = _CodexDerivedCoherence(None, frozenset())
|
|
8323
8377
|
|
|
8324
8378
|
conversation_metadata_read = (
|
|
8325
8379
|
capture.conversation_metadata
|
|
@@ -8557,24 +8611,13 @@ def _build_codex_source_state(
|
|
|
8557
8611
|
period_signature = _codex_accounting_period_signature(
|
|
8558
8612
|
context.cache_conn, context.range_start, metadata_incomplete,
|
|
8559
8613
|
)
|
|
8560
|
-
# The
|
|
8561
|
-
#
|
|
8562
|
-
#
|
|
8563
|
-
#
|
|
8564
|
-
#
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
if capture is not None:
|
|
8568
|
-
project_population_signature = (
|
|
8569
|
-
None if metadata_incomplete
|
|
8570
|
-
else _codex_project_label_signature(
|
|
8571
|
-
capture.accounting.population_signature)
|
|
8572
|
-
)
|
|
8573
|
-
else:
|
|
8574
|
-
project_population_signature = (
|
|
8575
|
-
None if metadata_incomplete
|
|
8576
|
-
else _codex_project_population_signature()
|
|
8577
|
-
)
|
|
8614
|
+
# The project caches' name for this build's population: the visible
|
|
8615
|
+
# population name G1 computed from THIS build's own capture (sound across
|
|
8616
|
+
# a cleared carrier and a moved visible start, #872), plus the label
|
|
8617
|
+
# algorithm version. `None` when the population cannot be named, and then
|
|
8618
|
+
# nothing is reused. See `_CodexAccountingCapture.population_signature`.
|
|
8619
|
+
project_population_signature = _codex_project_label_signature(
|
|
8620
|
+
derived_current_name)
|
|
8578
8621
|
daily = _cached_codex_period_view(
|
|
8579
8622
|
entries, changed_old=changed_old_entries,
|
|
8580
8623
|
changed_new=changed_new_entries, kind="daily", cache_key=("parent",),
|
|
@@ -8796,6 +8839,8 @@ def _build_codex_source_state(
|
|
|
8796
8839
|
hero_cycles_wire: list[dict[str, object]] = []
|
|
8797
8840
|
combined_accounting: dict[str, object] | None = None
|
|
8798
8841
|
account_scopes: dict[str, dict[str, object]] = {}
|
|
8842
|
+
# #872 G2 input: whether the per-account scopes were built this build.
|
|
8843
|
+
account_scope_status = "not-built"
|
|
8799
8844
|
# #556 S5 §3.8: bound OUTSIDE the try, because the degrade path below has to
|
|
8800
8845
|
# be able to clear it, and the retained `clock_data` reads it either way.
|
|
8801
8846
|
budget_events_by_account: dict[str, tuple[tuple[dt.datetime, float], ...]] = {}
|
|
@@ -8941,6 +8986,7 @@ def _build_codex_source_state(
|
|
|
8941
8986
|
scope_signature=period_signature,
|
|
8942
8987
|
project_population_signature=project_population_signature,
|
|
8943
8988
|
)
|
|
8989
|
+
account_scope_status = "built"
|
|
8944
8990
|
# #416 QA P1-A — the "All accounts" Blocks panel is the UNION of
|
|
8945
8991
|
# every account's 5-hour blocks. `_quota_wire` filters
|
|
8946
8992
|
# `str(root_key) not in cycle.source_root_keys` against a single
|
|
@@ -8997,6 +9043,7 @@ def _build_codex_source_state(
|
|
|
8997
9043
|
))
|
|
8998
9044
|
quota = {**quota, "blocks": merged_blocks}
|
|
8999
9045
|
except (sqlite3.Error, QualifiedMetadataUnavailable):
|
|
9046
|
+
account_scope_status = "degraded"
|
|
9000
9047
|
# A per-account wire failure must never fail the whole source build;
|
|
9001
9048
|
# degrade to the byte-stable undecorated shape.
|
|
9002
9049
|
accounts_wire = []
|
|
@@ -9129,6 +9176,31 @@ def _build_codex_source_state(
|
|
|
9129
9176
|
{name: operands[name] for name in _HERO_CARD_OPERANDS
|
|
9130
9177
|
if name in operands}
|
|
9131
9178
|
)
|
|
9179
|
+
# #872 G2: the record advances to this build's population only when the
|
|
9180
|
+
# build advanced EVERY delta-advanced consumer over the capture's whole
|
|
9181
|
+
# delta. Completeness is stated positively: metadata complete and not
|
|
9182
|
+
# transient (a transient generation skips both project legs), scopes not
|
|
9183
|
+
# degraded, and every account that holds per-account state still live
|
|
9184
|
+
# (an account that left the live set kept a stale state the delta never
|
|
9185
|
+
# reached). Anything else records `None`, so the next build's G1 discards.
|
|
9186
|
+
scope_keys = frozenset(str(key) for key in account_scopes)
|
|
9187
|
+
coherent = (
|
|
9188
|
+
derived_current_name is not None
|
|
9189
|
+
and not metadata_incomplete
|
|
9190
|
+
and not metadata_transient
|
|
9191
|
+
and account_scope_status != "degraded"
|
|
9192
|
+
and prior_coherence.account_keys <= scope_keys
|
|
9193
|
+
)
|
|
9194
|
+
record = _CodexDerivedCoherence(
|
|
9195
|
+
name=derived_current_name if coherent else None,
|
|
9196
|
+
account_keys=prior_coherence.account_keys | scope_keys,
|
|
9197
|
+
)
|
|
9198
|
+
if _CODEX_DERIVED_COHERENCE.get(_CODEX_DERIVED_COHERENCE_KEY) == record:
|
|
9199
|
+
# Unchanged (a warm-clean tick on the same ledger head): say it was
|
|
9200
|
+
# used without rewriting it, so an idle tick dirties nothing.
|
|
9201
|
+
_CODEX_DERIVED_COHERENCE.touch(_CODEX_DERIVED_COHERENCE_KEY)
|
|
9202
|
+
else:
|
|
9203
|
+
_CODEX_DERIVED_COHERENCE[_CODEX_DERIVED_COHERENCE_KEY] = record
|
|
9132
9204
|
return SourceDashboardState(
|
|
9133
9205
|
source="codex",
|
|
9134
9206
|
availability=availability,
|