cctally 1.116.1 → 1.117.2
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 +57 -3
- package/bin/_cctally_cache.py +5720 -885
- package/bin/_cctally_claude_attribution.py +997 -0
- package/bin/_cctally_completion.py +101 -28
- package/bin/_cctally_core.py +235 -12
- package/bin/_cctally_dashboard.py +263 -17
- package/bin/_cctally_dashboard_conversation.py +107 -21
- package/bin/_cctally_dashboard_envelope.py +44 -0
- package/bin/_cctally_db.py +928 -80
- package/bin/_cctally_diagnosis_sources.py +78 -13
- package/bin/_cctally_doctor.py +211 -86
- package/bin/_cctally_five_hour.py +446 -22
- package/bin/_cctally_forecast.py +302 -6
- package/bin/_cctally_journal.py +2418 -158
- package/bin/_cctally_milestone_history.py +379 -91
- package/bin/_cctally_oauth_samples.py +185 -7
- package/bin/_cctally_parser.py +38 -0
- package/bin/_cctally_project.py +34 -8
- package/bin/_cctally_quarantine.py +821 -343
- package/bin/_cctally_quota.py +69 -4
- package/bin/_cctally_quota_calibration.py +677 -3
- package/bin/_cctally_quota_model.py +927 -127
- package/bin/_cctally_record.py +922 -445
- package/bin/_cctally_rederive.py +119 -15
- package/bin/_cctally_statusline.py +1048 -176
- package/bin/_cctally_store.py +544 -110
- package/bin/_cctally_tui.py +455 -42
- package/bin/_cctally_update.py +77 -14
- package/bin/_lib_account_protocol.py +227 -19
- package/bin/_lib_claude_attribution.py +421 -0
- package/bin/_lib_clone_copy.py +30 -0
- package/bin/_lib_codex_conversation_query.py +45 -9
- package/bin/_lib_codex_conversation_watch.py +72 -13
- package/bin/_lib_codex_rollout_container.py +234 -19
- package/bin/_lib_codex_worker_lifecycle.py +374 -0
- package/bin/_lib_credit.py +22 -13
- package/bin/_lib_dashboard_sources.py +257 -18
- package/bin/_lib_detector_access_registry.py +1335 -166
- package/bin/_lib_doctor.py +276 -92
- package/bin/_lib_ingest_frontier.py +269 -36
- package/bin/_lib_journal.py +273 -22
- package/bin/_lib_jsonl.py +12 -0
- package/bin/_lib_meter_rate_change.py +72 -0
- package/bin/_lib_outcome_quarantine.py +917 -554
- package/bin/_lib_provider_activity.py +24 -6
- package/bin/_lib_quota_calibration.py +57 -3
- package/bin/_lib_quota_copy.py +7 -2
- package/bin/_lib_quota_lineage.py +613 -0
- package/bin/_lib_quota_model.py +464 -33
- package/bin/_lib_rate_change_delivery.py +16 -0
- package/bin/_lib_reclaim_planner.py +6 -4
- package/bin/_lib_rederive.py +2046 -21
- package/bin/_lib_selector_state.py +131 -3
- package/bin/_lib_snapshot_cache.py +8 -0
- package/bin/_lib_stale_proof.py +113 -4
- package/bin/_lib_statusline.py +72 -3
- package/bin/_lib_statusline_candidates.py +246 -42
- package/bin/_lib_tick_stats.py +10 -5
- package/bin/_lib_write_io.py +419 -6
- package/bin/cctally +28 -11
- package/dashboard/static/assets/{ConversationsView-Dflb_myA.js → ConversationsView-oqNsWzWV.js} +1 -1
- package/dashboard/static/assets/{DoctorModal-DFX5CfIs.js → DoctorModal-B0DnU9d9.js} +1 -1
- package/dashboard/static/assets/ModalRoot-CYua2wP_.js +1 -0
- package/dashboard/static/assets/{ProjectsDrillPanel-C3PJy1xx.js → ProjectsDrillPanel-DSxXQSa-.js} +1 -1
- package/dashboard/static/assets/{SourceDetailModal-BczsWKhD.js → SourceDetailModal-BleDpQqG.js} +1 -1
- package/dashboard/static/assets/{UpdateModal-Os-Lo5cs.js → UpdateModal-CMqaAkSK.js} +1 -1
- package/dashboard/static/assets/{index-CYZ3NLqF.js → index-BjQV-0-D.js} +3 -3
- package/dashboard/static/assets/index-D34eYhAc.css +1 -0
- package/dashboard/static/assets/{outlineNavigation-k-zIif6W.js → outlineNavigation-CO-VWBm7.js} +1 -1
- package/dashboard/static/assets/{useKeymap-CW7Up0Ay.js → useKeymap-CUgEnnua.js} +1 -1
- package/dashboard/static/dashboard.html +3 -3
- package/package.json +5 -1
- package/dashboard/static/assets/ModalRoot-HlxoagIa.js +0 -1
- package/dashboard/static/assets/index-BKITk1CT.css +0 -1
|
@@ -50,7 +50,15 @@ current decision can be appended concurrently). It
|
|
|
50
50
|
identical decision → nothing; only an excluded legacy decision, or an
|
|
51
51
|
admitted decision the gap changes → one completed correction batch whose
|
|
52
52
|
action is above every retained revision of the id; an admitted decision the
|
|
53
|
-
gap removes → a tombstone in that batch
|
|
53
|
+
gap removes → a tombstone in that batch. Closed five-hour money stays
|
|
54
|
+
frozen (#1040): for every block the gap touches that holds a closed close,
|
|
55
|
+
`_lib_rederive.plan_completion_five_hour` decides the close and milestone
|
|
56
|
+
records instead. A close keeps its recorded money group unless the gap
|
|
57
|
+
changes the block's entry population (or a recorded missing card is
|
|
58
|
+
corrected), and such a change lands only paired with its milestones in the
|
|
59
|
+
same batch; every candidate is checked against the #884 milestone lineage,
|
|
60
|
+
and none that makes a milestone's decision worse than the gap's required
|
|
61
|
+
additions alone is appended;
|
|
54
62
|
5. rebuilds the live index over the result. The consumed-input frontier stays
|
|
55
63
|
distinct from output positions: inputs another process appended between
|
|
56
64
|
the window and the appended outputs are completed in a further round, and
|
|
@@ -893,6 +901,9 @@ def _apply_owed_outputs(base, start, output_end, boundary) -> frozenset:
|
|
|
893
901
|
backfill(conn, only_missing=True,
|
|
894
902
|
journaled_first=(
|
|
895
903
|
_journal.journaled_block_first_observations(
|
|
904
|
+
prefix.slots, selection.by_id)),
|
|
905
|
+
retired_openings=(
|
|
906
|
+
_journal.retired_block_openings(
|
|
896
907
|
prefix.slots, selection.by_id)))
|
|
897
908
|
conn.execute("BEGIN IMMEDIATE")
|
|
898
909
|
try:
|
|
@@ -1246,8 +1257,16 @@ def _complete_window(base, tmp, start, target, boundary, round_index, *,
|
|
|
1246
1257
|
if record is not None:
|
|
1247
1258
|
_journal._normalize_legacy_account_stamp(
|
|
1248
1259
|
record, prefix.cutover_claude)
|
|
1260
|
+
accumulators: dict = {}
|
|
1249
1261
|
selection = _lib_journal.resolve_effective_events(
|
|
1250
|
-
prefix.slots, protocol_prefix_evidence=tuple(prefix.evidence)
|
|
1262
|
+
prefix.slots, protocol_prefix_evidence=tuple(prefix.evidence),
|
|
1263
|
+
accumulators=accumulators)
|
|
1264
|
+
# #1040 (spec §5.1): the #884 lineage, built exactly as `db rederive`
|
|
1265
|
+
# builds it; the fold is released before the replays.
|
|
1266
|
+
lineage = _lib_rederive.build_five_hour_lineage(
|
|
1267
|
+
accumulators.pop("fold"), selection=selection,
|
|
1268
|
+
physical_revisions=prefix.excluded_revisions)
|
|
1269
|
+
del accumulators
|
|
1251
1270
|
# Inert (incomplete or tainted) batch actions of OTHER families still
|
|
1252
1271
|
# occupy their revision: a later retry of that batch must not collide.
|
|
1253
1272
|
for record in prefix.slots:
|
|
@@ -1288,45 +1307,94 @@ def _complete_window(base, tmp, start, target, boundary, round_index, *,
|
|
|
1288
1307
|
with_gap = _owned_by_id(events_b)
|
|
1289
1308
|
without_gap = _owned_by_id(events_a)
|
|
1290
1309
|
|
|
1310
|
+
def physical_of(event_id):
|
|
1311
|
+
selected = selection.by_id.get(event_id)
|
|
1312
|
+
return [rev for rev in (
|
|
1313
|
+
prefix.excluded_revisions.get(event_id),
|
|
1314
|
+
prefix.inert_revisions.get(event_id),
|
|
1315
|
+
None if selected is None else selected.rev,
|
|
1316
|
+
) if rev is not None]
|
|
1317
|
+
|
|
1318
|
+
def current_of(event_id):
|
|
1319
|
+
selected = selection.by_id.get(event_id)
|
|
1320
|
+
return (selected.record if selected is not None
|
|
1321
|
+
and selected.status == "active" else None)
|
|
1322
|
+
|
|
1323
|
+
acted = frozenset(
|
|
1324
|
+
event_id for event_id in set(with_gap) | set(without_gap)
|
|
1325
|
+
if not (event_id in with_gap and event_id in without_gap
|
|
1326
|
+
and _lib_rederive._semantic_event(with_gap[event_id])
|
|
1327
|
+
== _lib_rederive._semantic_event(without_gap[event_id])))
|
|
1328
|
+
retirements = []
|
|
1329
|
+
for event_id in sorted(acted):
|
|
1330
|
+
current = current_of(event_id)
|
|
1331
|
+
if (event_id not in with_gap and current is not None
|
|
1332
|
+
and (current.get("payload") or {}).get("kind")
|
|
1333
|
+
in _lib_rederive._LINEAGE_KINDS):
|
|
1334
|
+
retirements.append({"action": "tombstone", "id": event_id,
|
|
1335
|
+
"rev": max(physical_of(event_id)) + 1,
|
|
1336
|
+
"at": str(current["at"]), "payload": None})
|
|
1337
|
+
five_hour_current = {
|
|
1338
|
+
event_id: item.record for event_id, item in selection.by_id.items()
|
|
1339
|
+
if item.status == "active" and item.record is not None
|
|
1340
|
+
and (item.record.get("payload") or {}).get("kind")
|
|
1341
|
+
in _lib_rederive._LINEAGE_KINDS}
|
|
1342
|
+
physical = {}
|
|
1343
|
+
for event_id in set(five_hour_current) | set(with_gap) | set(without_gap):
|
|
1344
|
+
revisions = physical_of(event_id)
|
|
1345
|
+
if revisions:
|
|
1346
|
+
physical[event_id] = max(revisions)
|
|
1347
|
+
# #1040 (spec §5): closed five-hour money stays frozen. The planner owns
|
|
1348
|
+
# every five-hour id of a touched non-OPEN block; today's per-id path
|
|
1349
|
+
# keeps every other id, retirements included.
|
|
1350
|
+
plan = _lib_rederive.plan_completion_five_hour(
|
|
1351
|
+
current=five_hour_current, with_gap=with_gap,
|
|
1352
|
+
without_gap=without_gap, acted=acted, physical=physical,
|
|
1353
|
+
selection=selection, lineage=lineage, retirements=retirements)
|
|
1354
|
+
del lineage, five_hour_current
|
|
1355
|
+
|
|
1291
1356
|
plain: list = []
|
|
1292
1357
|
actions: list = []
|
|
1293
|
-
|
|
1358
|
+
|
|
1359
|
+
def append_desired(event_id, desired):
|
|
1360
|
+
"""The binding append policy for one desired record (spec §5.5)."""
|
|
1361
|
+
current = current_of(event_id)
|
|
1362
|
+
if (current is not None and _lib_rederive._semantic_event(current)
|
|
1363
|
+
== _lib_rederive._semantic_event(desired)):
|
|
1364
|
+
return # an admitted identical decision: nothing
|
|
1365
|
+
physical = physical_of(event_id)
|
|
1366
|
+
if not physical:
|
|
1367
|
+
plain.append(desired)
|
|
1368
|
+
return
|
|
1369
|
+
actions.append({"action": "replace", "id": event_id,
|
|
1370
|
+
"rev": max(physical) + 1,
|
|
1371
|
+
"at": str(desired["at"]),
|
|
1372
|
+
"payload": dict(desired.get("payload") or {})})
|
|
1373
|
+
|
|
1374
|
+
for event_id in sorted(set(with_gap) | set(without_gap)
|
|
1375
|
+
| set(plan.targets)):
|
|
1376
|
+
if event_id in plan.targets:
|
|
1377
|
+
append_desired(event_id, plan.targets[event_id])
|
|
1378
|
+
continue
|
|
1379
|
+
if event_id in plan.handled:
|
|
1380
|
+
continue
|
|
1294
1381
|
desired = with_gap.get(event_id)
|
|
1295
|
-
|
|
1296
|
-
if (desired is not None and reference is not None
|
|
1297
|
-
and _lib_rederive._semantic_event(desired)
|
|
1298
|
-
== _lib_rederive._semantic_event(reference)):
|
|
1382
|
+
if event_id not in acted:
|
|
1299
1383
|
# Not caused by the gap: an unrelated difference against the
|
|
1300
1384
|
# journal (a repricing, a previous release's input the current
|
|
1301
1385
|
# pipeline extends) is never acted on.
|
|
1302
1386
|
continue
|
|
1303
|
-
|
|
1304
|
-
current = (selected.record if selected is not None
|
|
1305
|
-
and selected.status == "active" else None)
|
|
1306
|
-
physical = [rev for rev in (
|
|
1307
|
-
prefix.excluded_revisions.get(event_id),
|
|
1308
|
-
prefix.inert_revisions.get(event_id),
|
|
1309
|
-
None if selected is None else selected.rev,
|
|
1310
|
-
) if rev is not None]
|
|
1387
|
+
current = current_of(event_id)
|
|
1311
1388
|
if desired is None:
|
|
1312
1389
|
if current is not None:
|
|
1313
1390
|
actions.append({"action": "tombstone", "id": event_id,
|
|
1314
|
-
"rev": max(
|
|
1391
|
+
"rev": max(physical_of(event_id)) + 1,
|
|
1315
1392
|
"at": str(current["at"]), "payload": None})
|
|
1316
1393
|
continue
|
|
1317
1394
|
if current is not None:
|
|
1318
1395
|
desired = _lib_rederive._preserve_non_derivable_state(
|
|
1319
1396
|
current, desired)
|
|
1320
|
-
|
|
1321
|
-
== _lib_rederive._semantic_event(desired)):
|
|
1322
|
-
continue # an admitted identical decision: nothing
|
|
1323
|
-
if not physical:
|
|
1324
|
-
plain.append(desired)
|
|
1325
|
-
continue
|
|
1326
|
-
actions.append({"action": "replace", "id": event_id,
|
|
1327
|
-
"rev": max(physical) + 1,
|
|
1328
|
-
"at": str(desired["at"]),
|
|
1329
|
-
"payload": dict(desired.get("payload") or {})})
|
|
1397
|
+
append_desired(event_id, desired)
|
|
1330
1398
|
|
|
1331
1399
|
records: list = []
|
|
1332
1400
|
if actions:
|
|
@@ -1344,8 +1412,13 @@ def _complete_window(base, tmp, start, target, boundary, round_index, *,
|
|
|
1344
1412
|
# are byte-identical and the selector keeps each once.
|
|
1345
1413
|
at=str(prefix.begin_at.get(batch_id) or _iso_now()),
|
|
1346
1414
|
actions=actions))
|
|
1347
|
-
order
|
|
1348
|
-
|
|
1415
|
+
# Derivation order of each id's first with-gap candidate (the one
|
|
1416
|
+
# `_owned_by_id` keeps), so a planner target sorts where its id was
|
|
1417
|
+
# derived, exactly like the candidate it stands for.
|
|
1418
|
+
order: dict = {}
|
|
1419
|
+
for index, event in enumerate(events_b):
|
|
1420
|
+
order.setdefault(event.get("id"), index)
|
|
1421
|
+
plain.sort(key=lambda event: order.get(event.get("id"), 0))
|
|
1349
1422
|
records.extend(plain)
|
|
1350
1423
|
if not records:
|
|
1351
1424
|
return False
|
package/bin/_cctally_core.py
CHANGED
|
@@ -79,6 +79,7 @@ def _init_paths_from_env() -> None:
|
|
|
79
79
|
global STATUSLINE_ACCOUNT_BOUND_DIR, LEGACY_SPOOL_BASELINE_PATH
|
|
80
80
|
global ACCOUNT_BOUND_CUTOVER_CACHE_PATH, LEGACY_WRITER_EVIDENCE_PATH
|
|
81
81
|
global STATUSLINE_ACCOUNT_BOUND_V1_DIR, ACCOUNT_BOUND_REVISION_CACHE_PATH
|
|
82
|
+
global STATUSLINE_ACCOUNT_BOUND_V2_DIR, ACCOUNT_BOUND_REVISION3_CACHE_PATH
|
|
82
83
|
global OAUTH_BACKOFF_MARKER_PATH, OAUTH_BACKOFF_COUNT_PATH
|
|
83
84
|
global UPDATE_STATE_PATH, UPDATE_SUPPRESS_PATH
|
|
84
85
|
global UPDATE_LOCK_PATH, UPDATE_LOG_PATH, UPDATE_LOG_ROTATED_PATH
|
|
@@ -202,8 +203,15 @@ def _init_paths_from_env() -> None:
|
|
|
202
203
|
# `accounts/` tree and `transport.last` join the legacy spool inventory
|
|
203
204
|
# (`_cctally_journal._legacy_spool_paths`), and this binary never writes,
|
|
204
205
|
# prunes or deletes anything under it.
|
|
206
|
+
#
|
|
207
|
+
# #1019/#1014 (spec 4.7 C4): revision 3 moves every current coordination
|
|
208
|
+
# file again, to the v3 root, for the same reason; no v2 state is
|
|
209
|
+
# imported, the v2 root's `accounts/` tree and `transport.last` join the
|
|
210
|
+
# legacy spool inventory, and this binary never writes, prunes or deletes
|
|
211
|
+
# anything under v2 (or v1).
|
|
205
212
|
STATUSLINE_ACCOUNT_BOUND_V1_DIR = APP_DIR / "statusline-account-bound-v1"
|
|
206
|
-
|
|
213
|
+
STATUSLINE_ACCOUNT_BOUND_V2_DIR = APP_DIR / "statusline-account-bound-v2"
|
|
214
|
+
STATUSLINE_ACCOUNT_BOUND_DIR = APP_DIR / "statusline-account-bound-v3"
|
|
207
215
|
LEGACY_SPOOL_BASELINE_PATH = (
|
|
208
216
|
STATUSLINE_ACCOUNT_BOUND_DIR / "legacy-spool-baseline.json")
|
|
209
217
|
ACCOUNT_BOUND_CUTOVER_CACHE_PATH = (
|
|
@@ -212,6 +220,10 @@ def _init_paths_from_env() -> None:
|
|
|
212
220
|
#: accelerates the lookup and never confers authority.
|
|
213
221
|
ACCOUNT_BOUND_REVISION_CACHE_PATH = (
|
|
214
222
|
STATUSLINE_ACCOUNT_BOUND_DIR / "revision-coordinate.json")
|
|
223
|
+
#: The revision-3 boundary's coordinate cache (#1019/#1014), likewise an
|
|
224
|
+
#: accelerator only.
|
|
225
|
+
ACCOUNT_BOUND_REVISION3_CACHE_PATH = (
|
|
226
|
+
STATUSLINE_ACCOUNT_BOUND_DIR / "revision3-coordinate.json")
|
|
215
227
|
LEGACY_WRITER_EVIDENCE_PATH = (
|
|
216
228
|
STATUSLINE_ACCOUNT_BOUND_DIR / "legacy-writer-evidence.json")
|
|
217
229
|
OAUTH_BACKOFF_MARKER_PATH = APP_DIR / "oauth-backoff.until"
|
|
@@ -554,7 +566,40 @@ _init_paths_from_env()
|
|
|
554
566
|
# `baseline_generation`, `pending_generation`, `pending_position`) that the
|
|
555
567
|
# five-hour credit generation (Task 5) writes. Both projections are rebuilt
|
|
556
568
|
# from the journal; the confirmation state stays disposable.
|
|
557
|
-
|
|
569
|
+
# 1019 -> 1020 (#1019/#1014 spec 4.7 C6): revision 3 of the fence changes the
|
|
570
|
+
# authoritative interpretation once more — after the
|
|
571
|
+
# `account_bound_revision_v3` boundary a Claude-scoped record without
|
|
572
|
+
# `wpx == 3` is excluded — so an index, a derivation-completeness proof or a
|
|
573
|
+
# selector continuation produced under revision 2 is not current. No
|
|
574
|
+
# numbered migration and no accounting column; the rebuild runs under #997's
|
|
575
|
+
# bounded profile.
|
|
576
|
+
# 1020 -> 1021 (#943/#956 spec 4.7): a five-hour credit's exact boundary.
|
|
577
|
+
# `five_hour_credit_confirmation_state` gains `pending_captured_at_utc` (the
|
|
578
|
+
# arming observation's capture second, recorded by both detectors) and the
|
|
579
|
+
# new harvest family `five_hour_credit_boundaries` (`fhcb`, order 29) retains
|
|
580
|
+
# that second beside the credit, whose floored slot stays its identity. Every
|
|
581
|
+
# post-credit read compares against the resolved boundary, so an index
|
|
582
|
+
# materialized under the floored band is not current. No numbered migration;
|
|
583
|
+
# `DERIVATION_FRONTIER_EPOCH` is unchanged.
|
|
584
|
+
# The same epoch also covers #956 class 2 (spec 4.6): every five-hour block
|
|
585
|
+
# creation retains an opening fact in `five_hour_block_openings` (`fhbo`,
|
|
586
|
+
# order 35), whose fold materializes the open block, and open-block end
|
|
587
|
+
# columns come from one capture-order projection.
|
|
588
|
+
# 1021 -> 1022 (#1015 spec 5.1, 5.7): correction-to-fit lineage. New
|
|
589
|
+
# journal-truth projections `claude_weekly_observation_index`,
|
|
590
|
+
# `claude_weekly_correction_history`, `claude_weekly_correction_ops`,
|
|
591
|
+
# `correction_batch_lineage` and `correction_batch_footprint`, the
|
|
592
|
+
# `meter_rate_change_supersessions` table and the NULLABLE
|
|
593
|
+
# `meter_rate_change_events.fit_lineage` column, so an index built without
|
|
594
|
+
# them is not current. No numbered migration; `DERIVATION_FRONTIER_EPOCH` is
|
|
595
|
+
# unchanged.
|
|
596
|
+
# 1022 -> 1023 (#948 spec 6.11): per-axis automatic recovery from the
|
|
597
|
+
# outcome quarantine. `outcome_quarantine_envelope` and
|
|
598
|
+
# `outcome_quarantine_certificates` are replaced by
|
|
599
|
+
# `outcome_quarantine_episodes`, `outcome_quarantine_bounds` and
|
|
600
|
+
# `outcome_quarantine_axis_state`, so an index built without them is not
|
|
601
|
+
# current. No numbered migration; `DERIVATION_FRONTIER_EPOCH` is unchanged.
|
|
602
|
+
STATS_INDEX_EPOCH = 1023
|
|
558
603
|
LEGACY_STATS_HEAD = 13
|
|
559
604
|
|
|
560
605
|
|
|
@@ -1870,11 +1915,29 @@ def _apply_quota_projection_schema(conn: sqlite3.Connection) -> None:
|
|
|
1870
1915
|
detector_input_causes TEXT,
|
|
1871
1916
|
composition_provenance TEXT,
|
|
1872
1917
|
baseline_withheld_days INTEGER,
|
|
1918
|
+
fit_lineage TEXT,
|
|
1873
1919
|
UNIQUE(provider, account_key, effective_from)
|
|
1874
1920
|
);
|
|
1875
1921
|
CREATE INDEX IF NOT EXISTS idx_meter_rate_change_events_key
|
|
1876
1922
|
ON meter_rate_change_events(provider, account_key, effective_from);
|
|
1877
1923
|
|
|
1924
|
+
-- #1015 epoch 1022 (spec 5.7). `fit_lineage` above is the canonical
|
|
1925
|
+
-- JSON lineage of the fits a transition compared, NULL on a row
|
|
1926
|
+
-- detected before #1015. `meter_rate_change_supersessions` is the
|
|
1927
|
+
-- terminal, idempotent projection of the journalled
|
|
1928
|
+
-- `meter_rate_change_supersession` event: one row per superseded
|
|
1929
|
+
-- transition identity, `corrections` the canonical JSON op ids.
|
|
1930
|
+
CREATE TABLE IF NOT EXISTS meter_rate_change_supersessions (
|
|
1931
|
+
provider TEXT NOT NULL,
|
|
1932
|
+
account_key TEXT NOT NULL,
|
|
1933
|
+
effective_from TEXT NOT NULL,
|
|
1934
|
+
reason TEXT NOT NULL,
|
|
1935
|
+
corrections TEXT NOT NULL,
|
|
1936
|
+
superseded_at_utc TEXT NOT NULL,
|
|
1937
|
+
created_at_utc TEXT NOT NULL,
|
|
1938
|
+
PRIMARY KEY (provider, account_key, effective_from)
|
|
1939
|
+
);
|
|
1940
|
+
|
|
1878
1941
|
CREATE TABLE IF NOT EXISTS quota_alert_arming (
|
|
1879
1942
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1880
1943
|
source TEXT NOT NULL,
|
|
@@ -2747,6 +2810,7 @@ def open_db(*, _target_path=None) -> sqlite3.Connection:
|
|
|
2747
2810
|
baseline_generation TEXT,
|
|
2748
2811
|
pending_generation TEXT,
|
|
2749
2812
|
pending_position TEXT,
|
|
2813
|
+
pending_captured_at_utc TEXT,
|
|
2750
2814
|
PRIMARY KEY (account_key, five_hour_window_key, source)
|
|
2751
2815
|
)
|
|
2752
2816
|
"""
|
|
@@ -2802,6 +2866,78 @@ def open_db(*, _target_path=None) -> sqlite3.Connection:
|
|
|
2802
2866
|
)
|
|
2803
2867
|
"""
|
|
2804
2868
|
)
|
|
2869
|
+
# #1015 epoch 1022 (spec 5.1): correction-to-fit lineage facts, all
|
|
2870
|
+
# journal-truth projections filled identically by live ingest, the
|
|
2871
|
+
# scratch replays and both apply-only rebuilds.
|
|
2872
|
+
#
|
|
2873
|
+
# `claude_weekly_observation_index`: one row per admitted Claude usage
|
|
2874
|
+
# observation carrying a weekly reading, projected before snapshot
|
|
2875
|
+
# deduplication, the five-hour adjustment, weekly suppression and
|
|
2876
|
+
# credit detection, never pruned. `account_key` is the account live
|
|
2877
|
+
# ingest reads after the cutover-aware legacy normalization;
|
|
2878
|
+
# `captured_at_utc` is canonical full-precision UTC.
|
|
2879
|
+
conn.execute(
|
|
2880
|
+
"""
|
|
2881
|
+
CREATE TABLE IF NOT EXISTS claude_weekly_observation_index (
|
|
2882
|
+
observation_id TEXT PRIMARY KEY,
|
|
2883
|
+
account_key TEXT NOT NULL,
|
|
2884
|
+
captured_at_utc TEXT NOT NULL
|
|
2885
|
+
)
|
|
2886
|
+
"""
|
|
2887
|
+
)
|
|
2888
|
+
conn.execute(
|
|
2889
|
+
"CREATE INDEX IF NOT EXISTS idx_claude_weekly_observation_index_window "
|
|
2890
|
+
"ON claude_weekly_observation_index (account_key, captured_at_utc)"
|
|
2891
|
+
)
|
|
2892
|
+
# Every decision of every reviewed weekly op (not only the latest),
|
|
2893
|
+
# with the DECODED disposition: a `weekly_axis_retractions` member is
|
|
2894
|
+
# `retract`. Idempotent under the primary key.
|
|
2895
|
+
conn.execute(
|
|
2896
|
+
"""
|
|
2897
|
+
CREATE TABLE IF NOT EXISTS claude_weekly_correction_history (
|
|
2898
|
+
decision_op_id TEXT NOT NULL,
|
|
2899
|
+
observation_id TEXT NOT NULL,
|
|
2900
|
+
disposition TEXT NOT NULL CHECK(disposition IN ('hold','retract','accept')),
|
|
2901
|
+
PRIMARY KEY (decision_op_id, observation_id)
|
|
2902
|
+
)
|
|
2903
|
+
"""
|
|
2904
|
+
)
|
|
2905
|
+
# Where each reviewed op sits in the journal: the END coordinate of
|
|
2906
|
+
# its line, comparable through `journal_coordinate_key`. Scratch
|
|
2907
|
+
# replays never write it.
|
|
2908
|
+
conn.execute(
|
|
2909
|
+
"""
|
|
2910
|
+
CREATE TABLE IF NOT EXISTS claude_weekly_correction_ops (
|
|
2911
|
+
decision_op_id TEXT PRIMARY KEY,
|
|
2912
|
+
op_segment TEXT NOT NULL,
|
|
2913
|
+
op_offset INTEGER NOT NULL
|
|
2914
|
+
)
|
|
2915
|
+
"""
|
|
2916
|
+
)
|
|
2917
|
+
# Completed correction batches (a row means completed), at the END
|
|
2918
|
+
# coordinate of the commit marker, and the `(provider, account_key)`
|
|
2919
|
+
# pairs each one touched (`_lib_quota_lineage.batch_footprint`); an
|
|
2920
|
+
# empty batch has no footprint rows, `('*', '*')` only when ownership
|
|
2921
|
+
# is genuinely unknowable. Owned by lineage, never by the selector.
|
|
2922
|
+
conn.execute(
|
|
2923
|
+
"""
|
|
2924
|
+
CREATE TABLE IF NOT EXISTS correction_batch_lineage (
|
|
2925
|
+
batch_id TEXT PRIMARY KEY,
|
|
2926
|
+
commit_segment TEXT NOT NULL,
|
|
2927
|
+
commit_offset INTEGER NOT NULL
|
|
2928
|
+
)
|
|
2929
|
+
"""
|
|
2930
|
+
)
|
|
2931
|
+
conn.execute(
|
|
2932
|
+
"""
|
|
2933
|
+
CREATE TABLE IF NOT EXISTS correction_batch_footprint (
|
|
2934
|
+
batch_id TEXT NOT NULL,
|
|
2935
|
+
provider TEXT NOT NULL,
|
|
2936
|
+
account_key TEXT NOT NULL,
|
|
2937
|
+
PRIMARY KEY (batch_id, provider, account_key)
|
|
2938
|
+
)
|
|
2939
|
+
"""
|
|
2940
|
+
)
|
|
2805
2941
|
_backfill_week_reset_events(conn)
|
|
2806
2942
|
|
|
2807
2943
|
# ── five_hour_reset_events (Anthropic-issued in-place 5h credits) ──
|
|
@@ -2845,6 +2981,64 @@ def open_db(*, _target_path=None) -> sqlite3.Connection:
|
|
|
2845
2981
|
conn, "five_hour_reset_events", "account_key",
|
|
2846
2982
|
"TEXT NOT NULL DEFAULT 'unattributed'")
|
|
2847
2983
|
|
|
2984
|
+
# ── five_hour_credit_boundaries (#943 spec 4.2.2, epoch 1021) ──
|
|
2985
|
+
# The exact boundary of one five-hour credit: the arming
|
|
2986
|
+
# observation's capture second. A companion of the credit keyed by
|
|
2987
|
+
# its natural key (`effective_reset_at_utc` is the credit's floored
|
|
2988
|
+
# slot, a link without a formal FK) and bound to its content through
|
|
2989
|
+
# `credit_revision`; the boundary resolver
|
|
2990
|
+
# (`_cctally_record.resolve_five_hour_reset_event`) uses it only when
|
|
2991
|
+
# that revision matches the credit row's. Harvested as
|
|
2992
|
+
# `five_hour_credit_boundary` (`fhcb`, order 29), before its credit.
|
|
2993
|
+
conn.execute(
|
|
2994
|
+
"""
|
|
2995
|
+
CREATE TABLE IF NOT EXISTS five_hour_credit_boundaries (
|
|
2996
|
+
id INTEGER PRIMARY KEY,
|
|
2997
|
+
account_key TEXT NOT NULL,
|
|
2998
|
+
five_hour_window_key INTEGER NOT NULL,
|
|
2999
|
+
effective_reset_at_utc TEXT NOT NULL,
|
|
3000
|
+
boundary_at_utc TEXT NOT NULL,
|
|
3001
|
+
arming_observation_id TEXT NOT NULL,
|
|
3002
|
+
credit_revision TEXT NOT NULL,
|
|
3003
|
+
detected_at_utc TEXT NOT NULL,
|
|
3004
|
+
journal_id TEXT,
|
|
3005
|
+
UNIQUE(account_key, five_hour_window_key, effective_reset_at_utc)
|
|
3006
|
+
)
|
|
3007
|
+
"""
|
|
3008
|
+
)
|
|
3009
|
+
|
|
3010
|
+
# ── five_hour_block_openings (#956 spec 4.6.1, epoch 1021) ──
|
|
3011
|
+
# The opening fact of one five-hour block: the values the creating
|
|
3012
|
+
# upsert wrote (start columns, the fold-effective opening five-hour and
|
|
3013
|
+
# weekly readings with their held provenance, `created_at_utc` as
|
|
3014
|
+
# written) and the journal id of the observation or op that created
|
|
3015
|
+
# it. One row per block, inserted only by the live writer at creation
|
|
3016
|
+
# (`_cctally_record.maybe_update_five_hour_block`) and immutable.
|
|
3017
|
+
# Harvested as `five_hour_block_open` (`fhbo`, order 35); its fold
|
|
3018
|
+
# materializes the open block, so an apply-only replay reproduces a
|
|
3019
|
+
# block no surviving snapshot names.
|
|
3020
|
+
conn.execute(
|
|
3021
|
+
"""
|
|
3022
|
+
CREATE TABLE IF NOT EXISTS five_hour_block_openings (
|
|
3023
|
+
id INTEGER PRIMARY KEY,
|
|
3024
|
+
account_key TEXT NOT NULL,
|
|
3025
|
+
five_hour_window_key INTEGER NOT NULL,
|
|
3026
|
+
five_hour_resets_at TEXT NOT NULL,
|
|
3027
|
+
block_start_at TEXT NOT NULL,
|
|
3028
|
+
first_observed_at_utc TEXT NOT NULL,
|
|
3029
|
+
created_at_utc TEXT NOT NULL,
|
|
3030
|
+
opening_record_id TEXT NOT NULL,
|
|
3031
|
+
creation_kind TEXT NOT NULL,
|
|
3032
|
+
five_hour_percent REAL NOT NULL,
|
|
3033
|
+
seven_day_pct_at_block_start REAL,
|
|
3034
|
+
weekly_observation_held INTEGER NOT NULL DEFAULT 0,
|
|
3035
|
+
weekly_basis_captured_at_utc TEXT,
|
|
3036
|
+
journal_id TEXT,
|
|
3037
|
+
UNIQUE(account_key, five_hour_window_key)
|
|
3038
|
+
)
|
|
3039
|
+
"""
|
|
3040
|
+
)
|
|
3041
|
+
|
|
2848
3042
|
# ── five_hour_blocks (rollup, one row per API-anchored 5h block) ──
|
|
2849
3043
|
conn.execute(
|
|
2850
3044
|
"""
|
|
@@ -3312,6 +3506,7 @@ def open_db(*, _target_path=None) -> sqlite3.Connection:
|
|
|
3312
3506
|
"five_hour_reset_events", "five_hour_blocks", "weekly_credit_floors",
|
|
3313
3507
|
"percent_milestones", "five_hour_milestones", "budget_milestones",
|
|
3314
3508
|
"projected_milestones", "project_budget_milestones",
|
|
3509
|
+
"five_hour_credit_boundaries", "five_hour_block_openings",
|
|
3315
3510
|
):
|
|
3316
3511
|
add_column_if_missing(conn, _jtable, "journal_id", "TEXT")
|
|
3317
3512
|
conn.execute(
|
|
@@ -3323,13 +3518,14 @@ def open_db(*, _target_path=None) -> sqlite3.Connection:
|
|
|
3323
3518
|
# inserted this cycle (`WHERE journal_id IS NULL`). A partial index over
|
|
3324
3519
|
# exactly those un-stamped rows keeps that scan O(this-cycle inserts), not
|
|
3325
3520
|
# O(table) — at the 10x envelope the stamped rows are ~all of the table, so
|
|
3326
|
-
# a full scan would be pathological. Only the
|
|
3521
|
+
# a full scan would be pathological. Only the 10 HARVEST families need it
|
|
3327
3522
|
# (the Model-A / op-fold tables — weekly_usage_snapshots, weekly_cost_
|
|
3328
3523
|
# snapshots, weekly_credit_floors — are never harvest-scanned).
|
|
3329
3524
|
for _htable in (
|
|
3330
3525
|
"week_reset_events", "five_hour_reset_events", "five_hour_blocks",
|
|
3331
3526
|
"percent_milestones", "five_hour_milestones", "budget_milestones",
|
|
3332
3527
|
"projected_milestones", "project_budget_milestones",
|
|
3528
|
+
"five_hour_credit_boundaries", "five_hour_block_openings",
|
|
3333
3529
|
):
|
|
3334
3530
|
conn.execute(
|
|
3335
3531
|
f"CREATE INDEX IF NOT EXISTS idx_{_htable}_journal_id_null "
|
|
@@ -3531,18 +3727,45 @@ def open_db(*, _target_path=None) -> sqlite3.Connection:
|
|
|
3531
3727
|
"reason TEXT NOT NULL, "
|
|
3532
3728
|
"at TEXT)"
|
|
3533
3729
|
)
|
|
3730
|
+
# #948 epoch 1023 (spec 6.11): the per-axis recovery barrier replaces
|
|
3731
|
+
# the envelope and the certificates. Incomplete-inventory episodes by
|
|
3732
|
+
# the coordinate of their opener; the per-scope, per-axis bounds
|
|
3733
|
+
# summaries (plus the generation's cohort and expiry under scope "*",
|
|
3734
|
+
# axis "meta", and each account's latest control witness under axis
|
|
3735
|
+
# "control"); and each (generation, account, axis)'s recovery state.
|
|
3736
|
+
conn.execute(
|
|
3737
|
+
"CREATE TABLE IF NOT EXISTS outcome_quarantine_episodes ("
|
|
3738
|
+
"opener_segment TEXT, "
|
|
3739
|
+
"opener_offset INTEGER, "
|
|
3740
|
+
"opened_at TEXT, "
|
|
3741
|
+
"closer_segment TEXT, "
|
|
3742
|
+
"closer_offset INTEGER, "
|
|
3743
|
+
"closed_at TEXT, "
|
|
3744
|
+
"PRIMARY KEY (opener_segment, opener_offset))"
|
|
3745
|
+
)
|
|
3534
3746
|
conn.execute(
|
|
3535
|
-
"CREATE TABLE IF NOT EXISTS
|
|
3536
|
-
"scope TEXT
|
|
3537
|
-
"
|
|
3747
|
+
"CREATE TABLE IF NOT EXISTS outcome_quarantine_bounds ("
|
|
3748
|
+
"scope TEXT NOT NULL, "
|
|
3749
|
+
"axis TEXT NOT NULL, "
|
|
3750
|
+
"bounds_json TEXT NOT NULL, "
|
|
3751
|
+
"PRIMARY KEY (scope, axis))"
|
|
3538
3752
|
)
|
|
3539
3753
|
conn.execute(
|
|
3540
|
-
"CREATE TABLE IF NOT EXISTS
|
|
3541
|
-
"
|
|
3542
|
-
"
|
|
3543
|
-
"
|
|
3544
|
-
"
|
|
3545
|
-
"
|
|
3754
|
+
"CREATE TABLE IF NOT EXISTS outcome_quarantine_axis_state ("
|
|
3755
|
+
"generation INTEGER NOT NULL, "
|
|
3756
|
+
"account_key TEXT NOT NULL, "
|
|
3757
|
+
"axis TEXT NOT NULL, "
|
|
3758
|
+
"state TEXT NOT NULL, "
|
|
3759
|
+
"waiting_for TEXT, "
|
|
3760
|
+
"coverage_id TEXT, "
|
|
3761
|
+
"witness_id TEXT, "
|
|
3762
|
+
"release_id TEXT, "
|
|
3763
|
+
"revocation TEXT, "
|
|
3764
|
+
"since TEXT, "
|
|
3765
|
+
"recovered_at TEXT, "
|
|
3766
|
+
"not_before INTEGER, "
|
|
3767
|
+
"witness_key TEXT, "
|
|
3768
|
+
"PRIMARY KEY (generation, account_key, axis))"
|
|
3546
3769
|
)
|
|
3547
3770
|
conn.execute(
|
|
3548
3771
|
"CREATE TABLE IF NOT EXISTS journal_selector_batches ("
|