# CLAUDE-NOTES

Durable engineering notes that survive `/init` regeneration of `clavue.md` /
`AGENTS.md` / `CLAUDE.md`. Append-only by convention.

---

## Bash redirection prompts for shell observability pipelines

`extractOutputRedirections` flags any redirection target containing
shell expansion characters (`$`, `` ` ``, `*`, `?`, `[`, `{`, `~`, `!`,
`=`) as dangerous, so that we never path-validate a target whose final
value depends on runtime expansion.

As of 2026-05-24, `awk '{print $N}'`, `sed '...$...'`, and
`perl '...$...'` single-quoted program bodies are pre-masked before
parsing, so commands like

```bash
ss -tnp 2>/dev/null | grep -E "ESTAB" | awk '{print $5}' | sort | uniq -c
```

no longer prompt in `default` mode. Mixed forms such as
`awk -v X=$x '{print $5}'` keep their normal semantics — the `-v X=$x`
arg is still seen by the shell as a variable expansion.

If you regularly run other shell pipelines that rely on `$VAR` outside
those three tools (e.g. `bash -c 'echo $foo > /tmp/x'`), you can grant
yourself an explicit whitelist via your settings:

```json
{
  "permissions": {
    "allow": [
      "Bash(ss:*)",
      "Bash(netstat:*)",
      "Bash(lsof:*)"
    ]
  }
}
```

Trade-off: an `alwaysAllowedRule` match short-circuits BOTH the prompt
and the path-validation step. A rule like `Bash(ss:*)` will allow
`ss -tnp 2>$EVIL_VAR` without asking. Only whitelist commands that you
inspect end-to-end yourself, and prefer narrow `Bash(<prog> <subcmd>:*)`
patterns over broad ones.

## DeepSeek prefix-cache stability lessons from Reasonix (esengine/DeepSeek-Reasonix)

DeepSeek-Reasonix (Reasonix) is a DeepSeek-native terminal coding agent (Go rewrite on main-v2; legacy TS on v1) **engineered entirely around DeepSeek's automatic prefix cache** as a first-class invariant — not a toggle. Goal: leave the agent running for long sessions with 99%+ cache hits, 5-10x lower cost (real example: 435M input tokens for ~$12 vs $61 uncached on v4-flash), and stable performance.

**Key principles to apply in Clavue (especially when user slots DeepSeek flash/pro/reasoner as mainLoop or roles):**

- **Cache stability is an invariant, not a feature.** The reusable request prefix (system instructions + tool schemas + durable memory/project context + high-cap discipline) must remain **byte-identical** across turns. Only the turn tail (recent messages + tool results + ephemeral state) appends. DeepSeek's server-side prefix cache relies on this byte-stability for hits. Any mutation (even "small" prompt tweaks) busts the cache for the whole prefix.

- **Compaction as the sole, rare, cache-reset point.** Do not compact frequently. When context ~80% full (or threshold), summarize the *older middle* only into a concise, stable "handoff briefing" (<15k tokens target, like Reasonix 14k example). Keep a small verbatim recent tail (Reasonix uses 8; align backward to clean tool-result boundaries so no orphan tool_calls). Archive dropped history (jsonl). The new prefix becomes (old stable prefix + this summary + tail) — predictable and cache-friendly going forward. Never mutate the summary later.

- **Multi-model / subagent isolation via separate sessions.** Planner (low-freq, e.g. deepseek-pro) and executor (main, e.g. deepseek-flash) must run in **separate, independent sessions** (never shared transcript/prefix). Switching models or mixing subagent turns inside one conversation pollutes the main prefix and tanks cache. Clavue's forkedAgent for compact and subagent inherit are good starts — extend for deepseek "planner vs executor" roles (see modelRoles, agent-os). Use cheap flash for high-volume execution/summarize; reserve pro for planning.

- **Stable tool schemas + output discipline.** Tool descriptions must not change mid-session (dynamic agent lists etc. are sanitized in cache-break detection). High models get concise discipline prompts that explicitly say "keep prompt-cache shape and output size stable" + "prefer tool calls over narration" + "surgical one-unit changes". Deepseek reasoner/v4/think get the "deepseek-high" variant. Extend with Reasonix rules: "The prefix must stay byte-stable; compaction is the only reset."

- **Observability & cost control.** Track cache_read vs total input (already in usageMapping, promptCacheBreakDetection, tengu events). Expose in doctor / status / token bar for deepseek users ( "99% cache hit, session cost $X" ). Notify on breaks (already advanced in promptCacheBreakDetection — compact notifies to reset baseline; subagents isolated by agentId; microcompact cache_edits tracked). For deepseek, prefer providers/gateways with good prefix cache (official api.deepseek.com best).

- **MCP/plugins & extensibility without breaking prefix.** External tools via MCP (stdio/http) — register at runtime but keep core prefix (built-in tools + system) stable. Custom skills/commands as markdown (like Reasonix .reasonix/commands) — they become turns without mutating prefix. Clavue already has strong MCP + skills; ensure deepseek paths treat plugin discovery as non-prefix-mutating (use cache edits or sidecars).

**Clavue already applies many (highest-dimension native chat for deepseek/grok, high discipline prompts with "keep prompt-cache shape", advanced compaction with postCompactionState cooldowns + notifyCompaction for cache baseline, fingerprint disabled to protect prefix, separate forked for compact, model roles for summarize/extract, promptCacheBreakDetection with subagent isolation).**

**Adoptions from this study (2026-06):**
- Strengthened deepseek-high + gpt5-high + native-messages discipline prompts with explicit "byte-identical prefix invariant", "compaction as rare stable reset", "concise <15k handoff", "align at tool-result boundaries", "archive dropped".
- Enhanced compact prompt (BASE_COMPACT_PROMPT) with Reasonix + GrokCLI targets + deepseek-specific "stable byte-predictable handoff", "no orphan tool_calls", "new prefix = prior + summary (cache-friendly)".
- Added this durable note. Future: consider deepseek-specific compaction ratio (e.g. 0.65-0.8 of window for large ctx), default summarize role to cheap flash variant for deepseek profiles (via roles or provider), more UI for cache-hit % on deepseek, two-model planner/executor as separate sessions when configured.

Apply these when user uses deepseek (or any cache-sensitive high model): the agent "reasons" (long chains, high effort) efficiently because the cache stays warm, context is managed without thrashing, costs stay low, sessions can run "forever". For non-deepseek, the discipline still helps stability and output quality.

See also: Reasonix SPEC.md (cache-first loop, two-model Coordinator with separate sessions, low-freq compaction), benchmarks/real-world-cache, prompts for high models, services/compact/*, promptCacheBreakDetection.ts, modelRolesCore.ts, codingRoutePolicyCore.ts (native chat for deepseek).

This makes "our models" (esp. deepseek reasoner/v4) use reasoning reasonably: better results (stable context + handoffs), lower cost (max cache hits), higher efficiency (long stable runs, cheap models for parts, fast compaction via flash + emergency fallback).

**2026 continuation (planner wiring + cache % visibility per "按照计划 直接进行开发和升级" + "继续")**:
- 'planner' role now active: built-in Plan agent model changed from 'inherit' → 'planner' (wires separate cache session by default for high-level design tasks). Added full support in getAgentModel (special-case 'planner' → getModelForRole before parse/alias), AGENT_MODEL_OPTIONS + getAgentModelOptions + getAgentModelDisplay. In AgentTool: capture isPlannerRoleRequest + requestsPlannerIsolation (from param or selectedAgent.model), skip forkContextMessages on isForkPath for these (runAgent then starts query with only its promptMessages → fresh prefix using planModel, no mainLoop transcript mixed). Coordinator prompt already calls out separate sessions.
- Cache observability landed: CompactionResult extended with compactionCacheReadTokens / compactionCacheHitRate (computed from the summary's totalUsage which usageMapping already maps deepseek prompt_cache_hit_tokens / openai cached_tokens / anthropic cache_read to unified cache_read_input_tokens). Populated in both full and partial compact returns. Surfaced to user in query.ts post-compact yield: `Context compacted → 14.0k tokens (from ~269k, 12.3s) · 缓存命中 87%` (and fallback cache read count). Also appended to providerControlText roles line so /doctor /provider shows the hint. Matches GrokCLI "Context XX% full" + post-compact visibility + Reasonix "cache hit % core metric".
- Docs: model-roles.md added deepseek/grok rec section (set planModel/summarize to deepseek-chat etc + watch 缓存命中 after compact); snapshot example now lists planner. CLAUDE-NOTES append (this).
- Tests/docs/build: fixed model-roles.test (now "eight roles", 20/20); compact+agent tests 245 pass 0 fail; rebuild:dist succeeded (14.2MB, smoke --version 8.22.4 + --help ok); no new TS errors in edited files (pre-existing baseUrl/node only).
- Discipline: purely surgical (traced to planner active use + cache % display), minimal (no new stores/hooks/UI components, reused existing usage + fork guard + one yield suffix), goal verifiable (tests + smoke), durable note here not in generated. No speculative (e.g. no auto cheap downgrade in resolve, no full TokenWarning cache line — the compact message + doctor is the direct visible per request).

This completes the direct upgrade wave for grok+deepseek perfect native + Reasonix cache discipline + observable planner/compact UX. Sessions now cheaper, more stable, planning isolated, progress clear.
