cctally 1.87.0 → 1.87.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,16 @@ based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [1.87.2] - 2026-07-30
9
+
10
+ ### Fixed
11
+ - The dashboard no longer freezes provider data while Claude or Codex sessions are continuously active. A completed read now publishes the coherent cache snapshot it actually built even when newer session bytes arrive before the build finishes, so Codex quota percentages and Recent Sessions keep advancing instead of remaining pinned to an older generation. Stats-side changes during a build still fail closed to the prior complete bundle.
12
+
13
+ ## [1.87.1] - 2026-07-30
14
+
15
+ ### Fixed
16
+ - Codex session names now remain visible in Recent Sessions when an individual account is selected, whenever transcript visibility is enabled. Selecting an account no longer replaces every session name with an em dash; disabling transcript visibility still hides names in both the all-accounts and focused-account views.
17
+
8
18
  ## [1.87.0] - 2026-07-30
9
19
 
10
20
  ### Fixed
package/README.md CHANGED
@@ -30,9 +30,9 @@ Your Claude Code plan meters you with a percentage that creeps up all week. ccta
30
30
  </p>
31
31
 
32
32
  <!-- cctally:latest-stable:begin -->
33
- **Latest stable: v1.83.1** (2026-07-28)
33
+ **Latest stable: v1.87.1** (2026-07-30)
34
34
 
35
- - 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.
35
+ - Codex session names now remain visible in Recent Sessions when an individual account is selected, whenever transcript visibility is enabled. Selecting an account no longer replaces every session name with an em dash; disabling transcript visibility still hides names in both the all-accounts and focused-account views.
36
36
  <!-- cctally:latest-stable:end -->
37
37
 
38
38
  ## Quick start
@@ -1787,11 +1787,39 @@ def _overlay_claude_source_session_titles(
1787
1787
 
1788
1788
 
1789
1789
  def _codex_source_session_rows(envelope: dict) -> list:
1790
- """Return direct-Codex and All-tab Codex session row lists."""
1790
+ """Return every request-local Codex session row list.
1791
+
1792
+ Besides the merged parent used by ``All accounts``, decorated Codex data
1793
+ carries one ``account_scopes[*].sessions`` child per account. The client
1794
+ swaps that child into view when an account chip is focused, so the private
1795
+ label overlay must cover it under the same per-request transcript gate.
1796
+ """
1791
1797
  sources = envelope.get("sources")
1792
1798
  if not isinstance(sources, Mapping):
1793
1799
  return []
1794
1800
  out = []
1801
+
1802
+ def append_rows(data) -> None:
1803
+ if not isinstance(data, Mapping):
1804
+ return
1805
+ sessions = data.get("sessions")
1806
+ if isinstance(sessions, Mapping):
1807
+ rows = sessions.get("rows")
1808
+ if isinstance(rows, list):
1809
+ out.append(rows)
1810
+ account_scopes = data.get("account_scopes")
1811
+ if not isinstance(account_scopes, Mapping):
1812
+ return
1813
+ for child in account_scopes.values():
1814
+ if not isinstance(child, Mapping):
1815
+ continue
1816
+ sessions = child.get("sessions")
1817
+ if not isinstance(sessions, Mapping):
1818
+ continue
1819
+ rows = sessions.get("rows")
1820
+ if isinstance(rows, list):
1821
+ out.append(rows)
1822
+
1795
1823
  candidates = [(sources.get("codex") or {}).get("data")]
1796
1824
  all_data = (sources.get("all") or {}).get("data")
1797
1825
  if isinstance(all_data, Mapping):
@@ -1799,14 +1827,7 @@ def _codex_source_session_rows(envelope: dict) -> list:
1799
1827
  if isinstance(providers, Mapping):
1800
1828
  candidates.append(providers.get("codex"))
1801
1829
  for data in candidates:
1802
- if not isinstance(data, Mapping):
1803
- continue
1804
- sessions = data.get("sessions")
1805
- if not isinstance(sessions, Mapping):
1806
- continue
1807
- rows = sessions.get("rows")
1808
- if isinstance(rows, list):
1809
- out.append(rows)
1830
+ append_rows(data)
1810
1831
  return out
1811
1832
 
1812
1833
 
@@ -1818,8 +1839,9 @@ def _overlay_codex_source_session_labels(
1818
1839
  ``state_5.sqlite.threads.title`` is derived from transcript prompt content.
1819
1840
  The frozen source state therefore retains it only in a server-private key
1820
1841
  map, outside the published ``data`` tree. Closed requests return before
1821
- consulting that map; open requests match labels to direct and All rows by
1822
- their opaque resource keys. A missed call fails closed.
1842
+ consulting that map; open requests match labels to merged-parent,
1843
+ account-scoped, and All rows by their opaque resource keys. A missed call
1844
+ fails closed.
1823
1845
  """
1824
1846
  if not transcripts_visible:
1825
1847
  return
@@ -2808,21 +2808,33 @@ def _tui_build_source_bundle(
2808
2808
  source_order=("claude", "codex", "all"),
2809
2809
  sources={"claude": claude, "codex": codex, "all": combined},
2810
2810
  )
2811
- # End our snapshots before re-reading the cheap signatures. Seeing a
2812
- # different physical state means the build may have mixed independent
2813
- # database generations; retain only the prior complete bundle.
2811
+ # End the pinned cache snapshot before re-reading the cheap signatures.
2812
+ # New cache commits after BEGIN do not make this bundle incoherent: all
2813
+ # cache-backed reads above saw the same frozen generation, and its
2814
+ # data_version names that generation. Reject only stats-side movement,
2815
+ # whose statement-scoped autocommit reads may have mixed generations.
2816
+ # Rejecting ordinary cache advancement starves publication whenever
2817
+ # active Claude/Codex sessions append faster than this build completes.
2814
2818
  if cache_read_tx:
2815
2819
  cache_conn.rollback()
2816
2820
  cache_read_tx = False
2817
2821
  post_stats_digest = codex_stats_digest(stats_conn)
2822
+ post_accounts_digest = accounts_identity_digest(stats_conn)
2818
2823
  post_signature = c.compute_signature(
2819
2824
  cache_conn,
2820
2825
  stats_conn,
2821
2826
  generation=c.current_generation(),
2822
2827
  codex_stats_digest=post_stats_digest,
2823
- accounts_digest=accounts_identity_digest(stats_conn),
2828
+ accounts_digest=post_accounts_digest,
2829
+ )
2830
+ stats_generation_moved = (
2831
+ post_signature.max_wus_id != signature.max_wus_id
2832
+ or post_signature.max_wcs_id != signature.max_wcs_id
2833
+ or post_signature.reset_sig != signature.reset_sig
2834
+ or post_stats_digest != stats_digest
2835
+ or post_accounts_digest != accounts_digest
2824
2836
  )
2825
- if post_signature != signature:
2837
+ if stats_generation_moved:
2826
2838
  if prior_bundle is not None:
2827
2839
  return prior_bundle
2828
2840
  raise RuntimeError("source read generation moved during build")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cctally",
3
- "version": "1.87.0",
3
+ "version": "1.87.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": {