
Computers don't read text the way humans do. Language models convert text into tokens — numbered units represented as integer IDs.
[12045, 419, 7823, 1902]Even tiny changes produce different token IDs:
| Text | Looks the same? | Token IDs |
|---|---|---|
{"tool": "search"} | ✅ | [123, 456, 789] |
{ "tool": "search"} (extra space) | Almost | [123, 457, 789] ← different! |
Key takeaway: What looks identical to a human can be completely different to a model at the token level.
RL is a training technique where a model learns by attempting tasks, getting scored, and adjusting its behavior toward what worked.
RL math (specifically policy-gradient RL) has a strict requirement:
The trainer must optimize the model against the exact context the model actually experienced — not a reconstruction, not a summary, not an approximation.
To compute a parameter update, the trainer needs:
Imagine the model saw token sequence [A, B, C] and made decision D.
If the trainer later reconstructs [A, B', C] (slightly different due to reformatting):
D given context [A, B', C][A, B', C]Key takeaway: Training against a slightly wrong past is worse than it looks, because the error is silent.
A harness is software that wraps a language model to enable multi-step tasks:
During a single rollout, a harness might:
Each of these rewrites is a chance for token drift — the next request's token sequence diverges from what the model actually generated last turn.
| Drift Type | Cause |
|---|---|
| Retokenization drift | Re-running the tokenizer over already-seen text |
| Chat template drift | The format string wrapping messages changes |
A harness transcript is a faithful record of the conversation. It is not a faithful record of the tokens — and it is the tokens the trainer needs.
Instead of trying to reconstruct token-level state from text after the rollout ends, capture it at the moment of generation — the only point where it is unambiguously correct.
Agent Harness → [Turnstile Proxy] → Inference Backend (SGLang/vLLM)
↓
Records exact tokens,
log probs, loss masks
TrainingSequence objectThe harness runs completely unchanged — it doesn't need to know anything about RL training.
Treat every request as an independent training example.
Problems:
When Turn 2 is just Turn 1 + new tokens at the end:
Turn 1: [T1, T2, T3, T4, T5]
Turn 2: [T1, T2, T3, T4, T5, T6, T7, T8]
↑ new tokens
Turnstile verifies (at the token ID level, not by comparing text strings) that the prefix is identical, then merges them into one continuous trainable sequence.
If the harness rewrote earlier messages or tokens drifted:
Turn 1: [T1, T2, T3, T4, T5]
Turn 2: [T1, T2, T3', T4, T5, T6, T7] ← T3 changed to T3'
Turnstile starts a new sequence rather than pretend the prefix is the same.
Design philosophy: The goal is not maximum compactness. The goal is never lying to the trainer about what happened.
Some models don't activate all their parameters for every token. Instead:
Even if two requests have identical token IDs, the tokens might be routed to different experts on different runs — because tiny differences in how previous tokens were processed change the routing decisions.
When MoE capture is enabled:
Key insight: Token IDs alone are not sufficient to fully characterize what the model experienced in MoE models — the routing path is also part of the computation.
When a model takes images as input, the model doesn't see raw image bytes. It sees the output of an image processor:
The same image bytes can produce different tensors if:
Turnstile treats image processing as part of the rollout:
class TrainingSequence:
tokens: list[int] # token IDs
logprobs: list[float] # per-token log probabilities
segment_info: list[...] # loss mask info
weight_versions: list[...] # which model weights were active
routed_experts: str | None # MoE routing trace
images: list[TrainingImage] # raw images
processed_images: list[TrainingProcessedImage] # processed tensors
| Concept | Core Problem | Turnstile's Solution |
|---|---|---|
| Tokens | Text looks same but has different IDs | Capture IDs at generation time |
| RL requirement | Trainer needs exact context model saw | Record token IDs + log probs + masks |
| Rollouts | Reconstructed transcripts drift from reality | Proxy sits at generation boundary |
| Harnesses | Rewrite history, causing token drift | Harness runs unchanged; proxy intercepts |
| Multiturn | Naive storage duplicates tokens or loses turn relationships | Prefix-aware merging with token-level verification |
| MoE routing | Same tokens, different expert paths = different computation | Record and verify routing traces |
| Multimodal | Image reprocessing produces different tensors | Record processed pixel features in trajectory |
The unifying principle: The model sampled the tokens. Record them at that moment. Never reconstruct, never approximate, never lie to the trainer about what happened.