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.
Files changed (76) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +4 -6
  3. package/bin/_cctally_account.py +57 -3
  4. package/bin/_cctally_cache.py +5720 -885
  5. package/bin/_cctally_claude_attribution.py +997 -0
  6. package/bin/_cctally_completion.py +101 -28
  7. package/bin/_cctally_core.py +235 -12
  8. package/bin/_cctally_dashboard.py +263 -17
  9. package/bin/_cctally_dashboard_conversation.py +107 -21
  10. package/bin/_cctally_dashboard_envelope.py +44 -0
  11. package/bin/_cctally_db.py +928 -80
  12. package/bin/_cctally_diagnosis_sources.py +78 -13
  13. package/bin/_cctally_doctor.py +211 -86
  14. package/bin/_cctally_five_hour.py +446 -22
  15. package/bin/_cctally_forecast.py +302 -6
  16. package/bin/_cctally_journal.py +2418 -158
  17. package/bin/_cctally_milestone_history.py +379 -91
  18. package/bin/_cctally_oauth_samples.py +185 -7
  19. package/bin/_cctally_parser.py +38 -0
  20. package/bin/_cctally_project.py +34 -8
  21. package/bin/_cctally_quarantine.py +821 -343
  22. package/bin/_cctally_quota.py +69 -4
  23. package/bin/_cctally_quota_calibration.py +677 -3
  24. package/bin/_cctally_quota_model.py +927 -127
  25. package/bin/_cctally_record.py +922 -445
  26. package/bin/_cctally_rederive.py +119 -15
  27. package/bin/_cctally_statusline.py +1048 -176
  28. package/bin/_cctally_store.py +544 -110
  29. package/bin/_cctally_tui.py +455 -42
  30. package/bin/_cctally_update.py +77 -14
  31. package/bin/_lib_account_protocol.py +227 -19
  32. package/bin/_lib_claude_attribution.py +421 -0
  33. package/bin/_lib_clone_copy.py +30 -0
  34. package/bin/_lib_codex_conversation_query.py +45 -9
  35. package/bin/_lib_codex_conversation_watch.py +72 -13
  36. package/bin/_lib_codex_rollout_container.py +234 -19
  37. package/bin/_lib_codex_worker_lifecycle.py +374 -0
  38. package/bin/_lib_credit.py +22 -13
  39. package/bin/_lib_dashboard_sources.py +257 -18
  40. package/bin/_lib_detector_access_registry.py +1335 -166
  41. package/bin/_lib_doctor.py +276 -92
  42. package/bin/_lib_ingest_frontier.py +269 -36
  43. package/bin/_lib_journal.py +273 -22
  44. package/bin/_lib_jsonl.py +12 -0
  45. package/bin/_lib_meter_rate_change.py +72 -0
  46. package/bin/_lib_outcome_quarantine.py +917 -554
  47. package/bin/_lib_provider_activity.py +24 -6
  48. package/bin/_lib_quota_calibration.py +57 -3
  49. package/bin/_lib_quota_copy.py +7 -2
  50. package/bin/_lib_quota_lineage.py +613 -0
  51. package/bin/_lib_quota_model.py +464 -33
  52. package/bin/_lib_rate_change_delivery.py +16 -0
  53. package/bin/_lib_reclaim_planner.py +6 -4
  54. package/bin/_lib_rederive.py +2046 -21
  55. package/bin/_lib_selector_state.py +131 -3
  56. package/bin/_lib_snapshot_cache.py +8 -0
  57. package/bin/_lib_stale_proof.py +113 -4
  58. package/bin/_lib_statusline.py +72 -3
  59. package/bin/_lib_statusline_candidates.py +246 -42
  60. package/bin/_lib_tick_stats.py +10 -5
  61. package/bin/_lib_write_io.py +419 -6
  62. package/bin/cctally +28 -11
  63. package/dashboard/static/assets/{ConversationsView-Dflb_myA.js → ConversationsView-oqNsWzWV.js} +1 -1
  64. package/dashboard/static/assets/{DoctorModal-DFX5CfIs.js → DoctorModal-B0DnU9d9.js} +1 -1
  65. package/dashboard/static/assets/ModalRoot-CYua2wP_.js +1 -0
  66. package/dashboard/static/assets/{ProjectsDrillPanel-C3PJy1xx.js → ProjectsDrillPanel-DSxXQSa-.js} +1 -1
  67. package/dashboard/static/assets/{SourceDetailModal-BczsWKhD.js → SourceDetailModal-BleDpQqG.js} +1 -1
  68. package/dashboard/static/assets/{UpdateModal-Os-Lo5cs.js → UpdateModal-CMqaAkSK.js} +1 -1
  69. package/dashboard/static/assets/{index-CYZ3NLqF.js → index-BjQV-0-D.js} +3 -3
  70. package/dashboard/static/assets/index-D34eYhAc.css +1 -0
  71. package/dashboard/static/assets/{outlineNavigation-k-zIif6W.js → outlineNavigation-CO-VWBm7.js} +1 -1
  72. package/dashboard/static/assets/{useKeymap-CW7Up0Ay.js → useKeymap-CUgEnnua.js} +1 -1
  73. package/dashboard/static/dashboard.html +3 -3
  74. package/package.json +5 -1
  75. package/dashboard/static/assets/ModalRoot-HlxoagIa.js +0 -1
  76. package/dashboard/static/assets/index-BKITk1CT.css +0 -1
@@ -987,6 +987,34 @@ def _source_bundle_to_envelope(bundle: object | None) -> dict:
987
987
  METER_RATE_CHANGE_LIMIT = 20
988
988
 
989
989
 
990
+ def _rate_change_supersessions(conn: sqlite3.Connection) -> dict:
991
+ """Wire `supersession` objects by transition identity (#1015 spec 5.7).
992
+
993
+ Read through `read_rate_change_supersessions` on the build's own stats
994
+ connection, so both the explicit `meter_rate_change_supersessions` rows
995
+ and the supersessions derived from a transition's lineage before any
996
+ sweep journals one are covered. `superseded_at` is the explicit event's
997
+ sweep clock, or null for a derived-only state. Unreadable facts publish
998
+ no supersession — the pre-#1015 row — rather than emptying the array: the
999
+ marker and delivery paths refuse on their own when the facts cannot be
1000
+ verified.
1001
+ """
1002
+ try:
1003
+ qcal = sys.modules["cctally"]._load_sibling(
1004
+ "_cctally_quota_calibration")
1005
+ state = qcal.read_rate_change_supersessions(conn)
1006
+ except Exception: # noqa: BLE001
1007
+ return {}
1008
+ return {
1009
+ tuple(identity): {
1010
+ "reason": record.reason,
1011
+ "superseded_at": record.superseded_at,
1012
+ "corrections": list(record.corrections),
1013
+ }
1014
+ for identity, record in state.by_identity.items()
1015
+ }
1016
+
1017
+
990
1018
  def _build_meter_rate_change_array(
991
1019
  conn: sqlite3.Connection, limit: int = METER_RATE_CHANGE_LIMIT,
992
1020
  ) -> list:
@@ -1046,6 +1074,7 @@ def _build_meter_rate_change_array(
1046
1074
  # background worker. An empty array renders as "no change recorded",
1047
1075
  # which is the truth on that store, rather than emptying the envelope.
1048
1076
  return []
1077
+ supersessions = _rate_change_supersessions(conn) if rows else {}
1049
1078
  out: list[dict] = []
1050
1079
  for (provider, account_key, effective_from, previous, new, severity,
1051
1080
  detected_at, created_at, status, causes, provenance,
@@ -1082,6 +1111,11 @@ def _build_meter_rate_change_array(
1082
1111
  "composition_provenance": _decode_evidence_array(provenance),
1083
1112
  "baseline_withheld_days": (
1084
1113
  None if baseline_days is None else int(baseline_days)),
1114
+ # #1015 spec 5.7 (U2): published always and nulled, so the client
1115
+ # can tell an ordinary row (null) from one an older server sent
1116
+ # (absent). The row and its historical values are kept.
1117
+ "supersession": supersessions.get(
1118
+ (str(provider), str(account_key), str(effective_from))),
1085
1119
  }
1086
1120
  entry.update(account_fields(str(provider), account_key))
1087
1121
  out.append(entry)
@@ -2523,6 +2557,16 @@ def snapshot_to_envelope(snap: "DataSnapshot", *,
2523
2557
  # PREVIEW badge when set.
2524
2558
  **sys.modules["_cctally_dashboard"]._channel_env_fragment(),
2525
2559
  }
2560
+ # #836 (spec 5.1): the displayed account, decorated builds ONLY. Below the
2561
+ # R8 threshold both keys are omitted (never null), so an undecorated
2562
+ # `current_week` keeps its bytes. Appended after the literal so the key
2563
+ # order of every existing field is unchanged. This identity — never
2564
+ # `five_hour_block.account_key`, absent with no live block — is what the
2565
+ # client labels and scopes its history navigation by.
2566
+ if (cw is not None and getattr(cw, "account_decorated", False)
2567
+ and isinstance(envelope.get("current_week"), dict)):
2568
+ envelope["current_week"]["account_key"] = cw.account_key
2569
+ envelope["current_week"]["account_label"] = cw.account_label
2526
2570
  envelope.update(_source_bundle_to_envelope(getattr(snap, "source_bundle", None)))
2527
2571
  _overlay_claude_source_session_titles(envelope, snap, transcripts_visible)
2528
2572
  _overlay_codex_source_session_labels(envelope, snap, transcripts_visible)