Commit Graph

3 Commits

Author SHA1 Message Date
kshitij 3f832978d3 refactor(cache): share the boundary-declaration helper and simplify the registry
Follow-ups from review of #82049:
- extract append_user_instruction() into agent/skill_commands so the
  stable-prefix construction cannot drift between the skill and cron
  builders (the registered prefix must stay a byte-prefix of the built
  message); cron no longer imports the private _SINGLE_SKILL_INSTRUCTION
- add the startswith guard to the skill builder registration site,
  matching the stronger cron guard
- rename _MAX_BYTES to _MAX_CHARS (sum(map(len, ...)) counts characters,
  not bytes) and correct the comment
- collapse find_stable_prefix's two-lock dance into a single critical
  section (scan is <=32 short-circuiting startswith calls, measured
  2-4us; drops the snapshot copy and the TOCTOU re-check)
- document the split-shape lifetime (marked-endpoint window) in the
  module docstring
- add a contract test for the helper's byte-prefix invariant
  (mutation-checked)
2026-08-09 15:25:12 +05:30
joaomarcos 4c5be0c295 perf(cache): harden the stable-prefix boundary against eviction and memory growth
Follow-up review of the builder-declared cache boundary (#81867) found three
ways the split could silently stop paying off, or keep paying more than it
should, on a long-lived gateway process.

Flattening no longer consults the registry. `strip_anthropic_cache_control`
matched the decorated split by looking the first block up in the prefix
registry, so a mid-turn failover that re-decorates a request built many
messages earlier (#72626) would fail to flatten once _MAX_ENTRIES newer
scaffolds had been registered in between, and would hand the next provider
the two-part shape instead of the canonical string. The split is now matched
by its shape: a marker on the *first* part of a user message is something no
other decoration produces (list content otherwise gets its marker on the last
part, and the two-part [static, volatile] split is role-gated to system), so
the ""-join stays provably byte-exact without any process state. This drops
`is_registered_stable_prefix` and one lock acquisition per stripped message.

Lookups now refresh LRU position. A scaffold fired every minute by cron could
be evicted by a burst of one-off skill invocations while still being the
hottest prefix in the process, silently reverting it to whole-message caching.

Registration now also evicts by total retained bytes (4 MiB). Entries hold
whole expanded skill bodies, so a 32-entry cap alone does not bound memory.
The newest entry is always kept, so a single oversized scaffold still gets a
boundary instead of disabling the split.

Tests: eviction-then-failover round-trip, LRU refresh on hit, byte-cap
eviction, and oversized-single-entry survival.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 15:25:12 +05:30
joaomarcos 214f2b82db perf(cache): split skill turns at a builder-declared stable/volatile boundary (#81867)
Webhook/cron skill invocations concatenate a large static scaffold
(activation note + expanded skill body) with a small volatile tail
(ticket payload, timestamps) into one user string, and the Anthropic
cache planner marked that whole string as a single atomic block — so a
few changed tail bytes forced a full cache rewrite on every invocation.

Instead of re-parsing scaffold marker strings out of the message at
request time (fragile when a payload or skill body quotes the marker),
the builders now register the exact stable-prefix bytes in a small
process-local LRU registry at construction time. The cache planner
splits a registered user string into [marked stable prefix, unmarked
volatile tail] request-locally; canonical session history stays a plain
string, and the failover stripper flattens the split back byte-exactly
via an O(1) registry lookup. Unregistered messages keep the existing
whole-message policy.

Covers the single-skill builder (webhook + slash command + TUI) and the
cron job prompt assembler (multi-skill, bundles, skipped-skill notice),
with registration guarded against injection-scanner sanitization.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 15:25:12 +05:30