cctally 1.22.2 → 1.22.4
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 +21 -0
- package/bin/_cctally_cache_report.py +1133 -880
- package/bin/_cctally_codex.py +518 -0
- package/bin/_cctally_dashboard.py +3 -3
- package/bin/_cctally_diff.py +240 -0
- package/bin/_cctally_doctor.py +479 -0
- package/bin/_cctally_five_hour.py +1688 -0
- package/bin/_cctally_forecast.py +1979 -0
- package/bin/_cctally_milestones.py +433 -0
- package/bin/_cctally_percent_breakdown.py +199 -0
- package/bin/_cctally_pricing_check.py +393 -0
- package/bin/_cctally_record.py +8 -5
- package/bin/_cctally_reporting.py +749 -0
- package/bin/_cctally_setup.py +172 -13
- package/bin/_cctally_statusline.py +630 -0
- package/bin/_cctally_sync_week.py +5 -4
- package/bin/_cctally_weekrefs.py +484 -0
- package/bin/_lib_cache_report.py +938 -0
- package/bin/_lib_diff_kernel.py +5 -8
- package/bin/_lib_fmt.py +325 -0
- package/bin/_lib_pricing_debug.py +182 -0
- package/bin/_lib_render.py +9 -24
- package/bin/_lib_subscription_weeks.py +2 -2
- package/bin/cctally +466 -9190
- package/package.json +15 -1
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
"""WeekRef / reset-event cluster (impure-glue sibling).
|
|
2
|
+
|
|
3
|
+
Holds the seven DB-touching WeekRef helpers + the two reset-drop-threshold
|
|
4
|
+
constants that drive mid-week reset-event detection:
|
|
5
|
+
`_get_canonical_boundary_for_date`, `get_recent_weeks`,
|
|
6
|
+
`_apply_reset_events_to_weekrefs`, `_backfill_week_reset_events`,
|
|
7
|
+
`_week_ref_has_reset_event`, `_compute_cost_for_weekref`,
|
|
8
|
+
`_apply_overlap_clamp_to_weekrefs`, `_RESET_PCT_DROP_THRESHOLD`,
|
|
9
|
+
`_FIVE_HOUR_RESET_PCT_DROP_THRESHOLD`.
|
|
10
|
+
|
|
11
|
+
These operate on the `WeekRef` type and take `sqlite3.Connection` — the
|
|
12
|
+
IMPURE counterpart to the PURE `SubWeek` math in `_lib_subscription_weeks.py`
|
|
13
|
+
(which owns `_apply_reset_events_to_subweeks` / `_apply_overlap_clamp_to_subweeks`).
|
|
14
|
+
|
|
15
|
+
Honest *name* imports are KERNEL-ONLY (`_cctally_core`). The three
|
|
16
|
+
cctally-ns re-exports this module needs — `_floor_to_hour` (of `_lib_blocks`),
|
|
17
|
+
`_clamp_end_ats_to_next_start` (of `_lib_subscription_weeks`), and
|
|
18
|
+
`_sum_cost_for_range` (defined in `bin/cctally`) — are reached via the
|
|
19
|
+
call-time `c = _cctally()` accessor so test monkeypatches through the
|
|
20
|
+
`cctally` namespace are preserved. (No `for c in ...` row-loop in this
|
|
21
|
+
cluster → the accessor binds the conventional `c`.)
|
|
22
|
+
|
|
23
|
+
bin/cctally eager-re-exports all 7 functions + 2 constants; consumers reach
|
|
24
|
+
them via `c.` (forecast/percent_breakdown/view_models/tui/core/diff_kernel)
|
|
25
|
+
or bare `def`-shims (record.py). No consumer source edits.
|
|
26
|
+
|
|
27
|
+
Spec: docs/superpowers/specs/2026-06-01-extract-weekrefs-5h-backfill-design.md
|
|
28
|
+
"""
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import datetime as dt
|
|
32
|
+
import sqlite3
|
|
33
|
+
import sys
|
|
34
|
+
from dataclasses import replace
|
|
35
|
+
|
|
36
|
+
from _cctally_core import (
|
|
37
|
+
WeekRef,
|
|
38
|
+
_canonicalize_optional_iso,
|
|
39
|
+
make_week_ref,
|
|
40
|
+
parse_iso_datetime,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _cctally():
|
|
45
|
+
"""Resolve the current `cctally` module at call-time."""
|
|
46
|
+
return sys.modules["cctally"]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _get_canonical_boundary_for_date(
|
|
50
|
+
conn: sqlite3.Connection,
|
|
51
|
+
week_start_date_str: str,
|
|
52
|
+
) -> tuple[str | None, str | None]:
|
|
53
|
+
"""Return the first established (week_start_at, week_end_at) for a week."""
|
|
54
|
+
row = conn.execute(
|
|
55
|
+
"""
|
|
56
|
+
SELECT week_start_at, week_end_at
|
|
57
|
+
FROM weekly_usage_snapshots
|
|
58
|
+
WHERE week_start_date = ?
|
|
59
|
+
AND week_start_at IS NOT NULL AND week_start_at != ''
|
|
60
|
+
AND week_end_at IS NOT NULL AND week_end_at != ''
|
|
61
|
+
ORDER BY captured_at_utc ASC, id ASC
|
|
62
|
+
LIMIT 1
|
|
63
|
+
""",
|
|
64
|
+
(week_start_date_str,),
|
|
65
|
+
).fetchone()
|
|
66
|
+
if row:
|
|
67
|
+
start_at = _canonicalize_optional_iso(row["week_start_at"], "weekStartAt")
|
|
68
|
+
end_at = _canonicalize_optional_iso(row["week_end_at"], "weekEndAt")
|
|
69
|
+
if start_at and end_at:
|
|
70
|
+
return start_at, end_at
|
|
71
|
+
return None, None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_recent_weeks(conn: sqlite3.Connection, limit: int) -> list[WeekRef]:
|
|
75
|
+
rows = conn.execute(
|
|
76
|
+
"""
|
|
77
|
+
SELECT week_start_date, MAX(week_end_date) AS week_end_date
|
|
78
|
+
FROM (
|
|
79
|
+
SELECT week_start_date, week_end_date FROM weekly_usage_snapshots
|
|
80
|
+
UNION ALL
|
|
81
|
+
SELECT week_start_date, week_end_date FROM weekly_cost_snapshots
|
|
82
|
+
)
|
|
83
|
+
GROUP BY week_start_date
|
|
84
|
+
ORDER BY week_start_date DESC
|
|
85
|
+
LIMIT ?
|
|
86
|
+
""",
|
|
87
|
+
(limit,),
|
|
88
|
+
).fetchall()
|
|
89
|
+
|
|
90
|
+
refs: list[WeekRef] = []
|
|
91
|
+
for row in rows:
|
|
92
|
+
date_str = row["week_start_date"]
|
|
93
|
+
canon_start, canon_end = _get_canonical_boundary_for_date(conn, date_str)
|
|
94
|
+
try:
|
|
95
|
+
ref = make_week_ref(
|
|
96
|
+
week_start_date=date_str,
|
|
97
|
+
week_end_date=row["week_end_date"],
|
|
98
|
+
week_start_at=canon_start,
|
|
99
|
+
week_end_at=canon_end,
|
|
100
|
+
)
|
|
101
|
+
except ValueError:
|
|
102
|
+
continue
|
|
103
|
+
refs.append(ref)
|
|
104
|
+
# Reset-event boundary override runs BEFORE the generic overlap clamp.
|
|
105
|
+
# After the override, pre/post-reset refs are contiguous at the reset
|
|
106
|
+
# moment, so the clamp becomes a no-op for them; for installs with no
|
|
107
|
+
# reset events the clamp still does all the work it did before.
|
|
108
|
+
return _apply_overlap_clamp_to_weekrefs(
|
|
109
|
+
_apply_reset_events_to_weekrefs(conn, refs)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _apply_reset_events_to_weekrefs(
|
|
114
|
+
conn: sqlite3.Connection, refs: list[WeekRef]
|
|
115
|
+
) -> list[WeekRef]:
|
|
116
|
+
"""Override API-derived boundaries with reset-event effective moments.
|
|
117
|
+
|
|
118
|
+
For each row in week_reset_events:
|
|
119
|
+
- A ref whose week_end_at matches `old_week_end_at` was the PRE-reset
|
|
120
|
+
week: its API-declared end is in the future but Anthropic cut it
|
|
121
|
+
early. Override ref.week_end_at = effective_reset_at_utc so display
|
|
122
|
+
shows the real cut-off.
|
|
123
|
+
- A ref whose week_end_at matches `new_week_end_at` is the POST-reset
|
|
124
|
+
week: its API-derived start (= new resets_at - 7d) backdates into
|
|
125
|
+
the pre-reset week. Override ref.week_start_at = effective_reset_at_utc
|
|
126
|
+
so the new week starts at the actual reset moment.
|
|
127
|
+
- **In-place credit (v1.7.2 round-3, Bug B).** Detected via the row
|
|
128
|
+
shape ``old_week_end_at == effective_reset_at_utc`` (the live and
|
|
129
|
+
backfill detection paths both write this shape — see
|
|
130
|
+
``test_event_row_old_is_effective_not_cur_end``). For these events,
|
|
131
|
+
the credited week's ref matches ``new_week_end_at`` (the original
|
|
132
|
+
resets_at is unchanged), so the post-credit override above
|
|
133
|
+
rewrites ``week_start_at`` to ``effective``. But the pre-credit
|
|
134
|
+
segment of the SAME week — where the user spent the bulk of their
|
|
135
|
+
usage before the credit — is dropped, because no other ref in
|
|
136
|
+
``refs`` carries ``week_end_at == effective``. Synthesize a
|
|
137
|
+
pre-credit ref alongside the post-credit one: its
|
|
138
|
+
``week_start_at`` stays at the ref's original API-derived value,
|
|
139
|
+
its ``week_end_at`` becomes ``effective`` (closes the pre-credit
|
|
140
|
+
segment). Credited weeks render as TWO trend rows downstream.
|
|
141
|
+
|
|
142
|
+
The ref's `week_start` (date) and `key` fields are intentionally left at
|
|
143
|
+
the API-derived values — they're the lookup keys for
|
|
144
|
+
weekly_usage_snapshots / weekly_cost_snapshots. Only the display-facing
|
|
145
|
+
`week_start_at` / `week_end_at` (and the derived `week_end` date) shift.
|
|
146
|
+
Both the pre-credit and post-credit synthesized refs share the same
|
|
147
|
+
`key` so downstream per-segment readers
|
|
148
|
+
(``cmd_percent_breakdown`` / dashboard milestone panel) can still
|
|
149
|
+
filter milestones by ``reset_event_id`` against the same lookup keys.
|
|
150
|
+
"""
|
|
151
|
+
events = conn.execute(
|
|
152
|
+
"SELECT old_week_end_at, new_week_end_at, effective_reset_at_utc "
|
|
153
|
+
"FROM week_reset_events"
|
|
154
|
+
).fetchall()
|
|
155
|
+
if not events:
|
|
156
|
+
return refs
|
|
157
|
+
pre_map = {e["old_week_end_at"]: e["effective_reset_at_utc"] for e in events}
|
|
158
|
+
post_map = {e["new_week_end_at"]: e["effective_reset_at_utc"] for e in events}
|
|
159
|
+
# In-place credit events have `old == effective` (the row shape the
|
|
160
|
+
# live + backfill detection paths agree on). Project the set of
|
|
161
|
+
# `new_week_end_at` values for those events so we can detect them
|
|
162
|
+
# while iterating refs and split the credited week into TWO refs.
|
|
163
|
+
in_place_credit_new_ends: set[str] = {
|
|
164
|
+
e["new_week_end_at"]
|
|
165
|
+
for e in events
|
|
166
|
+
if e["old_week_end_at"] == e["effective_reset_at_utc"]
|
|
167
|
+
}
|
|
168
|
+
out: list[WeekRef] = []
|
|
169
|
+
for ref in refs:
|
|
170
|
+
new_ref = ref
|
|
171
|
+
if ref.week_end_at and ref.week_end_at in pre_map:
|
|
172
|
+
reset_at = pre_map[ref.week_end_at]
|
|
173
|
+
try:
|
|
174
|
+
reset_dt = parse_iso_datetime(reset_at, "reset_event.effective")
|
|
175
|
+
# internal fallback: host-local intentional
|
|
176
|
+
new_end_date = (reset_dt - dt.timedelta(seconds=1)).astimezone().date()
|
|
177
|
+
new_ref = replace(new_ref, week_end_at=reset_at, week_end=new_end_date)
|
|
178
|
+
except ValueError:
|
|
179
|
+
pass
|
|
180
|
+
if ref.week_end_at and ref.week_end_at in post_map:
|
|
181
|
+
reset_at = post_map[ref.week_end_at]
|
|
182
|
+
# In-place credit: synthesize a pre-credit ref FIRST so it
|
|
183
|
+
# lands in `out` before the post-credit ref. The pre-credit
|
|
184
|
+
# ref keeps the ORIGINAL API-derived week_start_at; only its
|
|
185
|
+
# week_end_at shifts to `effective`. The post-credit ref
|
|
186
|
+
# (constructed below via the standard `replace`) carries
|
|
187
|
+
# week_start_at = effective, week_end_at = original.
|
|
188
|
+
# Order: pre-credit BEFORE post-credit so chronological
|
|
189
|
+
# iteration in cmd_report's trend table renders them
|
|
190
|
+
# naturally (older segment above the newer one in DESC
|
|
191
|
+
# ordering: post-credit is "more recent" so the post-credit
|
|
192
|
+
# row should come FIRST in the DESC list — but the original
|
|
193
|
+
# ref was already in DESC position, and we insert pre-credit
|
|
194
|
+
# AFTER the post-credit. Concretely: post-credit takes the
|
|
195
|
+
# ref's original slot; pre-credit goes one slot later).
|
|
196
|
+
if ref.week_end_at in in_place_credit_new_ends:
|
|
197
|
+
try:
|
|
198
|
+
reset_dt = parse_iso_datetime(
|
|
199
|
+
reset_at, "reset_event.effective"
|
|
200
|
+
)
|
|
201
|
+
pre_end_date = (
|
|
202
|
+
# internal fallback: host-local intentional
|
|
203
|
+
reset_dt - dt.timedelta(seconds=1)
|
|
204
|
+
).astimezone().date()
|
|
205
|
+
pre_credit_ref = replace(
|
|
206
|
+
ref,
|
|
207
|
+
week_end_at=reset_at,
|
|
208
|
+
week_end=pre_end_date,
|
|
209
|
+
)
|
|
210
|
+
except ValueError:
|
|
211
|
+
pre_credit_ref = None
|
|
212
|
+
else:
|
|
213
|
+
pre_credit_ref = None
|
|
214
|
+
new_ref = replace(new_ref, week_start_at=reset_at)
|
|
215
|
+
out.append(new_ref)
|
|
216
|
+
if pre_credit_ref is not None:
|
|
217
|
+
out.append(pre_credit_ref)
|
|
218
|
+
continue
|
|
219
|
+
out.append(new_ref)
|
|
220
|
+
return out
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _backfill_week_reset_events(conn: sqlite3.Connection) -> None:
|
|
224
|
+
"""One-shot scan over historical snapshots to synthesize reset events
|
|
225
|
+
for past mid-week resets the tool lived through before this feature
|
|
226
|
+
shipped. Idempotent via UNIQUE(old_week_end_at, new_week_end_at) +
|
|
227
|
+
INSERT OR IGNORE — safe to re-run, safe to ship alongside the DDL.
|
|
228
|
+
|
|
229
|
+
Rule mirrors the runtime detection in cmd_record_usage: when a new
|
|
230
|
+
week_end_at arrives in a snapshot whose captured_at_utc is still
|
|
231
|
+
BEFORE the prior week's end, that's a mid-week reset. Boundary ISO
|
|
232
|
+
strings get canonicalized via `_canonicalize_optional_iso` and the
|
|
233
|
+
effective reset moment is floored to the hour via `_floor_to_hour`
|
|
234
|
+
so minute/second-level Anthropic jitter ("in X hr Y min" relative-text
|
|
235
|
+
drift) doesn't masquerade as a reset.
|
|
236
|
+
"""
|
|
237
|
+
c = _cctally()
|
|
238
|
+
try:
|
|
239
|
+
rows = conn.execute(
|
|
240
|
+
"SELECT captured_at_utc, week_end_at, weekly_percent "
|
|
241
|
+
"FROM weekly_usage_snapshots "
|
|
242
|
+
"WHERE week_end_at IS NOT NULL "
|
|
243
|
+
"ORDER BY captured_at_utc ASC, id ASC"
|
|
244
|
+
).fetchall()
|
|
245
|
+
except sqlite3.DatabaseError:
|
|
246
|
+
return
|
|
247
|
+
# Canonicalized (hour-rounded) previous end; stored canonical form is
|
|
248
|
+
# what WeekRef.week_end_at carries after make_week_ref, so maps in
|
|
249
|
+
# _apply_reset_events_to_weekrefs stay joinable without extra parsing.
|
|
250
|
+
prior_end = None
|
|
251
|
+
prior_pct: float | None = None
|
|
252
|
+
for row in rows:
|
|
253
|
+
cur_end_raw = row["week_end_at"]
|
|
254
|
+
cur_pct = row["weekly_percent"]
|
|
255
|
+
if not cur_end_raw:
|
|
256
|
+
continue
|
|
257
|
+
try:
|
|
258
|
+
cur_end = _canonicalize_optional_iso(cur_end_raw, "backfill.cur")
|
|
259
|
+
except ValueError:
|
|
260
|
+
continue
|
|
261
|
+
if cur_end is None:
|
|
262
|
+
continue
|
|
263
|
+
if prior_end and cur_end != prior_end:
|
|
264
|
+
try:
|
|
265
|
+
prior_end_dt = parse_iso_datetime(prior_end, "backfill.prior")
|
|
266
|
+
captured_dt = parse_iso_datetime(row["captured_at_utc"], "backfill.cap")
|
|
267
|
+
except ValueError:
|
|
268
|
+
prior_end = cur_end
|
|
269
|
+
prior_pct = cur_pct
|
|
270
|
+
continue
|
|
271
|
+
# Real mid-week reset needs three signals:
|
|
272
|
+
# 1. Boundary shifted (already checked).
|
|
273
|
+
# 2. Prior boundary was still in the future (not natural rollover).
|
|
274
|
+
# 3. weekly_percent dropped substantially (prior_pct - cur_pct
|
|
275
|
+
# >= RESET_PCT_DROP_THRESHOLD). Filters out API jitter where
|
|
276
|
+
# Anthropic briefly reported a different reset_at but usage
|
|
277
|
+
# stayed roughly the same.
|
|
278
|
+
if (
|
|
279
|
+
captured_dt < prior_end_dt
|
|
280
|
+
and prior_pct is not None and cur_pct is not None
|
|
281
|
+
and _is_reset_drop(prior_pct, cur_pct)
|
|
282
|
+
):
|
|
283
|
+
# Floor to the hour so the display boundary lands on the
|
|
284
|
+
# natural hour mark (Anthropic's reset times are always
|
|
285
|
+
# hour-aligned, and users think of weeks in hour-mark
|
|
286
|
+
# units). A reset at 18:08Z becomes 18:00Z in the event
|
|
287
|
+
# row, rendering as "21:00" local instead of "21:08".
|
|
288
|
+
effective_iso = c._floor_to_hour(captured_dt).isoformat(timespec="seconds")
|
|
289
|
+
conn.execute(
|
|
290
|
+
"INSERT OR IGNORE INTO week_reset_events "
|
|
291
|
+
"(detected_at_utc, old_week_end_at, new_week_end_at, "
|
|
292
|
+
" effective_reset_at_utc) VALUES (?, ?, ?, ?)",
|
|
293
|
+
(row["captured_at_utc"], prior_end, cur_end, effective_iso),
|
|
294
|
+
)
|
|
295
|
+
elif prior_end and cur_end == prior_end:
|
|
296
|
+
# In-place credit branch (v1.7.2). Mirrors the live detection
|
|
297
|
+
# in cmd_record_usage: same end_at across two captures + ≥25pp
|
|
298
|
+
# drop in weekly_percent + prior end still in the future at
|
|
299
|
+
# captured_dt → Anthropic-issued goodwill credit. One event
|
|
300
|
+
# row with old == new == cur_end, effective = floor_to_hour
|
|
301
|
+
# of the captured_at when the drop was first observed.
|
|
302
|
+
try:
|
|
303
|
+
prior_end_dt = parse_iso_datetime(prior_end, "backfill.prior")
|
|
304
|
+
captured_dt = parse_iso_datetime(row["captured_at_utc"], "backfill.cap")
|
|
305
|
+
except ValueError:
|
|
306
|
+
prior_end = cur_end
|
|
307
|
+
prior_pct = cur_pct
|
|
308
|
+
continue
|
|
309
|
+
if (
|
|
310
|
+
captured_dt < prior_end_dt
|
|
311
|
+
and prior_pct is not None and cur_pct is not None
|
|
312
|
+
and _is_reset_drop(prior_pct, cur_pct)
|
|
313
|
+
):
|
|
314
|
+
# Pre-check on ``new_week_end_at`` (mirrors the live
|
|
315
|
+
# detection path's pre-check). Necessary because the
|
|
316
|
+
# UNIQUE(old, new) constraint alone WON'T dedup against
|
|
317
|
+
# legacy/broken-shape rows: pre-fix DBs may have
|
|
318
|
+
# ``(cur_end, cur_end)`` rows for the same credit that
|
|
319
|
+
# the new shape writes as ``(effective_iso, cur_end)``.
|
|
320
|
+
# Without this pre-check, the backfill writes a second
|
|
321
|
+
# row for the same credit on every open_db() call after
|
|
322
|
+
# upgrade. (See round-2 review Bug 1.)
|
|
323
|
+
already = conn.execute(
|
|
324
|
+
"SELECT 1 FROM week_reset_events "
|
|
325
|
+
"WHERE new_week_end_at = ? LIMIT 1",
|
|
326
|
+
(cur_end,),
|
|
327
|
+
).fetchone()
|
|
328
|
+
if already is not None:
|
|
329
|
+
prior_end = cur_end
|
|
330
|
+
prior_pct = cur_pct
|
|
331
|
+
continue
|
|
332
|
+
# Canonicalize to UTC before isoformat so the stored
|
|
333
|
+
# offset is `+00:00`, matching the live detection path
|
|
334
|
+
# (cmd_record_usage uses now_utc which is already UTC).
|
|
335
|
+
# parse_iso_datetime returns .astimezone() (host-local
|
|
336
|
+
# fallback at bin/cctally:_local_tz_name gate); without
|
|
337
|
+
# this normalization, non-UTC hosts would store the
|
|
338
|
+
# column as e.g. `+03:00`, breaking lex comparisons
|
|
339
|
+
# downstream (CLAUDE.md gotcha: 5h-block cross-reset
|
|
340
|
+
# comparisons go through unixepoch(), NOT lex
|
|
341
|
+
# BETWEEN/</>; the reset-aware DB clamp here applies
|
|
342
|
+
# the same rule). The reset-aware clamp now wraps both
|
|
343
|
+
# sides with unixepoch() (Bug 2 fix), but a canonical
|
|
344
|
+
# UTC offset on write is the right defense-in-depth.
|
|
345
|
+
effective_iso = (
|
|
346
|
+
c._floor_to_hour(captured_dt.astimezone(dt.timezone.utc))
|
|
347
|
+
.isoformat(timespec="seconds")
|
|
348
|
+
)
|
|
349
|
+
# Row shape: old=effective_iso, new=cur_end (distinct
|
|
350
|
+
# values). See the live-detection site in
|
|
351
|
+
# bin/_cctally_record.py for the full rationale; in
|
|
352
|
+
# short, old==new collapses the credited week to a
|
|
353
|
+
# zero-width window in _apply_reset_events_to_weekrefs.
|
|
354
|
+
conn.execute(
|
|
355
|
+
"INSERT OR IGNORE INTO week_reset_events "
|
|
356
|
+
"(detected_at_utc, old_week_end_at, new_week_end_at, "
|
|
357
|
+
" effective_reset_at_utc) VALUES (?, ?, ?, ?)",
|
|
358
|
+
(row["captured_at_utc"], effective_iso, cur_end, effective_iso),
|
|
359
|
+
)
|
|
360
|
+
prior_end = cur_end
|
|
361
|
+
prior_pct = cur_pct
|
|
362
|
+
# Flush implicit transaction so callers using explicit BEGIN
|
|
363
|
+
# (e.g. _backfill_five_hour_blocks) don't trip "cannot start a
|
|
364
|
+
# transaction within a transaction".
|
|
365
|
+
conn.commit()
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
# Minimum weekly_percent drop that counts as a goodwill reset (percentage
|
|
369
|
+
# points). Real resets zero out usage, so the drop is large; transient API
|
|
370
|
+
# flaps show similar percents on both sides. 25pp catches both the known
|
|
371
|
+
# 86→0 and 34→0 cases while filtering 35→33-style jitter.
|
|
372
|
+
_RESET_PCT_DROP_THRESHOLD = 25.0
|
|
373
|
+
|
|
374
|
+
# In-place 5h-credit threshold. Mirrors `_RESET_PCT_DROP_THRESHOLD` but
|
|
375
|
+
# scaled down for the 5h dimension: typical 5h usage stays under ~10pp in
|
|
376
|
+
# a single block, so a 5pp drop sits well above natural variation while
|
|
377
|
+
# proportionally being a larger signal than 25pp is on the weekly scale.
|
|
378
|
+
# See spec docs/superpowers/specs/2026-05-16-5h-in-place-credit-detection.md
|
|
379
|
+
# §2.1 (Q1) for rationale.
|
|
380
|
+
_FIVE_HOUR_RESET_PCT_DROP_THRESHOLD = 5.0
|
|
381
|
+
|
|
382
|
+
# Reset-to-zero discriminator (2026-06-01 surprise-reset fix). Anthropic's
|
|
383
|
+
# weekly reset zeroes the counter mid-window, but the 25pp magnitude gate
|
|
384
|
+
# above silently masks it for any user below ~25% usage (e.g. the observed
|
|
385
|
+
# 14→0). A reset-to-zero is unambiguous REGARDLESS of magnitude: a lagging
|
|
386
|
+
# API replica reports a slightly-lower number, never a clean 0 against real
|
|
387
|
+
# usage. So the detector ALSO fires when the post value collapses to ~0
|
|
388
|
+
# (<= _RESET_ZERO_FLOOR_PCT) with a drop clearing a small min-drop floor.
|
|
389
|
+
# The floor rejects 1%→0% stale-replica jitter, which would otherwise write
|
|
390
|
+
# a spurious week_reset_events row and segment the week.
|
|
391
|
+
_RESET_ZERO_FLOOR_PCT = 1.0
|
|
392
|
+
_RESET_ZERO_MIN_DROP_PCT = 3.0
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def _is_reset_drop(prior_pct: float, cur_pct: float) -> bool:
|
|
396
|
+
"""True when ``prior_pct → cur_pct`` is a genuine weekly reset/credit.
|
|
397
|
+
|
|
398
|
+
Two independent percent-shape signals (OR):
|
|
399
|
+
|
|
400
|
+
* **Partial credit** — drop ``>= _RESET_PCT_DROP_THRESHOLD`` (25pp).
|
|
401
|
+
* **Reset-to-zero** — ``cur_pct`` collapses to ~0
|
|
402
|
+
(``<= _RESET_ZERO_FLOOR_PCT``) with a drop clearing
|
|
403
|
+
``_RESET_ZERO_MIN_DROP_PCT``.
|
|
404
|
+
|
|
405
|
+
Callers retain the boundary predicates (same/advanced ``week_end_at``
|
|
406
|
+
AND ``prior_end_dt > now``); this helper owns ONLY the percent-shape
|
|
407
|
+
discrimination so all four 7d detection sites (live advance, live
|
|
408
|
+
in-place, backfill advance, backfill in-place) stay byte-identical.
|
|
409
|
+
"""
|
|
410
|
+
cur = float(cur_pct)
|
|
411
|
+
drop = float(prior_pct) - cur
|
|
412
|
+
if drop >= _RESET_PCT_DROP_THRESHOLD:
|
|
413
|
+
return True
|
|
414
|
+
return cur <= _RESET_ZERO_FLOOR_PCT and drop >= _RESET_ZERO_MIN_DROP_PCT
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def _week_ref_has_reset_event(
|
|
418
|
+
conn: sqlite3.Connection, ref: WeekRef
|
|
419
|
+
) -> bool:
|
|
420
|
+
"""Return True if `ref`'s effective boundaries were rewritten by a
|
|
421
|
+
reset event (the ref went through _apply_reset_events_to_weekrefs
|
|
422
|
+
and either its start or end now equals some effective_reset_at_utc).
|
|
423
|
+
Lets cost callers bypass the weekly_cost_snapshots cache (which was
|
|
424
|
+
computed over API-derived range) and recompute live over the
|
|
425
|
+
effective range instead.
|
|
426
|
+
"""
|
|
427
|
+
if not ref.week_start_at and not ref.week_end_at:
|
|
428
|
+
return False
|
|
429
|
+
row = conn.execute(
|
|
430
|
+
"SELECT 1 FROM week_reset_events "
|
|
431
|
+
"WHERE effective_reset_at_utc IN (?, ?) LIMIT 1",
|
|
432
|
+
(ref.week_start_at, ref.week_end_at),
|
|
433
|
+
).fetchone()
|
|
434
|
+
return row is not None
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def _compute_cost_for_weekref(ref: WeekRef) -> float | None:
|
|
438
|
+
"""Live-compute USD cost over `ref`'s (possibly reset-adjusted) range
|
|
439
|
+
straight from session_entries. Mirrors what cmd_sync_week writes into
|
|
440
|
+
weekly_cost_snapshots, minus the cache write — used for reset-affected
|
|
441
|
+
weeks where the cached range disagrees with the effective range.
|
|
442
|
+
"""
|
|
443
|
+
c = _cctally()
|
|
444
|
+
if not ref.week_start_at or not ref.week_end_at:
|
|
445
|
+
return None
|
|
446
|
+
try:
|
|
447
|
+
start = parse_iso_datetime(ref.week_start_at, "weekRef.week_start_at")
|
|
448
|
+
end = parse_iso_datetime(ref.week_end_at, "weekRef.week_end_at")
|
|
449
|
+
except ValueError:
|
|
450
|
+
return None
|
|
451
|
+
if end <= start:
|
|
452
|
+
return 0.0
|
|
453
|
+
return c._sum_cost_for_range(start, end, mode="auto")
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def _apply_overlap_clamp_to_weekrefs(refs: list[WeekRef]) -> list[WeekRef]:
|
|
457
|
+
"""Clamp each WeekRef's end to the next WeekRef's start on overlap.
|
|
458
|
+
|
|
459
|
+
Caller-visible effect: report --weeks output (and its --json
|
|
460
|
+
weekEndAt / weekEndDate) now reflects the true observed week end
|
|
461
|
+
instead of the stale week_end_at captured from Anthropic's
|
|
462
|
+
--resets-at at week-start. See _clamp_end_ats_to_next_start for
|
|
463
|
+
the underlying signal. Input order is preserved (caller contract:
|
|
464
|
+
get_recent_weeks returns DESC by week_start_date).
|
|
465
|
+
|
|
466
|
+
Only refs with both week_start_at and week_end_at participate in
|
|
467
|
+
clamping; date-only refs (pre-boundary-tracking rows) pass through.
|
|
468
|
+
"""
|
|
469
|
+
c = _cctally()
|
|
470
|
+
candidates = [(i, r) for i, r in enumerate(refs) if r.week_start_at and r.week_end_at]
|
|
471
|
+
if len(candidates) < 2:
|
|
472
|
+
return refs
|
|
473
|
+
candidates.sort(key=lambda ir: ir[1].week_start_at) # type: ignore[arg-type,return-value]
|
|
474
|
+
pairs: list[tuple[str | None, str | None]] = [(r.week_start_at, r.week_end_at) for _, r in candidates]
|
|
475
|
+
new_ends = c._clamp_end_ats_to_next_start(pairs)
|
|
476
|
+
out = list(refs)
|
|
477
|
+
for (idx, cur), new_end in zip(candidates, new_ends):
|
|
478
|
+
if new_end is None or new_end == cur.week_end_at:
|
|
479
|
+
continue
|
|
480
|
+
new_end_dt = parse_iso_datetime(new_end, "week.end_at (clamped)")
|
|
481
|
+
# internal fallback: host-local intentional
|
|
482
|
+
new_end_date = (new_end_dt - dt.timedelta(seconds=1)).astimezone().date()
|
|
483
|
+
out[idx] = replace(cur, week_end_at=new_end, week_end=new_end_date)
|
|
484
|
+
return out
|