What's new
Plugin-data discovery just works. Token Optimizer now finds your live session data even when the plugin context is not active — running measure.py or the dashboard from a plain terminal walks ~/.claude/plugins/installed_plugins.json and lands on the right install. Dashboards stop showing stale snapshots.
User-level config edits take effect. Direct edits to ~/.claude/token-optimizer/config.json now win over plugin-data config in the priority chain. If you flip a v5 flag in your own config file, the hooks honor it on the next call.
Env var booleans are forgiving. TOKEN_OPTIMIZER_BASH_COMPRESS=true, TOKEN_OPTIMIZER_READ_CACHE_DELTA=yes, and TOKEN_OPTIMIZER_QUALITY_NUDGES=on all work as expected (case-insensitive). Tri-state flags like TOKEN_OPTIMIZER_STRUCTURE_MAP=beta keep their exact-match semantics.
Atomic config writes everywhere. Pricing-tier and quality-bar opt-out writes now use the same advisory-flock + tempfile + os.replace path as the dashboard toggle, so concurrent writers can never leave partial JSON on disk.
SessionStart legacy data migration also fires for CLI-discovered installs, so first-run dashboard users in a CLI shell get their trends.db and snapshots copied into the active plugin-data dir.
Hardened
The new plugin_env.py resolver is built for adversarial input. Marketplace names from installed_plugins.json are validated against [A-Za-z0-9._-]+ before being concatenated into a path. All resolved candidates must be real directories under ~/.claude/plugins/data/ — symlinks are rejected, traversal attempts are confined out via resolve().is_relative_to(). JSON reads are bounded at 1 MB and protected against RecursionError from pathological nesting. The CLAUDE_PLUGIN_DATA env var must itself resolve under ~/.claude/. Multi-key installs (e.g., -inline and -marketplace variants both registered) sort candidates by mtime so the freshest install wins instead of whatever Python's dict ordering surfaces first. Discovery is lru_cache(maxsize=1) so each hook process makes one filesystem traversal per call, not four.
Internals
A single plugin_env.py module is now the source of truth for plugin-data discovery and v5 feature flag resolution; bash_hook.py, read_cache.py, archive_result.py, and measure.py all consult it instead of inlining their own copies. The previous module name paths.py is renamed to make the two responsibilities self-evident.
Compatibility
OpenClaw stays at 2.3.0; this release only touches the Token Optimizer plugin. README badges and the OpenClaw version line are aligned across all surfaces.