cctally 1.95.0 → 1.95.2

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 CHANGED
@@ -5,6 +5,18 @@ based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [1.95.2] - 2026-08-09
9
+
10
+ ### Fixed
11
+ - Linux release validation now distinguishes rollback-journal contention from hosted-runner storage and scheduler speed, while retaining the reader latency, integrity, and no-sidecar acceptance gates.
12
+
13
+ ## [1.95.1] - 2026-08-09
14
+
15
+ ### Fixed
16
+ - Corruption recovery now preserves both incidents when two database families are quarantined within the same second. The second incident atomically reserves a distinct retention-compatible directory instead of reusing the first incident's path and failing because the old and new `stats.db` generations both exist.
17
+ - The authoritative test gate now runs its rollback-contention wall-clock checks after the parallel pytest estate, alongside the existing serial rebuild benchmark. Whole-estate CPU contention can no longer consume those latency ceilings while isolated regressions still fail the release.
18
+ - Concurrent usage ingestion no longer derives five-hour blocks or milestones from a snapshot that a retained suppression event deliberately removed. A duplicate observation now respects the absent Model-A target instead of passing `None` into the derivation path and repeatedly failing every ingester behind it.
19
+
8
20
  ## [1.95.0] - 2026-08-09
9
21
 
10
22
  ### Changed
@@ -1300,6 +1300,37 @@ def _load_pending_quarantine(db_path: pathlib.Path) -> "dict[str, Any] | None":
1300
1300
  _QUARANTINE_MISSING_CONTEXT_WARNED = False # one-shot warn flag
1301
1301
 
1302
1302
 
1303
+ def _reserve_quarantine_incident(
1304
+ root: pathlib.Path, db_name: str, timestamp: str
1305
+ ) -> pathlib.Path:
1306
+ """Atomically reserve a retention-compatible incident directory."""
1307
+ root.mkdir(parents=True, exist_ok=True)
1308
+ primary = root / f"{db_name}-{timestamp}"
1309
+ try:
1310
+ primary.mkdir()
1311
+ return primary
1312
+ except FileExistsError:
1313
+ pass
1314
+
1315
+ match = re.fullmatch(r"(\d{8}T\d{6})(?:Z|_(\d{6}))", timestamp)
1316
+ if match is None:
1317
+ stem = dt.datetime.now(dt.timezone.utc).strftime("%Y%m%dT%H%M%S")
1318
+ first = 1
1319
+ else:
1320
+ stem = match.group(1)
1321
+ first = int(match.group(2) or "0") + 1
1322
+ for microsecond in range(first, 1_000_000):
1323
+ candidate = root / f"{db_name}-{stem}_{microsecond:06d}"
1324
+ try:
1325
+ candidate.mkdir()
1326
+ return candidate
1327
+ except FileExistsError:
1328
+ continue
1329
+ raise OSError(
1330
+ f"could not allocate a unique quarantine incident for {db_name}"
1331
+ )
1332
+
1333
+
1303
1334
  def _warn_quarantine_created_without_context(incident: pathlib.Path) -> None:
1304
1335
  """Report a creation that supplied no `QuarantineContext` (#496 S6 §4.2).
1305
1336
 
@@ -1352,8 +1383,7 @@ def _quarantine_db_family_strict(
1352
1383
  if state is None:
1353
1384
  ts = ts or _db_backup_timestamp()
1354
1385
  root = _cctally_core.APP_DIR / "quarantine"
1355
- incident = root / f"{db_path.name}-{ts}"
1356
- incident.mkdir(parents=True, exist_ok=True)
1386
+ incident = _reserve_quarantine_incident(root, db_path.name, ts)
1357
1387
  for directory in (root, incident):
1358
1388
  try:
1359
1389
  os.chmod(directory, 0o700)
@@ -5812,6 +5812,15 @@ def _pipeline_claude_usage(ctx, rec):
5812
5812
  columns=cols,
5813
5813
  at=capture_at,
5814
5814
  )
5815
+ # An exact retry can meet effective event metadata whose physical row a
5816
+ # later suppression deliberately deleted. emit_model_a must not
5817
+ # resurrect that row and therefore returns None; deriving milestones or
5818
+ # blocks from the absent target would either create dangling references
5819
+ # or attempt int(None). This is distinct from an ordinary fold-decision
5820
+ # skip, which resolves a real latest snapshot below and still runs the
5821
+ # idempotent side-effect chokepoints.
5822
+ if rowid is None:
5823
+ return
5815
5824
  saved["id"] = rowid
5816
5825
  else:
5817
5826
  latest = conn.execute(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cctally",
3
- "version": "1.95.0",
3
+ "version": "1.95.2",
4
4
  "description": "Claude Code usage tracker and local dashboard for Pro/Max subscription limits - weekly cost-per-percent trend, quota forecasts, threshold alerts. ccusage-compatible.",
5
5
  "homepage": "https://github.com/omrikais/cctally",
6
6
  "repository": {