1jehuang / 1jehuang/jcode

[jcode] prompt-overlay.md / AGENTS.md re-read on every turn invalidate the provider prompt cache (silent, ~97K tokens/turn)

Open
#900 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug duplicate
Dominant language
Rust
Stars
19.9k
Forks
2.3k
Avg merge
2d 7h
Merged PRs (30d)
30

Description

Summary

~/.jcode/prompt-overlay.md (and .jcode/prompt-overlay.md, AGENTS.md, preferred-tools.md) are re-read from disk on every turn and appended to the static/cacheable part of the split system prompt. If any of these files changes mid-session, the cacheable prefix changes, so the provider's prompt cache misses for the entire system prompt and the whole prefix is re-processed at full cost — every turn until the file stabilizes — with zero diagnostics in logs or TUI.

Real incident (2026-08-07, jcode v0.68.0): a small watcher regenerated ~/.jcode/prompt-overlay.md every 3 seconds to inject live session state. Each regeneration shifted the static prefix → ~97K tokens of system prompt re-sent per turn, cache_read dropped to ~0, and nothing anywhere explained why. The file is exactly the one users are encouraged to keep dynamic (the whole point is injecting current state), so this footgun sits in the happy path.

Why this matters

  • Cost: a 100 KB+ system prompt resent per turn can dominate the bill for long sessions, especially with background/autonomous loops.
  • Latency: full prefix reprocessing on every turn instead of cache reads.
  • Silent: record_client_cache_request (agent.rs:718) hashes only the provider messages prefix — the system-prompt static part is not covered, so this whole class of cache miss is invisible to jcode's own diagnostics.
  • Self-inflicted: nothing warns the user that editing an overlay mid-session invalidates the cache; the "static" part is only static if the files never change.

Root cause

  • crates/jcode-base/src/prompt.rsload_prompt_overlay_files_from_dir (lines 862–904) reads the files fresh on every call; build_system_prompt_split_with_capabilities (line 506) pushes the result into static_parts (line 509). Same pattern for AGENTS.md (496–499) and preferred-tools.md (513–518).
  • crates/jcode-app-core/src/agent/turn_loops.rs:74 and crates/jcode-app-core/src/agent/turn_streaming_mpsc.rs:157build_system_prompt_split(None) runs per turn, so the "static" part is only static while the files don't change.
  • crates/jcode-app-core/src/agent.rs:718 record_client_cache_request — tracks message-prefix hashes only; system prompt prefix changes are not detected (memory is explicitly excluded there for good reason, but the overlay lives in the static part, not the message history).

Proposal

  1. Pin the static prefix per session. Read overlay/AGENTS.md/preferred-tools.md once at session start and keep the computed static part for the session's lifetime. Refresh only on explicit reload (new session, or an opt-in reload-style trigger). Deterministic behavior, cache protected by construction.
  2. Observability. Hash the static part per turn; if it changes mid-session, log SYSTEM_PROMPT_PREFIX_CHANGED with the byte delta and file names (surface it in the status widget / metrics too). Reuse the existing cache-tracker machinery so silent cache misses become a diagnosable event.
  3. Guardrail / docs. Add a note to the prompt-overlay docs: fast-changing state belongs in files the model reads via tools, not in the overlay. Optionally support a lazy-include marker (e.g. @load <path> expanded by the model at runtime rather than inlined) for cases where dynamic content genuinely belongs in context.

Acceptance criteria

  • Editing ~/.jcode/prompt-overlay.md mid-session does not change the static part sent to the provider until the session restarts / explicit reload.
  • A mid-session change produces at least one logged warning with the byte delta (no silent behavior).
  • Unit test: build_system_prompt_split returns a byte-identical static part across calls while files are unchanged, and the pinned-prefix path ignores mid-session file edits.
  • Optional: status widget / jcode doctor shows system-prompt prefix stability (chars, change count, estimated tokens).

Environment

  • jcode v0.68.0 (fcf53909f), Linux x86_64, provider with prompt caching (observed via cache_read usage metrics).

Workaround (current)

Keep the overlay static; have the model read dynamic state via tools (e.g. a short instruction "read state.json before answering"). That is what we switched to after the incident and it eliminated the misses entirely.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in crates/jcode-base/src/prompt.rs with load_prompt_overlay_files_from_dir and build_system_prompt_split_with_capabilities, then trace the per-turn calls in crates/jcode-app-core/src/agent/turn_loops.rs and turn_streaming_mpsc.rs. Review record_client_cache_request in crates/jcode-app-core/src/agent.rs and add focused tests for byte-identical static prompts, pinned prefixes after file edits, and the required warning.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
ai, cli, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.