Skip to content

directive candidate: system-shape-normalize extension — repair v2.1.154 system-role-at-messages[0] on /compact, /clear, model-switch (#63147 Cluster 16) #172

Description

@vsits-team-lead-agent

Background

In yurukusa's #63147 13A–G taxonomy, sub-pattern 13F is a v2.1.154-specific regression: after /compact, /clear, or a model switch, CC rebuilds the messages array and places the system role at messages[0] instead of using the top-level system field. The API rejects this with a 400 independent of the empty-text thinking-block check — and many sessions hit 13F on a casual /compact (which happens automatically under context pressure), so the wedge presents as "session just died with no clear trigger."

13F is the only sub-pattern in yurukusa's matrix with no available workaround other than version-pinning to ≤ 2.1.153. Our v3.8.0 extensions don't touch the system field or messages[0] shape (confirmed: grep "system\\|messages\\[0\\]" proxy/extensions/thinking-block-sanitize.mjs returns nothing). This is real product white space we can occupy.

Direction

New extension proxy/extensions/system-shape-normalize.mjs that:

  1. Detects the bad shape on the request path: Array.isArray(body.messages) && body.messages[0]?.role === "system".
  2. Repairs by lifting that message's text content into the top-level body.system (concatenating with any existing top-level system if both are present — order: lifted-first vs existing-first is a directive call), then removing the messages[0] entry and re-indexing.
  3. Counts only — emits system_role_lifted: true|false and a one-time stderr notice per session when it fires, so operators know they're running into 13F.

Design considerations

  • Order in the request-mutator chain. Must run before any other extension that scans messages and assumes the schema is valid (likely order 540, before thinking-block-sanitize at 550).
  • String vs array content. If messages[0] content is a list of blocks (not a string), the lift needs to extract text blocks only; non-text blocks in a "system" message would be an unrecoverable shape we should refuse to mutate (log + pass through).
  • Cache safety. The lift changes the request body shape; whether it changes the cacheable prefix in a way that defeats the prompt cache is a directive question. May want to make it opt-in initially (CACHE_FIX_SYSTEM_NORMALIZE=on) and validate the cache implications before flipping on by default.
  • Telemetry. Per-session: system_role_lifted_total counter. Useful for measuring how often 13F actually fires in production once enabled.

Why P1

  • Closes the matrix gap: this turns yurukusa's "only known full fix is stay on 2.1.152 + Opus 4.7 + DISABLE_INTERLEAVED_THINKING=1" into "or run cache-fix v3.9 with CACHE_FIX_SYSTEM_NORMALIZE=on."
  • It's a request-body mutator on a different surface from thinking-sanitize — clean separation of concerns.
  • Anthropic will eventually fix 13F upstream (it's a clear regression), but in the interim every operator hitting it has no path forward today.

Caveats / risks

  • Load-bearing request-body mutator. Same review-gate discipline as directive: prior-turn thinking-block sanitize (mitigate #63147) #162: Chris's human review remains the merge gate.
  • The 13F shape may have variants we haven't seen yet. Opt-in default until we have repro coverage from ≥ 2 distinct community reports.
  • If the system lift changes cache-prefix behavior negatively, this could increase cache misses for some workloads. Need to verify against the cache-fix integration test harness.

Acceptance

  • Extension lifts messages[0] (role=system) into top-level body.system when present.
  • Active tool_use/tool_result pairing unaffected.
  • New per-session telemetry fields: system_role_lifted_total (counter) + one-time stderr notice on first lift.
  • Tests cover: (a) the canonical 13F shape from the #63396 repro, (b) the no-op case (already-correct request), (c) mixed-content messages[0] refuse-to-mutate, (d) idempotency on repeated requests.
  • Opt-in via env var; default off until cache-impact verified.

References

— AI Team Lead

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High — near-term targetcommunity-reportedOriginally reported by a community memberdirective-stagePR is in directive/spec review stage; remove when implementation beginsenhancementNew feature or requestupstreamDepends on or relates to anthropics/claude-code behavior

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions