cctally 1.82.1 → 1.83.1
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 +63 -0
- package/README.md +12 -5
- package/bin/_cctally_alerts.py +8 -1
- package/bin/_cctally_cache.py +912 -149
- package/bin/_cctally_config.py +43 -4
- package/bin/_cctally_core.py +933 -759
- package/bin/_cctally_dashboard.py +157 -47
- package/bin/_cctally_dashboard_cache_report.py +13 -6
- package/bin/_cctally_dashboard_conversation.py +1 -0
- package/bin/_cctally_dashboard_envelope.py +116 -8
- package/bin/_cctally_dashboard_share.py +50 -19
- package/bin/_cctally_dashboard_sources.py +223 -48
- package/bin/_cctally_db.py +605 -128
- package/bin/_cctally_doctor.py +417 -28
- package/bin/_cctally_five_hour.py +12 -5
- package/bin/_cctally_journal.py +2050 -156
- package/bin/_cctally_journal_repair.py +519 -0
- package/bin/_cctally_milestone_history.py +142 -56
- package/bin/_cctally_milestones.py +179 -111
- package/bin/_cctally_parser.py +42 -0
- package/bin/_cctally_project.py +24 -18
- package/bin/_cctally_quota.py +139 -25
- package/bin/_cctally_record.py +279 -108
- package/bin/_cctally_rederive.py +1052 -0
- package/bin/_cctally_reporting.py +58 -53
- package/bin/_cctally_setup.py +1 -0
- package/bin/_cctally_source_analytics.py +4 -1
- package/bin/_cctally_statusline.py +11 -11
- package/bin/_cctally_store.py +1039 -31
- package/bin/_cctally_sync_week.py +17 -8
- package/bin/_cctally_tui.py +350 -44
- package/bin/_cctally_update.py +133 -8
- package/bin/_cctally_weekrefs.py +14 -0
- package/bin/_lib_aggregators.py +10 -6
- package/bin/_lib_cache_report.py +101 -9
- package/bin/_lib_codex_pools.py +82 -0
- package/bin/_lib_conversation_query.py +81 -33
- package/bin/_lib_dashboard_sources.py +75 -0
- package/bin/_lib_diff_kernel.py +28 -15
- package/bin/_lib_doctor.py +342 -4
- package/bin/_lib_journal.py +924 -2
- package/bin/_lib_jsonl.py +43 -14
- package/bin/_lib_pricing.py +140 -21
- package/bin/_lib_rederive.py +395 -0
- package/bin/_lib_share.py +58 -2
- package/bin/cctally +56 -8
- package/dashboard/static/assets/{index-BKM43pxK.js → index-3bgCMVHb.js} +52 -52
- package/dashboard/static/assets/index-D27EIHEI.css +1 -0
- package/dashboard/static/dashboard.html +2 -2
- package/package.json +5 -1
- package/dashboard/static/assets/index-Dk1nplOz.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,69 @@ based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [1.83.1] - 2026-07-28
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Doctor golden fixtures now pin the macOS backup/sync classifier as well as the `tmutil` response, keeping the public Linux CI matrix byte-stable across platforms.
|
|
12
|
+
|
|
13
|
+
## [1.83.0] - 2026-07-28
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- New preview-first `cctally db journal-repair` command records an exact append-only operator audit for structurally invalid correction batches, safely rebuilds the disposable index, recovers idempotently after interruption, and keeps acknowledged omissions visible as warnings. (#402, #378)
|
|
17
|
+
- Dashboard source envelopes now publish independently derived `hero`, `quota`, and `sessions` freshness beside provider-generation freshness, and the dashboard routes each client surface to its own axis, so an idle quota window can age without making unrelated accounting or Sessions data stale. (#396)
|
|
18
|
+
- The append-only journal now has a crash-safe correction protocol: completed manifest-checked batches can replace or tombstone a previously recorded decision, and live replay plus a fresh stats rebuild agree on the highest effective revision without rewriting history or replaying alerts. (#372)
|
|
19
|
+
- Claude usage history can now be replayed through current derivation rules into a deterministic, side-effect-free correction plan, including account-scoped reset, credit, milestone, and cache-cost decisions. Missing retained inputs stop the plan with a precise reason, and applying a plan through the journal correction protocol makes the next preview a no-op. (#372)
|
|
20
|
+
- New preview-first `cctally db rederive --family claude-usage` command audits and corrects wrong journaled Claude-usage decisions with one append-only correction batch and an atomic stats-index rebuild. `--yes` applies; interrupted batches resume safely, repeated application is a no-op, retained source gaps fail before mutation, and replay never fires historical alerts. (#372)
|
|
21
|
+
- `doctor` reports a `journal.writer_guard` leg surfacing unsanctioned stats.db writes. (#386)
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- stats.db auto-heal, `db rebuild --db stats`, and `db repair --db stats` now decline while any process still holds the database open, rather than replacing files underneath live readers. The usual holder is a running dashboard, so stop it first if a repair declines. (#386)
|
|
25
|
+
- Claude dollar and token totals are now explicitly documented as transcript-derived lower bounds: Claude Code bills title-generation and prompt-suggestion/side-query calls whose usable model and token fields are absent from normal transcripts, so cctally does not invent rows or apply a universal uplift. Retained main, resumed, and subagent assistant usage continues to be priced without double-counting `usage.iterations[]`. (#368, #377)
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- Claude responses retained as effective fast mode now use the correct rate across reporting, Projects, cache diagnostics, shares, statusline, and dashboard/conversation totals. Current Opus 5/4.8 fast rows use $10/$50 per MTok; historical retained Opus 4.6/4.7 fast rows keep $30/$150 pricing, while standard, missing, fallback, malformed, and recorded-cost rows remain unchanged. Recomputed views correct on the next read or sync without rewriting durable weekly, block, or milestone facts. (#413, #369)
|
|
29
|
+
- Percent milestones now preserve the first retained crossing's capture time, cost, logical snapshot references, and alert latch across stats-index replacement and replay. The stats cursor records the prefix atomically materialized beside its public position, so a cursor-only checkpoint can no longer skip an already-durable `pm:` event and let a later observation create a different revision-0 fact or fire the same alert twice. Upgrading rebuilds the disposable stats index at epoch 1004. (#410, #383)
|
|
30
|
+
- Codex quota history now identifies exactly one live physical cycle when two native identities reset near the same boundary, preventing overlapping account weeks, and the dashboard history cap retains the freshest independent model-pool fact without including it in account quota aggregates. (#412, #375)
|
|
31
|
+
- Dashboard current-cycle labels now render Claude's full week range once and distinguish a clipped Codex history range from its later nominal reset, preserving both provider facts without an apparent contradiction. (#412, #375)
|
|
32
|
+
- Weekly cost snapshots derived while recording a usage milestone now keep the triggering observation's exact subscription window, account, and capture-time cost boundary. Replaying an old observation after a later week anchor or later cache growth can no longer change its `wcs:` payload under the same journal identity, and a reset timestamp that rounds across UTC midnight now keeps usage, cost, and milestone facts on one canonical week key; explicit operator `sync-week` behavior is unchanged. (#410, #383)
|
|
33
|
+
- Disabling quota alerts now records a durable journal disarm so rebuilding and re-enabling above a threshold cannot dispatch historical alerts. Doctor now distinguishes a rebuildable stats-index mismatch from one with no journal data, warns when the live data directory is confirmed inside file-level backup/sync, and keeps dashboard health gathers subprocess-free. The remote full-suite watcher now documents and pins bare `--watch` as the only continuation form, while SQLite reader paths close their connections cleanly under `ResourceWarning` escalation. (#411, #340)
|
|
34
|
+
- Cache auto-heal now quarantines and rebuilds `cache.db` only when its locked forensics probe confirms corruption; transient classified triggers against an integrity-clean or unprobeable family preserve every file and record their precise origin for diagnosis. (#387, #404)
|
|
35
|
+
- Account-budget label, email, and key-prefix writes attempted during `stats.db` maintenance now report the maintenance hold and ask the user to retry, instead of incorrectly claiming that no accounts have been observed. The writer still accepts a raw 32-hex account key without opening the registry. (#391)
|
|
36
|
+
- Dashboard source refreshes no longer hold a long-lived `stats.db` read transaction that defeats SQLite's WAL autocheckpoint during concurrent usage recording, preventing the WAL from growing linearly while the dashboard is active. (#393)
|
|
37
|
+
- The dashboard now detects corruption that first appears in a real `stats.db` query after the lightweight opener probe, closes its live stats handle, performs one journal-backed auto-heal, and retries from the rebuilt index. If healing is unsafe or unavailable, the server still binds and shows a truthful stats repair action instead of blaming `cache.db`; cache-only and mixed failures keep deterministic database attribution. (#385, #407)
|
|
38
|
+
- The installed-build stats-writer guard now throttles violations across processes and rotates its diagnostic log at 1 MiB, while `doctor` reads only a bounded tail, preventing a multi-process violation storm from creating or loading an unbounded log. (#389)
|
|
39
|
+
- Dashboard startup verification no longer reads ambient Claude or Codex session data, eliminating an environment-dependent first-touch sync failure in the parity test. (#405, #348, #371, #392)
|
|
40
|
+
- Structurally invalid journal correction batches no longer prevent `stats.db` from rebuilding or the dashboard from starting. Every affected batch is omitted as a whole, valid corrections still apply, and `db rebuild --json` plus `doctor` identify the exact batch, violation kind, and stable fingerprint without rewriting history. (#402, #378)
|
|
41
|
+
- Current-week shared reports now keep provider spend visible while clearly
|
|
42
|
+
labeling stale hero-cycle evidence in Markdown, HTML, and SVG. Claude and
|
|
43
|
+
Codex disclosures stay separate in All reports, and a freshness-only change
|
|
44
|
+
now marks an existing composer section outdated without invalidating
|
|
45
|
+
unrelated panels or legacy source-less Claude shares. (#400, #361)
|
|
46
|
+
- Stable promotions now make the target GitHub Release describe the full upgrade from the previous promoted stable, including intermediate beta cuts. Notes are composed from the target tag's CHANGELOG, verified before the Latest flag moves, resume without duplicate edits, and refuse manual drift or an ambiguous concurrent baseline. (#355, #403)
|
|
47
|
+
- The dashboard's default 1440px bento now gives Recent Sessions titles two uniform readable lines instead of showing roughly 13 characters, while keeping Cost inside the card and preserving the mobile layout. (#364, #401)
|
|
48
|
+
- Closed five-hour blocks are now frozen as one deterministic journal fact at the first retained successor or expiry observation. A crash/retry, late-arriving session row, or later cache growth can no longer rewrite the block's totals, percentages, timestamps, model/project breakdowns, or create a second conflicting close event; open blocks remain live and mutable. (#399, #380)
|
|
49
|
+
- A stale migration-failure banner now clears after `cache.db` recovery or rebuild durably stamps that migration applied, while genuine unresolved migration failures remain visible. (#398, #352)
|
|
50
|
+
- Bare npm updates on the beta channel now resolve the live effective beta target before choosing an exact version or deciding the install is already current, so a fresh registry release cannot be hidden by the 24-hour cached check. Dry-runs preview the same fresh target without writing state; refresh failures retain the last-known-good target with an actionable warning; explicit version pins, stable npm, and Homebrew behavior are unchanged. The dashboard now resolves this target server-side after the user clicks Update and refreshes the running modal before installing it, rather than trusting a stale version supplied by an already-open browser. (#342, #397)
|
|
51
|
+
- `cctally cache-sync --rebuild` no longer appears to hang forever after compact Claude/Codex ingest completes. The separate transcript/search rebuild now reports provider, phase, elapsed time, and file progress; each provider runs in an isolated process with a 30-minute no-progress ceiling that active files refresh. A genuinely stuck transcript phase exits non-zero with a precise retry command while preserving committed accounting and leaving partial transcript state integrity-clean and retry-safe. (#395)
|
|
52
|
+
- A completed journal correction now automatically rebuilds the derived stats index on the next ingest when replacement is safe, then retries once without skipping later observations or replaying alerts. If a dashboard or another reader still holds the database open, cctally leaves the index untouched and names both the holder remedy and the manual rebuild command. (#394)
|
|
53
|
+
- A legacy interrupted `stats.db` rebuild that left an empty or missing index now self-heals from the append-only journal on the next CLI or dashboard open. Recovery serializes racing openers, preserves the maintenance lock and unrelated files, and removes only the matching stale rebuild scratch family. (#388)
|
|
54
|
+
- An interrupted `stats.db` rebuild no longer removes the current index before its replacement is ready, so commands never reopen an absent or empty database after a killed rebuild; retry safely converges from the journal. (#388)
|
|
55
|
+
- `db vacuum --db stats` serialized on the cache maintenance lock instead of the stats one, so a full rewrite of the stats database could run at the same time as a stats ingest, auto-heal, or rebuild. (#386)
|
|
56
|
+
- `db checkpoint --db stats`, `db skip`, and `db unskip` took no lock at all on stats.db, and first-open work — schema creation, migrations, backfills, and the journal cutover — ran unserialized no matter which command triggered it. (#386)
|
|
57
|
+
- stats.db file replacement (auto-heal, epoch rebuild, `db rebuild`) now verifies the database family has drained and blocks new readers from slipping in during cutover instead of replacing files under live SQLite handles. (#386)
|
|
58
|
+
- A stats.db repair or rebuild running in the background no longer parks every other cctally command indefinitely; they now report that maintenance is in progress and exit instead of hanging. (#386)
|
|
59
|
+
- Codex session names on the dashboard now follow the transcript privacy gate: LAN viewers without transcript access see a dash in both Codex and All views, while authorized local viewers keep the bounded task name. Shared snapshots no longer publish prompt-derived labels. (#382)
|
|
60
|
+
- Multi-account reset and credit detection now compares each Claude account only with its own prior usage, preventing one account's percentage drop from being misread as another account's reset or goodwill credit. (#372)
|
|
61
|
+
- Codex: a separate model pool (GPT-5.3-Codex-Spark) is no longer filed as account-level weekly quota, which had created a phantom cycle that truncated the real one and skewed the weekly card, cycle index, quota summary, forecast card and 5-hour blocks. (#373)
|
|
62
|
+
- Codex: the cycle modal's week navigation no longer disables itself when the newest retained cycle is not the current one. (#373)
|
|
63
|
+
- Codex: cycle detail now renders its retained per-percent milestones and 5h blocks, which had been empty for every cycle since multi-account support landed. (#373)
|
|
64
|
+
- Codex: opening a cycle now shows the same date range and current/historic state the cycle list shows; a cycle cut short by an early reset had opened with its full seven-day range and claimed to be the current one. (#373)
|
|
65
|
+
- The hero modal's week and block navigation arrows now meet the 24x24 minimum tap-target size on a phone, with no change to how they look. (#373)
|
|
66
|
+
- The dashboard starts again after upgrading on a machine whose history contains a rare recording conflict. A crash at exactly the wrong moment could leave two differently-worded versions of the same recorded fact in the history file, and because that file is append-only the duplicate could never be removed — so every attempt to rebuild the local index refused outright and the dashboard exited instead of opening. The rebuild now completes, keeps the first version it recorded, and tells you which facts were ambiguous instead of stopping. (#374)
|
|
67
|
+
- Recording a fact that would create such a conflict is now prevented rather than written: the check runs before the write instead of after it, and the affected row is brought back into agreement with what was already recorded. A rare duplicate recording also no longer re-runs on every refresh. (#374)
|
|
68
|
+
- `cctally doctor` reports ambiguous recorded facts as a warning naming each one. `cctally db rebuild --db stats` lists the same groups and exits successfully, and `cctally db rederive --family claude-usage` resolves them for good. (#374)
|
|
69
|
+
- Claude cost no longer undercounts 1-hour prompt-cache writes. Anthropic charges twice the base input rate for a 1-hour cache write and 1.25x for a 5-minute one, but every cache write was billed at the 5-minute rate — and Claude Code writes its main-session cache with the 1-hour TTL, so the cache line was reported about a third too low. Figures recomputed from your session history (`daily`, `monthly`, `weekly`, `session`, `blocks`, `range-cost`, `cache-report`, `diff`, `budget`, `forecast`, and the dashboard) correct themselves on the next sync; already-recorded weekly, block and milestone figures keep the value recorded at the time. One sync after upgrading re-reads your whole session history and will be noticeably slower than usual. (#195)
|
|
70
|
+
|
|
8
71
|
## [1.82.1] - 2026-07-24
|
|
9
72
|
|
|
10
73
|
### Changed
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
|
-
<strong>
|
|
9
|
+
<strong>Understand Claude Code and Codex spend: a local dashboard, conversation viewer, and CLI reports for your subscription quota.</strong>
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
@@ -18,16 +18,23 @@
|
|
|
18
18
|
|
|
19
19
|
Your Claude Code plan meters you with a percentage that creeps up all week. cctally reads your local session logs and turns that percentage into dollars: what each percent of quota costs you, whether you are on track to cap before the reset, and where the spend is going. It does the same for OpenAI's Codex CLI. Everything runs on your own machine, against your own data. No account, no API key, and nothing is uploaded.
|
|
20
20
|
|
|
21
|
+
> **Claude cost coverage:** Claude dollar and token totals are
|
|
22
|
+
> [transcript-derived lower bounds](docs/claude-cost-coverage.md), not exact
|
|
23
|
+
> `/usage` billing totals. Claude Code can bill title-generation and
|
|
24
|
+
> prompt-suggestion/side-query requests without retaining usable model/token
|
|
25
|
+
> fields. cctally does not guess the missing amount. Codex accounting uses a
|
|
26
|
+
> different retained source and is unaffected.
|
|
27
|
+
|
|
21
28
|
<p align="center">
|
|
22
29
|
<img src="docs/img/dashboard-desktop.png" alt="cctally dashboard, desktop view" width="900">
|
|
23
30
|
</p>
|
|
24
31
|
|
|
25
32
|
<!-- cctally:latest-stable:begin -->
|
|
26
|
-
**Latest stable: v1.
|
|
33
|
+
**Latest stable: v1.82.1** (2026-07-24)
|
|
27
34
|
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
35
|
+
- The public README is a fresh, shorter screenshot-led tour, and it now refreshes itself on every stable release: promoting a release regenerates the screenshots against that exact version and updates a "Latest stable" highlights block on the GitHub page automatically.
|
|
36
|
+
- The dashboard's Recent Sessions card shows session names again. Splitting transcript storage into its own database dropped the name lookup, so every row in the Session column had rendered a dash since then. Names come back from the stored conversation index, and a transcript store that is missing, locked, or rebuilding simply leaves the dash in place instead of holding up the rest of the dashboard.
|
|
37
|
+
- The All tab's Recent Sessions rows now show Claude session names too, matching the Codex rows beside them; previously only Codex rows were named there. Names still appear only for a local viewer, exactly as on the Claude tab.
|
|
31
38
|
<!-- cctally:latest-stable:end -->
|
|
32
39
|
|
|
33
40
|
## Quick start
|
package/bin/_cctally_alerts.py
CHANGED
|
@@ -193,7 +193,14 @@ def _alert_label_prefix(axis: str, account_key: "str | None") -> str:
|
|
|
193
193
|
db_path = _cctally_core.DB_PATH
|
|
194
194
|
if not db_path.exists():
|
|
195
195
|
return ""
|
|
196
|
-
|
|
196
|
+
# #386: `mode=ro` is not exempt from the opener protocol — such a
|
|
197
|
+
# connection CREATES `-shm`/`-wal` when they are absent (measured). The
|
|
198
|
+
# enclosing `except Exception` degrades the label, never the alert.
|
|
199
|
+
import _cctally_store
|
|
200
|
+
conn = _cctally_store.stats_open_guarded(
|
|
201
|
+
db_path,
|
|
202
|
+
connect=lambda p: _sq.connect(f"file:{p}?mode=ro", uri=True),
|
|
203
|
+
)
|
|
197
204
|
try:
|
|
198
205
|
if _cctally_account.real_account_count(conn, vendor) <= 1:
|
|
199
206
|
return ""
|