Temperature Zero Is Not a Lock
You ran the same prompt twice at temperature zero and got two answers. The knob you trusted to freeze the model was never a lock, so stop pinning the model and start pinning the pipeline.
Temperature Zero Is Not a Lock
Let's Call It Zero-Point Engineering
You go back to reproduce yesterday's result, because a colleague flagged something odd in the output and you want to see it again before you touch anything. Same prompt, same model name in the config, temperature pinned to zero, the setting everyone reaches for when they want the model to stop being creative. You run it. The answer comes back different. Not wildly different, not gibberish, just different enough that the field you were chasing now reads correctly, and you're left holding a genuinely unsettling question: did you fix something between yesterday and today, or did the ground move under you while you were looking away?
The reflex answer is that temperature zero makes a model deterministic, but that reflex isn’t true. In September of 2025, Thinking Machines Lab published the clearest account of why. They ran Qwen3-235B a thousand times on an identical prompt at temperature zero and got eighty distinct completions, with the first divergence appearing as early as the hundred-and-third token. The culprit isn't the sampler, which temperature zero pins. It's that production inference servers batch concurrent requests together, the batch size shifts with real-time load, and the floating-point reductions inside the model aren't associative, so (a + b) + c and a + (b + c) land on different bits. The same prompt gets a slightly different numerical answer depending on who else happened to be in its batch, which is to say depending on how busy the server was the moment you hit it.
The model won't hold still, the knob you were told would freeze it doesn't, and on a hosted endpoint you don't even control the one variable that actually moves the output, which is the request batch you land in. Stop trying to make the model deterministic. Start engineering the pipeline around a model that isn't. Pin what you can pin, snapshot what you can't, and test the behavior relentlessly so drift is caught by a machine on your side and not by a customer on theirs.
SPONSORED CONTENT -
Thinking about hiring globally? Start with an EOR.
The best person for your next role might not live near your office—or even in the same country.
More companies are realizing they don't need to open entities everywhere just to access global talent. Instead, they're using EOR to hire internationally faster, stay compliant, and avoid building local infrastructure before they're ready.
COMPANION SCRIPT
Companion script for this issue: holdstill. Point it at a prompt and a model CLI and it runs the same call N times, counts how many unique outputs come back, reports the first token where they diverge, and, if you name the value that actually matters, shows you the distribution of just that value across the runs. It turns "the model feels flaky" into a number you can put on a dashboard. Hand-raiser keyword: HOLDSTILL. The complete version is inline in the Quick Tip below.
Temperature Zero Is Not A Lock
The reason this matters more than a curiosity is that a great deal of production code is built on the unexamined belief that temperature zero buys reproducibility, and it doesn't buy reproducibility on any hosted API you're likely using. The Thinking Machines work is the cleanest demonstration, but the mechanism generalizes to every transformer served at scale: the numerics depend on batch shape, batch shape depends on load, and load is the one input to your output that you have no visibility into and no control over. Your prompt is deterministic. The server's traffic is not, and the server's traffic is now part of your function signature whether you acknowledge it or not.
There is a real fix, and its cost tells you why you don't already have it. Thinking Machines open-sourced batch-invariant kernels that make the reductions independent of batch size, and running them, the same thousand prompts collapsed to a thousand bit-identical outputs. LMSYS reproduced the approach in the SGLang engine shortly after. The catch is throughput: the deterministic kernels ran on the order of 1.6 to 2.1 times slower in the original benchmarks, with SGLang's optimized integration bringing the overhead down but not to zero. That price is exactly why the major hosted providers don't ship batch-invariant inference by default, and why "just call the API at temperature zero" will keep handing you eighty flavors of the same answer for the foreseeable future. The determinism is buildable. It is not free, and it is not on.
None of this means the model is broken. It means the model is a probabilistic component sitting behind a serving layer that adds its own noise, and if your architecture assumed a pure function where there's actually a load-dependent one, the assumption is the bug, not the model.
FOR FURTHER READING
Defeating Nondeterminism in LLM Inference (Thinking Machines Lab, September 2025): the batch-invariance finding, the thousand-runs-eighty-outputs experiment, and the open-source kernels that actually fix it, at a cost.
How Is ChatGPT's Behavior Changing over Time? (Chen, Zaharia, and Zou, 2023; also in Harvard Data Science Review): the study where GPT-4's accuracy on one fixed task fell from 84% to 51% between two dated snapshots of the same model.
Reproducible outputs with the seed parameter (OpenAI Cookbook): OpenAI's own words, that determinism is "not guaranteed," and what
system_fingerprintis actually for.LLM Output Drift: Cross-Provider Validation for Financial Workflows (AI4F at ICAIF '25): the counterintuitive result that a 120B model held only 12.5% output consistency at temperature zero while smaller models held 100%, and that retrieval tasks drifted 25 to 75 percent.
Issue #12: Zero Mental Math Architecture: the deterministic-wall pattern this issue extends from arithmetic to behavior, and where the recompute-outside-the-model discipline started.
The Model Name Is Not The Model
Batch noise is the fast, within-a-second version of the problem. There's a slower version that's arguably worse, because it moves on a timescale where you'll have forgotten you were exposed to it: the model behind a given name changes over time. OpenAI's own cookbook says it directly, that even with a fixed seed and matching parameters, "determinism is not guaranteed," and that the system_fingerprint field exists precisely so you can detect when the backend configuration shifted underneath you. Notice what that field is: a tool for detecting drift, not for preventing it. The provider is telling you the ground can move and handing you a seismograph, not a foundation.
The most rigorous public measurement of this remains the 2023 study by Chen, Zaharia, and Zou. On the fixed task of identifying whether a number is prime, GPT-4 scored 84% in its March 2023 snapshot and 51% in its June 2023 snapshot. Same name, same API, a thirty-three point collapse on identical inputs, with no version bump a caller would have noticed. Later that year came the episode where users reported GPT-4 had grown "lazier," and OpenAI responded that it hadn't updated the model since November 11th and that the behavior shift wasn't intentional, which is its own kind of unsettling: the weights held still and the behavior drifted anyway. "We pinned gpt-4" pins a string. It does not pin a distribution of outputs, and the distribution is the thing your pipeline actually consumes.
The counterintuitive part, for anyone who assumes a bigger model is a steadier one, arrived in a 2025 study of output drift across model architectures on regulated financial tasks. The smallest models tested held 100% output consistency at temperature zero, while a 120-billion-parameter model held only 12.5%, and retrieval-augmented tasks drifted somewhere between 25 and 75 percent depending on the model. Scale does not buy you stability. Sometimes it costs you stability, and the report that runs on last night's model isn't guaranteed to agree with the one that runs on tonight's, even when nothing in your repository changed.
Consistency Is Not Correctness
Here's the trap waiting for anyone who takes the first half of this issue too much to heart and sets out to make the pipeline perfectly reproducible as an end in itself. Reproducibility is not accuracy. A pinned, seed-locked, batch-invariant pipeline that returns the identical answer every single time can be returning the identical wrong answer every single time, and its beautiful consistency will do nothing but make the error look trustworthy.
A large medical study in late 2025 made this concrete at a scale worth respecting, generating on the order of nine hundred thousand model outputs across hundreds of clinical cases run a hundred times each. The finding that matters for us is that a model's repeatability, how consistently it gave the same answer, didn't reliably predict its diagnostic accuracy across most of the prompting styles tested. Consistent and correct are simply different axes. You can have either without the other, and a system optimized only for the first is a system that has learned to be confidently, repeatably mistaken.
So the point of pinning was never to be right. The point of pinning is that when you ARE right, you stay right until you deliberately change something, and when you do change something, the change is the only variable in the room. That's not a philosophical nicety. It's the entire precondition for the regression testing this newsletter turns to next week, because you can't detect a regression against a baseline that's drifting on its own.
Pin What You Can, Snapshot The Rest, Test The Behavior
The discipline that falls out of all this has three moves, in ascending order of how much it protects you.
Pin what the provider lets you pin. Use dated model snapshots everywhere you currently use a floating alias, so a silent server-side model swap can't reach you without a config change you made on purpose. Log the
system_fingerprinton every call so a backend shift is visible in your telemetry the day it happens. Understand the expiration date on this protection: OpenAI commits to roughly six months' notice before retiring a generally available model, Anthropic to a minimum of sixty days, and Google to as little as two weeks for preview models. Pinning defers the drift and buys you a scheduled migration instead of a surprise one. It does not abolish the drift.Snapshot what you can't pin. You can't pin the batch you land in or the exact numerics of a hosted endpoint, so capture the behavior instead. Freeze a set of representative inputs and the outputs you expect from them, stored in your repository next to the code, as an artifact a human reviewed and blessed.
Test the behavior, and assert only on what should be stable. Run the snapshot set on every model bump and every prompt change, and assert on the parts that must not move, the extracted value, the classification, the schema, the sign of the number, while tolerating the cosmetic churn in phrasing that was always going to wobble. This is the deterministic wall from issue #12, moved around the whole model: the probabilistic core produces its output, and a plain, non-probabilistic check on your side decides whether that output is inside the band you signed off on.
That third move is where the real protection lives, because it's the only one that catches a regression the provider introduced without telling you. Pinning and logging make drift visible. A behavior test makes drift fail a build, which is the only form of "visible" that reliably stops a bad output before a customer meets it.
SPONSORED CONTENT -
The toughest room in advertising. Ad Studio just cleared it.
QUICK TIP
Measure The Wobble Before You Trust It
You can't manage variance you've never measured. holdstill runs the same prompt N times through whatever model CLI you have, normalizes the outputs, and tells you how many are unique, where they first diverge, and, when you hand it a pattern for the value you actually care about, how that one value is distributed across the runs. Swap the LLM variable for llm, claude -p, or any command that reads a prompt on stdin.
#!/usr/bin/env bash
# holdstill.sh - probe an LLM's run-to-run variance on a fixed prompt.
# usage: holdstill.sh <prompt-file> [runs] [value-regex]
set -euo pipefail
PROMPT="${1:?usage: holdstill.sh <prompt-file> [runs] [value-regex]}"
RUNS="${2:-30}"
VALUE_RE="${3:-}" # optional: a regex capturing the value that matters
LLM="${LLM:-llm}" # any CLI that reads a prompt on stdin
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT
for i in $(seq 1 "$RUNS"); do
"$LLM" < "$PROMPT" > "$tmp/out.$i"
done
echo "runs: $RUNS"
echo "unique outputs: $(cat "$tmp"/out.* | sort -u | wc -l | tr -d ' ')"
# First line number at which any two runs disagree.
diverge="$(paste -d'\n' "$tmp"/out.1 "$tmp"/out.2 >/dev/null 2>&1; \
for i in $(seq 2 "$RUNS"); do diff <(cat "$tmp/out.1") <(cat "$tmp/out.$i") \
| grep -m1 '^[0-9]' | cut -d, -f1 | tr -dc '0-9'; echo; done \
| grep -E '^[0-9]+$' | sort -n | head -1)"
echo "first divergence: ${diverge:-none (all identical)}"
if [ -n "$VALUE_RE" ]; then
echo "--- distribution of the value that matters ---"
for i in $(seq 1 "$RUNS"); do
grep -oE "$VALUE_RE" "$tmp/out.$i" | head -1
done | sort | uniq -c | sort -rn
fiPoint it at a real production prompt and ask for the field you depend on:
$ LLM='claude -p' ./holdstill.sh extract.txt 50 '[0-9]+\.[0-9]{2}'
runs: 50
unique outputs: 7
first divergence: 4
--- distribution of the value that matters ---
47 90.00
3 100.00Forty-seven runs agreed the total was 90.00 and three insisted it was 100.00, at temperature zero, on the same input. Now you know the number, and you know exactly how much you can trust it, which is a strictly better position than finding out from the one customer who got a three-in-fifty answer.
Quick Wins
🟢 Easy (~20 min): Take one prompt your pipeline depends on and run holdstill on it fifty times at temperature zero. Count the unique outputs. If the number is greater than one, and it usually is, you now have direct evidence that your "deterministic" call isn't.
🟡 Medium (~1 hour): Replace every floating model alias in your codebase with a dated snapshot, and add the returned system_fingerprint to the structured logs on each call. The next time a provider shifts the backend under you, it shows up in your telemetry as an event with a timestamp instead of a mystery in your metrics.
🔴 Advanced (half day): Build a behavior snapshot over 20 to 50 representative inputs, asserting only on the fields that must stay stable, and wire it to run on every model or prompt change. Set a drift tolerance, fail the change when the tolerance is exceeded, and require a human to re-bless the snapshot before it can move. You've built the wall the next issue is going to lean on.
Next Week
A behavior snapshot over a handful of inputs is where an eval suite is born. Next Tuesday: how to grow it into something that catches a regression the night it lands, before it's the customer telling you.
You went back to reproduce yesterday's result and the answer had moved, and the honest reading of that isn't that your setup is broken. It's that you were counting on a lock that was never a lock. Temperature zero pins the sampler and nothing downstream of it; the batch you land in moves with the server's load, and the model behind the name you pinned drifts on a slower clock of its own. The variance is real, it's structural, and no knob in the API dashboard turns it off.
What you can do is refuse to build on the fiction. Pin the snapshots the provider offers and log when they shift, capture the behavior you can't pin into a set you reviewed, and put a plain deterministic check on your side of the boundary so a drifted output fails a build instead of reaching a person. You can't nail the model to the floor. You can build a floor solid enough that when the model moves, it can't fall through.
The model won't sit still. Make the pipeline that doesn't ask it to.
P.S. A deterministic gate that never guesses is the spine of the TestScout MCP suite, and LightScout is that idea made concrete: a check that fails a build only when the numbers genuinely miss a fixed threshold, the same steady wall this issue is asking you to build around your models. If measuring your own wobble surprised you, forward this to the engineer who swears the call is deterministic. If someone forwarded it to you, subscribe at bashmatica.com.
NODEBRIDGE AUTOMATION SOLUTIONS
Standing up agents in your stack and want the guardrails built in from day one?
NodeBridge sets up your team's Claude Code and AI dev environment with the two-key gates, deterministic checks, and safe-automation patterns from these issues already wired in. Fixed scope, done-for-you.
SPONSORED CONTENT -
You ship worldwide. Your follow-up doesn't.
International Shopify customers don't check email. They're on WhatsApp. connects your store directly to WhatsApp, Instagram DM, and SMS — set up automated cart recovery, order updates, and follow-up flows that reach customers wherever they are. Every conversation tracked in one place, nothing missed.