Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(cache): reorder prompt-cache breakpoints so the stable block leads

merged#2789CedarCopilot

CedarCopilot wants to merge 1 commit into staging from fix/cache-breakpoint-ordering

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Sep 15, 2026, 7:10 AM
  2. Sep 15, 2026, 7:18 AM
  3. Merged
    Sep 15, 2026, 7:29 AM
  4. Live on staging
    Sep 15, 2026, 7:29 AM
  5. Live on prod
    Sep 15, 2026, 7:29 AM
  6. Observed 2 days
    Sep 15, 2026, 7:29 AM
  7. Pipelines steady after this deploy
    Sep 15, 2026, 7:29 AM
  8. Pipelines steady after this deploy
    Sep 15, 2026, 7:29 AM
  9. Unobserved

    Live on prod, no production signal yet

    Sep 17, 2026, 2:01 PM

Behaviors Libra is checking

Conversation field updates place the stable preamble before volatile event content so repeated runs for the same conversation can reuse the Anthropic prompt cache.Inconclusivelow confidence

Unable to complete the production verification because the available telemetry query returned only wrapper logs and did not expose tied generateInput success/error counts or Anthropic cache_read_input_tokens usage.

prod, checked Sep 17, 2026, 12:29 PM
Conversation field updates merge the unique event content and task prompt into an uncached trailing block instead of paying cache-write overhead for content that cannot be reused.Inconclusivelow confidence

Verification reached its budget before a verdict.

prod, checked Sep 17, 2026, 12:30 PM
Strategist updates place the stable hydrated context block before volatile event content so repeated runs for the same conversation can reuse the Anthropic prompt cache.Inconclusivelow confidence

Verification showed no counted post-deploy activity, so Libra has no positive runtime evidence for how much traffic exercised this change. Original assessment: Production telemetry since 2026-09-15T14:29:14Z shows repeated strategist dispatches: at least 1,000 `run-strategist`.

prod, checked Sep 17, 2026, 1:30 PM
Strategist updates merge unique event content and task-specific text into an uncached trailing block instead of attempting to cache the per-call event.Inconclusivelow confidence

Strict CloudWatch fallback saw 217,737 success-shaped log lines matching generateinput, updatenextstepsandtaskstool, conversation-field-updates, strategist-updates, conversation, but no tied operation was present, so Libra is not calling this working.

prod, checked Sep 17, 2026, 1:32 PM
Next-steps and task updates merge unique event content with the task prompt in an uncached trailing block, avoiding cache-write markup for the per-call content.Inconclusivelow confidence

Verification reached its 130s outer budget before the verifier returned. Libra is keeping this intent verifying until the next check.

prod, checked Sep 17, 2026, 2:01 PM
Conversation field updates place the stable preamble first as the ephemeral prompt-cache breakpoint and send the per-event content and task instructions in one uncached trailing block.Inconclusivelow confidence

Unable to complete telemetry verification because the initial OTEL query timed out and no bounded follow-up telemetry query was executed.

staging, checked Sep 18, 2026, 2:52 AM

Failures attributed to this change

No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 4 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 15, 2026, 9:23 AM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 15, 2026, 9:29 AM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 4 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 15, 2026, 9:29 AM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 2 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 15, 2026, 9:29 AM
No prod customers are affected while this is only in staging. If promoted, requests touching this data path may fail or return stale/incomplete data. 0 hits · 1 user · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 15, 2026, 9:29 AM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 3 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 15, 2026, 10:08 AM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 15, 2026, 12:19 PM

Libra has verdicts on 0 of 6 tracked behaviors on prod; 6 are still being checked. Libra checks hourly for 3 days after each deploy.

Summary

conversation-field-updates.ts (crm-updater), updateNextStepsAndTasksTool.ts (next-steps), and strategist-updates.ts (strategist) each build a manual "3-layer cached message" with two cache_control breakpoints: the triggering event's raw content first, then the (mostly stable, per-user) preamble/hydrated-context block, then an uncached task prompt.

Anthropic's prompt cache is prefix-based: a cache_control breakpoint only hits if the entire request content before it matches a previously cached prefix, byte for byte. Because the event-content block is a fresh email/meeting/Slack message on every call, it's never identical across runs , which meant the preamble/context breakpoint that came after it could never accumulate a cross-run cache hit either, since its cached prefix always included that call's unique event text.

Confirmed against real production billing/trace data via the understudy CLI: these three workloads showed cache-write dollars exceeding cache-read dollars by 1.9x,5.3x over a 6-day window (write should be small relative to read for healthy reuse), while a sibling workload with a stable-content-first order did much better.

The fix: reorder each generateInput so the stable block (preamble / hydratedContextBlock) comes first and keeps its cache_control marker. The event-content block's cache_control is removed , it's write-once-read-never regardless of position, so marking it cacheable only paid Anthropic's 1.25x cache-write markup for zero benefit , and it's merged into the uncached tail together with the existing task prompt. Same text, same order, no behavior change to what the model sees; only cache eligibility changes.

Where cache_control was removed from

fileblock it was removed from
conversation-field-updates.tsthe <new_content>${rawEventContent}</new_content> block
updateNextStepsAndTasksTool.tsthe <event_content>${rawEventContent}</event_content> block
strategist-updates.tsthe <new_content>${rawEventContent}</new_content> block

In each case it's now merged uncached into the trailing block with the task prompt, and the block that keeps cache_control (preamble / hydratedContextBlock) moved to the front.

Estimated savings

Derived from real Understudy billing data, not assumed:

  • Confirmed actual cache pricing from Cedar's own billed $ and token counts: cache-write ≈ $3.7,3.76/Mtok, cache-read ≈ $0.295,0.301/Mtok , a consistent ~12.5x ratio (matches the standard 1.25x-write / 0.10x-read multipliers off a ~$3/Mtok base input rate).
  • Post-migration 6-day cache-write totals: conversation-field-updater $569.08, next-steps-tasks $788.06, strategist-updater $670.54 → combined $2,027.68 / 6 days = $337.95/day.
  • Measured volatile/stable byte split per workload (from a real trace): crm-updater & next-steps ≈ 74% volatile (event content) / 26% stable (preamble); strategist ≈ 56% volatile / 44% stable (it bundles more into its hydrated-context block).
  • Effect A , dropping cache_control on the volatile block, ~20% off its write cost (1.25x → 1.0x base rate), close to guaranteed with no dependency on traffic patterns: ~$14/day (crm-updater) + $19/day (next-steps) + $13/day (strategist) ≈ $46/day combined (~$1,380/month, ~$16,800/year).
  • Effect B , reordering lets the stable block accumulate cross-run cache hits instead of rewriting every time; modeled at 25/50/75% cross-run hit-rate scenarios: ~$25,75/day, ~$50/day midpoint.
  • Combined estimate: $70,120/day across these three workloads ($2,100,3,600/month, ~$26k,44k/year) , roughly 13,22% of what these three workloads currently spend, ~6,10% of org-wide daily spend. Not enough alone to hit a 50% overall reduction target; one contributing lever among several.

Effect A is low-risk and close to guaranteed. Effect B is a genuine estimate , confirm actual $ savings via a staged rollout comparing cache-read % before and after, rather than ass

Show production surfaces and changed-file mapping

Production surfaces

Libra has not measured any production surfaces for this change yet.

Changed files → surfaces

  • apps/server/src/mastra/tools/task/updateNextStepsAndTasksTool.tsno production surface mapped
  • apps/server/src/mastra/utils/conversation-field-updates.tsno production surface mapped
  • apps/server/src/mastra/utils/strategist-updates.tsno production surface mapped