Conversation
…e_dir Tokscale-core's caches (source-message bincode, pricing JSON, the OpenCode migration record) all need the same config dir resolution that `Settings::load()` and `load_star_cache()` use, but the helpers lived only in tokscale-cli. Move them to a new `tokscale_core::paths` module and re-export them from the CLI side so existing call sites stay untouched. Adds two new helpers: - `get_cache_dir()` returns `<config_dir>/cache`. The next commit consolidates every cache (TUI display data, source-message bincode, pricing JSON, opencode-migration.json, Wrapped fonts/images) under this single subdirectory so an isolated profile (`TOKSCALE_CONFIG_DIR=...`) covers everything in one shot. - `legacy_dirs_cache_dir()` and `legacy_dot_cache_tokscale_dir()` resolve the historic `dirs::cache_dir()/tokscale` and `~/.cache/tokscale` locations respectively. Both probes return None when `TOKSCALE_CONFIG_DIR` is set so legacy data does not leak into isolated profiles. Both are needed on macOS because the historic split put the TUI display cache under `~/.cache/tokscale` and the source message / pricing caches under `~/Library/Caches/tokscale`. No behavior change yet: all current callers still resolve the same paths they did before. The next commit switches the cache modules over and adds the legacy fallback chain.
…th legacy fallback Tokscale state was scattered across up to four directories per platform. On macOS specifically the source-message bincode and pricing JSON lived under ~/Library/Caches/tokscale while the TUI display cache lived under ~/.cache/tokscale, and Wrapped image/font caches were in a third place. The new defaultClients setting from #464 made this split visible: users who set TOKSCALE_CONFIG_DIR for an isolated profile got a hermetic config root but the caches still leaked from the host paths. Consolidates every cache under <config_dir>/cache so: - TOKSCALE_CONFIG_DIR controls everything in one shot (hermeticity) - rm -rf ~/.config/tokscale/cache wipes only regenerable state - The macOS-vs-Linux directory split is gone Per-file moves: - tui-data-cache.json: ~/.cache/tokscale/ -> <config_dir>/cache/ - source-message-cache.bin + .lock: dirs::cache_dir()/tokscale/ -> <config_dir>/cache/ - pricing-litellm.json + pricing-openrouter.json: same as above - opencode-migration.json: same as above - fonts/, images/ (Wrapped): ~/.cache/tokscale/<dir>/ -> <config_dir>/cache/<dir>/ Read-side migration is a one-time fallback per file: try the canonical path first, then probe the legacy locations (gated off when TOKSCALE_CONFIG_DIR is set). Writes always land at the new path. Legacy files are left in place so a downgrade keeps working — no copy, no delete. Schemas stay at TUI=6, core=7. Path moves alone don't warrant a bump. Closes #470
…refresh The 5-minute cache staleness threshold previously gated the render decision on TUI launch: a Stale cache rendered with a background refresh, but a hard Miss (cache older than its hard-expiry window, schema drift, or filter-set mismatch) blocked the user behind a full re-aggregation pass. Schema drift and filter mismatch still produce a Miss because rendering wrong-shape or wrong-filter data is worse than showing nothing — but the time-based staleness check no longer affects the render decision. The Fresh-vs-Stale distinction stays in CacheResult for future status-bar use (data age metadata), but the call site treats both as renderable. needs_background_load becomes unconditionally true on TUI launch so cached data always gets refreshed. Closes #471
Adds an opt-in mechanism to warm the TUI cache from a --light run. Previously tokscale --light was strictly read-only — no way to opportunistically refresh the TUI cache from a CLI report even though the underlying UsageData was already computed. Daily-cron users would see a cold TUI on the next interactive launch. CLI flags: - --write-cache (requires --light): overwrite the TUI cache atomically after the report renders - --no-write-cache (requires --light, conflicts with --write-cache): skip the cache write even if the setting opts in Settings file: - settings.json grows a light section with writeCache: false default - #[serde(default)] keeps existing settings.json files loading unchanged Resolution: --no-write-cache > --write-cache > settings.light.writeCache > false. Cache write uses the existing atomic temp-file rename, so a process crash mid-write never loses the cache. Closes #472
…iteCache setting - Cache layout: all regenerable caches now live under <config_dir>/cache. Documented for both posix and Windows in all four language READMEs. - TOKSCALE_CONFIG_DIR row: clarifies the override now covers caches too. - light.writeCache row: new opt-in setting documented in the configuration table. - Cache directory layout subsection: explains what lives where and notes the directory is safe to delete (caches regenerate).
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
~/.config/tokscale/cache, render TUI cache eagerly, add --write-cache opt-in
~/.config/tokscale/cache, render TUI cache eagerly, add --write-cache opt-in~/.config/tokscale/cache, render TUI cache eagerly, add --write-cache opt-in for light mode
…ject empty TOKSCALE_CONFIG_DIR
The TUI cache key is `(enabled_clients, group_by)` only — it does NOT
include `--since`, `--until`, `--year`, or `--home`. `write_light_cache`
previously forwarded all of those into the DataLoader, so a
`tokscale --since 2025-01-01 --light --write-cache` invocation built a
date-filtered or home-scoped slice and saved it under the unfiltered
key. Subsequent `tokscale tui` launches would hit that cache and render
the filtered slice as if it were the default report — silent data
correctness loss flagged by both Devin and Codex review.
`write_light_cache` now refuses the write when any of those filters is
present and prints an eprintln explaining why. The CLI report still
prints normally; only the cache-warm side-effect is skipped.
Separately, `get_config_dir` previously treated an empty
`TOKSCALE_CONFIG_DIR=""` as a valid override and returned
`PathBuf::from("")`, while `is_config_dir_overridden` treated empty as
unset. After cache consolidation that mismatch could send writes to
relative paths like `./cache/...` even though the legacy fallback logic
still behaved as if no override was active. Resolver now agrees with
the predicate: empty string falls through to the platform default.
…'t tank exit codes The cache-warm step ran AFTER the report had already been flushed to stdout, but propagated DataLoader errors via `?`. A scan failure (e.g. a corrupt session file in one client's directory) would surface as a non-zero exit code on a CLI invocation the user already saw print correctly, breaking scripts and CI pipelines that key off exit status. Changes write_light_cache return type from `Result<()>` to `()` and swallows loader errors via `if let Ok(data) = loader.load(...)`, matching the pattern in run_warm_tui_cache. The call site no longer needs `?`. The eprintln-on-skip behavior (when --since/--until/--year/ --home are set) is unchanged. Also fixes a stray clippy carryover in paths::tests where an empty TOKSCALE_CONFIG_DIR fallback assertion compared a PathBuf against a \&str literal.
Antigravity's scanner uses PathRoot::Config to resolve where to look
for synced sessions. Without this fix the resolver hardcoded
`{home_dir}/.config/tokscale` as the non-Linux fallback, while
get_antigravity_cache_dir() (the writer side) routes through
paths::get_config_dir() which calls dirs::config_dir() on Windows. The
two diverged on Windows: tokscale antigravity sync wrote to
%APPDATA%\\tokscale\\antigravity-cache\\ while the scanner read from
%USERPROFILE%\\.config\\tokscale\\antigravity-cache\\sessions\\, so
synced data silently never appeared in reports.
PathRoot::Config now mirrors get_config_dir's platform branches:
TOKSCALE_CONFIG_DIR override > Linux XDG_CONFIG_HOME > Windows
dirs::config_dir() > generic `{home}/.config/tokscale`. macOS still
falls through to `{home}/.config/tokscale` because get_config_dir()
deliberately overrides dirs::config_dir() there (which would otherwise
return ~/Library/Application Support/).
New regression test test_path_root_config_uses_dirs_config_dir_on_windows
locks the writer/scanner agreement on Windows.
…ng cache at canonical path CI runners can have XDG_CONFIG_HOME set globally outside the sandboxed HOME, so the post-#470 cache resolver leaks the binary's read+write to the host's config dir. Three integration tests (test_monthly_json_offline_*, test_submit_offline_*) flaked on Linux runners because the cost expectations assumed the sandboxed pricing cache (or absence thereof), but the binary was finding pricing data via the host's $XDG_CONFIG_HOME/tokscale/cache/ — or worse, would write to it. offline_cmd_with_home now sets XDG_CONFIG_HOME alongside the existing XDG_CACHE_HOME and XDG_DATA_HOME pins, mirroring cmd_with_home and keeping the binary's cache resolution fully inside the temp dir. write_pricing_cache also seeds the canonical .config/tokscale/cache/ directory so tests that exercise the new path work even when legacy fallback is suppressed (e.g. via TOKSCALE_CONFIG_DIR override).
…ermetic CI runners that set XDG_CONFIG_HOME globally were leaking the binary's canonical cache root outside the test's sandboxed HOME, so SourceMessageCache::load read from the host's $XDG_CONFIG_HOME/tokscale/cache/source-message-cache.bin (or wrote there) instead of the temp dir. Three message_cache tests (test_source_message_cache_round_trip, load_falls_back_to_legacy_dirs_cache_path, load_falls_back_to_legacy_dot_cache_path) and one pricing/cache test (load_falls_back_to_legacy_dirs_cache_path) flaked on Linux runners as a result. Adds sandbox_cache_env / restore_cache_env helpers to message_cache so the round-trip test pins HOME + XDG_CONFIG_HOME + XDG_CACHE_HOME together. The two legacy-fallback tests gain explicit XDG_CONFIG_HOME pinning to match. The pricing test does the same. All 4 now pass on Linux CI.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Consolidates tokscale state to a single root, removes the eager-refresh penalty on TUI launch, and adds an opt-in for warming the TUI cache from
--lightreports.Closes #470, #471, #472.
Changes
~/.config/tokscale/cache/is the new home for all caches (#470)Tokscale state was scattered across up to four directories per platform. On macOS specifically the source-message bincode and pricing JSON lived under
~/Library/Caches/tokscalewhile the TUI display cache lived under~/.cache/tokscale, and Wrapped image/font caches were in a third place. The newdefaultClientssetting from #464 made this split visible: users who setTOKSCALE_CONFIG_DIRfor an isolated profile got a hermetic config root but the caches still leaked from the host paths.Every cache now resolves under
<config_dir>/cache:tui-data-cache.jsonsource-message-cache.bin+.lockpricing-litellm.json+pricing-openrouter.jsonopencode-migration.jsonfonts/andimages/(Wrapped)TOKSCALE_CONFIG_DIRcontrols everything in one shot.rm -rf ~/.config/tokscale/cachewipes only regenerable state.Migration is read-side, not destructive: each cache module probes the new path first, then falls back once to the historic locations (
dirs::cache_dir()/tokscaleand~/.cache/tokscale) whenTOKSCALE_CONFIG_DIRis not set. Writes always land at the new path. Legacy files stay in place so a downgrade keeps working — no copy, no delete.TUI renders cached data immediately, regardless of age (#471)
The 5-minute hard staleness cutoff previously gated the render decision on TUI launch: a cache older than that window blocked the user behind a full re-aggregation pass. Schema drift and filter-set mismatch still produce a
Miss(rendering wrong-shape data is worse than showing nothing), but time-based aging no longer affects the render decision.FreshvsStaledistinction stays inCacheResultfor future status-bar use ("data is N minutes old"), but the call site treats both as renderable. Background refresh runs unconditionally on TUI launch.--write-cacheflag andlight.writeCachesetting (#472)CLI flags:
--write-cache(requires --light,conflicts_with --no-write-cache): atomically overwrite the TUI cache after--lightrenders, so the nexttokscale tuilaunch starts from this report's data.--no-write-cache: skip the cache write even if the setting opts in.settings.jsongrows alight.writeCache: boolfield (defaultfalse).#[serde(default)]keeps existing settings.json files loading unchanged — no migration needed.Resolution:
--no-write-cache>--write-cache>settings.light.writeCache>false. Cache write uses the existing atomic temp-file rename pattern, so a process crash mid-write never loses the cache.Test results
tokscale-coretokscale-cliTest count grew from 1,081 (pre-PR) → 1,114 (+33 new regression tests covering cache migration, TUI render-stale,
--write-cacheresolution matrix, schema/legacy fallback edges).cargo check --workspace --releaseclean.cargo clippyno new warnings on changed code.Backwards compatibility
settings.jsonmissinglightfield#[serde(default)]onSettings.lightandLightSettings.write_cacheLibrary/CachesAND.cache)XDG_CONFIG_HOMEsetdirs::config_dir()TOKSCALE_CONFIG_DIRusers (CI sandboxes)cli_tests.rsprimingCommits
Each commit compiles and tests green standalone, so review is doable commit-by-commit.
Summary by cubic
Unifies all regenerable caches under
~/.config/tokscale/cache, renders the TUI immediately from any valid cache while refreshing in the background, and adds an opt-in to write the TUI cache from--lightruns. Path resolution now matches across reader/writer on Windows (Antigravity), and--lightcache writes are best-effort and never change the exit code.New Features
<config_dir>/cache(controlled byTOKSCALE_CONFIG_DIR) for TUI data, source-message, pricing, OpenCode migration, and Wrapped assets.--write-cache(requires--light) and--no-write-cache;settings.jsonaddslight.writeCache(defaultfalse). Resolution:--no-write-cache>--write-cache>light.writeCache. Refuses to write when--since/--until/--year/--homeare set. Writes are atomic and best-effort.<config_dir>/antigravity-cache; Windows scanner now uses the same config root as the writer, so synced sessions appear in reports.Migration
~/Library/Caches/tokscale,~/.cache/tokscale) whenTOKSCALE_CONFIG_DIRis not set; writes always go to<config_dir>/cache. Wrapped fonts/images are copied once from legacy into the new path on first use; other caches are not copied. Downgrade-safe.TOKSCALE_CONFIG_DIRis treated as unset; overrides remain hermetic (no legacy probing). Safe to delete<config_dir>/cache; schemas unchanged.XDG_CONFIG_HOMEand seeds the canonical cache path to keep cache resolution hermetic in CI (no runtime behavior change).Written for commit f5bd599. Summary will update on new commits.