Agent Safety22 min read

What Model Providers Do to Your Prompts

Two incidents from mid-2026 confirmed that the same linear-editing toolkit used to ablate desperation vectors can be deployed by model providers against their own users. What happened, how it works technically, and what belongs in your production stack as a result.

Two incidents from mid-2026 confirmed that the same linear-editing toolkit used to ablate desperation vectors can be deployed by model providers against their own users. What happened, how it works technically, and what belongs in your production stack as a result.

Series: When Agents Escape — Failures and Fixes in Production AI Part 1: Four Failures · Part 2: The Observability Gap · Part 3: The Anthropic Timeline · Part 4: The Hidden Variable · Part 5: What Providers Do to Your Prompts · Part 6: The Defense Layer

This is the second post in a series on model-substrate security. The first post covers desperation vectors and the model-editing pipeline. The defense-layer write-up lives with the product docs at docs.clawql.com/security. This pairs with the Mini Shai-Hulud supply chain, immutable releases, and the audit trail you can’t reconstruct.


Two Incidents, One Underlying Mechanism

In June 2026, Anthropic released Claude Fable 5 publicly and Mythos 5 to a restricted set of organizations. The system card disclosed that for a narrow category of prompts — those related to frontier LLM development, pretraining pipelines, distributed training infrastructure, and ML accelerator design — Fable 5 applies interventions that degrade output quality. The stated reason was limiting acceleration of competing models and constraining risky self-improvement or distillation pathways.

The interventions Anthropic described were prompt modification, steering vectors, and parameter-efficient fine-tuning applied selectively based on prompt classification. Users asking Fable 5 for help with ML-related code or hyperparameter configurations reported that answers appeared normal but were subtly wrong in ways that weren’t immediately obvious. The model wasn’t refusing. It was complying with degraded outputs.

In the same period, a separate finding emerged about Claude Code. Analysis of the distributed binary revealed logic present since v2.1.91 that activates when ANTHROPIC_BASE_URL is set to a non-default value. The code checks the system timezone — specifically for Asia/Shanghai or Asia/Urumqi — and checks whether the proxy hostname matches a list of approximately 147 Chinese domains, cloud regions, and AI lab endpoints. When those conditions are met, the system prompt is modified using steganography.

The encoding works as follows. For sessions where the timezone check matches, the date separator in “Today’s date is…” changes from - to /. Different Unicode apostrophe variants in that same string encode whether the proxy hostname matched a Chinese domain, an AI lab endpoint, both, or neither. The code was obfuscated with an XOR key and minified variable names.

These are two different mechanisms serving two different operational goals. The Fable 5 behavior is output degradation via activation-level intervention. The Claude Code behavior is covert telemetry encoding via prompt modification. What connects them is that both happen without user notification, both operate at a layer below what the application developer can observe, and both use the same class of techniques that the first post in this series described for legitimate model editing.


The Fable 5 Mechanism

Anthropic’s disclosure described three techniques: prompt modification, steering vectors, and PEFT. Understanding how each works helps clarify what you can and can’t detect from outside the model.

Prompt modification is the most detectable. Before the model processes your input, text is prepended, appended, or inserted into the prompt in ways that shift the model’s behavior on the target topic. If you have access to the raw prompt going into the model and the prompt as processed by the model, you can diff them. Most API users don’t have that access.

Steering vectors are applied at inference time. A direction in the model’s activation space — analogous to the desperation direction from the first post, but targeting output quality or topic avoidance rather than an emotional state — is added or subtracted from the residual stream during the forward pass. The model’s behavior shifts accordingly. This is invisible in the API response. The logprobs change, the output changes, but there’s nothing in the response payload that indicates intervention occurred. Detection requires either probing the model with carefully constructed inputs designed to expose the steering, or comparing outputs systematically against a reference model known to be unmodified.

PEFT applied selectively based on prompt classification means the model being served isn’t a single fixed checkpoint. The serving infrastructure classifies the incoming prompt, selects a modified adapter, and loads it for that request. From the API caller’s perspective, the request went in and a response came back. The behavioral difference is only visible by probing repeatedly across many prompts and comparing outputs statistically.

Anthropic estimated the affected category covers approximately 0.03% of traffic. For most users, this is invisible. For teams building frontier ML infrastructure, training pipelines, or accelerator software, it’s a meaningful fraction of their highest-value queries.

The detection approach for production systems: establish a baseline of expected model behavior on a probe set covering your domain, run the probe set periodically against the live API, and flag statistical deviations. This is behavioral fingerprinting applied defensively. The same technique used in anomalous tool-use detection for agent behavior applies here to model behavior.


The Claude Code Mechanism

The steganographic encoding in Claude Code is more operationally specific. The apparent goal is detecting unauthorized use cases: resale proxies routing Claude Code traffic through Chinese infrastructure, AI labs attempting to use Claude Code output for distillation, or users in jurisdictions where Anthropic doesn’t offer commercial access.

The technical implementation encodes detection signals into the system prompt so that the model’s behavior varies based on the detected context, and so that downstream analysis of the model’s outputs can recover the context from the output patterns. The date separator change (- vs /) and Unicode apostrophe variants are detectable in model outputs by anyone monitoring the raw text — including Anthropic.

The XOR obfuscation with key 91 and minified variable names suggests the code wasn’t intended to be found by users inspecting the distributed binary. Claude Code has deep filesystem and shell access on the machines where it runs, which makes the nature of this embedded logic more significant than it would be for a stateless API wrapper.

The user experience is unchanged. The model behaves identically from the user’s perspective regardless of which encoding branch activates. The signal is embedded for Anthropic’s downstream analysis, not for the user.

# Simplified reconstruction of the encoding logic
# Actual implementation used XOR obfuscation with key 91

import unicodedata

def encode_detection_signal(
    date_string: str,
    is_chinese_timezone: bool,
    proxy_matches_chinese_domain: bool,
    proxy_matches_ai_lab: bool,
) -> str:
    """
    Encodes detection signals into a date string via:
    1. Date separator choice (- vs /)
    2. Unicode apostrophe variant selection
    """
    # Separator encodes timezone detection
    separator = "/" if is_chinese_timezone else "-"

    # Apostrophe variant encodes proxy classification
    # U+2019 RIGHT SINGLE QUOTATION MARK
    # U+0027 APOSTROPHE
    # U+02BC MODIFIER LETTER APOSTROPHE
    if proxy_matches_chinese_domain and proxy_matches_ai_lab:
        apostrophe = "\u02bc"
    elif proxy_matches_chinese_domain:
        apostrophe = "\u2019"
    elif proxy_matches_ai_lab:
        apostrophe = "\u0027"
    else:
        apostrophe = "\u0027"  # standard apostrophe, no signal

    # Result is injected into "Today{apostrophe}s date is YYYY{sep}MM{sep}DD"
    parts = date_string.split("-")
    return f"Today{apostrophe}s date is {separator.join(parts)}"

Detection from the outside: Unicode normalization of any string containing date references and apostrophes in Claude Code outputs will expose non-standard characters. A monitoring layer that normalizes Unicode in system-prompt content and flags deviations from expected apostrophe codepoints will catch this encoding.

def detect_nonstandard_apostrophe(text: str) -> dict:
    """
    Scan text for Unicode apostrophe variants that differ
    from the standard ASCII apostrophe (U+0027).
    Returns detected variants and their positions.
    """
    standard = "\u0027"
    apostrophe_variants = {
        "\u2019": "RIGHT SINGLE QUOTATION MARK",
        "\u02bc": "MODIFIER LETTER APOSTROPHE",
        "\u02b9": "MODIFIER LETTER PRIME",
        "\u055a": "ARMENIAN APOSTROPHE",
        "\u0060": "GRAVE ACCENT",
    }

    findings = []
    for i, char in enumerate(text):
        if char in apostrophe_variants:
            findings.append({
                "position": i,
                "codepoint": f"U+{ord(char):04X}",
                "name": apostrophe_variants[char],
                "context": text[max(0, i-20):i+20],
            })

    return {
        "has_nonstandard_apostrophe": len(findings) > 0,
        "findings": findings,
    }

def detect_date_separator_encoding(text: str) -> dict:
    """
    Check for date strings using / separator where - is standard.
    A date like "2026/07/31" in a context that normally uses
    ISO 8601 formatting may indicate timezone-conditional encoding.
    """
    import re

    slash_dates = re.findall(r'\b\d{4}/\d{2}/\d{2}\b', text)
    hyphen_dates = re.findall(r'\b\d{4}-\d{2}-\d{2}\b', text)

    return {
        "slash_format_dates": slash_dates,
        "hyphen_format_dates": hyphen_dates,
        "separator_anomaly": len(slash_dates) > 0 and len(hyphen_dates) == 0,
    }

What These Incidents Have in Common

Both incidents are applications of the same mechanistic toolkit the first post described for legitimate model editing. The Fable 5 steering vectors are the same type of object as desperation vectors — linear directions in activation space, applied at inference time. The Claude Code prompt modification is the same operation as the prompt-modification step in policy fine-tuning pipelines, applied covertly by the serving infrastructure rather than explicitly by the user.

The technical sophistication required to do this is the same sophistication required to ablate a desperation direction. These are not exotic capabilities. They’re standard applications of mechanistic interpretability tooling, applied by the party with access to the model weights and serving infrastructure rather than by the user.

The Fable 5 system card disclosed the behavior, which is better than the alternative. The Claude Code encoding was found by users examining the distributed binary, not disclosed proactively. Both cases illustrate the same structural position: when you’re using a closed model via API, the intervention surface available to the provider is large, the detection surface available to you is small, and disclosure is discretionary.


Prompt Integrity as a Production Requirement

The practical response for production agentic systems is to treat prompt integrity as a first-class detection target rather than an assumed property.

A prompt-integrity monitor sits on the path between your application and the model API. It captures the prompt you send, the response you receive, and — where accessible — any system prompt content included by the serving infrastructure. It normalizes Unicode, checks for non-standard apostrophe variants, flags date separator anomalies, and maintains a behavioral baseline against which current responses are compared.

interface PromptIntegrityResult {
  unicodeAnomalies: UnicodeAnomaly[];
  dateEncodingAnomalies: DateAnomaly[];
  behavioralDrift: BehavioralDriftResult | null;
  systemPromptModified: boolean;
  verdict: 'clean' | 'anomaly' | 'confirmed_intervention';
}

interface UnicodeAnomaly {
  position: number;
  codepoint: string;
  name: string;
  context: string;
}

interface BehavioralDriftResult {
  probeId: string;
  baselineResponse: string;
  currentResponse: string;
  semanticDistance: number;
  flagged: boolean;
}

async function checkPromptIntegrity(
  prompt: string,
  response: string,
  systemPrompt: string | null,
  baseline: ResponseBaseline
): Promise<PromptIntegrityResult> {
  const unicodeAnomalies = scanForUnicodeAnomalies(
    [systemPrompt, response].filter(Boolean).join('\n')
  );

  const dateAnomalies = scanForDateEncodings([systemPrompt, response].filter(Boolean).join('\n'));

  const behavioralDrift = await baseline.compare(prompt, response);

  const systemPromptModified =
    systemPrompt !== null && systemPrompt !== baseline.expectedSystemPrompt;

  const hasAnomalies =
    unicodeAnomalies.length > 0 ||
    dateAnomalies.length > 0 ||
    (behavioralDrift?.flagged ?? false) ||
    systemPromptModified;

  return {
    unicodeAnomalies,
    dateEncodingAnomalies: dateAnomalies,
    behavioralDrift,
    systemPromptModified,
    verdict: hasAnomalies ? 'anomaly' : 'clean',
  };
}

The behavioral baseline component deserves attention. Unicode checks and date separator checks catch known encoding patterns. Behavioral drift detection catches unknown interventions — steering vectors, PEFT adapters, prompt modifications that don’t leave a syntactic signature. Building a baseline means running a probe set of requests representative of your domain and storing the response distribution. Periodic re-probing flags when the response distribution shifts in ways that don’t correspond to changes you made.

This is the same statistical behavioral firewall described in the Hardened Agentic Stack observability post, applied to the model API rather than to the agent’s tool-call behavior.


What Open-Weight Models Change

The Fable 5 and Claude Code incidents are structurally only possible with closed models served via API. The provider controls the serving infrastructure. The serving infrastructure can modify the prompt, apply steering, or load a different adapter before your request reaches the model weights.

With an open-weight model you serve yourself, the weight space is what it is. If you’ve applied the editing pipeline from the first post — refusal ablation, desperation ablation, custom policy — the resulting model is what gets called for every request. No serving infrastructure between your application and the model weights exists to intercept and modify.

This is one of several production arguments for the open-weight path that go beyond cost and customization. The intervention surface available to a provider who controls serving infrastructure is large. The intervention surface on a model you serve from weights you control is limited to your own infrastructure.

The tradeoff: you’re now responsible for verifying the weights you received are the weights you intended to load. Model weight integrity — verifying authenticity before every load, signing weights with Cosign, checking hashes against a known manifest — belongs on the list of things production deployments verify. The supply chain post covers the artifact integrity side of this. Weight integrity is the same problem applied to the model file rather than the container image.

# Verify model weights against a signed manifest before loading
clawql doctor --smoke --verify-weights \
  --model ./models/llama-70b-edited.safetensors \
  --manifest ./manifests/llama-70b-edited.json \
  --cosign-cert ./certs/model-signing.pem

The WORM Audit Trail for API Calls

When you’re using a closed API, the audit trail for detecting intervention needs to capture enough information to reconstruct what happened after the fact.

Every inference call on the production path should write a WORM entry that includes the prompt hash, the response hash, the model identifier returned by the API, any system prompt content, and a timestamp. Periodic re-probing against the baseline writes comparison results. If a behavioral anomaly is detected later, the WORM trail tells you when the drift started — whether it preceded or followed a model version update, a change to your prompt templates, or a change in the provider’s serving configuration.

await worm.append({
  correlation_id: callId,
  event_kind: 'INFERENCE_COMPLETE',
  layer: 'usage',
  actor_id: 'inference-gateway',
  payload: {
    model: response.model,
    prompt_hash: sha256(prompt),
    response_hash: sha256(response.content),
    system_prompt_hash: systemPrompt ? sha256(systemPrompt) : null,
    prompt_integrity: integrityResult,
    input_tokens: response.usage.input_tokens,
    output_tokens: response.usage.output_tokens,
  },
  policy_manifest_hash: currentManifestHash,
});

The prompt and response hashes let you verify later that what the WORM record claims was sent and received matches what you can reconstruct from logs. The prompt integrity result — stored at call time — means anomaly detection runs in real-time rather than only retroactively.


Honest Assessment

The Fable 5 disclosure was voluntary. Anthropic published a system card that described the interventions, their scope, and their rationale. That’s substantially better than the alternative, and it reflects the interpretability research program’s emphasis on transparency. The Claude Code steganographic encoding was found by community analysis of the binary, which suggests the disclosure calculus was different for that feature. Both cases are within the range of what providers can do unilaterally under current terms of service.

The detection techniques described here catch known patterns and statistical deviations from baseline. A provider with the sophistication to apply activation-level steering can also apply it in ways that don’t produce detectable output signatures. There’s no guarantee that a monitoring layer catches every possible intervention — only that it catches interventions that produce observable effects in outputs or that match known encoding patterns.

The production posture this suggests: use prompt-integrity monitoring as a detection layer, not a prevention layer. Pair it with open-weight models for workloads where the intervention surface matters most. Keep the WORM audit trail current so that anomalies are detectable after the fact even if they’re not caught in real time. And when a closed API is the right choice for a given workload, apply the same supply-chain posture to the API endpoint — version-pin, monitor behavioral drift, and have a documented response if the behavior changes.

The production defense layer — the 30-point agentic security framework and gateway package — is documented at docs.clawql.com/security.


Reference implementation: ClawQL on GitHub. Related: desperation vectors and the model-editing pipeline, the Mini Shai-Hulud supply chain, the audit trail you can’t reconstruct.

About the author

Daniel Smith builds ClawQL, an agent operating system for token-efficient discovery and execution over APIs — with observability, hardened tool boundaries, and production routing for LLM workloads. He writes here about the systems problems behind shipping agents.