<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://wai-org.com/draft/feed.xml" rel="self" type="application/atom+xml" /><link href="https://wai-org.com/draft/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-10-17T18:15:33-07:00</updated><id>https://wai-org.com/draft/feed.xml</id><title type="html">WAI</title><subtitle>WAI is a student-led organization formed by AI PhD students from the University of Washington, working on open agentic research across the full stack — data, training, and evaluation.</subtitle><author><name>WAI</name></author><entry><title type="html">TMax v1.1: Repaired Data, Self-Generated Data, and Stable RL</title><link href="https://wai-org.com/draft/blog/tmax-v1-1/" rel="alternate" type="text/html" title="TMax v1.1: Repaired Data, Self-Generated Data, and Stable RL" /><published>2026-10-16T00:00:00-07:00</published><updated>2026-10-16T00:00:00-07:00</updated><id>https://wai-org.com/draft/blog/tmax-v1-1</id><content type="html" xml:base="https://wai-org.com/draft/blog/tmax-v1-1/"><![CDATA[<div class="post-hero">
<h1 class="title">
<img class="title-logo" src="/draft/assets/img/tmax/logo.png" alt="TMax mascot logo">
TMax v1.1
</h1>
<div class="post-subtitle">
<p>Repaired data, self-generated data, and stable RL</p>
</div>
<div class="byline">
<p>Oscar Yin, Hamish Ivison</p>
</div>
<div class="post-date">
<p>October 16, 2026</p>
</div>
</div>
<div class="draft-note">
<p>Set the publication date: the date line above, plus the file name and
<code>date:</code> in the front matter.</p>
</div>
<figure>
<img src="/draft/assets/img/tmax-v11/teaser.gif"
alt="Terminal-Bench 2.0 against model size, from v1.0 to v1.1. Every TMax model moves up: TMax-9B goes from 27.2% to 33.0%, past Claude Haiku 4.5 and GPT-5-mini, and TMax-27B (43.8%) edges past the 1T-parameter Kimi-K2.5. We re-ran the Qwen base models too. TMax and Qwen scores use our harness on Daytona; other scores are self-reported or from the leaderboard." />
<figcaption aria-hidden="true">Terminal-Bench 2.0 against model size,
from v1.0 to v1.1. Every TMax model moves up: TMax-9B goes from 27.2% to
33.0%, past Claude Haiku 4.5 and GPT-5-mini, and TMax-27B (43.8%) edges
past the 1T-parameter Kimi-K2.5. We re-ran the Qwen base models too.
TMax and Qwen scores use our harness on Daytona; other scores are
self-reported or from the leaderboard.</figcaption>
</figure>
<div class="draft-note">
<p>The teaser GIF still has draft labels: the first frame’s subtitle
says “Previous draft scores”, and the last frame says “27B is
provisional (266/267 graded)” with an asterisk on TMax-27B. The first
frame also shows Qwen3.6-27B at 39.5%, where the v1.0 post says 39.6%.
Re-export before publishing. The site copy holds the first frame for 1.5
s and the last for 3 s; the original advances every ~65 ms and loops
straight back from the final frame.</p>
</div>
<p><a href="/blog/tmax/">TMax v1.0</a> introduced a simple recipe for
training terminal agents with RL: synthetic environments sampled along
nine structured task axes, trained with outcome-only DPPO.<span
class="sidenote-wrapper"><label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span
class="sidenote">Hamish Ivison*, Junjie Oscar Yin*, Rulin Shao, Teng
Xiao, Nathan Lambert, and Hannaneh Hajishirzi. “<a
href="https://arxiv.org/abs/2606.23321">TMax: A simple recipe for
terminal agents</a>.” arXiv preprint arXiv:2606.23321, 2026. *Equal
contribution.<br />
<br />
</span></span> In v1.1, we keep the recipe and improve the data it
trains on and the infrastructure it runs on.</p>
<div class="tldr">
<p><span class="tldr-label">TL;DR</span></p>
<ul>
<li><strong>Repaired data.</strong> An LM auditor found that 87% of our
v1.0 environments had a verifier too weak to enforce the task or leaked
the answer. An LM repairer fixed most of them. The repaired
<strong>TMax-v1.1-13k</strong> matches or beats all 12 prior and
concurrent terminal datasets we compare against.</li>
<li><strong>Self-generated data.</strong> Running the whole pipeline
with Qwen3.5-9B produces data that improves Qwen3.5-9B itself, beating
more than half of those 12 datasets. No stronger teacher model
needed.</li>
<li><strong>Stable training.</strong> Long v1.0 runs were often
unstable. The main cause turned out to be noise from the sandbox
infrastructure, not the RL algorithm. With a dedicated sandbox fleet, a
1,000-step run trains smoothly without collapsing.</li>
</ul>
<p>TMax-9B now scores <strong>33.0%</strong> on Terminal-Bench 2.0 (up
from 27.2% in v1.0), ahead of Claude Haiku 4.5 (29.8%) and GPT-5-mini
(31.9%), and every model size from 2B to 27B improves.</p>
</div>
<div class="release">
<p><span class="release-label">Release</span> Everything below will be
public. Links are placeholders until release.</p>
<div class="release-grid">
<div class="release-card">
<p>Models</p>
<p><a href="#" class="dataset-tag tbd">TMax-2B-v1.1</a> <a href="#"
class="dataset-tag tbd">TMax-4B-v1.1</a> <a href="#"
class="dataset-tag tbd">TMax-9B-v1.1</a> <a href="#"
class="dataset-tag tbd">TMax-27B-v1.1</a> <a href="#"
class="dataset-tag tbd">TMax-Qwen3-8B-v1.1</a></p>
</div>
<div class="release-card">
<p>Data</p>
<p><a href="#" class="dataset-tag tbd">TMax-v1.1-13k</a> <a href="#"
class="dataset-tag tbd">TMax-Qwen3.5-9B-3.1k</a> <a href="#"
class="dataset-tag tbd">TMax-SFT-16.5k</a> <a href="#"
class="dataset-tag tbd">Audit labels</a></p>
</div>
<div class="release-card">
<p>Code and paper</p>
<p><a href="#" class="dataset-tag tbd">Training code</a> <a href="#"
class="dataset-tag tbd">Paper</a></p>
</div>
</div>
</div>
<div class="draft-note">
<p>Fill in the links once artifacts are up, and drop the
<code>.tbd</code> class from each tag as it goes live. The paper calls
the data TMax-13k; rename it there too, or note the alias. Confirm which
data extras ship (audit labels, self-generated set).</p>
</div>
<h2 id="whats-new-in-v1.1">What’s new in v1.1</h2>
<div class="changes">
<table>
<colgroup>
<col style="width: 33%" />
<col style="width: 33%" />
<col style="width: 33%" />
</colgroup>
<thead>
<tr class="header">
<th></th>
<th>v1.0 (June)</th>
<th>v1.1</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>RL data</td>
<td>TMax-15k: 14,601 environments, unaudited</td>
<td>TMax-v1.1-13k: 13,565 environments, audited and repaired</td>
</tr>
<tr class="even">
<td>Datasets compared</td>
<td>6</td>
<td>12</td>
</tr>
<tr class="odd">
<td>Sandboxes</td>
<td>Podman, on the same nodes as inference</td>
<td>Dedicated Apptainer sandbox fleet on CPU nodes</td>
</tr>
<tr class="even">
<td>Training stability</td>
<td>Long runs often unstable</td>
<td>No collapse over 1,000 steps</td>
</tr>
<tr class="odd">
<td>TMax-9B on TB 2.0</td>
<td>27.2%</td>
<td>33.0%</td>
</tr>
</tbody>
</table>
</div>
<h2 id="auditing-and-repairing-every-environment">1. Auditing and
repairing every environment</h2>
<p>v1.0 deliberately skipped validating its environments. The pipeline
only checked that each environment builds, and relied on RL to drop
tasks the model can never solve (a group where every rollout scores zero
carries no gradient). That handles tasks that are too hard. It does not
handle tasks that are broken the other way: a verifier that a lazy
solution can pass. RL is very good at finding those shortcuts.</p>
<p>So v1.1 adds a one-shot audit-and-repair pass after generation,
inspired by River.<span
class="sidenote-wrapper"><label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span
class="sidenote">Yao et al., “River,” 2026. River uses rubric-based
repair and filtering on existing datasets; we apply the same idea to our
own generated data.<br />
<br />
</span></span></p>
<figure>
<img src="/draft/assets/img/tmax-v11/data-pipeline.png"
alt="The v1.1 data pipeline. After the generator writes a task, an auditor labels whether its verifier actually checks the instruction, and a repairer rewrites the verifier or cleans the image where the problem is fixable. Unfixable tasks are dropped." />
<figcaption aria-hidden="true">The v1.1 data pipeline. After the
generator writes a task, an auditor labels whether its verifier actually
checks the instruction, and a repairer rewrites the verifier or cleans
the image where the problem is fixable. Unfixable tasks are
dropped.</figcaption>
</figure>
<ul>
<li><strong>Auditor.</strong> GPT-5.6 Sol reads each task’s instruction,
environment setup, and verifier, and assigns one of eight labels, such
as <em>verifier too weak</em>, <em>answer leak</em>,
<em>instruction–verifier mismatch</em>, or <em>clean</em>.</li>
<li><strong>Repairer.</strong> For the three most common fixable labels,
GPT-5.6 Sol gets the original task plus the auditor’s diagnosis, and
writes a hardened verifier or a cleaned image specification.</li>
<li><strong>Rebuild and spot-check.</strong> We rebuild the repaired
images, check that they still build with the dependencies they need, and
drop everything else that was flagged.</li>
</ul>
<p>The audit found far more problems than we expected:</p>
<div class="stat-row">
<p><span class="stat"><span class="stat-num">14,601</span> v1.0 tasks
audited</span> <span class="stat"><span class="stat-num">76%</span>
verifier too weak</span> <span class="stat"><span
class="stat-num">11%</span> answer leaked in the environment</span>
<span class="stat"><span class="stat-num">13,565</span> kept as
TMax-v1.1-13k</span></p>
</div>
<div class="tmax-chart" data-chart="audit">
<p><strong>What the auditor found in TMax-15k, and what we did with each
task.</strong> We repair the three most common fixable labels, keep
clean tasks as they are, and drop everything else. The 1,036 dropped
tasks include flagged tasks whose repair could not be applied.</p>
</div>
<p>Weak verifiers dominate. A typical one checks that an output file
exists and matches one fixed expected value, but never runs the program
the task asked for, so an agent can hardcode the answer. Below are real
verdicts from the auditor, lightly trimmed.</p>
<div class="tmax-tabs audit-examples">
<div class="tab" data-tab="Verifier too weak">
<p><strong>Task:</strong> write a C++ tool, <code>analyze_gc.cpp</code>,
that computes statistics over a FASTA file.</p>
<blockquote>
<p>“The verifier recomputes the correct stats for the single fixed FASTA
file and exact-compares <code>stats.txt</code>, but it only checks that
<code>analyze_gc.cpp</code> exists. It never compiles or executes that
source, so an empty C++ file plus hardcoded expected output can
pass.”</p>
</blockquote>
<p><span class="fix"><strong>Repaired.</strong> The repairer rewrites
the verifier so it tests the behavior the instruction asks for, not just
one fixed output file.</span></p>
</div>
<div class="tab" data-tab="Tamperable verifier">
<p><strong>Task:</strong> craft a payload that gets a Go validator to
grant admin access.</p>
<blockquote>
<p>“The verifier executes <code>validator.go</code> from the writable
task filesystem without checking that it is unchanged; setup even
applies <code>chmod -R 777</code>. An agent can replace the validator
with a program that always prints
<code>Access Granted: Admin</code>.”</p>
</blockquote>
<p><span class="fix"><strong>Repaired.</strong> Also labeled
<em>verifier too weak</em>. The repaired verifier must not trust files
the agent can edit.</span></p>
</div>
<div class="tab" data-tab="Answer leak">
<p><strong>Task:</strong> write <code>parse_logs.py</code> to summarize
a set of log files into a CSV.</p>
<blockquote>
<p>“The setup writes the complete ground-truth parsing result to
<code>/tmp/expected_summary.csv</code>, and that file remains accessible
to the agent. A fake <code>parse_logs.py</code> can simply copy this
file, passing both the exact-content and timing checks without parsing
logs.”</p>
</blockquote>
<p><span class="fix"><strong>Repaired.</strong> The leaked file is
removed from the image. If the verifier needs it, it ships with the
tests instead (see below).</span></p>
</div>
<div class="tab" data-tab="Instruction–verifier mismatch">
<p><strong>Task:</strong> an ETL job that, among other things, reports
the covariance determinant of three sensors.</p>
<blockquote>
<p>“The verifier hardcodes the sample-covariance determinant as 1.7225,
but the supplied CSV yields approximately 0.35237. Thus a faithful ETL
implementation fails the verifier.”</p>
</blockquote>
<p><span class="fix"><strong>Repaired.</strong> The repairer makes the
verifier consistent with the instruction and the data.</span></p>
</div>
<div class="tab" data-tab="Dropped">
<p><strong>Task:</strong> implement <code>config_manager.py</code> and
match a reference implementation.</p>
<blockquote>
<p>“The verifier requires <code>/tmp/oracle_config_manager.py</code> and
explicitly asserts that it exists, but the setup script never creates
that file. Therefore even a fully correct implementation fails.”</p>
</blockquote>
<p><span class="fix dropped"><strong>Dropped.</strong>
Instruction–environment mismatches like this one, and the other rare
labels, are removed rather than repaired.</span></p>
</div>
<p>Real GPT-5.6 Sol verdicts on TMax-15k tasks (a 40-task audit sample).
Task descriptions are paraphrased from the verdicts.</p>
</div>
<div class="draft-note">
<p>The examples come from a 40-task sample of auditor verdicts (tasks
000011, 000022, 000278, 000322, 000344). That sample is from the
prototype labelling run, so double-check these tasks got the same labels
in the final pass. The “Repaired” lines describe what the repairer does
for each label, not the exact patch for each task.</p>
</div>
<p><strong>A leaked answer has to leave the image.</strong> Our first
leak repairs added a check that the agent’s output is not a
byte-for-byte copy of the leaked reference file. That doesn’t work: the
agent can copy the reference, append a comment, and pass both the
inequality check and the behavioral tests. Of the first 18 leak repairs
we accepted, 11 relied on exactly that check. The final repairer instead
rebuilds the image without the leaked file. If the verifier needs the
reference, it ships with the tests, which are only copied into the
sandbox at verification time, after the agent has finished.</p>
<p>As in v1.0, we still do not filter out tasks a strong model can’t
solve; RL’s zero-variance filtering takes care of those.</p>
<p>So does repair help? Holding everything else fixed, training on the
repaired data improves Terminal-Bench Lite by 5.8 points and
Terminal-Bench 2.1 by 2.3 points over the unrepaired v1.0 data. Our
interpretation: weak verifiers reward shortcuts, so a policy trained on
them partly learns to take shortcuts instead of doing the task.</p>
<div class="tmax-chart" data-chart="repair">
<p><strong>Repair is worth +5.8 points on TB Lite and +2.3 on TB
2.1.</strong> Qwen3.5-9B trained with the same recipe on the v1.0 data
before repair and on the repaired TMax-v1.1-13k. Mean ± standard error
over three trials per task.</p>
</div>
<h2 id="results">2. Results</h2>
<h3 id="tmax-v1.1-13k-against-12-other-datasets">TMax-v1.1-13k against
12 other datasets</h3>
<p>Since v1.0 we have doubled the set of baselines, adding six recent
datasets: LiteCoder, SETA, CalibForge, Terminal-Lego, RST, and Facet. As
before, we hold everything fixed (Qwen3.5-9B, our recipe, our harness)
and change only the RL data. For several of these datasets, this is the
first time they have been used for RL training in the open.</p>
<div class="tmax-chart" data-chart="datasets">
<p><strong>RL on Qwen3.5-9B, changing only the dataset.</strong> Switch
benchmarks, hide the v1.1 ablations, or plot the gain over the base
model. The peach bar is the v1.0 data before repair; the purple bar is
data generated by Qwen3.5-9B itself (§3). Mean ± standard error over
three trials per task.</p>
</div>
<p>TMax-v1.1-13k has the highest mean on both benchmarks: 65.6 on TB
Lite and 29.6 on TB 2.1. Its lead over the strongest baselines (RST on
TB Lite, Facet on TB 2.1) is within the error bars, so the fair summary
is that TMax-v1.1-13k matches or beats every dataset we tried. Notice
where the unrepaired v1.0 data lands on TB Lite: mid-pack. Repair is
what moves it to the top.</p>
<h3 id="every-model-size-improves">Every model size improves</h3>
<p>We apply the recipe without modification to the other Qwen3.5 sizes
and to Qwen3.6-27B.</p>
<div class="tmax-chart" data-chart="sizes">
<p><strong>Base model vs. TMax v1.0 vs. TMax v1.1.</strong> TB 2.0 runs
on Daytona over all tasks, the same setting as the v1.0 headline
numbers, so the two versions are directly comparable there. TB 2.1 and
TB Lite run on our own sandbox fleet and are shown for v1.1 only. Mean
over three trials per task.</p>
</div>
<p>On Terminal-Bench 2.0, v1.1 beats v1.0 at every size: TMax-2B goes
from 2.9 to 6.4, TMax-4B from 18.9 to 25.1, TMax-9B from 27.2 to 33.0,
and TMax-27B from 42.7 to 43.8.<span
class="sidenote-wrapper"><label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span
class="sidenote">We re-ran the base models for v1.1 (three trials per
task instead of five), so their scores differ slightly from the v1.0
post. For example, Qwen3.6-27B moved from 39.6% to 42.0%.<br />
<br />
</span></span> TMax-9B now beats every prior terminal agent up to 30B
parameters in our comparison, including CalibForge-30B-A3B (32.6%), as
well as Claude Haiku 4.5 and GPT-5-mini. The 27B gain is small, since
Qwen3.6-27B is already strong (it even dips slightly on TB Lite), but
TMax-27B still edges past the 1T-parameter Kimi-K2.5 (43.2%).</p>
<p>The v1.0 generalization results also hold for the new TMax-9B. It
improves SWE-bench Verified from 46.2 to 55.3 and AIME’24/25 (solved
with the terminal) from 72.2 to 93.3, and it gains 8 to 14 points on TB
Lite under every other harness we tried (OpenHands, mini-SWE-agent,
Terminus-2).</p>
<h2 id="models-can-generate-their-own-training-data">3. Models can
generate their own training data</h2>
<p>v1.0 used Gemini-3-Pro to write every environment, and v1.1 adds
GPT-5.6 Sol for auditing and repair. That raises a fair question: does
the recipe only work because these teachers are much stronger than the
9B policy?</p>
<p>To test this, we reran the entire pipeline (generation, auditing, and
repair) with <strong>Qwen3.5-9B</strong>, the same model we then train.
This produced 3.1k environments.</p>
<div class="tmax-chart" data-chart="selfgen">
<p><strong>Qwen3.5-9B trained on data it generated itself.</strong> RL
on the 3.1k self-generated environments, compared with the median of the
12 prior datasets and with TMax-v1.1-13k. Mean ± standard error over
three trials per task.</p>
</div>
<p>The self-generated data improves Qwen3.5-9B by 12.6 points on TB Lite
(49.4 to 62.0) and 6.8 points on TB 2.1 (20.5 to 27.3). That beats more
than half of the prior datasets, despite being smaller than most of
them, and lands within a few points of TMax-v1.1-13k. <strong>The
pipeline does not need a strong teacher: a model of similar strength to
the policy is enough.</strong></p>
<p>Why would this work? We think it comes down to an information
asymmetry: for many tasks, building the environment is easier than
solving it. Writing a sudoku puzzle together with a checker is easy;
solving it through a terminal is harder. The generator only has to do
the easy side.</p>
<p>Self-generation does cost more in yield. In our first self-generation
run, only about 13–17% of the 9B model’s environments built and passed
their initial-state check, so it took roughly six times as many task
templates per kept environment as with Gemini.</p>
<div class="draft-note">
<p>The yield numbers come from the 1k self-generation pilot; confirm or
update them for the 3.1k run, or cut the paragraph.</p>
</div>
<h2 id="stable-rl-it-was-the-infrastructure">4. Stable RL: it was the
infrastructure</h2>
<p>In v1.0, long training runs were often unstable, and some collapsed
after a few hundred steps. The standard fixes, an FP32 LM head to reduce
the mismatch between trainer and inference engine and larger groups,
helped but did not remove the instability. The v1.0 paper also named a
suspect: to save cost, our Podman sandboxes ran on the same nodes as the
inference engines and competed with them for CPU and memory.</p>
<p>That suspect turned out to be the main cause. Once we reduced
infrastructure noise, training ran smoothly for 1,000 steps.</p>
<h3 id="why-sandbox-noise-breaks-rl">Why sandbox noise breaks RL</h3>
<p>Every rollout is a long agent episode, around 25 commands, inside its
own sandbox, and every layer underneath can fail. An image fails to
pull, a node is preempted, a command hangs because the node is
overloaded, or the rollout spends its time budget waiting for a free
sandbox. In v1.0, all of these looked exactly like the model failing:
reward 0.</p>
<p>That is worse than random noise. Group-based RL scores each rollout
against its siblings, so a fake zero actively pushes the policy away
from whatever that rollout was doing, even if it was on track.</p>
<div class="tmax-chart" data-chart="fakezero">
<p><strong>A fake zero is a real gradient.</strong> One group of eight
rollouts for the same task, where two sandboxes died partway through.
Scored as 0, they get pushed down exactly like genuine failures. Masked,
they drop out of the group and the six valid rollouts set the
baseline.</p>
</div>
<p>Failures are also correlated. When a registry or a node goes down, it
takes many rollouts with it at once, so the update is not just noisy but
large and wrong. In one incident during development, an image registry
outage turned over 4,000 rollouts into zeros. The measured training
solve rate fell from 0.70 to 0.14 even though the model had not changed,
and the policy kept training on that reward for 24 steps.</p>
<h3 id="a-dedicated-sandbox-fleet">A dedicated sandbox fleet</h3>
<p>We fixed this on two fronts: give sandboxes their own machines, and
make the trainer tell “the model failed” apart from “the infrastructure
failed”.</p>
<figure>
<img src="/draft/assets/img/tmax-v11/sandbox-fleet.svg"
alt="The v1.1 training setup. Each rollout leases an isolated 2-CPU Apptainer sandbox from a small controller, which grows and shrinks a pool of CPU worker nodes through Slurm. The agent’s commands go straight to the worker. Hidden tests run inside the sandbox at the end and produce the reward. A rollout that fails for infrastructure reasons is flagged and masked out of its group instead of being scored as 0." />
<figcaption aria-hidden="true">The v1.1 training setup. Each rollout
leases an isolated 2-CPU Apptainer sandbox from a small controller,
which grows and shrinks a pool of CPU worker nodes through Slurm. The
agent’s commands go straight to the worker. Hidden tests run inside the
sandbox at the end and produce the reward. A rollout that fails for
infrastructure reasons is flagged and masked out of its group instead of
being scored as 0.</figcaption>
</figure>
<ul>
<li><strong>Sandboxes get their own machines.</strong> Sandboxes run on
CPU nodes that the controller requests from Slurm on demand and releases
when idle, so they no longer compete with inference for CPU, memory, and
disk. Each rollout holds one 2-CPU sandbox lease. Leases expire unless
renewed, and lost or orphaned sandboxes are cleaned up, so a multi-day
run does not slowly leak capacity.</li>
<li><strong>Every task has a prebuilt image.</strong> We build and check
one image per task before training starts, so nothing is pulled from a
registry mid-run. A missing image is an error, never a silent
fallback.</li>
<li><strong>Infrastructure failures are masked, not scored.</strong>
Lost sandboxes, preempted workers, and setup timeouts raise typed
errors. The trainer drops those rollouts and computes advantages from
the valid siblings only. Failures the policy causes, like filling the
disk or exhausting memory, still score 0. If more than half of recent
rollouts are masked, the run stops rather than train on a broken
fleet.</li>
<li><strong>The clock is fair.</strong> A rollout’s time budget starts
once it holds a sandbox, not while it waits in the queue. A command that
times out comes back to the agent as an observation instead of ending
the episode.</li>
<li><strong>Same task, same behavior, wherever it lands.</strong>
Sandboxes start without the host’s environment variables, run without
root, have capped memory, disk, and output, and see thread counts that
match their two CPUs. That last one mattered more than we expected: Go
programs sized their thread pools to every CPU on the host and crashed
under the sandbox’s memory cap. In a replay of 10 Go tasks, 1 passed
with all host CPUs visible and all 10 passed with the thread count
pinned.</li>
</ul>
<div class="tmax-chart" data-chart="stability">
<p><strong>A 1,000-step run on the sandbox fleet.</strong> Qwen3.5-9B
trained with the v1.0 recipe on the v1.0 data (DPPO, FP32 LM head, 8
prompts × 32 rollouts per step). Switch metrics to see that entropy,
gradient norm, and the logprob gap between vLLM and the learner stay
flat while the solve rate keeps rising. One seed, resumed twice after
crashes.</p>
</div>
<p>With essentially the same recipe and data as v1.0, training now runs
to 1,000 steps without collapsing. The same fleet also runs our TB Lite
and TB 2.1 evaluations, at a fraction of the cost of a hosted sandbox
service.</p>
<div class="draft-note">
<p>Caveats on this section, from the codebase investigation:</p>
<ul>
<li>We could not confirm that the v1.0 main runs collapsed at 300 steps,
so the post no longer says so. The repo has no curves from before the
fleet. The only v1.0 curves (the paper’s group-size and GRPO-vs-DPPO
figures) show group-size-8 runs collapsing from about step 230, while
the main config (DPPO, group size 32) stays stable through step 350,
where the plot ends. The v1.0 main runs trained for 500 steps with
best-checkpoint selection (27B for 300). The paper draft still says
“collapses at 300 steps and smooth training to 1000 steps”; align the
paper and the post. If the training logs show the main runs collapsing,
we can restore the sharper claim.</li>
<li>The 1,000-step runs predate loss masking: at that commit, flagged
rollouts crashed the run instead of being scored 0, and masking landed
about a week later. So the 1,000-step result credits the fleet and
fail-fast behavior; masking is extra hardening. The text above describes
the final v1.1 system.</li>
<li>The registry outage happened on an interim VM-based sandbox setup
during development, not on the v1.0 setup. The <code>GOMAXPROCS</code>
replay is from the October hardening, after the 1,000-step runs.</li>
<li>The cluster changed at the same time as the sandboxes, so the
before/after is not a clean ablation. The June launcher already had the
FP32 LM head, DPPO, and 8×32 groups; confirm it matches the runs that
collapsed.</li>
<li>The current main launcher runs Apptainer locally on the GPU nodes
instead of on the CPU fleet, with the same hardening. Confirm which
setup trained the released models.</li>
<li>The fleet controller is a separate repository and is not listed in
the release box. If it ships, add a tag under “Code and paper”.</li>
<li>The cost claim relies on the paper’s estimate of about $3,150 per
training run on Daytona; we could add it as a footnote.</li>
</ul>
</div>
<h2 id="smaller-changes">5. Smaller changes</h2>
<ul>
<li><strong>New evaluation setup.</strong> TB Lite and TB 2.1 now run on
our own sandbox fleet over a fixed task subset (96 of 100 TB Lite tasks,
88 of 89 TB 2.1 tasks). We exclude, for every model, the few tasks that
rootless Apptainer cannot run reliably. These scores are not comparable
to the TB Lite and TB 2.1 tables in the v1.0 post: the infrastructure
change alone moves the base Qwen3.5-9B from 41.9 to 49.4 on TB Lite.
Headline TB 2.0 numbers still use Daytona on all tasks.</li>
<li><strong>Reward hacking.</strong> A light manual check of TMax-9B’s
TB 2.0 runs found two clear hacking attempts, both scored zero. In one,
the model notes that reading the cipher’s internal key is “cheating”,
then submits it anyway.</li>
<li><strong>SFT.</strong> Our v1.0 finding still holds: SFT warm-starts
help Qwen3-8B but hurt Qwen3.5, so we go straight to RL for Qwen3.5 and
3.6.</li>
</ul>
<h2 id="citation">Citation</h2>
<div class="citation">
<p>Please cite this work as:</p>
<pre><code>Ivison, Hamish and Yin, Junjie Oscar and Shao, Rulin and Xiao, Teng and
Lambert, Nathan and Hajishirzi, Hannaneh, &quot;Tmax: A simple recipe for terminal
agents&quot;, arXiv preprint arXiv:2606.23321, 2026.</code></pre>
<p>Or use the BibTeX citation:</p>
<pre><code>@misc{ivison2026tmaxsimplerecipeterminal,
      title={Tmax: A simple recipe for terminal agents},
      author={Hamish Ivison and Junjie Oscar Yin and Rulin Shao and Teng Xiao and Nathan Lambert and Hannaneh Hajishirzi},
      year={2026},
      eprint={2606.23321},
      archivePrefix={arXiv},
      primaryClass={cs.CL},
      url={https://arxiv.org/abs/2606.23321},
}</code></pre>
</div>
<div class="draft-note">
<p>Update the citation if v1.1 ships as a new arXiv version or a new
paper.</p>
</div>]]></content><author><name>WAI</name></author><summary type="html"><![CDATA[TMax v1.1 audits and repairs every training environment, shows that a 9B model can generate its own RL data, and stabilizes RL training with a dedicated sandbox fleet. TMax-9B now reaches 33.0% on Terminal-Bench 2.0.]]></summary></entry><entry><title type="html">Suffix Cache Reuse Explained</title><link href="https://wai-org.com/draft/blog/clm/" rel="alternate" type="text/html" title="Suffix Cache Reuse Explained" /><published>2026-09-28T00:00:00-07:00</published><updated>2026-09-28T00:00:00-07:00</updated><id>https://wai-org.com/draft/blog/context-language-models</id><content type="html" xml:base="https://wai-org.com/draft/blog/clm/"><![CDATA[<div class="post-hero">
<h1 class="title">
Suffix Cache Reuse Explained
</h1>
<div class="post-subtitle">
<p>Deep Dive in Efficient Serving for <span class="clm-blue">Context
Language Models</span></p>
</div>
<div class="byline">
<p>Rulin Shao &amp; Oscar Yin in collaboration with others in CLM
team</p>
</div>
<div class="post-date">
<p>September 28, 2026</p>
</div>
</div>
<div class="scr-tagline">
<p>“How to stop KV cache from crying<br />
when they’re no longer in an append-only relationship with the
context” 😉</p>
</div>
<div class="scr-teaser">
<p><strong>Suffix Cache Reuse.</strong> After an edit replaces
<em>B</em> with a shorter <em>B′</em>, only <em>B′</em> is prefilled.
The cache of <em>A</em> is reused as a prefix, and the cache of the
unchanged <em>C</em> moves to its new positions.</p>
</div>
<div class="draft-note">
<p>Before publishing: confirm author formatting (the TMax post bolds
co-first authors; the paper marks none), the publication date, and the
missing links in the resources line (arXiv, tweet). Draft notes like
this one only show up in local builds; production builds hide them.</p>
</div>
<p><strong>Resources:</strong> <a
href="https://arxiv.org/abs/2609.37725">📄 Paper</a> · <a
href="https://github.com/facebookresearch/context-language-models">👨‍💻
GitHub</a> · <a
href="https://x.com/RulinShao/status/2105282444270448647">🐦
Tweet</a></p>
<p>We recently introduced Context Language Models (CLMs),<span
class="sidenote-wrapper"><label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span
class="sidenote">Rulin Shao, Shannon Zejiang Shen, Junjie Oscar Yin,
Yuetai Li, Minheng Wang, Hamish Ivison, Radha Poovendran, Nathan
Lambert, Teng Xiao, Mike Lewis, Wen-tau Yih, Luke Zettlemoyer, and Pang
Wei Koh. “<a href="https://arxiv.org/abs/2609.37725">Context Language
Models</a>.” arXiv preprint arXiv:2609.37725, 2026.<br />
<br />
</span></span><span class="marginnote"><img
src="/draft/assets/img/clm/clm-paper-cover.jpg" class="clm-cover"
alt="The Context Language Models paper at a glance" /></span> which
treat context as a file and can perform arbitrary manipulations on it.
We showed that CLMs outperform state-of-the-art, human-designed
context-management harnesses at lower cost. In this blog, we dive deeper
into the efficiency side of CLMs: what metric do we use to capture the
realistic serving cost while being cache aware, and how could we further
improve the cache hit rate by designing serving systems for agents?</p>
<h2 id="prefix-reuse-flops">Prefix-Reuse FLOPs</h2>
<p><strong>Background: existing cache reuse often assumes append-only
context.</strong> Serving engines such as <a
href="https://arxiv.org/abs/2312.07104">SGLang</a> and <a
href="https://arxiv.org/abs/2309.06180">vLLM</a> cache KV states, for
example in a <a href="https://en.wikipedia.org/wiki/Radix_tree">radix
tree</a>, and reuse the longest matching prefix of a new request. Tokens
after the first mismatch must be re-prefilled. This works naturally for
append-only histories, but after an in-the-middle edit, even unchanged
suffix tokens are recomputed.</p>
<p>Below is a simplified example: the context <span class="nowrap"><span
class="ctx-g"><em>A</em></span> <span class="ctx-g"><em>B</em></span>
<span class="ctx-g"><em>C</em></span></span> is edited into <span
class="nowrap"><span class="ctx-o"><em>A</em></span> <span
class="ctx-e"><em>B′</em></span> <span
class="ctx-e"><em>C</em></span></span>, with the colors matching the
token strip. The prefix <span class="ctx-o"><em>A</em></span> still
matches, so its cache is reused; the match breaks at <span
class="ctx-e"><em>B′</em></span>, so <span
class="ctx-e"><em>B′</em></span> and the unchanged <span
class="ctx-e"><em>C</em></span> after it are prefilled together.</p>
<div class="tok-viz">
<div class="tok-row" label="previous prompt, already in the cache">
<p><span class="tok-seg prev"><span class="tok">this</span> <span
class="tok">is</span> <span class="tok">sentence</span> <span
class="tok">A</span> <span
class="tok-seg-label"><em>A</em></span></span> <span
class="tok-seg prev"><span class="tok">this</span> <span
class="tok">is</span> <span class="tok">sentence</span> <span
class="tok">B</span> <span
class="tok-seg-label"><em>B</em></span></span> <span
class="tok-seg prev"><span class="tok">this</span> <span
class="tok">is</span> <span class="tok">sentence</span> <span
class="tok">C</span> <span
class="tok-seg-label"><em>C</em></span></span></p>
</div>
<div class="tok-row" label="after rewriting B to B′">
<p><span class="tok-seg"><span class="tok">this</span> <span
class="tok">is</span> <span class="tok">sentence</span> <span
class="tok">A</span> <span class="tok-seg-label"><em>A</em> · hit prefix
cache</span></span> <span class="tok-seg prefill first-change"><span
class="tok">compacted</span> <span class="tok">B</span> <span
class="tok-seg-label"><em>B′</em> · prefilled</span></span> <span
class="tok-seg prefill"><span class="tok">this</span> <span
class="tok">is</span> <span class="tok">sentence</span> <span
class="tok">C</span> <span class="tok-seg-label"><em>C</em> ·
re-prefilled</span></span></p>
</div>
</div>
<p>When measuring CLM efficiency, we account for <strong>prefix-cache
reuse</strong>, i.e., which tokens can reuse cached KV states. We
capture this with a theoretical inference-cost metric we call
<strong>Prefix-Reuse FLOPs</strong>:</p>
<p><span class="math display">
\mathrm{FLOPs}_{\text{Prefix Reuse}}
= \underbrace{\mathrm{FLOPs}_{\text{prefill}}\big(\text{unmatched
suffix}\big)}_{\text{from the first prefix mismatch onward}}
+ \underbrace{\mathrm{FLOPs}_{\text{decode}}\big(\text{generated
tokens}\big)}_{\text{new output tokens}}
</span></p>
<p>By default, we use this metric to measure CLM efficiency under
standard serving. <span class="clm-blue">Thus, when we say CLMs are
cheaper than the baselines, we’ve accounted for the lower cache hit rate
caused by context edits.</span><span
class="sidenote-wrapper"><label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span
class="sidenote">For example, in these performance-efficiency Pareto
plots, we used prefix-reuse FLOPs with standard serving. <img
src="/draft/assets/img/clm/pareto_q36_main.png" class="sn-fig"
alt="Performance-efficiency Pareto plots from the paper: accuracy against prefix-reuse PFLOPs per question on BrowseComp-Plus, TerminalBench 2.1 and TBLite." /><br />
<br />
</span></span> The visualization below shows how an edit affects the
Prefix-Reuse FLOPs of a single turn.</p>
<div class="flops-sim" data-mode="standard">
<p><strong>Prefix Reuse FLOPs of one Qwen3.6-27B turn under standard
serving.</strong> Move the edit or change its size; the bar splits the
Prefix Reuse FLOPs of the turn into the prefill of <span
class="ctx-e"><em>B′</em></span>, the re-prefill of the unchanged <span
class="ctx-e"><em>C</em></span>, and decoding.</p>
</div>
<p>The hit rate does drop with in-the-middle edits and incurs
re-prefilling of the unchanged C.<span
class="sidenote-wrapper"><label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span
class="sidenote">This also happens beyond CLM editing: some chat
endpoints remove the thinking tokens of earlier turns, so the tokens
after them are re-prefilled in the next turn.<br />
<br />
</span></span> In our experiments with a Qwen3.6-27B CLM on
BrowseComp-Plus, standard SGLang serves 72.9% of all prompt tokens from
its prefix cache, but only 24.2% on the turns right after a context
edit. The rest of an edited turn is prefilled again, including the large
part of the context that the edit left unchanged.</p>
<h2 id="suffix-cache-reuse">Suffix Cache Reuse</h2>
<p>Prefix cache reuse has been the tradition in serving engines because
context has always been append-only. But we ask: <strong>can we adapt
serving engines for AI’s convenience?</strong> To that end, we propose a
simple yet effective method, Suffix Cache Reuse, to make CLM serving
even more efficient on the system side.</p>
<div class="tok-viz tok-viz-fig">
<div class="tok-row" label="before the edit">
<p><span class="tok-seg prev"><span class="tok">this</span> <span
class="tok">is</span> <span class="tok">sentence</span> <span
class="tok">A</span> <span
class="tok-seg-label"><em>A</em></span></span> <span
class="tok-seg prev"><span class="tok">this</span> <span
class="tok">is</span> <span class="tok">sentence</span> <span
class="tok">B</span> <span
class="tok-seg-label"><em>B</em></span></span> <span
class="tok-seg prev"><span class="tok">this</span> <span
class="tok">is</span> <span class="tok">sentence</span> <span
class="tok">C</span> <span
class="tok-seg-label"><em>C</em></span></span></p>
</div>
<div class="tok-row" label="standard serving">
<p><span class="tok-seg"><span class="tok">this</span> <span
class="tok">is</span> <span class="tok">sentence</span> <span
class="tok">A</span> <span class="tok-seg-label"><em>A</em> · hit prefix
cache</span></span> <span class="tok-seg prefill first-change"><span
class="tok">compacted</span> <span class="tok">B</span> <span
class="tok-seg-label"><em>B′</em> · prefilled</span></span> <span
class="tok-seg prefill"><span class="tok">this</span> <span
class="tok">is</span> <span class="tok">sentence</span> <span
class="tok">C</span> <span class="tok-seg-label"><em>C</em> ·
re-prefilled</span></span></p>
</div>
<div class="tok-row" label="Suffix Cache Reuse">
<p><span class="tok-seg"><span class="tok">this</span> <span
class="tok">is</span> <span class="tok">sentence</span> <span
class="tok">A</span> <span class="tok-seg-label"><em>A</em> · hit prefix
cache</span></span> <span class="tok-seg prefill first-change"><span
class="tok">compacted</span> <span class="tok">B</span> <span
class="tok-seg-label"><em>B′</em> · prefilled</span></span> <span
class="tok-seg reused"><span class="tok">this</span> <span
class="tok">is</span> <span class="tok">sentence</span> <span
class="tok">C</span> <span class="tok-seg-label"><em>C</em> · suffix
cache reused</span></span></p>
</div>
<div class="tok-caption">
<p>Standard serving versus Suffix Cache Reuse after an edit replaces B
with B′. Standard serving hits the prefix cache for A but must prefill
B′ and re-prefill all of C; Suffix Cache Reuse also reuses the cached
states of C. The × marks the first prefix-mismatch position.</p>
</div>
</div>
<p>Say the context is <span class="nowrap"><span
class="ctx-g"><em>A</em></span> <span class="ctx-g"><em>B</em></span>
<span class="ctx-g"><em>C</em></span></span>, and an edit replaces <span
class="ctx-g"><em>B</em></span> with <span
class="ctx-e"><em>B′</em></span>. Standard serving reuses the cache for
<span class="ctx-o"><em>A</em></span> and then stops, because a prefix
cache only matches up to the first changed token: <span
class="ctx-e"><em>B′</em></span> and all of <span
class="ctx-e"><em>C</em></span> are prefilled again, even though <span
class="ctx-e"><em>C</em></span> did not change. <strong>Suffix Cache
Reuse (SCR)</strong> keeps the cached states for <span
class="ctx-r"><em>C</em></span> instead of throwing them away. When the
next prompt arrives, SCR compares it with the previous prompt of the
same session to find the spans that survived the edit, shifts their
rotary position encodings to their new positions, and splices them in
after <span class="ctx-e"><em>B′</em></span>. Only <span
class="ctx-e"><em>B′</em></span> and newly appended tokens are
prefilled. Below is an interactive visualization of SCR serving compared
with prefix-reuse-only serving.</p>
<div class="flops-sim" data-mode="scr">
<p><strong>Adding Suffix Cache Reuse on top of prefix caching.</strong>
The same turn as in the first figure; the bars compare prefix caching
with and without SCR, and the dashed green outline is the re-prefill of
<span class="ctx-e"><em>C</em></span> that SCR removes. SCR is counted
with <span class="ctx-e"><em>B′</em></span> prefilled through every
layer and <span class="ctx-r"><em>C</em></span> relocated as one
span.</p>
</div>
<p><strong>Technical details.</strong> For full-attention layers, SCR
reuses the cached keys and values of <span
class="ctx-r"><em>C</em></span> as they are, except for position: after
the edit, <span class="ctx-r"><em>C</em></span> sits earlier by the
length difference between <span class="ctx-g"><em>B</em></span> and
<span class="ctx-e"><em>B′</em></span>, so SCR re-rotates the rotary
position encodings of its cached keys by that offset. Because rotary
encodings depend only on position, this rotation is exact and far
cheaper than recomputing the entries. SCR is an approximation: <span
class="ctx-r"><em>C</em></span>’s cached states were computed under the
old context, before the edit. To bound how much approximation one edit
can introduce, SCR relocates at most six surviving spans per edit, the
longest first, and re-prefills the rest.<span
class="sidenote-wrapper"><label for="sn-3" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-3" class="margin-toggle"/><span
class="sidenote">More details, such as how the cap on relocated spans is
set and what effect it has, are in Appendix B of the paper; the main
text uses k = 6.<br />
<br />
</span></span></p>
<figure>
<img src="/draft/assets/img/clm/scr-rope-rotation.svg"
alt="How Suffix Cache Reuse relocates the cached entries of C: the new prompt is diffed against the cached one, every surviving token of C moves by Δ positions, and because rotary encodings compose by adding angles, each cached key is moved with one rotation by Δ while the value is copied, in every full-attention layer." />
<figcaption aria-hidden="true">How Suffix Cache Reuse relocates the
cached entries of C: the new prompt is diffed against the cached one,
every surviving token of C moves by Δ positions, and because rotary
encodings compose by adding angles, each cached key is moved with one
rotation by Δ while the value is copied, in every full-attention
layer.</figcaption>
</figure>
<p>Qwen3.6-27B is a hybrid model: 48 of its 64 layers use linear
attention, which keeps a fixed-size recurrent state rather than a
per-token cache, so there are no per-token entries to move. For those
layers, SCR continues from a snapshot of the recurrent state taken
before the edit. The edit itself is seen by the 16 full-attention
layers, and through their outputs it still reaches the later
linear-attention layers.</p>
<figure>
<img src="/draft/assets/img/clm/full-vs-linear-attention-reuse.png"
alt="Suffix Cache Reuse for full-attention layers (left) and linear-attention layers (right)." />
<figcaption aria-hidden="true">Suffix Cache Reuse for full-attention
layers (left) and linear-attention layers (right).</figcaption>
</figure>
<h3 id="results-on-browsecomp-plus">Results on BrowseComp-Plus</h3>
<p>We perform an end-to-end evaluation of SCR on BrowseComp-Plus with
the <a
href="https://github.com/facebookresearch/context-language-models/tree/main/clm/clm_harness">CLM
harness</a>, by simply switching the Qwen3.6-27B endpoint from standard
SGLang to our patched SGLang with SCR (Section 5.3 of the <a
href="https://arxiv.org/abs/2609.37725">paper</a>).<span
class="sidenote-wrapper"><label for="sn-4" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-4" class="margin-toggle"/><span
class="sidenote">Our implementation of Suffix Cache Reuse is available
at <a
href="https://github.com/facebookresearch/context-language-models/tree/main/suffix_cache_reuse">facebookresearch/context-language-models/suffix_cache_reuse</a>.<br />
<br />
</span></span></p>
<figure>
<img src="/draft/assets/img/clm/scr-bcp830-results.png"
alt="Suffix Cache Reuse on BrowseComp-Plus with a Qwen3.6-27B CLM. All 830 questions were served both ways, with three seeds each. Left: task accuracy. Middle: compute per question, split into prefill and decode. Right: where the prompt tokens came from, over all turns and over the turns right after a context edit." />
<figcaption aria-hidden="true">Suffix Cache Reuse on BrowseComp-Plus
with a Qwen3.6-27B CLM. All 830 questions were served both ways, with
three seeds each. Left: task accuracy. Middle: compute per question,
split into prefill and decode. Right: where the prompt tokens came from,
over all turns and over the turns right after a context
edit.</figcaption>
</figure>
<p>Averaged over three seeds, accuracy is on par (59.0% with standard
serving, 60.6% with SCR), while compute drops from 11.00 to 7.21 PFLOPs
per question. On the turns right after an edit, SCR serves an extra
28.1% of the prompt from relocated cache that standard serving would
have recomputed. The savings show up in wall-clock time too: each
question finishes 10% faster (874 s to 786 s). CLMs were already cheaper
than the baselines under standard serving, through better context
management alone; SCR brings their serving cost down to 65% of that.</p>
<p><strong>Bonus: reasoning-token stripping is a context edit
too.</strong> Other cache misses arise from reasoning-token stripping in
some chat-template serving. For models such as Qwen3.6, earlier
reasoning blocks are removed from subsequent prompts, forcing the
unchanged suffix to be prefilled again. SCR reuses this suffix cache as
well. In fact, most of SCR’s extra reuse comes from stripped reasoning
rather than CLM edits. The figure below shows a decomposition of the
tokens hit by Suffix Cache Reuse.</p>
<figure>
<img src="/draft/assets/img/clm/scr-strip-savings.png"
alt="Where Suffix Cache Reuse finds reusable cache on BrowseComp-Plus, over all turns and over the turns right after an edit. Qwen3.6-27B, 830 questions, three seeds." />
<figcaption aria-hidden="true">Where Suffix Cache Reuse finds reusable
cache on BrowseComp-Plus, over all turns and over the turns right after
an edit. Qwen3.6-27B, 830 questions, three seeds.</figcaption>
</figure>
<p><strong>Improvement space for cache misses.</strong> We also looked
at the unchanged tokens that are still prefilled with SCR. SCR removes
most re-prefilling of unchanged suffixes after an edit. Much of the
remaining overhead comes from unchanged prefixes that standard SGLang
fails to reuse efficiently for hybrid models, because linear-attention
states are cached only at request boundaries. This leaves substantial
room for improvement: finer-grained recurrent-state checkpoints could
increase cache hit rates for both standard prefix caching and SCR. More
broadly, serving models with editable context opens many new systems
research directions.</p>
<figure>
<img src="/draft/assets/img/clm/scr-prefill-split.png"
alt="Remaining re-prefill under standard serving and under Suffix Cache Reuse on BrowseComp-Plus, split by why each token was prefilled. Same runs as above." />
<figcaption aria-hidden="true">Remaining re-prefill under standard
serving and under Suffix Cache Reuse on BrowseComp-Plus, split by why
each token was prefilled. Same runs as above.</figcaption>
</figure>
<p><strong>Suffix Cache Reuse in vLLM.</strong> We also ported SCR to
vLLM as a <a
href="https://github.com/facebookresearch/context-language-models/tree/main/suffix_cache_reuse/vllm">plugin</a>,
without modifying vLLM itself, and repeated the BrowseComp-Plus
comparison on both engines: Qwen3.6-27B, all 830 questions, three seeds,
with standard serving and SCR running side by side on the same node. On
both engines, SCR lowers compute and wall-clock time per question with
accuracy on par.</p>
<figure>
<img src="/draft/assets/img/clm/scr-engines-bcp.png"
alt="Suffix Cache Reuse on SGLang and vLLM, BrowseComp-Plus with Qwen3.6-27B, 830 questions, three seeds. Arrows mark the better direction." />
<figcaption aria-hidden="true">Suffix Cache Reuse on SGLang and vLLM,
BrowseComp-Plus with Qwen3.6-27B, 830 questions, three seeds. Arrows
mark the better direction.</figcaption>
</figure>
<p>Time per question covers the whole trajectory, from the first request
to the final answer, and is compared within each engine, since the two
engines run with different server settings.</p>
<p>SCR also speeds up serving itself. On a multi-turn editing workload,
where each conversation starts from a document of about 11K tokens and
60% of later turns delete or rewrite an earlier section, SCR on vLLM
raises request throughput by 1.4 to 1.6 times and cuts the median time
to first token by 32% to 54% (one H200 per server).</p>
<figure>
<img src="/draft/assets/img/clm/scr-vllm-serving.png"
alt="Serving speed of vLLM with and without Suffix Cache Reuse on the multi-turn editing workload, for Qwen3-8B and Qwen3.6-27B at 8 and 16 concurrent conversations. Arrows mark the better direction." />
<figcaption aria-hidden="true">Serving speed of vLLM with and without
Suffix Cache Reuse on the multi-turn editing workload, for Qwen3-8B and
Qwen3.6-27B at 8 and 16 concurrent conversations. Arrows mark the better
direction.</figcaption>
</figure>
<h2 id="related-work">Related work</h2>
<p>With standard prefix caching, changing an early part of a prompt
forces the serving system to recompute the KV states of everything that
follows, even when the later text is unchanged. Prior work relaxes this
requirement in different settings. <a
href="https://arxiv.org/abs/2311.04934">Prompt Cache</a> precomputes
attention states for predefined prompt modules, allowing a module to be
reused in prompts that do not share the same preceding text. In
retrieval-augmented generation, the same document may appear after
different documents or instructions. <a
href="https://arxiv.org/abs/2405.16444">CacheBlend</a> and <a
href="https://arxiv.org/abs/2410.15332">EPIC</a> reuse cached document
chunks in these new contexts, recomputing selected tokens to account for
the changed surroundings.</p>
<p><a href="https://arxiv.org/abs/2407.03157">PIE</a> studies cache
reuse when a user modifies previously processed code and requests a new
completion. It retains cached states for unchanged text after an edit
and corrects their rotary positions, avoiding suffix recomputation. <a
href="https://arxiv.org/abs/2604.09852">Memento</a> evicts each
completed reasoning block from the KV cache but keeps the cached states
of its summary, which were computed while the block was still in
context, and finds that these states retain useful information from the
evicted block. Concurrently, <a
href="https://arxiv.org/abs/2609.35750">KV-streams</a>, released in the
last few days, keeps the KV cache across agentic compaction during
reinforcement learning instead of flushing it, and reports a 2× speed-up
on SWE tasks with any compaction method. Suffix Cache Reuse applies the
same reuse principle to an agent’s live context: when the agent replaces
a span, the unchanged suffix retains its cached states rather than being
prefilled again. We integrate this mechanism into SGLang for
agent-driven context editing and further extend it to hybrid
architectures that combine full-attention layers with linear-attention
layers.</p>
<p>We believe cache space can enable more context management
opportunities than pure token space. We are excited to see more work
along this line.</p>
<h2 id="limitations">Limitations</h2>
<p>Suffix Cache Reuse makes more approximations than prefix cache reuse.
A reused prefix is exact, since its states depend only on tokens that
have not changed. A relocated suffix is not. Its cached states were
computed while the old span was still in context, so they may still
reflect text the edit removed and miss text it added. The approximation
is larger for hybrid models, whose linear-attention layers resume from a
recurrent state taken before the edit and see the new span only through
the full-attention layers. We cap the number of relocated spans per edit
to limit this, and on BrowseComp-Plus SCR matches standard serving in
accuracy. We have not yet characterized where these approximations break
down. More work is needed to profile their failure cases and to make
reuse adaptive, deciding for each edit what to reuse and what to
recompute.</p>
<h2 id="references">References</h2>
<div class="references">
<ol type="1">
<li>Rulin Shao, Shannon Zejiang Shen, Junjie Oscar Yin, Yuetai Li,
Minheng Wang, Hamish Ivison, Radha Poovendran, Nathan Lambert, Teng
Xiao, Mike Lewis, Wen-tau Yih, Luke Zettlemoyer, and Pang Wei Koh. <a
href="https://arxiv.org/abs/2609.37725">Context Language Models</a>.
arXiv:2609.37725, 2026.</li>
<li>Lianmin Zheng et al. <a
href="https://arxiv.org/abs/2312.07104">SGLang: Efficient Execution of
Structured Language Model Programs</a>. arXiv:2312.07104, 2023.</li>
<li>Woosuk Kwon et al. <a
href="https://arxiv.org/abs/2309.06180">Efficient Memory Management for
Large Language Model Serving with PagedAttention</a> (vLLM). SOSP
2023.</li>
<li><a href="https://en.wikipedia.org/wiki/Radix_tree">Radix tree</a>.
Wikipedia.</li>
<li>In Gim et al. <a href="https://arxiv.org/abs/2311.04934">Prompt
Cache: Modular Attention Reuse for Low-Latency Inference</a>.
arXiv:2311.04934, 2023.</li>
<li>Jiayi Yao et al. <a
href="https://arxiv.org/abs/2405.16444">CacheBlend: Fast Large Language
Model Serving for RAG with Cached Knowledge Fusion</a>.
arXiv:2405.16444, 2024.</li>
<li>Junhao Hu et al. <a href="https://arxiv.org/abs/2410.15332">EPIC:
Efficient Position-Independent Caching for Serving Large Language
Models</a>. arXiv:2410.15332, 2024.</li>
<li>Zhenyu He et al. <a href="https://arxiv.org/abs/2407.03157">Let the
Code LLM Edit Itself When You Edit the Code</a> (PIE). arXiv:2407.03157,
2024.</li>
<li>Vasilis Kontonis et al. <a
href="https://arxiv.org/abs/2604.09852">MEMENTO: Teaching LLMs to Manage
Their Own Context</a>. arXiv:2604.09852, 2026.</li>
<li>Emiliano Penaloza et al. <a
href="https://arxiv.org/abs/2609.35750">KV-streams for Efficient
Compaction in Agentic Reinforcement Learning</a>. arXiv:2609.35750,
2026.</li>
</ol>
</div>
<h2 id="citation">Citation</h2>
<div class="citation">
<p>Please cite this work as:</p>
<pre><code>@article{shao2026context,
  title   = {Context Language Models},
  author  = {Shao, Rulin and Shen, Shannon Zejiang and Yin, Junjie Oscar and
             Li, Yuetai and Wang, Minheng and Ivison, Hamish and
             Poovendran, Radha and Lambert, Nathan and Xiao, Teng and
             Lewis, Mike and Yih, Wen-tau and Zettlemoyer, Luke and
             Koh, Pang Wei},
  journal = {arXiv preprint arXiv:2609.37725},
  year    = {2026}
}</code></pre>
</div>
<div class="draft-note">
<p>Add the tweet link to the resources line once it is posted.</p>
</div>]]></content><author><name>WAI</name></author><summary type="html"><![CDATA[Context Language Models edit their own context, which breaks prefix caching after every edit. Suffix Cache Reuse reuses the cache of the text that survives an edit, cutting serving compute by 35% at the same accuracy.]]></summary></entry><entry><title type="html">TMax: A Simple Recipe for Terminal Agents</title><link href="https://wai-org.com/draft/blog/tmax/" rel="alternate" type="text/html" title="TMax: A Simple Recipe for Terminal Agents" /><published>2026-06-16T00:00:00-07:00</published><updated>2026-06-16T00:00:00-07:00</updated><id>https://wai-org.com/draft/blog/tmax-a-simple-recipe-for-terminal-agents</id><content type="html" xml:base="https://wai-org.com/draft/blog/tmax/"><![CDATA[<div class="post-hero">
<h1 class="title">
<img class="title-logo" src="/assets/img/tmax/logo.png" alt="TMax mascot logo">
TMax: A Simple Recipe for Terminal Agents
</h1>
<div class="byline">
<p><strong>Hamish Ivison</strong>*, <strong>Junjie Oscar
Yin</strong>*,<br> Rulin Shao, Teng Xiao, Nathan Lambert, Hannaneh
Hajishirzi<span
class="sidenote-wrapper"><label for="sn-0" class="margin-toggle">&#8853;</label><input type="checkbox" id="sn-0" class="margin-toggle"/><span
class="marginnote">*Co-first authors. Work done while HI and NL were at
Ai2.<br />
<br />
</span></span></p>
</div>
<div class="affiliations">
<p>Allen Institute for AI · University of Washington</p>
</div>
<div class="post-date">
<p>June 16, 2026</p>
</div>
</div>
<figure>
<img src="/assets/img/tmax/teaser.png"
alt="Terminal Bench 2.0 vs. model size. TMax dominates the Pareto frontier for models under 32B: TMax-9B is the strongest open model under 10B we compare against, and TMax-27B is competitive with open-weight models an order of magnitude larger." />
<figcaption aria-hidden="true">Terminal Bench 2.0 vs. model size. TMax
dominates the Pareto frontier for models under 32B: TMax-9B is the
strongest open model under 10B we compare against, and TMax-27B is
competitive with open-weight models an order of magnitude
larger.</figcaption>
</figure>
<div class="tldr">
<p><span class="tldr-label">TL;DR</span></p>
<p><strong>TMax</strong> is the strongest open RL recipe for terminal
agents to date, bringing open data recipes closer to the frontier. We
release two things. The first is <strong>TMax-15k</strong>, a dataset of
<strong>14,600 RL environments</strong> built from a compositional
pipeline with explicit control over difficulty and diversity. It is over
<strong>2.5× larger</strong> than the next-largest open terminal dataset
that releases full environment data. The second is a <strong>simple,
outcome-only RL recipe</strong> (GRPO plus a few stability fixes), which
we use to train a family of open models from <strong>2B to
27B</strong>.</p>
<p><a href="https://huggingface.co/allenai/tmax-9b"
class="dataset-tag">TMax-9B</a> reaches <strong>27.2%</strong> on <a
href="https://www.tbench.ai/leaderboard/terminal-bench/2.0">Terminal
Bench 2.0</a>. Under official Terminal Bench settings this is the
strongest open-weights model under 10B we are aware of: it beats 32B
terminal agents from prior work and approaches closed models like Claude
Haiku 4.5 (29.8%). Scaling the same recipe up, <a
href="https://huggingface.co/allenai/tmax-27b"
class="dataset-tag">TMax-27B</a> improves to <strong>42.7%</strong>,
approaching models 10 to 40× its size like the 1T-parameter Kimi K2.5
(43.2%).</p>
<p><strong>Resources:</strong> <a
href="https://arxiv.org/abs/2606.23321">📄 Paper</a> · <a
href="https://github.com/hamishivi/tmax">👨‍💻 GitHub</a> · <a
href="https://huggingface.co/collections/allenai/tmax">🤗 HF Models</a>
· <a href="https://huggingface.co/collections/allenai/tmax">🤗 HF
Dataset</a> · <a
href="https://x.com/waiorg/status/2069205740699725944">🐦 Tweet</a></p>
</div>
<h2 id="terminal-agents-and-why-theyre-hard-to-train">1. Terminal
agents, and why they’re hard to train</h2>
<p>Terminal-using agents have quickly become the most popular way people
put language models to work. A <em>terminal agent</em> is a language
model that interacts with a terminal environment: by issuing commands in
a shell, calling tools, reading the output, and iterating. It might
resolve a failing build, profile a slow service, recover a corrupted
archive, or wire up a multi-service stack. The model plans, runs a
command, looks at <code>stdout</code>/<code>stderr</code>, and tries
again. This is now the dominant interface for agentic coding
products,<span
class="sidenote-wrapper"><label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span
class="sidenote">For example, Anthropic’s Claude Code and other
terminal-native coding products.<br />
<br />
</span></span> and the tasks these agents are asked to solve keep
getting more complex and long-horizon.</p>
<p>Despite this popularity, there is surprisingly little <em>open</em>
academic work on training terminal agents with RL. We think this comes
down to three gaps:</p>
<ul>
<li><strong>Hard benchmarks.</strong> Evaluations like <a
href="https://www.tbench.ai/">Terminal Bench</a><span
class="sidenote-wrapper"><label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span
class="sidenote">Merrill et al., “Terminal Bench,” 2026.<br />
<br />
</span></span> capture genuinely long-horizon, multi-step terminal work,
but they are hard, and much prior work sidesteps them in favor of
simpler bug-fixing or natural-language-to-bash setups.</li>
<li><strong>A lack of data.</strong> Real-world terminal traces are
scarce or proprietary, and the synthetic datasets that do exist tend to
be small, narrow (mostly file manipulation or one dominant domain), or
never release their actual RL environments.</li>
<li><strong>A lack of a simple baseline recipe.</strong> Most
data-generation papers stop at supervised fine-tuning; the ones that try
RL often report ~1-point gains, leaving little signal for the community
to build on.</li>
</ul>
<p>TMax closes the data and recipe gaps. We provide a large,
difficulty-controlled dataset of complex terminal tasks and a simple RL
recipe that produces large, reliable improvements of over <strong>5
points</strong> on Terminal Bench, with gains that transfer across tasks
and harnesses. Our contributions are:</p>
<ol type="1">
<li><strong>TMax-15k</strong>, a dataset of 14,600 RL environments, over
2.5× larger than prior open terminal datasets and notably harder.</li>
<li><strong>Open-weights terminal agents at 2B/4B/9B/27B</strong> scale,
trained on this data. Our best 9B model reaches 27.2% on Terminal Bench
2.0, the strongest open model under 10B we are aware of under official
settings.</li>
<li><strong>A simple, reproducible RL recipe</strong>, with all
components released for training the models.</li>
<li><strong>Evidence that terminal RL generalizes</strong> across
harnesses and across tasks, suggesting the model learns real new skills
rather than overfitting a single setup.</li>
</ol>
<h2 id="generating-terminal-environments-at-scale">2. Generating
terminal environments at scale</h2>
<p>Training a terminal agent with RL needs a lot of executable
environments: each task has to ship with the files, dependencies, and an
automated check that verifies whether the agent succeeded. Existing
pipelines tend to fall short in three ways. They lean on
<strong>complex, multi-stage generation</strong> that is hard to scale;
they produce <strong>homogeneous</strong> task suites dominated by one
or two domains; and they offer <strong>little control over
difficulty</strong>, yielding tasks that are either trivially solved or
entirely out of reach.</p>
<p>We propose a simple terminal environment generation pipeline that is
scalable, diverse, and difficulty-aware. We use <a
href="https://deepmind.google/technologies/gemini/">Gemini-3-Pro</a> as
the generator,<span
class="sidenote-wrapper"><label for="sn-3" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-3" class="margin-toggle"/><span
class="sidenote">We chose Gemini-3-Pro for its strong performance on
Terminal Bench.<br />
<br />
</span></span> and the core idea is simple: <strong>compose each task by
hierarchically sampling from a set of structured axes.</strong></p>
<figure>
<img src="/assets/img/tmax/data-pipeline.png"
alt="The TMax data pipeline. Each task is composed by hierarchically sampling across 9 structured axes, after which the generator instantiates a Dockerfile, a unit-test verifier, source files, and task instructions. A single build step, with no teacher-based validation, keeps generation cheap to scale." />
<figcaption aria-hidden="true">The TMax data pipeline. Each task is
composed by hierarchically sampling across 9 structured axes, after
which the generator instantiates a Dockerfile, a unit-test verifier,
source files, and task instructions. A single build step, with no
teacher-based validation, keeps generation cheap to scale.</figcaption>
</figure>
<h3 id="generation-pipeline">2.1 Generation pipeline</h3>
<p>A task is composed by drawing one value from each of <strong>nine
structured axes</strong>. We follow <a
href="https://huggingface.co/nvidia">Nemotron-Terminal</a> to seed the
first two axes<span
class="sidenote-wrapper"><label for="sn-4" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-4" class="margin-toggle"/><span
class="sidenote">Pi et al., “Nemotron-Terminal,” 2026. We seed our
domain and skill axes from their taxonomy.<br />
<br />
</span></span> (domain and skills), and introduce six orthogonal axes
targeting diversity and difficulty as shown in the figure above.</p>
<p>Because each task is sampled across these axes, the sampler yields
combinatorially many distinct task signatures, while still letting us
calibrate each axis on its own. The sampling is hierarchical because a
few axes, such as personas and skills, are conditioned on the sampled
domain to keep combination compatible. We <em>intentionally make three
design choices</em> for the synthetic terminal data generation.</p>
<p><strong>Scalability via soft filtering.</strong> Prior pipelines
validate every task by having a strong model attempt it for multiple
rollouts, which is expensive. We skip that step entirely. Our RL
training already does <em>soft</em> filtering for free: it drops any
task where all rollouts get the same reward, since those contribute no
gradient. In practice the all-zero rate on our data is low (fewer than 8
samples filtered per batch). All we need to guarantee at inference time
is that the environment successfully <em>builds</em>, which we do with
one Docker image per task (sharing a pre-built base image per domain).
Turning two build steps into one allows synthetic environments to scale
cheaply.</p>
<p><strong>Diversity via hierarchical sampling.</strong> The
hierarchical sampler is itself the main diversity mechanism, and we add
two more. First, we condition generation on domain-specific
<strong>personas</strong><span
class="sidenote-wrapper"><label for="sn-5" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-5" class="margin-toggle"/><span
class="sidenote">Persona-conditioned synthesis follows a line of prior
data work, e.g. Ge et al., “Scaling Synthetic Data Creation with
Personas,” 2025.<br />
<br />
</span></span> (6–18 per domain), so that a “red-team operator crafting
an evasion payload” naturally shapes a plausible security task. Second,
we incorporate <strong>multi-modal fixtures</strong> as part of task
files: PNG, audio file, video, stripped binary, vendored package, etc.
The policy itself stays a text-only model. It inspects and interacts
these artifacts through ordinary terminal tooling (OCR, audio
transcription, <code>ffmpeg</code>).</p>
<p><strong>Difficulty via explicit calibration.</strong> To avoid the
usual bimodal “trivial or impossible” task split, we calibrate
difficulty along two new complexity axes (<em>task complexity</em>, from
a handful of commands to intricate 30–60-command workflows, and
<em>command complexity</em>, from bash-only to bash + code + system
services) and sample uniformly across buckets. We also go beyond exact
string-equality checks with <strong>graded verifiers</strong>: metric
thresholds (e.g. accuracy ≥ 0.95), adversarial corpora (accept clean,
reject malicious), fuzz equivalence against an oracle, or multi-protocol
service checks. Thresholds give a continuous signal for terminal agents
to hillclimb, while also increasing turn length.</p>
<p>We release the full set of RL environments as <a
href="https://huggingface.co/datasets/allenai/tmax-15k"
class="dataset-tag">TMax-15K</a>.</p>
<h3 id="comparing-terminal-datasets">2.2 Comparing terminal
datasets</h3>
<p>We annotated our data and six prior datasets with <a
href="https://deepmind.google/technologies/gemini/">Gemini-3-Pro</a> and
measured difficulty with <a
href="https://deepmind.google/technologies/gemini/">Gemini-3-Flash</a>
(250-task subsample per dataset, 8 rollouts each).</p>
<table style="width:100%;">
<colgroup>
<col style="width: 16%" />
<col style="width: 16%" />
<col style="width: 16%" />
<col style="width: 16%" />
<col style="width: 16%" />
<col style="width: 16%" />
</colgroup>
<thead>
<tr class="header">
<th>Dataset</th>
<th>Size</th>
<th>Pass@1</th>
<th>Pass@8</th>
<th>Domain balance</th>
<th>Skill balance</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><strong>TMax-15k (ours)</strong></td>
<td><strong>15k</strong></td>
<td><strong>42%</strong></td>
<td><strong>53%</strong></td>
<td><strong>0.998</strong></td>
<td><strong>0.732</strong></td>
</tr>
<tr class="even">
<td>Endless Terminals</td>
<td>2.4k</td>
<td>92%</td>
<td>95%</td>
<td>0.481</td>
<td>0.284</td>
</tr>
<tr class="odd">
<td>Open Thoughts Agents</td>
<td>0.7k</td>
<td>51%</td>
<td>60%</td>
<td>0.292</td>
<td>0.153</td>
</tr>
<tr class="even">
<td>TermiGen</td>
<td>3k</td>
<td>57%</td>
<td>66%</td>
<td>0.646</td>
<td>0.477</td>
</tr>
<tr class="odd">
<td>TerminalTraj</td>
<td>5.5k</td>
<td>54%</td>
<td>65%</td>
<td>0.363</td>
<td>0.374</td>
</tr>
<tr class="even">
<td>CLI-Gym</td>
<td>1.5k</td>
<td>41%</td>
<td>55%</td>
<td>0.283</td>
<td>0.061</td>
</tr>
<tr class="odd">
<td>SWE-smith</td>
<td>59k</td>
<td>54%</td>
<td>72%</td>
<td>0.146</td>
<td>0.042</td>
</tr>
</tbody>
</table>
<p><em>Difficulty and balance statistics (Gemini-3-Flash, pass@k as mean
over tasks). Balance is a “fraction-of-uniform” diversity score in [0,
1].</em></p>
<p>Two things worth to note. First, <strong>balance</strong>. Prior
datasets pile 34–95% of their mass onto a single domain (software
engineering alone is 95% of SWE-smith), while ours spreads roughly
uniformly across all nine. That gives us the highest domain balance
(0.998) and skill-type balance (0.732) of any dataset.</p>
<figure>
<img src="/assets/img/tmax/data-composition.png"
alt="Domain distribution across datasets. Prior datasets skew heavily toward one or two domains; our compositional sampler yields balanced coverage across all nine." />
<figcaption aria-hidden="true">Domain distribution across datasets.
Prior datasets skew heavily toward one or two domains; our compositional
sampler yields balanced coverage across all nine.</figcaption>
</figure>
<p>Second, <strong>difficulty</strong>. Our data is among the hardest of
the bunch. Pass@1 is 42% (vs. 41–92% for prior datasets), and it has the
<strong>lowest pass@8</strong> (53%). The difficulty gap persists even
as we draw more rollouts, so the tasks are genuinely hard rather than
just high-variance. We also checked for contamination with a 13-gram
sliding window against the Terminal Bench tasks and found <strong>0%
overlap</strong>.</p>
<h3 id="sft-data-and-a-simple-harness">2.3 SFT data and a simple
harness</h3>
<p>A light SFT warm-up can help stability and performance before
RL,<span
class="sidenote-wrapper"><label for="sn-6" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-6" class="margin-toggle"/><span
class="sidenote">Shown e.g. in DeepSeek-R1 and Olmo 3.<br />
<br />
</span></span> so for our weaker-model experiments on the older <a
href="https://huggingface.co/Qwen">Qwen3-8B</a>, we also generate a
small SFT dataset. Reusing the same pipeline, we make 2.2k more
environments and sample 8 trajectories each from <a
href="https://huggingface.co/Qwen">Qwen3.6-27B</a>, for 16.5K
trajectories (8K of them successful). We use this SFT data only for the
Qwen3-8B runs, and release it as <a
href="https://huggingface.co/datasets/allenai/tmax-sft-16.5k"
class="dataset-tag">TMax-SFT-16.5K</a>.</p>
<p>Both data generation and RL rollouts run through a simple harness
based on <a
href="https://github.com/SWE-agent/mini-swe-agent">mini-swe-agent</a>
with a persistent shell. We found the default Terminus-2 harness more
brittle with small models, since it expects the agent to send raw
keystrokes.</p>
<h2 id="training-tmax-with-rl">3. Training TMax with RL</h2>
<p>With the data in hand, the question is whether a <em>simple</em>
recipe can turn it into a strong agent. We deliberately keep the
algorithm plain. The goal is a clean, reproducible testbed with obvious
room for future algorithmic improvement.</p>
<h3 id="training-recipe">3.1 Training recipe</h3>
<p>We train with a variant of <strong>GRPO</strong><span
class="sidenote-wrapper"><label for="sn-7" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-7" class="margin-toggle"/><span
class="sidenote">Shao et al., “DeepSeekMath,” 2024.<br />
<br />
</span></span>, outcome-only with no learned reward model, plus a
handful of changes for stability in the long-horizon agentic
setting:</p>
<ul>
<li><strong>DPPO</strong> instead of vanilla GRPO.<span
class="sidenote-wrapper"><label for="sn-8" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-8" class="margin-toggle"/><span
class="sidenote">Qi et al., “Rethinking the Trust Region for LLM RL”
(DPPO), 2026.<br />
<br />
</span></span> DPPO masks tokens where the inference (vLLM) and training
logprobs disagree, using a binary approximation of the total-variation
divergence. This small change meaningfully tames training collapse.</li>
<li>A <strong>token-level loss</strong>,<span
class="sidenote-wrapper"><label for="sn-9" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-9" class="margin-toggle"/><span
class="sidenote">Token-level loss as in Yu et al., “DAPO,” 2025.<br />
<br />
</span></span> fully <strong>asynchronous</strong> training, filtering
of zero-standard-deviation groups, and active sampling to keep batches
full.<span
class="sidenote-wrapper"><label for="sn-10" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-10" class="margin-toggle"/><span
class="sidenote">Following Olmo 3.<br />
<br />
</span></span></li>
<li>An <strong>FP32 LM head</strong>, keeping the language-model head in
full precision to minimize the train/inference mismatch. This mattered
most for the hybrid Qwen3.5 models.</li>
</ul>
<p>Infrastructure-wise, we extend <a
href="https://github.com/allenai/open-instruct">open-instruct</a>, use
<a href="https://github.com/vllm-project/vllm">vLLM</a> for rollouts,
and run sandboxes via <a href="https://podman.io/">Podman</a> or
Apptainer. A typical run uses 2 nodes for training and 6 for inference
on H100s, and takes 2–3 days. The key hyperparameters are:</p>
<table>
<thead>
<tr class="header">
<th>Hyperparameter</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Training steps</td>
<td>500</td>
</tr>
<tr class="even">
<td>Group size</td>
<td>32</td>
</tr>
<tr class="odd">
<td>Prompts per batch</td>
<td>8</td>
</tr>
<tr class="even">
<td>Max context length</td>
<td>65,536 tokens</td>
</tr>
<tr class="odd">
<td>Max tool calls per episode</td>
<td>64</td>
</tr>
</tbody>
</table>
<p>We keep thinking on intermediate turns (“interleaved thinking”), and
evaluate on Terminal Bench 2.1 and <a
href="https://www.tbench.ai/">Terminal Bench Lite</a>, averaging over 3
runs.</p>
<h3 id="tmax-15k-outperforms-other-terminal-data">3.2 TMax-15K
outperforms other terminal data</h3>
<p>We first hold the model fixed (<a
href="https://huggingface.co/Qwen">Qwen3.5-9B</a>) and vary only the RL
dataset. Training on <strong>TMax-15k gives the strongest Terminal Bench
performance of any dataset we tried</strong>. We release the resulting
model as <a href="https://huggingface.co/allenai/tmax-9b"
class="dataset-tag">TMax-9B</a>.</p>
<table>
<thead>
<tr class="header">
<th>RL dataset</th>
<th>TB Lite</th>
<th>TB 2.1</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>None (Qwen3.5-9B)</td>
<td>41.9</td>
<td>16.1</td>
</tr>
<tr class="even">
<td>TermiGen</td>
<td>49.4</td>
<td>25.1</td>
</tr>
<tr class="odd">
<td>Endless Terminals</td>
<td>52.6</td>
<td>25.5</td>
</tr>
<tr class="even">
<td>OpenThinker-Agent</td>
<td>53.0</td>
<td>25.1</td>
</tr>
<tr class="odd">
<td>TerminalTraj</td>
<td>45.8</td>
<td>18.0</td>
</tr>
<tr class="even">
<td>CLI-Gym</td>
<td>50.7</td>
<td>25.1</td>
</tr>
<tr class="odd">
<td>SWE-Smith</td>
<td>47.2</td>
<td>21.0</td>
</tr>
<tr class="even">
<td><strong>TMax-15k (ours)</strong></td>
<td><strong>57.2</strong></td>
<td><strong>28.8</strong></td>
</tr>
</tbody>
</table>
<p><em>RL on Qwen3.5-9B across datasets; mean over 3 runs. Our data
improve Terminal Bench 2.1 by nearly 13 points over the base
model.</em></p>
<p>We find that TMax-15K stays difficult throughout training. If we plot
the average number of steps the model takes during training,
<strong>TMax-15k consistently drives more steps per episode</strong>
than other datasets. The tasks keep demanding real, multi-step work
rather than being solved in a couple of commands.</p>
<figure>
<img src="/assets/img/tmax/step-count.png"
alt="Average steps per episode over training (15-step smoothing). Training on TMax-15k sustains higher step counts than other datasets, indicating the tasks stay difficult throughout." />
<figcaption aria-hidden="true">Average steps per episode over training
(15-step smoothing). Training on TMax-15k sustains higher step counts
than other datasets, indicating the tasks stay difficult
throughout.</figcaption>
</figure>
<p>We also see the model <em>learn to think more</em> over the course of
training: the number of tokens it spends per assistant turn, for both
reasoning and tool-calling, climbs steadily. This is the agentic
analogue of inference-time reasoning scaling, and it tracks the model’s
improving performance.</p>
<figure>
<img src="/assets/img/tmax/turn-tokens.png" class="fig-small"
alt="Average assistant-turn and tool-call length (tokens) over training. Per-turn output grows, suggesting the model learns to make better use of inference-time compute." />
<figcaption aria-hidden="true">Average assistant-turn and tool-call
length (tokens) over training. Per-turn output grows, suggesting the
model learns to make better use of inference-time compute.</figcaption>
</figure>
<h3 id="tmax-9b-outperforms-prior-small-terminal-agents">3.3 TMax-9B
outperforms prior small terminal agents</h3>
<p>At <strong>27.2%</strong> on Terminal Bench 2.0, TMax-9B is the best
open-weights model under 10B we compare against under official Terminal
Bench settings,<span
class="sidenote-wrapper"><label for="sn-11" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-11" class="margin-toggle"/><span
class="sidenote">Terminal Bench scores can be sensitive to harness and
timeout settings. With non-standard settings (for example, overriding
the default timeouts) the base Qwen3.5-9B can score noticeably higher,
so we report all numbers under official Terminal Bench settings for a
fair comparison.<br />
<br />
</span></span> beating 32B terminal agents from several prior works and
approaching closed offerings like Claude Haiku 4.5 (29.8%).</p>
<p>The same recipe scales across the whole Qwen3.5 family. TMax-2B and
TMax-4B reach 2.9% and 18.9% (from 2.3% and 16.6%), and <a
href="https://huggingface.co/allenai/tmax-27b"
class="dataset-tag">TMax-27B</a> (built on Qwen3.6-27B) reaches
<strong>42.7%</strong> (from 39.6%). At that size, TMax-27B approaches
models that are a few order of magnitude larger, like the 1T-parameter
Kimi K2.5 (43.2%) and the 230B MiniMax M2.7 (45.1%).</p>
<h3 id="generalization-across-tasks-harnesses-and-models">3.4
Generalization across tasks, harnesses, and models</h3>
<p>Finally, we show our training and data recipe generalizes well across
three important axes: tasks (i.e., what it is asked to do), harnesses
(i.e., what tools and prompts it is provided when performing tasks), and
model families (i.e., the starting point model).</p>
<p><strong>Across tasks.</strong> Evaluating TMax-9B beyond Terminal
Bench, performance improves across the board, including on a non-agentic
math benchmark:</p>
<table>
<thead>
<tr class="header">
<th>Benchmark</th>
<th>Qwen3.5-9B</th>
<th>TMax-9B</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>SWE-Bench Verified</td>
<td>44.0</td>
<td><strong>53.5</strong></td>
</tr>
<tr class="even">
<td>AIME’24/25 (terminal-agent)</td>
<td>73.3</td>
<td><strong>91.1</strong></td>
</tr>
</tbody>
</table>
<p><em>Generalization to other tasks; mean over 3 runs. RL on terminal
data improves an agentic SWE benchmark by ~9 points and math by
~18.</em></p>
<p><strong>Across harnesses.</strong> When we swap out the harness for
different prompts and tools than the one used during RL, TMax-9B
improves by <strong>roughly 9 to 15 points in every harness we
tried</strong>, even ones it never saw in training.</p>
<table>
<thead>
<tr class="header">
<th>Harness</th>
<th>Qwen3.5-9B</th>
<th>TMax-9B</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Ours (mini-swe-agent + persistent shell)</td>
<td>41.9</td>
<td><strong>57.2</strong></td>
</tr>
<tr class="even">
<td>OpenHands</td>
<td>36.0</td>
<td><strong>46.9</strong></td>
</tr>
<tr class="odd">
<td>mini-swe-agent</td>
<td>44.1</td>
<td><strong>55.3</strong></td>
</tr>
<tr class="even">
<td>Terminus-2</td>
<td>36.4</td>
<td><strong>45.3</strong></td>
</tr>
</tbody>
</table>
<p><em>Terminal Bench Lite across evaluation harnesses; mean over 3
runs.</em></p>
<p><strong>Across models.</strong> We also apply it to the older <a
href="https://huggingface.co/Qwen/Qwen3-8B">Qwen3-8B</a>, with a short
SFT warm-start and shorter context. It improves substantially on
Terminal Bench Lite (7.3 → 17.7), though gains on the harder Terminal
Bench 2.1 are smaller, since at this scale the benchmark’s difficulty
makes improvements hard to see.</p>
<table>
<thead>
<tr class="header">
<th>Model</th>
<th>TB Lite</th>
<th>TB 2.1</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Qwen3-8B</td>
<td>7.3</td>
<td>1.1</td>
</tr>
<tr class="even">
<td>  + SFT</td>
<td>11.5</td>
<td><strong>6.0</strong></td>
</tr>
<tr class="odd">
<td>  + RL</td>
<td><strong>17.7</strong></td>
<td>5.2</td>
</tr>
</tbody>
</table>
<p><em>Qwen3-8B on Terminal Bench Lite and 2.1 after SFT and then RL;
mean over 3 runs.</em></p>
<p>Taken together, these results strongly suggest that TMax RL training
teaches the model <strong>new, transferable terminal skills</strong>,
not mere harness-fitting or overfitting to Terminal Bench-style
tasks.</p>
<h2 id="analysis-discussion">4. Analysis &amp; discussion</h2>
<p>Two findings shaped the TMax recipe, and we think both are useful for
the community training terminal agent in this setting.</p>
<h3 id="strong-models-dont-always-want-your-sft-data">4.1 Strong models
don’t always want your SFT data</h3>
<p>Common post-training pipelines advise warm-start RL with SFT for
training stability. However, we find that existing datasets degrades
Qwen 3.5’s performance, likedy due to the model undergoing heavy
post-training already. This holds even for our own mixture, which we
generated from a strong Qwen3.6-27B teacher. The older Qwen3-8B, by
contrast, clearly <em>benefits</em> from the same SFT.</p>
<table>
<thead>
<tr class="header">
<th>Model</th>
<th>TB Lite</th>
<th>TB 2.1</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Qwen3.5-9B</td>
<td><strong>41.9</strong></td>
<td>16.1</td>
</tr>
<tr class="even">
<td>  + TMax SFT</td>
<td>35.5</td>
<td>15.0</td>
</tr>
<tr class="odd">
<td>  + large SFT</td>
<td>31.3</td>
<td>16.9</td>
</tr>
<tr class="even">
<td>Qwen3-8B</td>
<td>7.3</td>
<td>1.1</td>
</tr>
<tr class="odd">
<td>  + TMax SFT</td>
<td>11.5</td>
<td>6.0</td>
</tr>
<tr class="even">
<td>  + large SFT</td>
<td><strong>16.4</strong></td>
<td><strong>7.9</strong></td>
</tr>
</tbody>
</table>
<p><em>SFT before RL helps the older model but hurts the stronger
one.</em></p>
<p>We suspect the larger mixtures lean on relatively weak teacher models
from prior work, and that a heavily post-trained model has less to gain
and more to lose from imitation. We leave further exploration of good
SFT mixtures for Qwen 3.5 as future work.</p>
<h3 id="terminal-agent-rl-is-hard-to-stabilize">4.2 Terminal-agent RL is
hard to stabilize</h3>
<p>We found training was frequently unstable and often collapsed past
200–300 steps. The main culprit is numeric mismatch between training and
inference, which the hybrid Qwen3.5 architecture makes worse. Keeping
the LM head in FP32 removes the worst logprob spikes (below), and moving
from GRPO to DPPO, together with a larger group size, further limits
collapse. Long horizons (often 20+ steps) and infrastructure load from
running many sandboxes make things harder still. We give the full
analysis, including the GRPO-vs-DPPO and group-size comparisons, in the
paper.</p>
<figure>
<img src="/assets/img/tmax/fp32-lm-head.png" class="fig-small"
alt="Maximum inference-vs-trainer logprob difference over the first 100 steps for Qwen3.5-9B. The FP32 LM head removes the large spikes (Qwen3-8B does not show them even without it)." />
<figcaption aria-hidden="true">Maximum inference-vs-trainer logprob
difference over the first 100 steps for Qwen3.5-9B. The FP32 LM head
removes the large spikes (Qwen3-8B does not show them even without
it).</figcaption>
</figure>
<p>We saw the same instabilities on Qwen3-8B, so this is not a quirk of
one model family. We believe more stable, longer training would yield
substantially better models.</p>
<h2 id="conclusion">5. Conclusion</h2>
<p>TMax is a simple recipe for training strong terminal agents at small
scale. Its two pieces are <strong>TMax-15k</strong>, a difficulty- and
diversity-controlled dataset of 14,600 RL environments, and a plain DPPO
training recipe. Together they are enough to train
<strong>TMax-9B</strong> and <strong>TMax-27B</strong>, which is among
the strongest open-weights models under 10B and 30B at the time of
writing, respectively. The gains transfer to SWE-Bench, to AIME, and
across harnesses, which is evidence that the model has genuinely
improved at using a terminal rather than memorizing one setup. We
release the data, models, and code as a starting point for others.</p>
<p><strong>Where we’d go next.</strong> Follow from our results, three
directions naturally falls out. Better training stability, more complex
harnesses, and more complex data.</p>
<p>We hope TMax serves as a strong baseline and a useful testbed for the
community to improve the stability, performance, and efficiency of
terminal agents.</p>
<h2 id="acknowledgements">Acknowledgements</h2>
<p>TMax is a collaboration between the University of Washington and the
Allen Institute for AI (Ai2). We thank members of UW NLP and the Open
Ecosystem team at Ai2 for feedback and discussion throughout this
project, Michael Noukhovitch for useful discussions on RL stability, and
the Ai2 Beaker team for help with infrastructure.</p>
<h2 id="citation">Citation</h2>
<div class="citation">
<p>Please cite this work as:</p>
<pre><code>Ivison, Hamish and Yin, Junjie Oscar and Shao, Rulin and Xiao, Teng and
Lambert, Nathan and Hajishirzi, Hannaneh, &quot;Tmax: A simple recipe for terminal
agents&quot;, arXiv preprint arXiv:2606.23321, 2026.</code></pre>
<p>Or use the BibTeX citation:</p>
<pre><code>@misc{ivison2026tmaxsimplerecipeterminal,
      title={Tmax: A simple recipe for terminal agents},
      author={Hamish Ivison and Junjie Oscar Yin and Rulin Shao and Teng Xiao and Nathan Lambert and Hannaneh Hajishirzi},
      year={2026},
      eprint={2606.23321},
      archivePrefix={arXiv},
      primaryClass={cs.CL},
      url={https://arxiv.org/abs/2606.23321},
}</code></pre>
</div>]]></content><author><name>WAI</name></author><summary type="html"><![CDATA[TMax is the strongest open RL recipe for terminal agents to date, bringing open data recipes closer to the frontier: a 14,600-environment dataset and a simple outcome-only RL recipe, with open models from 2B to 27B.]]></summary></entry></feed>