diff --git a/docs/claude-instructions-cache-stabilize/PAPER_OCXFIX.pdf b/docs/claude-instructions-cache-stabilize/PAPER_OCXFIX.pdf
new file mode 100644
index 0000000000..5206d30d1f
Binary files /dev/null and b/docs/claude-instructions-cache-stabilize/PAPER_OCXFIX.pdf differ
diff --git a/docs/claude-instructions-cache-stabilize/README.md b/docs/claude-instructions-cache-stabilize/README.md
new file mode 100644
index 0000000000..2725f7d45d
--- /dev/null
+++ b/docs/claude-instructions-cache-stabilize/README.md
@@ -0,0 +1,36 @@
+# Claude instructions cache stabilize (OCXFIX)
+
+OpenCodex inbound conversion puts Claude Code system text into OpenAI Responses
+`instructions`. Claude Code then appends a growing `…`
+footer (and occasional TaskCreate nudges) on every turn. That prefix churn
+causes prompt-cache misses on Muse/Go.
+
+This change strips those dynamic footers from `instructions` and reattaches the
+latest notice as a trailing `input` message so the cacheable prefix stays stable.
+
+Relocation is opt-in: `translateAnthropicRequest` / `anthropicToResponsesTranslation`
+take `stabilizePromptCache?: boolean` (default **false**). Ordinary Anthropic
+callers keep a matching suffix in `instructions`. The Claude Code `/v1/messages`
+inbound path passes `true`. The matcher is `N tokens left`
+plus the exact TaskCreate paragraph; it is not gated on `metadata.user_id`.
+Outside opt-in, the Desktop `prompt_cache_key` fallback hashes raw `systemParts`.
+When opted in, that fallback hashes the same string as `body.instructions`.
+
+## Paper
+
+See [PAPER_OCXFIX.pdf](./PAPER_OCXFIX.pdf) (Warexpor).
+
+Measured cache-hit rates on Muse Spark 1.3 via OpenCode Go / OpenCodex:
+
+| Slice | Baseline mean | OCXFIX mean |
+| --- | ---: | ---: |
+| Claude Code (n=75) | 0.168384 | 0.864374 |
+| Claude S/T4 (n=5) | 0.134922 | 0.982700 |
+| Grok Build (n=75) | 0.966526 | 0.941345 |
+
+Cause: Anthropic→Responses conversion stores Claude system text in `instructions`;
+Claude Code appends growing `` (and rare TaskCreate nudges), so
+`instructions_sha` changes every turn. Grok traffic has no `instructions` field
+and is the control.
+
+Code: `src/claude/inbound-cache-stabilize.ts`, wired from `src/claude/inbound.ts`.
diff --git a/scripts/test-layout/layout.json b/scripts/test-layout/layout.json
index 0ff7d4861c..ef04b588e2 100644
--- a/scripts/test-layout/layout.json
+++ b/scripts/test-layout/layout.json
@@ -304,6 +304,7 @@
"claude-desktop-remote-hub.test.ts": "claude-integration",
"claude-dotenv-provenance-transport.test.ts": "claude-integration",
"claude-gateway-cache.test.ts": "claude-integration",
+ "claude-inbound-cache-stabilize.test.ts": "claude-integration",
"claude-inbound-debug.test.ts": "claude-integration",
"claude-inbound.test.ts": "claude-integration",
"claude-management-api.test.ts": "claude-integration",
diff --git a/src/claude/inbound-cache-stabilize.ts b/src/claude/inbound-cache-stabilize.ts
new file mode 100644
index 0000000000..78b859350e
--- /dev/null
+++ b/src/claude/inbound-cache-stabilize.ts
@@ -0,0 +1,128 @@
+/**
+ * Claude Code appends growing `…` footers (and
+ * occasional TaskCreate nudges) into system text that becomes Responses
+ * `instructions`. That churn breaks Muse/Go prefix cache on the instructions
+ * prefix even when tools stay stable. Strip dynamics from instructions;
+ * surface the latest notice on `input` instead.
+ *
+ * Relocation is identified by harness shape: only a trailing, unfenced,
+ * canonical notice at the end of instructions is moved. An unmatched fence
+ * opener covers through EOF. No match → the original string is returned
+ * byte-for-byte. The matcher is content identity only; `translateAnthropicRequest`
+ * must pass `stabilizePromptCache: true` before this helper runs. Claude Code
+ * writes `N tokens left`.
+ */
+
+const TRAILING_TOTAL_RE =
+ /(?:^|(?:\r?\n)+)[ \t]*(\d+\s+tokens left<\/total_tokens>)[ \t]*(?:\r?\n)*$/;
+
+const TRAILING_NUDGE_RE =
+ /(?:^|(?:\r?\n)+)[ \t]*(The task tools haven't been used recently\.\s+If you're working on tasks that would benefit from tracking, consider using TaskCreate to add them\.\s+Only use these if relevant to the current work\.\s+This is just a gentle reminder - ignore if not applicable\.)[ \t]*(?:\r?\n)*$/;
+
+interface FenceRange {
+ start: number;
+ end: number;
+}
+
+const FENCE_OPEN_RE = /^( {0,3})(`{3,}|~{3,})/;
+const FENCE_CLOSE_RE = /^( {0,3})(`{3,}|~{3,})[ \t]*$/;
+
+/**
+ * Markdown fence ranges. An unmatched opener covers through EOF: unfinished
+ * fenced examples are code content, not a harness suffix. A closer must be a
+ * standalone fence line (no info string) of the same character and at least
+ * the opener's length.
+ */
+function fencedRanges(source: string): FenceRange[] {
+ const ranges: FenceRange[] = [];
+ let offset = 0;
+ let openAt: number | null = null;
+ let openFence = "";
+ while (offset <= source.length) {
+ const nl = source.indexOf("\n", offset);
+ const lineEnd = nl === -1 ? source.length : nl;
+ const line = source.slice(offset, lineEnd).replace(/\r$/, "");
+ if (openAt === null) {
+ const open = FENCE_OPEN_RE.exec(line);
+ if (open) {
+ openAt = offset;
+ openFence = open[2]!;
+ }
+ } else {
+ const close = FENCE_CLOSE_RE.exec(line);
+ if (
+ close
+ && close[2]![0] === openFence[0]
+ && close[2]!.length >= openFence.length
+ ) {
+ ranges.push({ start: openAt, end: lineEnd });
+ openAt = null;
+ openFence = "";
+ }
+ }
+ if (nl === -1) break;
+ offset = nl + 1;
+ }
+ if (openAt !== null) ranges.push({ start: openAt, end: source.length });
+ return ranges;
+}
+
+function isInsideFence(ranges: readonly FenceRange[], index: number): boolean {
+ return ranges.some(range => index >= range.start && index < range.end);
+}
+
+function peelOne(
+ rest: string,
+ ranges: readonly FenceRange[],
+): { rest: string; total?: string; nudge?: string } | null {
+ const total = rest.match(TRAILING_TOTAL_RE);
+ if (total?.[1]) {
+ const matchStart = rest.length - total[0].length;
+ const tagAt = matchStart + total[0].indexOf(total[1]);
+ if (!isInsideFence(ranges, tagAt)) {
+ return { rest: rest.slice(0, rest.length - total[0].length), total: total[1] };
+ }
+ }
+ const nudge = rest.match(TRAILING_NUDGE_RE);
+ if (nudge?.[1]) {
+ const matchStart = rest.length - nudge[0].length;
+ const tagAt = matchStart + nudge[0].indexOf(nudge[1]);
+ if (!isInsideFence(ranges, tagAt)) {
+ return { rest: rest.slice(0, rest.length - nudge[0].length), nudge: nudge[1] };
+ }
+ }
+ return null;
+}
+
+export function stabilizeClaudeInstructionsForPromptCache(
+ instructions: string,
+): { instructions: string; dynamicNotice: string | null } {
+ if (!instructions) {
+ return { instructions: "", dynamicNotice: null };
+ }
+
+ const ranges = fencedRanges(instructions);
+ let rest = instructions;
+ let latestTotal: string | null = null;
+ let latestNudge: string | null = null;
+ let peeled = false;
+ for (;;) {
+ const next = peelOne(rest, ranges);
+ if (!next) break;
+ peeled = true;
+ rest = next.rest;
+ if (next.total && latestTotal === null) latestTotal = next.total;
+ if (next.nudge && latestNudge === null) latestNudge = next.nudge;
+ }
+
+ if (!peeled) {
+ return { instructions, dynamicNotice: null };
+ }
+
+ const noticeParts: string[] = [];
+ if (latestTotal) noticeParts.push(latestTotal);
+ if (latestNudge) noticeParts.push(latestNudge);
+ const dynamicNotice = noticeParts.length > 0 ? noticeParts.join("\n\n") : null;
+
+ return { instructions: rest, dynamicNotice };
+}
diff --git a/src/claude/inbound.ts b/src/claude/inbound.ts
index c2e3ded9b2..eddac06ac8 100644
--- a/src/claude/inbound.ts
+++ b/src/claude/inbound.ts
@@ -17,6 +17,7 @@ export { resolveInboundModel, effortForThinkingBudget, effortFromOutputConfig, e
import { AnthropicRequestError, isRec, type Rec } from "./inbound-records";
import { resolveInboundModel, effortForThinkingBudget, effortFromOutputConfig, formatFromOutputConfig } from "./inbound-model-options";
import { systemToInstructions, toolsToResponses, toolChoiceToResponses } from "./inbound-content-options";
+import { stabilizeClaudeInstructionsForPromptCache } from "./inbound-cache-stabilize";
import { decodeReasoningEnvelope, encodeReasoningEnvelope, OCX_REASONING_PREFIX } from "../responses/reasoning-envelope";
import { createTranslatorBudget, type TranslatorBudget } from "../lib/translator-budget";
@@ -287,12 +288,25 @@ export interface ClaudeInboundTranslation {
cacheKeySource: ClaudeCacheKeySource;
}
+/**
+ * Translator-level opt-in for Claude Code harness footer relocation.
+ * Default is off: a matching textual suffix is not permission to change its role.
+ * The `/v1/messages` inbound path passes true. Do not infer this from metadata.user_id.
+ */
+export interface ClaudeInboundTranslateOptions {
+ stabilizePromptCache?: boolean;
+}
+
/**
* Translate an Anthropic Messages request body into a /v1/responses request body.
* Throws AnthropicRequestError (-> 400 invalid_request_error) on malformed input.
*/
-export function anthropicToResponsesBody(raw: unknown, cc?: OcxClaudeCodeConfig): Rec {
- return anthropicToResponsesTranslation(raw, cc).body;
+export function anthropicToResponsesBody(
+ raw: unknown,
+ cc?: OcxClaudeCodeConfig,
+ options?: ClaudeInboundTranslateOptions,
+): Rec {
+ return anthropicToResponsesTranslation(raw, cc, undefined, options).body;
}
/**
@@ -300,16 +314,26 @@ export function anthropicToResponsesBody(raw: unknown, cc?: OcxClaudeCodeConfig)
* OUT-OF-BODY tuple (audit 133 R3#1 — an in-body marker would leak upstream through
* the native Responses forward and 400).
*/
-export function anthropicToResponsesTranslation(raw: unknown, cc?: OcxClaudeCodeConfig, budget?: TranslatorBudget): ClaudeInboundTranslation {
+export function anthropicToResponsesTranslation(
+ raw: unknown,
+ cc?: OcxClaudeCodeConfig,
+ budget?: TranslatorBudget,
+ options?: ClaudeInboundTranslateOptions,
+): ClaudeInboundTranslation {
const activeBudget = budget ?? createTranslatorBudget();
try {
- return translateAnthropicRequest(raw, cc, activeBudget);
+ return translateAnthropicRequest(raw, cc, activeBudget, options);
} finally {
if (!budget) activeBudget.dispose();
}
}
-function translateAnthropicRequest(raw: unknown, cc: OcxClaudeCodeConfig | undefined, budget: TranslatorBudget): ClaudeInboundTranslation {
+function translateAnthropicRequest(
+ raw: unknown,
+ cc: OcxClaudeCodeConfig | undefined,
+ budget: TranslatorBudget,
+ options?: ClaudeInboundTranslateOptions,
+): ClaudeInboundTranslation {
if (!isRec(raw)) throw new AnthropicRequestError("request body must be a JSON object");
if (typeof raw.model !== "string" || raw.model.length === 0) {
throw new AnthropicRequestError("model is required");
@@ -345,7 +369,32 @@ function translateAnthropicRequest(raw: unknown, cc: OcxClaudeCodeConfig | undef
stream: raw.stream === true,
};
- if (systemParts.length > 0) body.instructions = systemParts.join("\n\n");
+ const joinedSystem = systemParts.length > 0 ? systemParts.join("\n\n") : "";
+ const stabilizePromptCache = options?.stabilizePromptCache === true;
+ // Desktop fallback hashes raw systemParts unless the caller opted into
+ // harness cleanup. Opt-in then hashes the same string as body.instructions.
+ let cacheSystem: string | string[] = systemParts;
+ if (joinedSystem) {
+ if (stabilizePromptCache) {
+ // Claude Code appends growing N tokens left
+ // footers (and occasional TaskCreate nudges) into system text. That churn
+ // breaks Muse/Go prefix cache on the Responses instructions prefix even
+ // when tools stay stable. Relocation is caller-opted, not inferred from
+ // a matching suffix or metadata.user_id.
+ const stabilized = stabilizeClaudeInstructionsForPromptCache(joinedSystem);
+ if (stabilized.instructions) body.instructions = stabilized.instructions;
+ if (stabilized.dynamicNotice) {
+ input.push({
+ type: "message",
+ role: "user",
+ content: [{ type: "input_text", text: stabilized.dynamicNotice }],
+ });
+ }
+ cacheSystem = stabilized.instructions;
+ } else {
+ body.instructions = joinedSystem;
+ }
+ }
const tools = toolsToResponses(raw.tools);
if (tools) body.tools = tools;
@@ -383,11 +432,14 @@ function translateAnthropicRequest(raw: unknown, cc: OcxClaudeCodeConfig | undef
// Exact-prefix matching still isolates content; the key only steers routing
// affinity. Callers must NOT synthesize a session_id header from this fallback
// (audit 133 R2#3).
+ // Outside opt-in, hash the raw systemParts array (pre-stabilize Desktop
+ // key). Opt-in hashes the same string used for body.instructions so the
+ // key tracks the cacheable prefix after peel.
body.prompt_cache_key = createHash("sha256")
.update(canonicalJson({
version: 2,
model: body.model,
- system: systemParts,
+ system: cacheSystem,
tools: Array.isArray(body.tools) ? body.tools : [],
}))
.digest("hex").slice(0, 32);
diff --git a/src/server/claude-messages.ts b/src/server/claude-messages.ts
index 8c3e37eea8..f672f9f911 100644
--- a/src/server/claude-messages.ts
+++ b/src/server/claude-messages.ts
@@ -755,7 +755,9 @@ async function handleClaudeMessagesWithBudget(
};
delete anthropicBody.thinking;
}
- const translation = anthropicToResponsesTranslation(anthropicBody, config.claudeCode, translatorBudget);
+ const translation = anthropicToResponsesTranslation(anthropicBody, config.claudeCode, translatorBudget, {
+ stabilizePromptCache: true,
+ });
internalBody = translation.body;
// The Anthropic translator builds its body from model/input/store/stream plus sampling
// fields only, so the caller intent is applied to the TRANSLATED body rather than the
diff --git a/tests/claude-integration/claude-inbound-cache-stabilize.test.ts b/tests/claude-integration/claude-inbound-cache-stabilize.test.ts
new file mode 100644
index 0000000000..fe044f358a
--- /dev/null
+++ b/tests/claude-integration/claude-inbound-cache-stabilize.test.ts
@@ -0,0 +1,327 @@
+import { describe, expect, test } from "bun:test";
+import { readFileSync } from "node:fs";
+import { stabilizeClaudeInstructionsForPromptCache } from "../../src/claude/inbound-cache-stabilize";
+import { anthropicToResponsesTranslation } from "../../src/claude/inbound";
+import { repoPath } from "../helpers/repo-root";
+
+const TASKCREATE_NUDGE = [
+ "The task tools haven't been used recently. If you're working on tasks that would benefit from tracking, consider using TaskCreate to add them.",
+ "Only use these if relevant to the current work. This is just a gentle reminder - ignore if not applicable.",
+].join(" ");
+
+function footer(used: number): string {
+ return `${used} tokens left`;
+}
+
+function translate(
+ system: string,
+ options?: { user_id?: string; stabilizePromptCache?: boolean },
+) {
+ return anthropicToResponsesTranslation(
+ {
+ model: "m",
+ max_tokens: 1,
+ system,
+ messages: [{ role: "user", content: "hi" }],
+ ...(options?.user_id ? { metadata: { user_id: options.user_id } } : {}),
+ },
+ undefined,
+ undefined,
+ options?.stabilizePromptCache === undefined
+ ? undefined
+ : { stabilizePromptCache: options.stabilizePromptCache },
+ );
+}
+
+function translateHarness(system: string, metadata?: { user_id: string }) {
+ return anthropicToResponsesTranslation(
+ {
+ model: "m",
+ max_tokens: 1,
+ system,
+ messages: [{ role: "user", content: "hi" }],
+ ...(metadata ? { metadata } : {}),
+ },
+ undefined,
+ undefined,
+ { stabilizePromptCache: true },
+ );
+}
+
+function userTurns(body: { input: unknown }) {
+ return body.input as Array>;
+}
+
+describe("stabilizeClaudeInstructionsForPromptCache", () => {
+ test("empty input is a no-op", () => {
+ expect(stabilizeClaudeInstructionsForPromptCache("")).toEqual({
+ instructions: "",
+ dynamicNotice: null,
+ });
+ });
+
+ test("stable instructions without dynamics pass through", () => {
+ const instructions = "You are Claude Code.\n\nPrefer terse answers.";
+ expect(stabilizeClaudeInstructionsForPromptCache(instructions)).toEqual({
+ instructions,
+ dynamicNotice: null,
+ });
+ });
+
+ test("no-match whitespace is returned byte-for-byte", () => {
+ const instructions = "You are Claude Code.\n\n\nPrefer terse answers.\n";
+ expect(stabilizeClaudeInstructionsForPromptCache(instructions)).toEqual({
+ instructions,
+ dynamicNotice: null,
+ });
+ });
+
+ test("whitespace-only system without a footer is unchanged", () => {
+ const instructions = " \n\n ";
+ expect(stabilizeClaudeInstructionsForPromptCache(instructions)).toEqual({
+ instructions,
+ dynamicNotice: null,
+ });
+ });
+
+ test("three trailing total_tokens footers keep only the latest in the notice", () => {
+ const stable = "You are Claude Code.";
+ const first = footer(1000);
+ const second = footer(4000);
+ const third = footer(8000);
+ const result = stabilizeClaudeInstructionsForPromptCache(
+ [stable, first, second, third].join("\n\n"),
+ );
+ expect(result.instructions).toBe(stable);
+ expect(result.instructions).not.toContain("");
+ expect(result.dynamicNotice).toBe(third);
+ });
+
+ test("real Claude Code 15000000 tokens left trailing footer peels", () => {
+ const stable = "You are Claude Code.";
+ const harness = footer(15_000_000);
+ const result = stabilizeClaudeInstructionsForPromptCache(`${stable}\n\n${harness}`);
+ expect(result.instructions).toBe(stable);
+ expect(result.dynamicNotice).toBe("15000000 tokens left");
+ });
+
+ test("bare numeric total_tokens without tokens left is not a harness footer", () => {
+ const docs = "You are Claude Code.\n\n123";
+ const result = stabilizeClaudeInstructionsForPromptCache(docs);
+ expect(result.instructions).toBe(docs);
+ expect(result.dynamicNotice).toBeNull();
+ });
+
+ test("mid-document total_tokens stays; only the trailing harness footer relocates", () => {
+ const result = stabilizeClaudeInstructionsForPromptCache(
+ `System.\n${footer(1)}\nMore system.\n${footer(3)}`,
+ );
+ expect(result.instructions).toBe(`System.\n${footer(1)}\nMore system.`);
+ expect(result.dynamicNotice).toBe(footer(3));
+ });
+
+ test("TaskCreate nudge is stripped from instructions and kept in the notice", () => {
+ const stable = "You are Claude Code.";
+ const result = stabilizeClaudeInstructionsForPromptCache(
+ `${stable}\n\n${TASKCREATE_NUDGE}`,
+ );
+ expect(result.instructions).toBe(stable);
+ expect(result.instructions).not.toContain("TaskCreate");
+ expect(result.dynamicNotice).toBe(TASKCREATE_NUDGE);
+ });
+
+ test("latest footer and latest nudge both surface in the notice", () => {
+ const stable = "Stay stable.";
+ const older = footer(10);
+ const latest = footer(50);
+ const result = stabilizeClaudeInstructionsForPromptCache(
+ [stable, older, TASKCREATE_NUDGE, latest].join("\n\n"),
+ );
+ expect(result.instructions).toBe(stable);
+ expect(result.dynamicNotice).toBe(`${latest}\n\n${TASKCREATE_NUDGE}`);
+ });
+
+ test("inline documentation of total_tokens tags stays in instructions", () => {
+ const docs = "The harness may emit a 123 footer; do not invent one.";
+ const result = stabilizeClaudeInstructionsForPromptCache(docs);
+ expect(result.instructions).toBe(docs);
+ expect(result.dynamicNotice).toBeNull();
+ });
+
+ test("TaskCreate mentioned in docs is not treated as the harness nudge", () => {
+ const docs = "The task tools haven't been used recently. You may mention TaskCreate in docs without the reminder.";
+ const result = stabilizeClaudeInstructionsForPromptCache(docs);
+ expect(result.instructions).toBe(docs);
+ expect(result.dynamicNotice).toBeNull();
+ });
+
+ test("fenced standalone total_tokens example stays byte-for-byte", () => {
+ const docs = ["You are a docs bot.", "```", footer(123), "```", ""].join("\n");
+ const result = stabilizeClaudeInstructionsForPromptCache(docs);
+ expect(result.instructions).toBe(docs);
+ expect(result.dynamicNotice).toBeNull();
+ });
+
+ test("docs plus a real trailing footer keep the docs and move only the latest footer", () => {
+ const docs = "Describe 0 in the protocol guide.";
+ const latest = footer(8000);
+ const result = stabilizeClaudeInstructionsForPromptCache(
+ [docs, footer(1), latest].join("\n\n"),
+ );
+ expect(result.instructions).toBe(docs);
+ expect(result.dynamicNotice).toBe(latest);
+ });
+
+ test("fenced example plus a trailing harness footer moves only the footer", () => {
+ const docs = ["Docs:", "```", footer(123), "```"].join("\n");
+ const latest = footer(8000);
+ const result = stabilizeClaudeInstructionsForPromptCache(`${docs}\n\n${latest}`);
+ expect(result.instructions).toBe(docs);
+ expect(result.dynamicNotice).toBe(latest);
+ });
+
+ test("unclosed fence through EOF is not a harness suffix", () => {
+ const docs = ["You are a docs bot.", "```", footer(123)].join("\n");
+ const result = stabilizeClaudeInstructionsForPromptCache(docs);
+ expect(result.instructions).toBe(docs);
+ expect(result.dynamicNotice).toBeNull();
+ });
+
+ test("a fence line with an info string does not close an open fence", () => {
+ const docs = ["```", footer(123), "```xml"].join("\n");
+ const result = stabilizeClaudeInstructionsForPromptCache(docs);
+ expect(result.instructions).toBe(docs);
+ expect(result.dynamicNotice).toBeNull();
+ });
+});
+
+describe("anthropicToResponsesTranslation cache-stabilize wire-in", () => {
+ test("ordinary caller with the exact unfenced suffix keeps instructions and input unchanged", () => {
+ const latest = footer(15_000_000);
+ const system = ["You are Claude Code.", latest].join("\n\n");
+ const { body } = translate(system);
+ expect(body.instructions).toBe(system);
+ expect(userTurns(body)).toEqual([
+ { type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] },
+ ]);
+ });
+
+ test("ordinary caller with the exact TaskCreate paragraph keeps instructions and input unchanged", () => {
+ const system = ["You are Claude Code.", TASKCREATE_NUDGE].join("\n\n");
+ const { body } = translate(system);
+ expect(body.instructions).toBe(system);
+ expect(userTurns(body)).toEqual([
+ { type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] },
+ ]);
+ });
+
+ test("opted-in harness relocates the latest total_tokens footer onto a trailing input user message", () => {
+ const first = footer(1000);
+ const latest = footer(8000);
+ const { body } = translateHarness(["You are Claude Code.", first, latest].join("\n\n"));
+ expect(body.instructions).toBe("You are Claude Code.");
+ expect(String(body.instructions)).not.toContain("");
+ const input = userTurns(body);
+ const last = input[input.length - 1]!;
+ expect(last).toEqual({
+ type: "message",
+ role: "user",
+ content: [{ type: "input_text", text: latest }],
+ });
+ expect(input.some(item => item.role === "user" && item !== last)).toBe(true);
+ });
+
+ test("opted-in peel does not require metadata.user_id", () => {
+ const latest = footer(14_980_071);
+ const { body } = translateHarness(["You are Claude Code.", latest].join("\n\n"));
+ expect(body.instructions).toBe("You are Claude Code.");
+ const input = userTurns(body);
+ expect(input[input.length - 1]).toEqual({
+ type: "message",
+ role: "user",
+ content: [{ type: "input_text", text: latest }],
+ });
+ });
+
+ test("fenced standalone total_tokens example is a translator no-op when opted in", () => {
+ const system = ["You are a docs bot.", "```", footer(123), "```", ""].join("\n");
+ const { body } = translateHarness(system);
+ expect(body.instructions).toBe(system);
+ expect(userTurns(body)).toEqual([
+ { type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] },
+ ]);
+ });
+
+ test("open fence to EOF with a trailing total_tokens tag is not relocated", () => {
+ const system = ["You are a docs bot.", "```", footer(123)].join("\n");
+ const { body } = translateHarness(system);
+ expect(body.instructions).toBe(system);
+ expect(userTurns(body)).toEqual([
+ { type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] },
+ ]);
+ });
+
+ test("real footer after a closed fence still relocates when opted in", () => {
+ const docs = ["Docs:", "```", footer(123), "```"].join("\n");
+ const latest = footer(8000);
+ const { body } = translateHarness(`${docs}\n\n${latest}`);
+ expect(body.instructions).toBe(docs);
+ const input = userTurns(body);
+ expect(input[input.length - 1]).toEqual({
+ type: "message",
+ role: "user",
+ content: [{ type: "input_text", text: latest }],
+ });
+ });
+
+ test("whitespace-only system without a footer is preserved byte-for-byte", () => {
+ const system = " \n\n ";
+ const { body } = translate(system);
+ expect(body.instructions).toBe(system);
+ expect(userTurns(body)).toEqual([
+ { type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] },
+ ]);
+ });
+
+ test("Claude Code session prompt_cache_key is unchanged across trailing footers", () => {
+ const stable = "You are Claude Code.";
+ const keyOf = (system: string) =>
+ translateHarness(system, { user_id: "user-abc" }).body.prompt_cache_key as string;
+ const stableKey = keyOf(stable);
+ expect(stableKey).toMatch(/^[0-9a-f]{32}$/);
+ expect(keyOf([stable, footer(1000), footer(8000)].join("\n\n"))).toBe(stableKey);
+ expect(keyOf([stable, footer(99999)].join("\n\n"))).toBe(stableKey);
+ });
+
+ test("outside opt-in, Desktop prompt_cache_key hashes raw systemParts including footers", () => {
+ const stable = "You are Claude Code.";
+ const keyOf = (system: string) => translate(system).body.prompt_cache_key as string;
+ const stableKey = keyOf(stable);
+ expect(stableKey).toMatch(/^[0-9a-f]{32}$/);
+ expect(keyOf(stable)).toBe(stableKey);
+ expect(keyOf([stable, footer(8000)].join("\n\n"))).not.toBe(stableKey);
+ });
+
+ test("opted-in Desktop prompt_cache_key hashes stabilized instructions, not total_tokens footers", () => {
+ const stable = "You are Claude Code.";
+ const keyOf = (system: string) => translateHarness(system).body.prompt_cache_key as string;
+ const stableKey = keyOf(stable);
+ expect(stableKey).toMatch(/^[0-9a-f]{32}$/);
+ expect(keyOf([stable, footer(1000), footer(8000)].join("\n\n"))).toBe(stableKey);
+ expect(keyOf([stable, footer(99999)].join("\n\n"))).toBe(stableKey);
+ });
+
+ test("outside opt-in, a no-match Desktop key differs from the opted-in instructions-string key", () => {
+ const system = "You are Claude Code.\n\nPrefer terse answers.";
+ const rawKey = translate(system).body.prompt_cache_key as string;
+ const optedInKey = translateHarness(system).body.prompt_cache_key as string;
+ expect(rawKey).toMatch(/^[0-9a-f]{32}$/);
+ expect(optedInKey).toMatch(/^[0-9a-f]{32}$/);
+ expect(rawKey).not.toBe(optedInKey);
+ });
+
+ test("Claude Code /v1/messages inbound opts into prompt-cache stabilize", () => {
+ const source = readFileSync(repoPath("src/server/claude-messages.ts"), "utf8");
+ expect(source).toContain("stabilizePromptCache: true");
+ });
+});
diff --git a/tests/fixtures/test-layout-expected.json b/tests/fixtures/test-layout-expected.json
index 98907bd26a..a213efebf0 100644
--- a/tests/fixtures/test-layout-expected.json
+++ b/tests/fixtures/test-layout-expected.json
@@ -139,6 +139,7 @@
"claude-desktop-remote-hub.test.ts": "claude-integration",
"claude-dotenv-provenance-transport.test.ts": "claude-integration",
"claude-gateway-cache.test.ts": "claude-integration",
+ "claude-inbound-cache-stabilize.test.ts": "claude-integration",
"claude-inbound-debug.test.ts": "claude-integration",
"claude-inbound.test.ts": "claude-integration",
"claude-management-api.test.ts": "claude-integration",