Story-2083 Walkthrough

Change: This is S-8 of the agent prompt-caching build (Epic-2050, RFD docs/notes/rfd-agent-caching-strategy.md §7.3 / §4). S-2 landed the wiring primitive (model_settings(seat), cache_point(seat), session_id plumbing) but left the panel call sites on the plain ZDR_SETTINGS. S-8 flips Mira's command panel onto that wiring: the four panel seats (the proposer roster, the critic, the synthesizer) now carry the within_run cache policy on every call, keyed by session_id = run_id. Ambient agents (Vega/Blake) are untouched — they never pay cache writes (that is S-9).

What within-run buys (honesty over hope, §7.3)

Caches are per-provider-per-model, and a panel run is one call per model — so cross-seat reads on today's diverse roster are ~zero. The two real within-run wins are: a same-model pair (e.g. minimax as both a proposer and the synthesizer) reading the shared context prefix warm, and a retried seat re-sending its identical prompt. This story ships the wiring for both; it does not promise a hit-rate (the S-4 live probe measures realized reads). What pinning always buys the panel is correctness-shaped: ONE nonce and ONE session_id per run, so every seat pins one frame — provenance never forks between draft and final.

What ships

FileRole
agents/mira/panel_prompts.pyThe three seat builders (build_proposer_prompt/build_critic_prompt/build_synthesizer_prompt) now return a list[str | CachePoint] sandwich: the shared context prefix, a cache_point(seat) breakpoint, then the per-seat suffix (drafts / critique / instruction). prompt_text() is the plain-string view for structural tests.
agents/mira/panel.pyEach seat call passes model_settings(seat, session_id=run_id) (ZDR + the seat's cache breakpoint + the sticky x-session-id header); the @cached role-agents bake model_settings(role) instead of ZDR_SETTINGS. run_command_panel(data, *, session_id=None) threads the run's sticky key to every seat and its retries.
agents/mira/command.py, agents/mira/runtime.pyrun_command(data, *, run_id=None) passes the agent_run_id down as the panel session_id. The broker seam already carries run_id; it just reaches the panel now.
agents/mira/tests/test_panel_cache.py (new)The acceptance suite: a CachePoint on every seat prompt (absent when the seat is off); session_id = run_id + ZDR on every call; a retried seat re-sends the same cacheable prompt under the same session; a different run gets a different session (cold across runs); the shared context prefix is byte-identical across seats.
agents/mira/tests/conftest.py, agents/mira/evals/adversarial.py_user_text now flattens a [str, CachePoint, str] UserPromptPart (pydantic-ai stores a sequence prompt as one part with list content), so the panel stub + the injection-wall check still read all walled text through the sandwich shape.

The sandwich breakpoint (§5.1 placement)

The breakpoint sits at the stable/volatile boundary: the shared context (matter meta, command, current frame, locks, notes, docs) is byte-identical across every seat and across a seat's retries; the per-seat suffix is not. cache_point(seat) returns None when the seat is cold (policy off), so the prompt shape is unchanged when caching is off — caching is economics, not correctness.

def _sandwich(seat, context_parts, suffix_parts):
    seq = ["\n\n".join(context_parts)]
    bp = cache_point(seat)          # None when the seat is cold -> no marker, no drift
    if bp is not None:
        seq.append(bp)             # closes the shared, cacheable prefix
    seq.append("\n\n".join(suffix_parts))
    return seq

session_id = run_id: warm within, cold across

One session_id per run (the agent_run_id) rides the sticky x-session-id header on every seat call and its retries, so a re-send lands on the same provider endpoint and reads the cached prefix warm. It is never carried across runs — each run has a fresh agent_run_id — so cross-run seats stay cold. That is the within_run policy, not designed cross-run caching (§4). ZDR rides the same settings object, so cache can never be split from ZDR.

Acceptance criteria → tests

ACProof
provenance still one version_hash across seatstest_shared_context_prefix_is_identical_across_seats — one nonce, one context prefix for proposer/critic/synth, so every seat pins one frame.
measured within-run reads reported (even if ~0 — honesty over hope)The wiring is asserted, not a hit-rate: test_session_id_is_run_id_on_every_seat (marker + sticky key + ZDR on every seat) and test_retry_re_sends_the_same_cacheable_prompt_within_the_run (the retry re-uses the identical CachePoint prompt under the same session). Realized reads are the S-4 live probe's job; the S-5 model.usage cache fields (merged, pramaan-agents#201) carry them on the wire when they land.

Verification

uv run --package mira pytest agents/mira/tests -q          # 139 passed
uv run --package mira python agents/mira/evals/run.py adversarial   # 13 held / 0 failures
uv run pytest tests -q                                    # 175 passed (governance)
uv run ruff check agents/mira tests/ scripts/             # All checks passed!

Enablement only, no new egress: no CodeZip rebuild or AgentCore redeploy needed for the wiring; the panel emits cache markers on the wire now, but reads are earned only where a provider endpoint serves a cache (S-4). Ambient agents (Vega/Blake) are untouched by this story — their no-paid-writes audit is S-9. llm.yaml already carries the panel seats' within_run policy from S-1; this story consumes it.