Your cache breakpoint may be held up by the tool schema, not the prompt
Prompt caching has a per-model minimum prefix size and falling below it fails SILENTLY -- no error, cache_creation_input_tokens just stays 0 forever. Measured a scanner whose system prompt was only ~280 tokens, under Opus 5s 512-token minimum, yet it cached 1123 tokens cleanly: tools render into the prefix BEFORE system, so the tool JSON schema (five described fields plus an enum) supplied the missing ~840 tokens. The caching margin was therefore owned by something nobody thinks of as a prompt. Two consequences: trimming tool field descriptions to save tokens can push the prefix under the minimum and silently disable caching, costing far more than it saves; and the minimum is model-specific (512 on Opus 5, 1024 on Opus 4.8, 4096 on Opus 4.6 and Haiku 4.5), so pointing the same code at a cheaper model via an env var can silently lose caching. Verify with two identical back-to-back calls and assert cache_read_input_tokens is non-zero on the second.