clear_model_scoped_telemetry() now resets last_pinned_prefix_hash to
None so /cache stats does not show the previous model/provider cache
scope's fingerprint after a switch.
- Move pinned_hash extraction after check_and_update() so the reported
hash reflects the current prefix state, not the previous turn
- Skip non-cache-aware turns (cache_hit_tokens=None) in /cache stats
aggregation; infer miss tokens when cache_miss_tokens is None
- Clear last_pinned_prefix_hash to None when pinned_combined_hash is
empty (e.g. switching to a non-caching model/provider)
Surfaces the current pinned prefix fingerprint hash and stability
diagnostics via a new `/cache stats` subcommand, per the low-risk
0.8.x slice agreed on in #2264.
Changes:
- EngineEvent::PrefixCacheChange now carries pinned_combined_hash
from PrefixStabilityManager so the TUI layer can surface it
- App.last_pinned_prefix_hash stores the hash per-turn for /cache stats
- New /cache stats command renders three sections:
- Prefix Stability: pct, checks, changes, last-change description
- Prefix Fingerprint: full SHA-256 hash, short id, drift warning
- Cache Hit Rate: aggregated token totals + low-hit advisory
- format_tokens() helper for compact K/M token display
- 6 unit tests covering: no-data, stable, drift warning, hit-rate
summary, low-hit advisory, token formatting
Refs: #2264
Article VII Tier 5 currently lists four hard-coded path conventions
(AGENTS.md / CLAUDE.md / .codewhale/instructions.md / .deepseek/instructions.md)
as the canonical Local Law sources. Embedders that inject instructions via
`EngineConfig.instructions` (rather than placing files at one of those four
paths) get their files classified by path — and since their paths don't
match, the model defaults to treating their imperatives as Tier 7 Memory
(the lowest tier per Article VII), overridable by a single user sentence.
Adds an explicit clause to Tier 5: '...and any file configured via
`EngineConfig.instructions` (rendered as <instructions source=...> blocks
above). ...embedder-declared imperatives are Local Law, not Memory
preferences.'
Pairs naturally with #2311 (InstructionSource enum) but stands alone — this
is a doc/law clarification, not an API change.
Adds `local_law_tier_covers_engine_config_instructions` test pinning the
new clause's presence.
The environment block (`platform`, `shell`, `pwd`, `lang`) was inserted
above the volatile-content boundary as a workspace-static cache layer. The
original comment claimed 'workspace path is fixed for the run' → static-
cacheable — true for the terminal use case where one process owns one
workspace for its lifetime.
It is **not** true for embedders that swap workspaces between sessions:
- IDE/GUI integrations binding the engine to a per-tab workspace
- Multi-engine pools with one engine per session
- Anything sending Op::SyncSession with a new workspace
For these embedders, `pwd` drifts session-to-session, dragging the entire
static prefix (mode prompt, project context, skills, context management,
compact template) out of cache reuse on every session switch. That's a
~30KB+ cache miss for a few-byte path change.
Moves the environment block below the volatile-content boundary, alongside
the configured `instructions = [...]` block. Effect:
- Static prefix stays byte-stable across sessions even when workspace
changes (better KV prefix cache hit rate)
- Model still sees `pwd` / `platform` / `shell` (needed for exec_shell
and structured search tools), just in a slightly later position
- No behavior change for terminal callers (workspace fixed → block content
unchanged → still in same logical place, just below the boundary)
Added a comment explaining the per-session-workspace motivation.
The general-purpose sub-agent intro tells the agent how to plan, but says
nothing about when to *stop*. With less capable models this leads to a
failure mode where the agent retries the same failing tool call (e.g. an
unreachable or rate-limited external API) over and over until it hits the
elapsed/step ceiling, wasting the whole budget and returning nothing useful.
Add two short clauses to GENERAL_AGENT_INTRO:
- Stop quickly on failure: after the same call fails twice, return partial
results with a one-line note instead of looping.
- Bounded effort: prefer one focused attempt; if the task can't be completed
within a few tool calls, return current findings so the parent can compensate.
Prompt-only change; no behavioral code paths touched.
Keep the /statusline picker selection visible when navigating through all
available footer items in smaller terminal windows.
The picker now scrolls its visible rows as the cursor moves, wraps Up/Down at
the list edges, and renders the selected row with a continuous, slightly
brighter background.
The right-click context menu rendered all entries in English regardless of ui_locale. Added 26 MessageId variants with translations for en, ja, zh-Hans, zh-Hant, pt-BR, es-419. ContextMenuView now accepts a localized title. build_context_menu_entries() uses app.tr() instead of hardcoded strings.
Treat inputs like `/ hello` as plain user messages instead of slash commands.
This lets users start a message with a literal slash while preserving normal
slash command behavior for `/help`, `/model ...`, and other commands.
Run the stdio MCP server inside a blocking section when launched from the async CLI entrypoint.
This prevents Tokio from panicking when the MCP server's internal runtime is dropped after stdin closes.
Replace the incorrect CodeWhale backend example in /provider help text with DeepSeek.
CodeWhale is the app name, while deepseek is the actual provider id accepted by /provider.
Add a regression test for shipped locale descriptions.