cctally 1.103.0 → 1.105.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 +94 -0
- package/README.md +6 -6
- package/bin/_cctally_alerts.py +104 -10
- package/bin/_cctally_cache.py +193 -71
- package/bin/_cctally_config.py +62 -2
- package/bin/_cctally_core.py +62 -1
- package/bin/_cctally_dashboard.py +694 -70
- package/bin/_cctally_dashboard_conversation.py +400 -6
- package/bin/_cctally_dashboard_envelope.py +336 -14
- package/bin/_cctally_dashboard_perf.py +93 -0
- package/bin/_cctally_dashboard_share.py +56 -25
- package/bin/_cctally_dashboard_sources.py +988 -94
- package/bin/_cctally_db.py +30 -3
- package/bin/_cctally_diagnosis_sources.py +699 -186
- package/bin/_cctally_doctor.py +77 -0
- package/bin/_cctally_forecast.py +932 -51
- package/bin/_cctally_journal.py +430 -24
- package/bin/_cctally_parser.py +66 -0
- package/bin/_cctally_project.py +535 -10
- package/bin/_cctally_quota.py +28 -0
- package/bin/_cctally_quota_calibration.py +146 -0
- package/bin/_cctally_quota_model.py +2142 -0
- package/bin/_cctally_record.py +204 -30
- package/bin/_cctally_share.py +16 -8
- package/bin/_cctally_statusline.py +34 -0
- package/bin/_cctally_tui.py +531 -115
- package/bin/_lib_codex_conversation_query.py +258 -12
- package/bin/_lib_codex_hooks.py +26 -0
- package/bin/_lib_conversation_query.py +145 -15
- package/bin/_lib_dashboard_json.py +105 -0
- package/bin/_lib_dashboard_settings_contract.py +2 -0
- package/bin/_lib_diagnosis.py +21 -2
- package/bin/_lib_doctor.py +215 -1
- package/bin/_lib_forecast.py +337 -43
- package/bin/_lib_ingest_frontier.py +889 -0
- package/bin/_lib_meter_rate_change.py +360 -0
- package/bin/_lib_perf.py +22 -0
- package/bin/_lib_pricing.py +30 -3
- package/bin/_lib_quota_calibration.py +311 -0
- package/bin/_lib_quota_copy.py +157 -0
- package/bin/_lib_quota_model.py +2520 -0
- package/bin/_lib_rate_change_delivery.py +119 -0
- package/bin/_lib_record.py +50 -0
- package/bin/_lib_rederive.py +10 -0
- package/bin/_lib_render.py +6 -0
- package/bin/_lib_retained_size.py +187 -0
- package/bin/_lib_share_templates.py +37 -5
- package/bin/_lib_snapshot_cache.py +504 -17
- package/bin/_lib_statusline.py +200 -2
- package/bin/_lib_tick_stats.py +28 -4
- package/bin/_lib_view_models.py +30 -12
- package/bin/cctally +32 -0
- package/dashboard/static/assets/ConversationsView-DA4j7Yso.js +72 -0
- package/dashboard/static/assets/DoctorModal-D4PbVnbE.js +1 -0
- package/dashboard/static/assets/ModalRoot-D4oSPxPp.js +1 -0
- package/dashboard/static/assets/ProjectsDrillPanel-DkPlB9JH.js +1 -0
- package/dashboard/static/assets/SourceDetailModal-BE7FONsS.js +1 -0
- package/dashboard/static/assets/UpdateModal-BJuI8glf.js +7 -0
- package/dashboard/static/assets/index-klO46NcU.css +1 -0
- package/dashboard/static/assets/index-w_bINkJ8.js +13 -0
- package/dashboard/static/assets/outlineNavigation-zDVm6Hdd.js +9 -0
- package/dashboard/static/assets/useKeymap-CJ-Pi17D.js +1 -0
- package/dashboard/static/dashboard.html +3 -2
- package/package.json +10 -1
- package/dashboard/static/assets/index-Di2hljvB.css +0 -1
- package/dashboard/static/assets/index-XYCIWjVG.js +0 -97
|
@@ -966,16 +966,24 @@ def _build_forecast_share_panel_data(options: dict,
|
|
|
966
966
|
Reuses ``DataSnapshot.forecast`` (ForecastOutput) and, when populated
|
|
967
967
|
by the sync thread, ``DataSnapshot.forecast_view`` (the kernel
|
|
968
968
|
wrapper from issue #57) for the (100, 90) budget pair.
|
|
969
|
-
|
|
970
|
-
``
|
|
971
|
-
|
|
972
|
-
|
|
969
|
+
|
|
970
|
+
``projected_end_pct`` is the kernel's SELECTED projection (#661 S2 spec
|
|
971
|
+
section 3.3) rather than a sixth re-derivation of it, and the curve and
|
|
972
|
+
the ceiling distances are measured from the same ceiling-corrected base
|
|
973
|
+
the kernel used. This function previously paired a RAW ``inputs.p_now``
|
|
974
|
+
with the corrected ``r_avg`` the kernel publishes and described that as
|
|
975
|
+
"the same arithmetic ``snapshot_to_envelope`` does", which stopped being
|
|
976
|
+
true the moment the correction landed.
|
|
977
|
+
|
|
978
|
+
Every one of those quantities is withheld at a right-censored reading and
|
|
979
|
+
on a week with no observed usage, because each is derived from a meter
|
|
980
|
+
that supplies no point estimate.
|
|
973
981
|
"""
|
|
974
982
|
fc = getattr(snap, "forecast", None) if snap else None
|
|
975
983
|
fc_view = getattr(snap, "forecast_view", None) if snap else None
|
|
976
984
|
if fc is None:
|
|
977
985
|
return {
|
|
978
|
-
"projected_end_pct":
|
|
986
|
+
"projected_end_pct": None,
|
|
979
987
|
# With no forecast at all there is no rate, so neither ceiling has
|
|
980
988
|
# a distance — the same withholding the populated path performs.
|
|
981
989
|
"days_to_100pct": None,
|
|
@@ -987,25 +995,43 @@ def _build_forecast_share_panel_data(options: dict,
|
|
|
987
995
|
"projection_curve": [],
|
|
988
996
|
"confidence": "LOW CONF",
|
|
989
997
|
}
|
|
998
|
+
from _lib_forecast import projection_base
|
|
999
|
+
|
|
990
1000
|
inputs = getattr(fc, "inputs", None)
|
|
991
|
-
|
|
1001
|
+
# The CEILING-CORRECTED base, through the one named resolver. `None` at a
|
|
1002
|
+
# right-censored reading, where there is no point estimate at all.
|
|
1003
|
+
base = projection_base(inputs) if inputs is not None else None
|
|
1004
|
+
p_now = None if base is None else float(base)
|
|
992
1005
|
remaining_hours = float(
|
|
993
1006
|
getattr(inputs, "remaining_hours", 0.0) or 0.0
|
|
994
1007
|
) if inputs else 0.0
|
|
995
1008
|
confidence = getattr(inputs, "confidence", "ok") if inputs else "ok"
|
|
996
|
-
|
|
1009
|
+
r_avg_raw = getattr(fc, "r_avg", None)
|
|
1010
|
+
r_avg = None if r_avg_raw is None else float(r_avg_raw)
|
|
997
1011
|
r_recent_raw = getattr(fc, "r_recent", None)
|
|
998
1012
|
r_recent = float(r_recent_raw) if r_recent_raw is not None else r_avg
|
|
999
|
-
# End-of-week projected %
|
|
1000
|
-
|
|
1013
|
+
# End-of-week projected % — the kernel's selected value, expressed as the
|
|
1014
|
+
# fraction this panel's contract carries. `None` when the kernel withheld
|
|
1015
|
+
# it, which the templates render as `n/a` rather than as `0.0%`.
|
|
1016
|
+
selected = getattr(fc, "week_avg_projection_pct", None)
|
|
1017
|
+
projected_end_pct = None if selected is None else float(selected) / 100.0
|
|
1001
1018
|
# Days to ceilings (simple inverse: hours-to-target / 24).
|
|
1002
|
-
# The
|
|
1003
|
-
# `p_now >= target_pct` means the target is already reached, and
|
|
1004
|
-
# to it is true. `r_avg <= 0` means no rate was observed, so the
|
|
1005
|
-
# not reachable on any timeline this data describes
|
|
1006
|
-
#
|
|
1007
|
-
#
|
|
1019
|
+
# The three exit conditions are different facts and must not share a
|
|
1020
|
+
# value. `p_now >= target_pct` means the target is already reached, and
|
|
1021
|
+
# zero days to it is true. `r_avg <= 0` means no rate was observed, so the
|
|
1022
|
+
# target is not reachable on any timeline this data describes. A withheld
|
|
1023
|
+
# base or rate means the meter supplies no distance at all.
|
|
1024
|
+
#
|
|
1025
|
+
# #661 S2 §3.1 changed which of these fires in the no-usage state. Before
|
|
1026
|
+
# the ceiling correction both `p_now == 0` and `r_avg == 0` held there,
|
|
1027
|
+
# and the shared `0.0` rendered `Days->90% 0.0`, stating the opposite of
|
|
1028
|
+
# the truth. The corrected point of a displayed 0 is 0.25, so NEITHER
|
|
1029
|
+
# holds now: the distance is a real, finite, very large number. Bounding
|
|
1030
|
+
# it is a PRESENTATION decision and is made in `_optional_days`, so this
|
|
1031
|
+
# function keeps publishing the exact figure.
|
|
1008
1032
|
def _days_to_ceiling(target_pct: float) -> "float | None":
|
|
1033
|
+
if p_now is None or r_avg is None:
|
|
1034
|
+
return None
|
|
1009
1035
|
if p_now >= target_pct:
|
|
1010
1036
|
return 0.0
|
|
1011
1037
|
if r_avg <= 0:
|
|
@@ -1045,18 +1071,23 @@ def _build_forecast_share_panel_data(options: dict,
|
|
|
1045
1071
|
# defect one layer below the fix.
|
|
1046
1072
|
_raw_dpp = getattr(inputs, "dollars_per_percent", None) if inputs else None
|
|
1047
1073
|
dpp = None if _raw_dpp is None else float(_raw_dpp)
|
|
1048
|
-
budgets["avg"] = None if dpp is None
|
|
1049
|
-
|
|
1050
|
-
|
|
1074
|
+
budgets["avg"] = (None if dpp is None or r_avg is None
|
|
1075
|
+
else dpp * r_avg * 24.0)
|
|
1076
|
+
budgets["recent_24h"] = (None if dpp is None or r_recent is None
|
|
1077
|
+
else dpp * r_recent * 24.0)
|
|
1078
|
+
# Projection curve — 7-day forward, using r_avg from the corrected base.
|
|
1079
|
+
# Empty when either is withheld: a curve drawn from a censored reading is
|
|
1080
|
+
# the same fabrication the scalar above refuses.
|
|
1051
1081
|
today = _share_now_utc().date()
|
|
1052
1082
|
projection_curve: list[dict] = []
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1083
|
+
if p_now is not None and r_avg is not None:
|
|
1084
|
+
for i in range(7):
|
|
1085
|
+
d = today + dt.timedelta(days=i)
|
|
1086
|
+
pct = (p_now + r_avg * (i * 24.0)) / 100.0
|
|
1087
|
+
projection_curve.append({
|
|
1088
|
+
"date": d.isoformat(),
|
|
1089
|
+
"projected_pct_used": pct,
|
|
1090
|
+
})
|
|
1060
1091
|
return {
|
|
1061
1092
|
"projected_end_pct": projected_end_pct,
|
|
1062
1093
|
"days_to_100pct": days_to_100,
|