cctally 1.93.0 → 1.94.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/bin/cctally CHANGED
@@ -703,6 +703,8 @@ _share_history_recipe_id = _cctally_share._share_history_recipe_id
703
703
  _share_resolve_version = _cctally_share._share_resolve_version
704
704
  _share_period_label = _cctally_share._share_period_label
705
705
  _share_parse_date_to_dt = _cctally_share._share_parse_date_to_dt
706
+ _share_display_zone = _cctally_share._share_display_zone
707
+ _share_ground_civil_date = _cctally_share._share_ground_civil_date
706
708
  _share_display_tz_label = _cctally_share._share_display_tz_label
707
709
  _build_report_snapshot = _cctally_share._build_report_snapshot
708
710
  _build_daily_snapshot = _cctally_share._build_daily_snapshot
@@ -1002,6 +1004,13 @@ cmd_stats_epoch_rebuild_internal = (
1002
1004
  cmd_stats_corruption_heal_internal = (
1003
1005
  _cctally_store.cmd_stats_corruption_heal_internal
1004
1006
  )
1007
+ # #496 S6: the retained-artifact reclamation worker and `cctally db prune`
1008
+ # share one sweep implementation, so both resolve through the same sibling.
1009
+ _cctally_retention = _load_sibling("_cctally_retention")
1010
+ cmd_artifact_retention_internal = (
1011
+ _cctally_retention.cmd_artifact_retention_internal
1012
+ )
1013
+ cmd_db_prune = _cctally_retention.cmd_db_prune
1005
1014
  _print_migration_error_banner_if_needed = _cctally_db._print_migration_error_banner_if_needed
1006
1015
  cmd_db_status = _cctally_db.cmd_db_status
1007
1016
  _db_status_for = _cctally_db._db_status_for
@@ -3346,9 +3355,13 @@ def main(argv: list[str] | None = None) -> int:
3346
3355
  eprint(f"{exc}") # message already carries the 'cctally:' prefix
3347
3356
  return 2
3348
3357
  except StatsDbCorruptError as exc:
3349
- # #279 S1 F4: corrupt stats.db (non-re-derivable) — one-line diagnosis
3350
- # + recovery guidance, exit 2. Caught before the generic handler so it
3351
- # never renders as a raw traceback.
3358
+ # #279 S1 F4: corrupt stats.db — one-line diagnosis + recovery
3359
+ # guidance, exit 3. Caught before the generic handler so it never
3360
+ # renders as a raw traceback. With retained journal data stats.db is a
3361
+ # disposable index the heal hook rebuilds; this handler is reached when
3362
+ # that did not resolve it, and the message it prints distinguishes the
3363
+ # rebuild from the pre-cutover case where stats.db may be the only copy
3364
+ # of the recorded history.
3352
3365
  eprint(f"cctally: {exc}")
3353
3366
  return 3
3354
3367
  except StatsDbMaintenanceError as exc:
@@ -3407,6 +3420,25 @@ def main(argv: list[str] | None = None) -> int:
3407
3420
  _post_command_update_hooks(getattr(args, "command", None), args)
3408
3421
  except Exception:
3409
3422
  pass
3423
+ # #496 S6 §5.2. Its own try/except and its own admission predicate: an
3424
+ # automatic reclamation must never perturb the command the user ran, and
3425
+ # the predicate is what keeps it off `statusline`, `doctor`, every hidden
3426
+ # worker, every failed command and `db prune` in every mode. `hook-tick`
3427
+ # is admitted from inside its own FORKED CHILD, never here — the parent
3428
+ # returns before the body runs, and the inline fallback is on Claude
3429
+ # Code's blocking path.
3430
+ try:
3431
+ _cctally_retention.maybe_defer_artifact_retention(
3432
+ command=getattr(args, "command", None),
3433
+ action=getattr(args, "db_action", None),
3434
+ exit_code=rc,
3435
+ # `db journal-repair` and `db rederive` are preview-by-default, and
3436
+ # the former's preview carries a literal no-mutation contract that
3437
+ # the update hook above already honours by skipping it entirely.
3438
+ applied=getattr(args, "yes", None),
3439
+ )
3440
+ except Exception:
3441
+ pass
3410
3442
  return rc
3411
3443
 
3412
3444
 
@@ -3486,7 +3518,7 @@ def _post_command_update_hooks(command: str | None, args) -> None:
3486
3518
  return
3487
3519
  if command in ("_update-check", "_telemetry-beat", "_codex-quota-verify",
3488
3520
  "_stats-epoch-rebuild", "_stats-corruption-heal",
3489
- "_codex-replay-drain"):
3521
+ "_codex-replay-drain", "_artifact-retention"):
3490
3522
  # All six hidden workers are detached and have already done their one job
3491
3523
  # in their own command handler; none must re-enter this hook.
3492
3524
  # Without the guard the ``_update-check`` worker would fall through to