cctally 1.115.0 → 1.116.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 +16 -0
- package/bin/_cctally_cache.py +85 -4
- package/bin/_cctally_completion.py +80 -12
- package/bin/_cctally_core.py +106 -9
- package/bin/_cctally_dashboard.py +6 -9
- package/bin/_cctally_dashboard_envelope.py +26 -32
- package/bin/_cctally_db.py +85 -4
- package/bin/_cctally_doctor.py +38 -5
- package/bin/_cctally_five_hour.py +83 -1017
- package/bin/_cctally_journal.py +1307 -180
- package/bin/_cctally_journal_repair.py +9 -7
- package/bin/_cctally_oauth_samples.py +280 -0
- package/bin/_cctally_parser.py +5 -0
- package/bin/_cctally_quarantine.py +14 -4
- package/bin/_cctally_quota.py +70 -8
- package/bin/_cctally_quota_model.py +35 -4
- package/bin/_cctally_record.py +414 -123
- package/bin/_cctally_rederive.py +632 -235
- package/bin/_cctally_refresh.py +29 -0
- package/bin/_cctally_statusline.py +984 -47
- package/bin/_cctally_store.py +972 -58
- package/bin/_cctally_tui.py +62 -7
- package/bin/_cctally_update.py +25 -0
- package/bin/_lib_account_protocol.py +201 -18
- package/bin/_lib_clone_copy.py +46 -11
- package/bin/_lib_dashboard_sources.py +57 -15
- package/bin/_lib_detector_access_registry.py +273 -52
- package/bin/_lib_doctor.py +302 -1
- package/bin/_lib_journal.py +145 -25
- package/bin/_lib_journal_router.py +166 -48
- package/bin/_lib_oauth_samples.py +238 -0
- package/bin/_lib_pricing_check.py +0 -12
- package/bin/_lib_provider_activity.py +770 -0
- package/bin/_lib_quarantine.py +3 -2
- package/bin/_lib_quota_model.py +96 -6
- package/bin/_lib_rebuild_progress.py +146 -0
- package/bin/_lib_record.py +22 -0
- package/bin/_lib_rederive.py +299 -3
- package/bin/_lib_snapshot_cache.py +43 -1
- package/bin/_lib_stale_proof.py +708 -0
- package/bin/_lib_statusline_candidates.py +65 -5
- package/bin/_lib_worker_launches.py +315 -0
- package/bin/cctally +6 -11
- package/dashboard/static/assets/{ConversationsView-Bc8LCOAZ.js → ConversationsView-Dflb_myA.js} +1 -1
- package/dashboard/static/assets/DoctorModal-DFX5CfIs.js +1 -0
- package/dashboard/static/assets/{ModalRoot-DLPgyAM_.js → ModalRoot-HlxoagIa.js} +1 -1
- package/dashboard/static/assets/{ProjectsDrillPanel-z53a4v4M.js → ProjectsDrillPanel-C3PJy1xx.js} +1 -1
- package/dashboard/static/assets/{SourceDetailModal-Bw0T8W-W.js → SourceDetailModal-BczsWKhD.js} +1 -1
- package/dashboard/static/assets/{UpdateModal-D5AKx4Yw.js → UpdateModal-Os-Lo5cs.js} +1 -1
- package/dashboard/static/assets/{index-BXDOzSng.js → index-CYZ3NLqF.js} +3 -3
- package/dashboard/static/assets/{outlineNavigation-JObY7cUY.js → outlineNavigation-k-zIif6W.js} +1 -1
- package/dashboard/static/dashboard.html +1 -1
- package/package.json +7 -2
- package/bin/_lib_merge_gate.py +0 -452
- package/dashboard/static/assets/DoctorModal-D--qVBwe.js +0 -1
package/bin/_lib_rederive.py
CHANGED
|
@@ -19,6 +19,7 @@ import json
|
|
|
19
19
|
import math
|
|
20
20
|
from collections.abc import Iterable, Mapping
|
|
21
21
|
|
|
22
|
+
import _lib_accounts
|
|
22
23
|
from _lib_cost_provenance import (
|
|
23
24
|
HISTORICAL_CLOSE_BASIS, RECORDED_CLOSE_CORRECTION_BASIS,
|
|
24
25
|
)
|
|
@@ -162,6 +163,14 @@ _OP_CLASSIFICATIONS = {
|
|
|
162
163
|
# by the selector directly and never registered).
|
|
163
164
|
"journal_protocol_resolution_v2": KindClassification(
|
|
164
165
|
"retained", "journal-repair acknowledgements are audit records"),
|
|
166
|
+
# #797/#823/#840 spec 4.6 B2, B5: the revision-2 boundary is fence
|
|
167
|
+
# metadata like the cutover, and the v3 acknowledgement is the v2's
|
|
168
|
+
# successor.
|
|
169
|
+
"account_bound_revision": KindClassification(
|
|
170
|
+
"retained", "the revision-2 journal fence is replayed, never "
|
|
171
|
+
"re-derived"),
|
|
172
|
+
"journal_protocol_resolution_v3": KindClassification(
|
|
173
|
+
"retained", "journal-repair acknowledgements are audit records"),
|
|
165
174
|
}
|
|
166
175
|
|
|
167
176
|
|
|
@@ -912,6 +921,132 @@ _MILESTONE_BASE_DEPENDENCIES = {
|
|
|
912
921
|
}
|
|
913
922
|
|
|
914
923
|
|
|
924
|
+
# ── Reviewed weekly decisions (#797/#823/#840 spec 4.7, Q5, D4, U2) ─────────
|
|
925
|
+
#
|
|
926
|
+
# Op kind `claude_weekly_observation_decision_v2` (and the previous release's
|
|
927
|
+
# `claude_weekly_observation_decision`) carries one reviewed manifest. Payload
|
|
928
|
+
# schema 1 lists `decisions`, schema 2 `weekly_axis_decisions` (and snapshot
|
|
929
|
+
# identity decisions), both of `hold` / `accept`. Schema 3 adds `retract`
|
|
930
|
+
# WITHOUT a new projection: `weekly_axis_decisions` projects each retraction as
|
|
931
|
+
# an ordinary `hold` (the previous release's completion, which recognizes only
|
|
932
|
+
# a literal `hold`, then sees a weekly exclusion) and the canonical
|
|
933
|
+
# `weekly_axis_retractions` list — sorted, unique, each naming a same-op
|
|
934
|
+
# projected hold — gives it its causal meaning. This is the ONE strict decoder
|
|
935
|
+
# every current consumer of a retained payload uses.
|
|
936
|
+
|
|
937
|
+
REVIEWED_WEEKLY_SCHEMA_VERSIONS = (1, 2, 3)
|
|
938
|
+
REVIEWED_WEEKLY_DISPOSITIONS = ("hold", "accept", "retract")
|
|
939
|
+
_REVIEWED_WEEKLY_IDENTITY_KEYS = frozenset({
|
|
940
|
+
"acceptedObservationId", "replayObservationId", "disposition",
|
|
941
|
+
})
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
@dataclasses.dataclass(frozen=True)
|
|
945
|
+
class ReviewedWeeklyDecision:
|
|
946
|
+
"""One decoded reviewed weekly decision payload: its schema version, the
|
|
947
|
+
disposition per observation (``hold``, ``accept`` or ``retract``, in
|
|
948
|
+
payload order), and its snapshot identity decisions."""
|
|
949
|
+
|
|
950
|
+
version: int
|
|
951
|
+
dispositions: Mapping[str, str]
|
|
952
|
+
identity_decisions: tuple
|
|
953
|
+
|
|
954
|
+
|
|
955
|
+
def decode_reviewed_weekly_payload(payload) -> ReviewedWeeklyDecision:
|
|
956
|
+
"""Strictly decode one retained reviewed weekly decision payload, or raise
|
|
957
|
+
:class:`RederiveConflict`."""
|
|
958
|
+
if not isinstance(payload, Mapping):
|
|
959
|
+
raise RederiveConflict("malformed retained reviewed weekly decision op")
|
|
960
|
+
version = payload.get("schema_version")
|
|
961
|
+
if type(version) is not int or version not in REVIEWED_WEEKLY_SCHEMA_VERSIONS:
|
|
962
|
+
raise RederiveConflict("malformed retained reviewed weekly decision op")
|
|
963
|
+
decisions = (payload.get("decisions") if version == 1
|
|
964
|
+
else payload.get("weekly_axis_decisions"))
|
|
965
|
+
identity = ([] if version == 1
|
|
966
|
+
else payload.get("snapshot_identity_decisions"))
|
|
967
|
+
if (not isinstance(decisions, list) or not isinstance(identity, list)
|
|
968
|
+
or not decisions and not identity):
|
|
969
|
+
raise RederiveConflict("malformed retained reviewed weekly decision op")
|
|
970
|
+
dispositions: dict = {}
|
|
971
|
+
for item in decisions:
|
|
972
|
+
if (not isinstance(item, dict)
|
|
973
|
+
or set(item) != {"observationId", "disposition"}
|
|
974
|
+
or not isinstance(item["observationId"], str)
|
|
975
|
+
or not item["observationId"]
|
|
976
|
+
or item["disposition"] not in ("hold", "accept")):
|
|
977
|
+
raise RederiveConflict(
|
|
978
|
+
"malformed retained reviewed weekly decision entry")
|
|
979
|
+
obs_id = item["observationId"]
|
|
980
|
+
if obs_id in dispositions:
|
|
981
|
+
raise RederiveConflict(
|
|
982
|
+
f"unknown or duplicate reviewed weekly observation: {obs_id}")
|
|
983
|
+
dispositions[obs_id] = item["disposition"]
|
|
984
|
+
retractions = payload.get("weekly_axis_retractions")
|
|
985
|
+
if version < 3:
|
|
986
|
+
if "weekly_axis_retractions" in payload:
|
|
987
|
+
raise RederiveConflict(
|
|
988
|
+
"malformed retained reviewed weekly decision op")
|
|
989
|
+
else:
|
|
990
|
+
if (not isinstance(retractions, list)
|
|
991
|
+
or any(not isinstance(r, str) or not r for r in retractions)
|
|
992
|
+
or retractions != sorted(set(retractions))):
|
|
993
|
+
raise RederiveConflict(
|
|
994
|
+
"reviewed weekly retractions must be a canonical list")
|
|
995
|
+
for obs_id in retractions:
|
|
996
|
+
if dispositions.get(obs_id) != "hold":
|
|
997
|
+
raise RederiveConflict(
|
|
998
|
+
"reviewed weekly retraction references no same-op hold: "
|
|
999
|
+
+ obs_id)
|
|
1000
|
+
dispositions[obs_id] = "retract"
|
|
1001
|
+
seen: set = set()
|
|
1002
|
+
for item in identity:
|
|
1003
|
+
if (not isinstance(item, dict)
|
|
1004
|
+
or set(item) != _REVIEWED_WEEKLY_IDENTITY_KEYS
|
|
1005
|
+
or not all(isinstance(item.get(key), str) and item[key]
|
|
1006
|
+
for key in ("acceptedObservationId",
|
|
1007
|
+
"replayObservationId"))
|
|
1008
|
+
or item.get("disposition") not in ("preserve", "rederive")
|
|
1009
|
+
or item["acceptedObservationId"] == item["replayObservationId"]
|
|
1010
|
+
or item["acceptedObservationId"] in seen
|
|
1011
|
+
or item["replayObservationId"] in seen):
|
|
1012
|
+
raise RederiveConflict(
|
|
1013
|
+
"malformed retained snapshot identity decision")
|
|
1014
|
+
seen.update((item["acceptedObservationId"], item["replayObservationId"]))
|
|
1015
|
+
return ReviewedWeeklyDecision(version, dispositions, tuple(identity))
|
|
1016
|
+
|
|
1017
|
+
|
|
1018
|
+
def effective_reviewed_weekly_dispositions(payloads) -> dict:
|
|
1019
|
+
"""The effective disposition per observation over ``payloads`` in journal
|
|
1020
|
+
order: the latest logical decision wins."""
|
|
1021
|
+
effective: dict = {}
|
|
1022
|
+
for payload in payloads:
|
|
1023
|
+
effective.update(decode_reviewed_weekly_payload(payload).dispositions)
|
|
1024
|
+
return effective
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
def reviewed_weekly_payload(*, version: int, decisions, base: Mapping) -> dict:
|
|
1028
|
+
"""The persisted payload of one reviewed manifest: ``base`` (kind, bound,
|
|
1029
|
+
reason and the other shared fields) plus ``decisions`` as
|
|
1030
|
+
``[{"observationId", "disposition"}]``, with each retraction projected as
|
|
1031
|
+
an ordinary `hold` and listed in `weekly_axis_retractions` (schema 3)."""
|
|
1032
|
+
payload = dict(base)
|
|
1033
|
+
projected = sorted(
|
|
1034
|
+
({"observationId": item["observationId"],
|
|
1035
|
+
"disposition": ("hold" if item["disposition"] == "retract"
|
|
1036
|
+
else item["disposition"])}
|
|
1037
|
+
for item in decisions),
|
|
1038
|
+
key=lambda item: item["observationId"])
|
|
1039
|
+
if version == 1:
|
|
1040
|
+
payload["decisions"] = projected
|
|
1041
|
+
else:
|
|
1042
|
+
payload["weekly_axis_decisions"] = projected
|
|
1043
|
+
if version >= 3:
|
|
1044
|
+
payload["weekly_axis_retractions"] = sorted(
|
|
1045
|
+
item["observationId"] for item in decisions
|
|
1046
|
+
if item["disposition"] == "retract")
|
|
1047
|
+
return payload
|
|
1048
|
+
|
|
1049
|
+
|
|
915
1050
|
def _milestone_dependencies(kind: str) -> dict[str, str]:
|
|
916
1051
|
dependencies = dict(_MILESTONE_BASE_DEPENDENCIES)
|
|
917
1052
|
dependencies["reset_event_ref"] = (
|
|
@@ -1367,9 +1502,94 @@ def _without_snapshot_suppression(payload: Mapping) -> Mapping:
|
|
|
1367
1502
|
return {key: value for key, value in payload.items() if key != "suppression"}
|
|
1368
1503
|
|
|
1369
1504
|
|
|
1505
|
+
def _retraction_unsupported_resets(
|
|
1506
|
+
current: Mapping[str, Mapping], desired: Mapping[str, Mapping],
|
|
1507
|
+
retracted_snapshot_ids, raw_observations: Iterable[Mapping] = (),
|
|
1508
|
+
) -> frozenset:
|
|
1509
|
+
"""#797/#823/#840 spec 4.7 (D4): the current `week_reset` events the
|
|
1510
|
+
corrected replay no longer supports BECAUSE of a retraction — absent from
|
|
1511
|
+
``desired``, and whose causal predecessor was a retracted observation.
|
|
1512
|
+
|
|
1513
|
+
The predecessor is read from the raw journal observation stream
|
|
1514
|
+
(``raw_observations``), not from the current graph: the latest
|
|
1515
|
+
weekly-bearing Claude observation of the reset's account (attributed as
|
|
1516
|
+
the replay attributes it, `_cctally_record._pipeline_claude_usage`)
|
|
1517
|
+
captured strictly before the reset's originating low observation (whose
|
|
1518
|
+
identity the reset follows, `_lib_journal` `week_reset_identity_parts`),
|
|
1519
|
+
or before its `detected_at_utc` when it has no origin; equal instants
|
|
1520
|
+
break by journal order. Whether the replay accepted that observation,
|
|
1521
|
+
held it or stored no snapshot for it does not matter: a stale replay an
|
|
1522
|
+
earlier reviewed `hold` turned into a held snapshot, or never accepted,
|
|
1523
|
+
is still the reading the reset was detected against, and the latest
|
|
1524
|
+
unheld snapshot before the origin is then an unrelated genuine reading
|
|
1525
|
+
(review of 79847ac5a, D1). Exact identity, never a percentage
|
|
1526
|
+
comparison. A reset the corrected replay still produces (alternate
|
|
1527
|
+
support, review F4) is in ``desired`` and is never here."""
|
|
1528
|
+
retracted = frozenset(retracted_snapshot_ids or ())
|
|
1529
|
+
if not retracted:
|
|
1530
|
+
return frozenset()
|
|
1531
|
+
weekly: dict = {}
|
|
1532
|
+
captured: dict = {}
|
|
1533
|
+
for index, record in enumerate(raw_observations or ()):
|
|
1534
|
+
payload = record.get("payload") or {}
|
|
1535
|
+
observation_id = record.get("id")
|
|
1536
|
+
if (record.get("t") != "obs" or record.get("provider") != "claude"
|
|
1537
|
+
or "resets_at" not in payload
|
|
1538
|
+
or payload.get("weekly_percent") is None
|
|
1539
|
+
or not isinstance(observation_id, str) or not observation_id):
|
|
1540
|
+
continue
|
|
1541
|
+
instant = _iso_instant(payload.get("captured_at") or record.get("at"))
|
|
1542
|
+
if instant is None:
|
|
1543
|
+
continue
|
|
1544
|
+
account = record.get("account") or _lib_accounts.UNATTRIBUTED
|
|
1545
|
+
weekly.setdefault(account, []).append((instant, index, observation_id))
|
|
1546
|
+
captured.setdefault(observation_id, instant)
|
|
1547
|
+
unsupported = set()
|
|
1548
|
+
for event_id, record in current.items():
|
|
1549
|
+
payload = record.get("payload") or {}
|
|
1550
|
+
if payload.get("kind") != "week_reset" or event_id in desired:
|
|
1551
|
+
continue
|
|
1552
|
+
origin = payload.get("origin_observation_id")
|
|
1553
|
+
origin = origin if isinstance(origin, str) and origin else None
|
|
1554
|
+
origin_payload = (
|
|
1555
|
+
(current.get(f"sa:{origin}") or {}).get("payload") or {}
|
|
1556
|
+
if origin is not None else {})
|
|
1557
|
+
at = ((captured.get(origin) if origin is not None else None)
|
|
1558
|
+
or _iso_instant(origin_payload.get("captured_at_utc"))
|
|
1559
|
+
or _iso_instant(payload.get("detected_at_utc")))
|
|
1560
|
+
if at is None:
|
|
1561
|
+
continue
|
|
1562
|
+
prior = [item for item in weekly.get(payload.get("account_key"), ())
|
|
1563
|
+
if item[0] < at and item[2] != origin]
|
|
1564
|
+
if prior and f"sa:{max(prior)[2]}" in retracted:
|
|
1565
|
+
unsupported.add(event_id)
|
|
1566
|
+
return frozenset(unsupported)
|
|
1567
|
+
|
|
1568
|
+
|
|
1569
|
+
def _retraction_scope(current: Mapping[str, Mapping], retracted_snapshot_ids,
|
|
1570
|
+
unsupported_resets) -> frozenset:
|
|
1571
|
+
"""The event ids whose reviewed actions a retraction itself causes and
|
|
1572
|
+
the reviewed delta therefore always carries (spec 4.7: explicit
|
|
1573
|
+
retractions survive the baseline/reviewed plan intersection): the
|
|
1574
|
+
retracted snapshots, the resets they leave unsupported, and every
|
|
1575
|
+
current milestone citing either."""
|
|
1576
|
+
scope = set(retracted_snapshot_ids or ()) | set(unsupported_resets or ())
|
|
1577
|
+
if not scope:
|
|
1578
|
+
return frozenset()
|
|
1579
|
+
for event_id, record in current.items():
|
|
1580
|
+
payload = record.get("payload") or {}
|
|
1581
|
+
if payload.get("kind") not in _SNAPSHOT_DEPENDENT_KINDS:
|
|
1582
|
+
continue
|
|
1583
|
+
if (payload.get("usage_snapshot_ref") in scope
|
|
1584
|
+
or payload.get("reset_event_ref") in scope):
|
|
1585
|
+
scope.add(event_id)
|
|
1586
|
+
return frozenset(scope)
|
|
1587
|
+
|
|
1588
|
+
|
|
1370
1589
|
def _preserve_reviewed_hold_dependencies(
|
|
1371
1590
|
current: Mapping[str, Mapping], desired: dict[str, Mapping],
|
|
1372
1591
|
reviewed_weekly_hold_ids=(), historical_close_markers=None,
|
|
1592
|
+
unsupported_resets=frozenset(),
|
|
1373
1593
|
) -> None:
|
|
1374
1594
|
"""Keep frozen five-hour facts reached from exact reviewed holds.
|
|
1375
1595
|
|
|
@@ -1384,6 +1604,13 @@ def _preserve_reviewed_hold_dependencies(
|
|
|
1384
1604
|
children stay the selected frozen fact, so that update keeps the close's
|
|
1385
1605
|
money and its block stays frozen (spec §4.1). Any unfamiliar reference
|
|
1386
1606
|
shape refuses rather than being guessed or rewritten.
|
|
1607
|
+
|
|
1608
|
+
#797/#823/#840 spec 4.7 (D4): ``reviewed_weekly_hold_ids`` names the
|
|
1609
|
+
ordinary holds only — a retraction is never preserved — and a held
|
|
1610
|
+
milestone whose reset a retraction left unsupported
|
|
1611
|
+
(``unsupported_resets``) is not kept: keeping it would restore that
|
|
1612
|
+
reset, and the retraction's tombstone wins over the dangling milestone,
|
|
1613
|
+
which then follows the corrected replay.
|
|
1387
1614
|
"""
|
|
1388
1615
|
snapshot_ids = {
|
|
1389
1616
|
f"sa:{raw_id}" for raw_id in reviewed_weekly_hold_ids
|
|
@@ -1458,6 +1685,8 @@ def _preserve_reviewed_hold_dependencies(
|
|
|
1458
1685
|
"reviewed weekly hold dependency has an unfamiliar kind: "
|
|
1459
1686
|
+ event_id
|
|
1460
1687
|
)
|
|
1688
|
+
if payload.get("reset_event_ref") in unsupported_resets:
|
|
1689
|
+
continue
|
|
1461
1690
|
snapshot = current.get(usage_ref)
|
|
1462
1691
|
if ((snapshot or {}).get("payload") or {}).get("kind") != "snapshot_accept":
|
|
1463
1692
|
raise RederiveConflict(
|
|
@@ -1643,6 +1872,10 @@ class RederivePlan:
|
|
|
1643
1872
|
action_counts_by_event_kind: Mapping[str, Mapping[str, int]] = dataclasses.field(
|
|
1644
1873
|
default_factory=dict
|
|
1645
1874
|
)
|
|
1875
|
+
#: #797/#823/#840 spec 4.7: the event ids a retraction itself acts on
|
|
1876
|
+
#: (`_retraction_scope`). Not part of the plan body: the retractions are
|
|
1877
|
+
#: already in the config fingerprint through the decision op's payload.
|
|
1878
|
+
retraction_event_ids: frozenset = frozenset()
|
|
1646
1879
|
|
|
1647
1880
|
def _body(self) -> dict:
|
|
1648
1881
|
body = {
|
|
@@ -1703,6 +1936,7 @@ def build_claude_usage_plan(*, selection, desired_events: Iterable[Mapping],
|
|
|
1703
1936
|
raw_observations=(),
|
|
1704
1937
|
snapshot_identity_decisions=(),
|
|
1705
1938
|
reviewed_weekly_hold_ids=(),
|
|
1939
|
+
reviewed_weekly_retraction_ids=(),
|
|
1706
1940
|
uncertain_cost_fact_ids=(),
|
|
1707
1941
|
historical_close_markers=None,
|
|
1708
1942
|
physical_revisions=None,
|
|
@@ -1754,12 +1988,26 @@ def build_claude_usage_plan(*, selection, desired_events: Iterable[Mapping],
|
|
|
1754
1988
|
and selected.record is not None
|
|
1755
1989
|
and _is_owned_event(selected.record)
|
|
1756
1990
|
}
|
|
1991
|
+
if not isinstance(raw_observations, (list, tuple)):
|
|
1992
|
+
raw_observations = tuple(raw_observations)
|
|
1757
1993
|
_reconcile_snapshot_identities(
|
|
1758
1994
|
current, desired, selection, raw_observations,
|
|
1759
1995
|
snapshot_identity_decisions,
|
|
1760
1996
|
)
|
|
1997
|
+
# #797/#823/#840 spec 4.7 (D4): `reviewed_weekly_hold_ids` are the
|
|
1998
|
+
# ordinary holds; a retraction is excluded from preservation, and the
|
|
1999
|
+
# resets it leaves unsupported are never restored by a held milestone.
|
|
2000
|
+
retracted_snapshot_ids = frozenset(
|
|
2001
|
+
f"sa:{raw_id}" for raw_id in reviewed_weekly_retraction_ids or ()
|
|
2002
|
+
if isinstance(raw_id, str) and raw_id)
|
|
2003
|
+
unsupported_resets = _retraction_unsupported_resets(
|
|
2004
|
+
current, desired, retracted_snapshot_ids,
|
|
2005
|
+
raw_observations=raw_observations)
|
|
2006
|
+
retraction_event_ids = _retraction_scope(
|
|
2007
|
+
current, retracted_snapshot_ids, unsupported_resets)
|
|
1761
2008
|
_preserve_reviewed_hold_dependencies(
|
|
1762
2009
|
current, desired, reviewed_weekly_hold_ids, historical_close_markers,
|
|
2010
|
+
unsupported_resets=unsupported_resets,
|
|
1763
2011
|
)
|
|
1764
2012
|
retained_event_count = sum(
|
|
1765
2013
|
1 for selected in selection.by_id.values()
|
|
@@ -1970,6 +2218,7 @@ def build_claude_usage_plan(*, selection, desired_events: Iterable[Mapping],
|
|
|
1970
2218
|
preserved_event_count=len(preserved),
|
|
1971
2219
|
uncertain_cost_fact_count=len(uncertain_ids),
|
|
1972
2220
|
action_counts_by_event_kind=counts_by_kind,
|
|
2221
|
+
retraction_event_ids=retraction_event_ids,
|
|
1973
2222
|
)
|
|
1974
2223
|
if enforce_guard:
|
|
1975
2224
|
enforce_week_reset_add_burst_guard(plan)
|
|
@@ -2162,8 +2411,14 @@ def _monetary_chain_closure(
|
|
|
2162
2411
|
if action.event_id in required_ids]
|
|
2163
2412
|
|
|
2164
2413
|
|
|
2165
|
-
def _causal_snapshot_closure(actions, reviewed_by_id, current_events
|
|
2166
|
-
|
|
2414
|
+
def _causal_snapshot_closure(actions, reviewed_by_id, current_events,
|
|
2415
|
+
protected=frozenset()):
|
|
2416
|
+
"""Close explicit references, keeping a selected dependency if needed.
|
|
2417
|
+
|
|
2418
|
+
#797/#823/#840 spec 4.7 (D4): a tombstone in ``protected`` (a retraction's
|
|
2419
|
+
own scope) is never cancelled to keep a milestone that still names it —
|
|
2420
|
+
the retraction wins, so such a milestone is a conflict here (the planner
|
|
2421
|
+
never pins one, `_preserve_reviewed_hold_dependencies`)."""
|
|
2167
2422
|
action_by_id = {action.event_id: action for action in actions}
|
|
2168
2423
|
dependency_tombstones = set()
|
|
2169
2424
|
for action in actions:
|
|
@@ -2177,6 +2432,10 @@ def _causal_snapshot_closure(actions, reviewed_by_id, current_events):
|
|
|
2177
2432
|
dependency_action = action_by_id.get(reference)
|
|
2178
2433
|
if (dependency_action is not None
|
|
2179
2434
|
and dependency_action.disposition == "tombstone"):
|
|
2435
|
+
if reference in protected:
|
|
2436
|
+
raise RederiveConflict(
|
|
2437
|
+
"a retracted dependency's tombstone would be cancelled "
|
|
2438
|
+
"by milestone: " + action.event_id)
|
|
2180
2439
|
current = current_events.get(reference)
|
|
2181
2440
|
if ((current or {}).get("payload") or {}).get("kind") \
|
|
2182
2441
|
!= expected_kind:
|
|
@@ -2215,6 +2474,34 @@ def _causal_snapshot_closure(actions, reviewed_by_id, current_events):
|
|
|
2215
2474
|
return actions
|
|
2216
2475
|
|
|
2217
2476
|
|
|
2477
|
+
def _retained_reference_closure(actions, reviewed_by_id, current_events):
|
|
2478
|
+
"""#797/#823/#840 spec 4.7: unchanged retained references are validated
|
|
2479
|
+
as well as proposed actions. A current milestone the delta does not act
|
|
2480
|
+
on but which names an event the delta tombstones takes its reviewed
|
|
2481
|
+
action; with none, the plan would leave it dangling and refuses."""
|
|
2482
|
+
acted = {action.event_id for action in actions}
|
|
2483
|
+
tombstoned = {action.event_id for action in actions
|
|
2484
|
+
if action.disposition == "tombstone"}
|
|
2485
|
+
if not tombstoned:
|
|
2486
|
+
return actions
|
|
2487
|
+
for event_id, record in sorted(current_events.items()):
|
|
2488
|
+
payload = (record or {}).get("payload") or {}
|
|
2489
|
+
kind = payload.get("kind")
|
|
2490
|
+
if event_id in acted or kind not in _SNAPSHOT_DEPENDENT_KINDS:
|
|
2491
|
+
continue
|
|
2492
|
+
if not any(payload.get(field) in tombstoned
|
|
2493
|
+
for field in _milestone_dependencies(kind)):
|
|
2494
|
+
continue
|
|
2495
|
+
reviewed = reviewed_by_id.get(event_id)
|
|
2496
|
+
if reviewed is None:
|
|
2497
|
+
raise RederiveConflict(
|
|
2498
|
+
"a retained milestone would name a tombstoned event: "
|
|
2499
|
+
+ event_id)
|
|
2500
|
+
actions.append(reviewed)
|
|
2501
|
+
acted.add(event_id)
|
|
2502
|
+
return actions
|
|
2503
|
+
|
|
2504
|
+
|
|
2218
2505
|
def causal_delta_plan(
|
|
2219
2506
|
baseline: RederivePlan, reviewed: RederivePlan, *,
|
|
2220
2507
|
current_events: Mapping[str, Mapping],
|
|
@@ -2250,6 +2537,7 @@ def causal_delta_plan(
|
|
|
2250
2537
|
}
|
|
2251
2538
|
authorized_week_reset_origins = frozenset(
|
|
2252
2539
|
authorized_week_reset_origins or ())
|
|
2540
|
+
retraction_scope = frozenset(reviewed.retraction_event_ids or ())
|
|
2253
2541
|
actions = [
|
|
2254
2542
|
action for action in reviewed.actions
|
|
2255
2543
|
if (
|
|
@@ -2261,6 +2549,9 @@ def causal_delta_plan(
|
|
|
2261
2549
|
and (action.payload or {}).get("origin_observation_id")
|
|
2262
2550
|
in authorized_week_reset_origins
|
|
2263
2551
|
)
|
|
2552
|
+
# #797/#823/#840 spec 4.7: an explicit retraction survives the
|
|
2553
|
+
# baseline/reviewed intersection.
|
|
2554
|
+
or action.event_id in retraction_scope
|
|
2264
2555
|
)
|
|
2265
2556
|
]
|
|
2266
2557
|
# Close block, monetary and explicit-reference edges to a fixed point.
|
|
@@ -2272,7 +2563,11 @@ def causal_delta_plan(
|
|
|
2272
2563
|
actions = _monetary_chain_closure(
|
|
2273
2564
|
actions, reviewed.actions, current_events)
|
|
2274
2565
|
actions = _causal_snapshot_closure(
|
|
2275
|
-
actions, reviewed_by_id, current_events
|
|
2566
|
+
actions, reviewed_by_id, current_events,
|
|
2567
|
+
protected=retraction_scope)
|
|
2568
|
+
if retraction_scope:
|
|
2569
|
+
actions = _retained_reference_closure(
|
|
2570
|
+
actions, reviewed_by_id, current_events)
|
|
2276
2571
|
if {action.event_id for action in actions} == previous_ids:
|
|
2277
2572
|
break
|
|
2278
2573
|
included_ids = {action.event_id for action in actions}
|
|
@@ -2303,6 +2598,7 @@ def causal_delta_plan(
|
|
|
2303
2598
|
preserved_event_count=reviewed.preserved_event_count,
|
|
2304
2599
|
uncertain_cost_fact_count=reviewed.uncertain_cost_fact_count,
|
|
2305
2600
|
action_counts_by_event_kind=counts_by_kind,
|
|
2601
|
+
retraction_event_ids=reviewed.retraction_event_ids,
|
|
2306
2602
|
)
|
|
2307
2603
|
|
|
2308
2604
|
|
|
@@ -1887,9 +1887,51 @@ def doctor_payload_memo(
|
|
|
1887
1887
|
|
|
1888
1888
|
|
|
1889
1889
|
def reset_doctor_memo() -> None:
|
|
1890
|
-
"""Drop the memoized doctor payload
|
|
1890
|
+
"""Drop the memoized doctor payload and the deferred frames' memoized
|
|
1891
|
+
``rebuild_worker`` value (test hook + M5 invalidation entry)."""
|
|
1891
1892
|
with _DOCTOR_MEMO_LOCK:
|
|
1892
1893
|
_DOCTOR_MEMO.clear()
|
|
1894
|
+
_REBUILD_WORKER_MEMO.clear()
|
|
1895
|
+
|
|
1896
|
+
|
|
1897
|
+
# #997 D10: while an epoch rebuild is pending or running every stats open
|
|
1898
|
+
# defers, so the dashboard serves only the deferred startup frame and deferred
|
|
1899
|
+
# ticks, neither of which runs the doctor gather. Their doctor block still
|
|
1900
|
+
# carries the `db.rebuild_worker` aggregate, from the worker snapshots alone
|
|
1901
|
+
# (file reads and a `ps` probe per pid; no stats open, no cctally lock). This
|
|
1902
|
+
# memo keeps a deferred tick from forking `ps` every tick, on the doctor
|
|
1903
|
+
# payload's TTL and freshness rule. Same injected-`compute` decoupling.
|
|
1904
|
+
REBUILD_WORKER_MEMO_TTL_S = DOCTOR_MEMO_TTL_S
|
|
1905
|
+
|
|
1906
|
+
_REBUILD_WORKER_MEMO: "dict" = {}
|
|
1907
|
+
|
|
1908
|
+
|
|
1909
|
+
def rebuild_worker_memo(
|
|
1910
|
+
now_utc: dt.datetime,
|
|
1911
|
+
*,
|
|
1912
|
+
ttl_s: float,
|
|
1913
|
+
compute: "Callable[[dt.datetime], dict | None]",
|
|
1914
|
+
) -> "dict | None":
|
|
1915
|
+
"""Return the deferred frames' ``rebuild_worker`` value, recomputing via
|
|
1916
|
+
``compute(now_utc)`` only when the memo is cold: never computed, older
|
|
1917
|
+
than ``ttl_s``, or a clock regression. ``compute`` runs outside the
|
|
1918
|
+
lock, exactly as in `doctor_payload_memo`."""
|
|
1919
|
+
with _DOCTOR_MEMO_LOCK:
|
|
1920
|
+
cached = _REBUILD_WORKER_MEMO
|
|
1921
|
+
computed_at = cached.get("computed_at")
|
|
1922
|
+
fresh = (
|
|
1923
|
+
computed_at is not None
|
|
1924
|
+
and now_utc >= computed_at
|
|
1925
|
+
and (now_utc - computed_at).total_seconds() < ttl_s
|
|
1926
|
+
)
|
|
1927
|
+
if fresh:
|
|
1928
|
+
return cached["value"]
|
|
1929
|
+
value = compute(now_utc)
|
|
1930
|
+
with _DOCTOR_MEMO_LOCK:
|
|
1931
|
+
_REBUILD_WORKER_MEMO.clear()
|
|
1932
|
+
_REBUILD_WORKER_MEMO["value"] = value
|
|
1933
|
+
_REBUILD_WORKER_MEMO["computed_at"] = now_utc
|
|
1934
|
+
return value
|
|
1893
1935
|
|
|
1894
1936
|
|
|
1895
1937
|
# === Task 5.1 — idle-path dispatch state (last signature + snapshot) ========
|