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
package/bin/_lib_five_hour.py
CHANGED
|
@@ -11,6 +11,11 @@ drift independently; the regression `bin/cctally-5h-canonical-test`
|
|
|
11
11
|
pins the cross-shape equivalence (epoch-int → datetime → epoch round-trip
|
|
12
12
|
matches the modulo floor on 600-aligned base epochs).
|
|
13
13
|
|
|
14
|
+
`latest_five_hour_reading` is the one read-only selection of an account's
|
|
15
|
+
newest five-hour reading (#825/#832 spec 4.7 revision 7): it takes the
|
|
16
|
+
caller's connection and runs one bounded statement, so the module stays free
|
|
17
|
+
of I/O at import time and of sibling imports.
|
|
18
|
+
|
|
14
19
|
`bin/cctally` re-exports every symbol below so internal call sites and
|
|
15
20
|
SourceFileLoader-based tests/fixtures (`tests/test_blocks_recorded_anchor`,
|
|
16
21
|
`tests/test_five_hour_block_selector`, `tests/test_five_hour_blocks_json`,
|
|
@@ -24,6 +29,7 @@ from __future__ import annotations
|
|
|
24
29
|
|
|
25
30
|
import datetime as dt
|
|
26
31
|
import math
|
|
32
|
+
from dataclasses import dataclass
|
|
27
33
|
|
|
28
34
|
|
|
29
35
|
_FIVE_HOUR_JITTER_FLOOR_SECONDS = 600 # 10 minutes; tolerance band for resets_at jitter
|
|
@@ -141,3 +147,123 @@ def _canonical_5h_window_key(
|
|
|
141
147
|
):
|
|
142
148
|
return prior_key
|
|
143
149
|
return resets_at_epoch - (resets_at_epoch % _FIVE_HOUR_JITTER_FLOOR_SECONDS)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass(frozen=True)
|
|
153
|
+
class FiveHourReading:
|
|
154
|
+
"""One five-hour reading, every field taken from ONE snapshot row.
|
|
155
|
+
|
|
156
|
+
``resets_at`` is the row's stored ``five_hour_resets_at`` text (``None`` on
|
|
157
|
+
a hand-built or legacy row that stores only the canonical key) and
|
|
158
|
+
``window_key`` its canonical window: the stored key, or the
|
|
159
|
+
``_canonical_5h_window_key`` floor of the stored reset for a row that
|
|
160
|
+
predates the key. ``snapshot_id`` / ``captured_at_utc`` / ``account_key`` identify the
|
|
161
|
+
row, and ``weekly_observation_held`` says whether its WEEKLY axis was
|
|
162
|
+
carried forward — its five-hour fields are the writing tick's own either
|
|
163
|
+
way.
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
percent: float
|
|
167
|
+
resets_at: "str | None"
|
|
168
|
+
window_key: int
|
|
169
|
+
captured_at_utc: str
|
|
170
|
+
snapshot_id: int
|
|
171
|
+
account_key: str
|
|
172
|
+
weekly_observation_held: bool
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def latest_five_hour_reading(
|
|
176
|
+
conn,
|
|
177
|
+
*,
|
|
178
|
+
account_key: "str | None" = None,
|
|
179
|
+
as_of_utc: "str | None" = None,
|
|
180
|
+
captured_at_or_after_utc: "str | None" = None,
|
|
181
|
+
week_start_date: "str | None" = None,
|
|
182
|
+
week_start_at_in: "tuple[str, ...] | list[str] | None" = None,
|
|
183
|
+
) -> "FiveHourReading | None":
|
|
184
|
+
"""The newest ``weekly_usage_snapshots`` row carrying a five-hour reading
|
|
185
|
+
(#825/#832 spec 4.7 revision 7 *Latest five-hour reading*).
|
|
186
|
+
|
|
187
|
+
The ONE shared, read-only selection every reader of the newest five-hour
|
|
188
|
+
value uses. A five-hour credit retires a stale replica in place (its three
|
|
189
|
+
five-hour columns NULL, its weekly evidence kept), and an ordinary
|
|
190
|
+
weekly-only reading has the same shape, so either can be the newest row
|
|
191
|
+
while saying nothing about the five-hour axis. Eligible rows carry a
|
|
192
|
+
finite, non-negative percent and a resolvable canonical window (the stored
|
|
193
|
+
``five_hour_window_key``, else ``_canonical_5h_window_key`` of a parseable
|
|
194
|
+
``five_hour_resets_at``; the recorder always writes all three together),
|
|
195
|
+
held rows included (a held row's five-hour fields are the writing tick's
|
|
196
|
+
own). Zero is a valid percent. Weekly evidence is NOT selected here: callers select it
|
|
197
|
+
independently, with their own freshness and held-row rules.
|
|
198
|
+
|
|
199
|
+
Every restriction is the CALLER's, applied unchanged:
|
|
200
|
+
|
|
201
|
+
- ``account_key``: ``None`` adds no account predicate (the caller's own
|
|
202
|
+
account-blind contract); a key, including ``unattributed``, scopes the
|
|
203
|
+
selection to that account, so one account never borrows another's
|
|
204
|
+
reading.
|
|
205
|
+
- ``as_of_utc`` / ``captured_at_or_after_utc``: an inclusive capture
|
|
206
|
+
window, compared as instants.
|
|
207
|
+
- ``week_start_date`` alone: one week date key. With ``week_start_at_in``:
|
|
208
|
+
the week whose ``week_start_at`` is one of those texts, or a
|
|
209
|
+
``week_start_at``-less row of that date (the current-week reader's
|
|
210
|
+
scope).
|
|
211
|
+
|
|
212
|
+
The selection is one ``LIMIT 1`` statement, so no history-sized sort is
|
|
213
|
+
materialized. ``None`` means no eligible row.
|
|
214
|
+
"""
|
|
215
|
+
clauses = [
|
|
216
|
+
"five_hour_percent IS NOT NULL",
|
|
217
|
+
"five_hour_percent >= 0",
|
|
218
|
+
"five_hour_percent <= 1e308",
|
|
219
|
+
"(five_hour_window_key IS NOT NULL "
|
|
220
|
+
" OR unixepoch(five_hour_resets_at) IS NOT NULL)",
|
|
221
|
+
]
|
|
222
|
+
params: list = []
|
|
223
|
+
if account_key is not None:
|
|
224
|
+
clauses.append("account_key = ?")
|
|
225
|
+
params.append(account_key)
|
|
226
|
+
if as_of_utc is not None:
|
|
227
|
+
clauses.append("unixepoch(captured_at_utc) <= unixepoch(?)")
|
|
228
|
+
params.append(as_of_utc)
|
|
229
|
+
if captured_at_or_after_utc is not None:
|
|
230
|
+
clauses.append("unixepoch(captured_at_utc) >= unixepoch(?)")
|
|
231
|
+
params.append(captured_at_or_after_utc)
|
|
232
|
+
if week_start_at_in is not None:
|
|
233
|
+
texts = tuple(week_start_at_in)
|
|
234
|
+
if not texts:
|
|
235
|
+
clauses.append("(week_start_at IS NULL AND week_start_date = ?)")
|
|
236
|
+
else:
|
|
237
|
+
clauses.append(
|
|
238
|
+
"(week_start_at IN (" + ",".join("?" * len(texts)) + ") "
|
|
239
|
+
"OR (week_start_at IS NULL AND week_start_date = ?))")
|
|
240
|
+
params.extend(texts)
|
|
241
|
+
params.append(week_start_date)
|
|
242
|
+
elif week_start_date is not None:
|
|
243
|
+
clauses.append("week_start_date = ?")
|
|
244
|
+
params.append(week_start_date)
|
|
245
|
+
row = conn.execute(
|
|
246
|
+
"SELECT id, account_key, captured_at_utc, five_hour_percent, "
|
|
247
|
+
" five_hour_resets_at, five_hour_window_key, "
|
|
248
|
+
" unixepoch(five_hour_resets_at), weekly_observation_held "
|
|
249
|
+
"FROM weekly_usage_snapshots WHERE " + " AND ".join(clauses) +
|
|
250
|
+
" ORDER BY captured_at_utc DESC, id DESC LIMIT 1",
|
|
251
|
+
tuple(params),
|
|
252
|
+
).fetchone()
|
|
253
|
+
if row is None:
|
|
254
|
+
return None
|
|
255
|
+
percent = float(row[3])
|
|
256
|
+
if not math.isfinite(percent) or percent < 0:
|
|
257
|
+
return None
|
|
258
|
+
window_key = (
|
|
259
|
+
int(row[5]) if row[5] is not None
|
|
260
|
+
else _canonical_5h_window_key(int(row[6])))
|
|
261
|
+
return FiveHourReading(
|
|
262
|
+
percent=percent,
|
|
263
|
+
resets_at=row[4],
|
|
264
|
+
window_key=window_key,
|
|
265
|
+
captured_at_utc=row[2],
|
|
266
|
+
snapshot_id=int(row[0]),
|
|
267
|
+
account_key=row[1],
|
|
268
|
+
weekly_observation_held=bool(row[7]),
|
|
269
|
+
)
|
package/bin/_lib_fmt.py
CHANGED
|
@@ -292,6 +292,17 @@ def _fmt_num(n: int) -> str:
|
|
|
292
292
|
return f"{n:,}"
|
|
293
293
|
|
|
294
294
|
|
|
295
|
+
def _fmt_usd_accounting(value: float, places: int) -> str:
|
|
296
|
+
"""`-$71.21`: the sign goes before the currency symbol (#886).
|
|
297
|
+
|
|
298
|
+
The CLI convention shared with `_lib_diff_kernel` and `_lib_share`'s
|
|
299
|
+
MoneyCell: an ASCII hyphen, no `+` for a plain amount, and a sign only
|
|
300
|
+
when `value < 0` — so a negative zero prints unsigned.
|
|
301
|
+
"""
|
|
302
|
+
sign = "-" if value < 0 else ""
|
|
303
|
+
return f"{sign}${abs(value):.{places}f}"
|
|
304
|
+
|
|
305
|
+
|
|
295
306
|
def _truncate_num(formatted: str, width: int) -> str:
|
|
296
307
|
"""Truncate a formatted number to fit width, replacing tail with '…'."""
|
|
297
308
|
if len(formatted) <= width:
|