cctally 1.74.0 → 1.75.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 +13 -0
- package/bin/_cctally_cache.py +9 -0
- package/bin/_cctally_config.py +3 -3
- package/bin/_cctally_core.py +12 -3
- package/bin/_cctally_dashboard.py +33 -15
- package/bin/_cctally_dashboard_conversation.py +2 -2
- package/bin/_cctally_dashboard_sources.py +401 -18
- package/bin/_cctally_db.py +694 -1
- package/bin/_cctally_parser.py +100 -6
- package/bin/_cctally_percent_breakdown.py +59 -34
- package/bin/_cctally_quota.py +311 -45
- package/bin/_cctally_record.py +52 -1
- package/bin/_cctally_refresh.py +26 -0
- package/bin/_cctally_statusline.py +3 -3
- package/bin/_cctally_tui.py +3 -0
- package/bin/_lib_aggregators.py +7 -0
- package/bin/_lib_cache_report.py +25 -9
- package/bin/_lib_conversation_retention.py +22 -2
- package/bin/_lib_doctor.py +4 -4
- package/bin/cctally +27 -1
- package/dashboard/static/assets/index-fZRxsSf5.js +80 -0
- package/dashboard/static/assets/{index-ZiPO8veo.css → index-yftBNnLR.css} +1 -1
- package/dashboard/static/dashboard.html +2 -2
- package/package.json +1 -1
- package/dashboard/static/assets/index-DeQjEMO6.js +0 -80
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""#313 P3:
|
|
1
|
+
"""#313 P3: conversation-transcript retention prune kernel.
|
|
2
2
|
|
|
3
3
|
Prunes ONLY the re-derivable transcript rows:
|
|
4
4
|
* Claude: ``conversation_messages`` + their ``conversation_file_touches`` /
|
|
@@ -63,7 +63,7 @@ def _cutoff_iso(cutoff_utc: dt.datetime) -> str:
|
|
|
63
63
|
``...Z`` timestamps.
|
|
64
64
|
|
|
65
65
|
Second-granular: the sub-second mixed-precision edge at the exact cutoff
|
|
66
|
-
second is immaterial for a
|
|
66
|
+
second is immaterial for a multi-day retention window — any message wrongly classified
|
|
67
67
|
there is re-derivable from JSONL and the boundary self-corrects on the next
|
|
68
68
|
(daily) prune.
|
|
69
69
|
"""
|
|
@@ -259,6 +259,26 @@ def _maybe_prune_conversation_retention(
|
|
|
259
259
|
except Exception:
|
|
260
260
|
conn.rollback()
|
|
261
261
|
raise
|
|
262
|
+
# Return the freed pages to the OS. On an INCREMENTAL auto-vacuum
|
|
263
|
+
# cache.db (the default for freshly-created DBs, #313 P3) this
|
|
264
|
+
# shrinks the file on disk instead of leaving a growing freelist,
|
|
265
|
+
# so the transcript prune reclaims space automatically without a
|
|
266
|
+
# manual `cctally db vacuum`; on a legacy auto_vacuum=NONE cache it
|
|
267
|
+
# is a harmless no-op (those still reclaim via `db vacuum` or a
|
|
268
|
+
# `cache-sync --rebuild`). Runs OUTSIDE the committed transaction,
|
|
269
|
+
# still under the maintenance + provider flocks (no concurrent
|
|
270
|
+
# writer), and best-effort — a reclaim error must never fail the
|
|
271
|
+
# already-durable prune.
|
|
272
|
+
if stats.total_rows > 0:
|
|
273
|
+
try:
|
|
274
|
+
# `.fetchall()` drives the pragma to completion — a bare
|
|
275
|
+
# execute() steps it once and reclaims a single page. This
|
|
276
|
+
# clears the freelist and drops page_count; the physical
|
|
277
|
+
# file shrinks on the next `wal_checkpoint(TRUNCATE)` the
|
|
278
|
+
# sync loop already forces (#297).
|
|
279
|
+
conn.execute("PRAGMA incremental_vacuum").fetchall()
|
|
280
|
+
except sqlite3.Error:
|
|
281
|
+
pass
|
|
262
282
|
return stats
|
|
263
283
|
finally:
|
|
264
284
|
try:
|
package/bin/_lib_doctor.py
CHANGED
|
@@ -1650,10 +1650,10 @@ def _check_db_integrity(s: DoctorState) -> CheckResult:
|
|
|
1650
1650
|
summary=f"stats.db quick_check: {s.stats_db_quick_check}",
|
|
1651
1651
|
remediation=(
|
|
1652
1652
|
"stats.db (the non-re-derivable DB) reports corruption. "
|
|
1653
|
-
"
|
|
1654
|
-
"`
|
|
1655
|
-
"
|
|
1656
|
-
"
|
|
1653
|
+
"Stop the dashboard and other cctally processes, then run "
|
|
1654
|
+
"`cctally db repair --db stats --yes`. The command preserves "
|
|
1655
|
+
"a backup of the corrupt original before replacing anything. "
|
|
1656
|
+
"Do not copy, restore, move, or delete the live DB by hand."),
|
|
1657
1657
|
details=details,
|
|
1658
1658
|
)
|
|
1659
1659
|
if s.cache_db_quick_check is not None and s.cache_db_quick_check != "ok":
|
package/bin/cctally
CHANGED
|
@@ -933,6 +933,9 @@ Migration = _cctally_db.Migration
|
|
|
933
933
|
DowngradeDetected = _cctally_db.DowngradeDetected
|
|
934
934
|
ProdMigrationRefused = _cctally_db.ProdMigrationRefused
|
|
935
935
|
StatsDbCorruptError = _cctally_db.StatsDbCorruptError
|
|
936
|
+
StatsDbMaintenanceError = _cctally_db.StatsDbMaintenanceError
|
|
937
|
+
_is_sqlite_corruption_error = _cctally_db._is_sqlite_corruption_error
|
|
938
|
+
_stats_corruption_guidance = _cctally_db._stats_corruption_guidance
|
|
936
939
|
_STATS_MIGRATIONS = _cctally_db._STATS_MIGRATIONS
|
|
937
940
|
_CACHE_MIGRATIONS = _cctally_db._CACHE_MIGRATIONS
|
|
938
941
|
_make_migration_decorator = _cctally_db._make_migration_decorator
|
|
@@ -957,6 +960,8 @@ _db_path_for_label = _cctally_db._db_path_for_label
|
|
|
957
960
|
cmd_db_skip = _cctally_db.cmd_db_skip
|
|
958
961
|
cmd_db_unskip = _cctally_db.cmd_db_unskip
|
|
959
962
|
cmd_db_recover = _cctally_db.cmd_db_recover
|
|
963
|
+
cmd_db_repair = _cctally_db.cmd_db_repair
|
|
964
|
+
cmd_db_backup = _cctally_db.cmd_db_backup
|
|
960
965
|
cmd_db_checkpoint = _cctally_db.cmd_db_checkpoint
|
|
961
966
|
cmd_db_vacuum = _cctally_db.cmd_db_vacuum
|
|
962
967
|
|
|
@@ -1384,6 +1389,7 @@ cmd_codex_quota_statusline = _cctally_quota.cmd_codex_quota_statusline
|
|
|
1384
1389
|
cmd_codex_quota_forecast = _cctally_quota.cmd_codex_quota_forecast
|
|
1385
1390
|
cmd_codex_quota_blocks = _cctally_quota.cmd_codex_quota_blocks
|
|
1386
1391
|
cmd_codex_quota_breakdown = _cctally_quota.cmd_codex_quota_breakdown
|
|
1392
|
+
cmd_codex_percent_breakdown = _cctally_quota.cmd_codex_percent_breakdown
|
|
1387
1393
|
|
|
1388
1394
|
# S3's qualified Codex accounting adapter is intentionally cache-only: callers
|
|
1389
1395
|
# that need project identity must not downgrade to an unqualified rollout parse.
|
|
@@ -2926,6 +2932,7 @@ _is_reset_drop = _cctally_weekrefs._is_reset_drop
|
|
|
2926
2932
|
# handler (C8, #125 Batch B). Parser dispatch reaches c.cmd_percent_breakdown.
|
|
2927
2933
|
_cctally_percent_breakdown = _load_sibling("_cctally_percent_breakdown")
|
|
2928
2934
|
cmd_percent_breakdown = _cctally_percent_breakdown.cmd_percent_breakdown
|
|
2935
|
+
_render_percent_breakdown_terminal = _cctally_percent_breakdown._render_percent_breakdown_terminal
|
|
2929
2936
|
|
|
2930
2937
|
|
|
2931
2938
|
# _is_cctally_hook_command, SetupError, _load_claude_settings,
|
|
@@ -3207,7 +3214,26 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
3207
3214
|
# + recovery guidance, exit 2. Caught before the generic handler so it
|
|
3208
3215
|
# never renders as a raw traceback.
|
|
3209
3216
|
eprint(f"cctally: {exc}")
|
|
3210
|
-
return
|
|
3217
|
+
return 3
|
|
3218
|
+
except StatsDbMaintenanceError as exc:
|
|
3219
|
+
eprint(f"cctally: {exc}")
|
|
3220
|
+
return 3
|
|
3221
|
+
except sqlite3.DatabaseError as exc:
|
|
3222
|
+
if _is_sqlite_corruption_error(exc):
|
|
3223
|
+
eprint(
|
|
3224
|
+
"cctally: a SQLite database is corrupt — run `cctally doctor` "
|
|
3225
|
+
"to identify it. For stats.db run `cctally db repair --db "
|
|
3226
|
+
"stats --yes`; cache.db is re-derivable via `cctally "
|
|
3227
|
+
"cache-sync --rebuild`."
|
|
3228
|
+
)
|
|
3229
|
+
return 3
|
|
3230
|
+
if _lib_log.debug_enabled():
|
|
3231
|
+
_lib_log.get_logger("cli").debug(
|
|
3232
|
+
"unhandled exception in %r",
|
|
3233
|
+
getattr(args, "command", None), exc_info=True,
|
|
3234
|
+
)
|
|
3235
|
+
eprint(f"Error: {exc}")
|
|
3236
|
+
rc = 1
|
|
3211
3237
|
except Exception as exc: # pragma: no cover
|
|
3212
3238
|
# #279 S2 F2: CCTALLY_DEBUG=1 surfaces the full traceback on stderr
|
|
3213
3239
|
# via the _lib_log chokepoint; default mode stays byte-identical.
|