Skip to content
Markdown

Verified task synthesis for agent RL

Scope: manufacturing the executable tasks that agentic RL trains on, when there are not enough real ones. A trainable task is not a prompt: it is a runnable workspace, a public instruction, a reference solution that provably works, and a private verifier that agrees with the instruction. This page covers the recursive solution-first synthesis loop that produces such bundles at scale, using Recursive Synthetic Terminal Tasks (RST, arXiv 2608.05466) as the worked instance, the gate cascade that decides where an attempt dies, what the released pool measurably contains, and the sandbox reservation the published cost figure omits. The reward side of these tasks is RLVR, the training loop that consumes them is agentic and tool-use RL, and the capacity to run them is the rollout sandbox fleet.

Evidence status, verified 2026-08-07. Method and result claims come from arXiv 2608.05466v1, submitted 2026-08-05 by authors at Tencent HY LLM Frontier and seven universities. No synthesis code is released and no repository is named, so the pipeline itself was not run. What was independently checked: the three CC BY 4.0 datasets and three checkpoints in the authors' HuggingFace collection resolve, and every statistic below labelled MEASURED was computed here from metadata/tasks.parquet (37,484 rows, 124 MB) and the shard manifests, not read off the paper. Both NumPy blocks were executed and their assertions pass. Section Where the paper and the release disagree records four discrepancies found by that check.

flowchart LR
  SEED["Verified seed bundle<br/>instruction, toml, Dockerfile,<br/>solve.sh, verifier"] --> OP["Pick rewrite operator<br/>40 operators, 5 families"]
  OP --> PLAN["Transformation contract<br/>new requirement, artifacts,<br/>shortcut rejections"]
  PLAN --> GROW["Extend solve.sh first"]
  GROW --> ALIGN["Realign verifier,<br/>instruction, environment"]
  ALIGN --> G1{"Generation-quality gate<br/>cheap, no sandbox"}
  G1 -->|reject| DEAD["Discarded"]
  G1 --> G2{"Static preflight<br/>Docker and artifact paths"}
  G2 -->|reject| DEAD
  G2 --> SBX["Fresh sandbox:<br/>build, run oracle, run verifier"]
  SBX -->|fail, repairable| REP["Bounded repair<br/>at most 2 rounds"]
  REP --> G2
  SBX -->|fail| DEAD
  SBX -->|reward 1| POOL["Verified pool"]
  POOL --> RLTASK["RL task pool"]
  POOL --> NEXT["Next-round seeds<br/>under diversity caps"]
  NEXT --> OP

What it is

Verified task synthesis is the manufacture of complete, executable agent tasks rather than instructions. The unit of production is a bundle whose parts must agree with each other. In RST that bundle is six files: instruction.md (public), task.toml (runtime metadata), environment/Dockerfile (initial workspace), solution/solve.sh (the reference solution), and tests/test.sh plus tests/test_state.py (the private verifier). Generation is restricted to exactly those six paths, and writes outside the copied task directory are rejected.

The load-bearing design choice is that synthesis runs solution first. The generator extends the reference solution with new executable work, adjusts the environment so that work can run, then derives verifier checks from the new workflow, and only then rewrites the public instruction. Generating an instruction first and hoping a solution and a verifier can be fitted to it is what breaks the mutual consistency; growing the executable path first means the reference solution is a constructive proof that the task is solvable before anything else is written.

Acceptance has two conditions and both are checkable. Oracle validity: the reference solution must make the private verifier return full reward in a fresh sandbox, with no execution exception. Contract validity: every requirement the verifier checks must be stated in the public instruction or discoverable from the workspace, so a private test cannot smuggle in a requirement the agent had no way to know. The second condition is what separates a trainable task from a trap; without it a low pass rate measures the agent's clairvoyance rather than its competence.

Recursion is what produces difficulty. Accepted tasks become seeds for the next round, so the pipeline compounds executable work instead of sampling independently from a fixed generator. Across fifteen rounds RST reports the median reference solution growing from 67 to 374 lines and the median command count from 40 to 244, while the median instruction grows only from 85 to 122 words. That gap is the point: the tasks get harder to do, not longer to read.

Why use it

Because task supply, not GPU supply, is what caps an agentic RL run. A verifier-based RL pool is consumed at rollout rate, and every task in it needs an environment that builds, a verifier that discriminates, and a difficulty that is neither trivially passed nor never passed. Hand-authored tasks at this standard are reported to cost hundreds to thousands of dollars each, which puts a pool of tens of thousands of tasks out of reach of anything but a synthesis pipeline.

The second reason is difficulty targeting. A fixed corpus has a fixed ceiling: once the policy passes most of it, the gradient signal thins out and the pool stops teaching. Recursion moves the ceiling with the policy. RST's own difficulty evidence is a fixed solver evaluated across rounds under unchanged inference settings, so the decline is a property of the tasks rather than the model: DeepSeek-V4-Pro pass@4 falls from 90% at R1 to 2.5% at R15, and mean verifier partial credit from 0.970 to 0.170. Failed attempts change character too, with near-misses above 0.75 partial credit falling from 86.4% to 1.2%.

The third reason is that it produces the RL task pool and the SFT trajectory corpus from the same artifact. An accepted bundle is simultaneously a verifier-based RL task and, once rolled out, a source of rejection-sampled trajectories. RST reports supervised fine-tuning gains of up to 10 points across Terminal-Bench 2, Terminal-Bench Hard, and Long-Horizon Terminal Bench, and a PPO run reaching 49.44%, 32.00%, and 22.07% on those three from a Qwen3.5-27B base.

When to use it (and when not)

  • Use it when real tasks exist but not in quantity. Synthesis works best as an amplifier over a verified seed pool. RST bootstraps from 639 tasks drawn from TerminalWorld, which were themselves built from real interaction records. Starting from nothing gives the generator no grounding to preserve.
  • Use it when the domain has a decidable verifier. Terminal work qualifies because outcomes are file, process, and exit-code state. If success in your domain needs a human or a model judge, the acceptance gate that makes this loop safe does not exist, and the failure mode is reward hacking rather than a discarded candidate.
  • Do not use it to replace an evaluation set. Synthesized tasks descend from your seeds and share their idioms. RST audits this directly, reporting zero exact 13-token overlaps against all 89 TB2, 100 Terminal-Bench Hard, and 46 LHTB tasks, with maximum pairwise 5-gram Jaccard below 0.009. That is evidence of no contamination of those benchmarks, not evidence that a synthesized pool is a valid held-out set for anything. Keep the eval gate in evaluation integrity.
  • Do not run recursion without a per-parent cap. Uncapped reseeding concentrates the pool on whichever lineages happen to survive the gates, and the diversity statistics stop meaning anything. RST caps descendants per parent at four, per category at 160, per rewrite family at 320, and per source cohort at 280, relaxing them on a recorded schedule rather than silently filling the round.
  • Do not treat the pool as free once built. The tasks are cheap to generate and expensive to use: each one is a container that has to build and run for every rollout. That recurring cost is sized in the rollout sandbox fleet, and it dominates the synthesis cost within a single training run.

Architecture

Each round is a funnel with two cheap gates in front of one expensive one, and the ordering is the whole economy of the pipeline. A generation-quality gate runs first on text alone: it demands a substantive cross-file change (at least three task files touched, at least eight changed solution lines, at least twelve changed verifier lines) and rejects instructions that expose private test paths, read as step-by-step recipes, exceed 180 words, or exceed 1.6 times the parent instruction's length. A static preflight then checks Docker build consistency, required files, and that every artifact the verifier expects is actually produced by the solution or provided by the environment. Only survivors get a sandbox.

That ordering is worth a number, and the paper's own two published curves supply it. Yield is reported per 1,000 seed attempts and pass rate is reported over candidates, so their ratio is the share of attempts that reach the sandbox at all.

# synthesis_gates.py -- where a recursive-synthesis attempt dies, how much sandbox
# time an accepted task reserves, and what container price would exhaust the
# published $0.05 per task on its own. PAPER constants are from arXiv 2608.05466;
# MEASURED constants come from the 37,484 released task.toml files. The repair
# model and the price inversion are this page's, and are labelled where used.
import numpy as np

# PAPER: passed tasks per 1,000 seed attempts, and pass rate over candidates
YIELD_PER_1K = {"R1": 551.6, "R15": 530.0, "min": 498.2, "max": 572.2}
CAND_PASS = {"R1": 0.775, "R15": 0.780, "min": 0.745, "max": 0.815}
BOOTSTRAP_SEEDS, R1_ACCEPTED, TOTAL_ACCEPTED = 639, 2_820, 37_484
MAX_REPAIRS = 2               # "at most two feedback-repair rounds"
CLAIMED_USD_PER_TASK = 0.05

# MEASURED: task.toml medians over 37,372 tasks that carry the timeout tables
BUILD_TO_S, AGENT_TO_S, VERIFY_TO_S = 600.0, 900.0, 900.0
AGENT_TO_P95_S = 1800.0
# MEASURED: difficulty labels still carried by the released bundles
DIFFICULTY = {"easy": 28_116, "medium": 6_881, "hard": 58, "unlabelled": 2_429}
INSTR_OVER_180_WORDS = 56     # the generation-quality gate rejects these


def candidates_per_attempt(yield_per_1k, cand_pass):
    """Yield is per seed attempt, pass rate is per candidate reaching the sandbox,
    so their ratio is the share of attempts that survive the deterministic gates."""
    return yield_per_1k / 1000.0 / cand_pass


def validations_per_candidate(overall_pass, max_repairs):
    """Model: each candidate gets up to 1 + max_repairs independent sandbox runs at
    a constant per-run success probability s, chosen so the compound pass rate
    matches the published figure. Repairs are not independent in reality; this is
    an upper bound on s and therefore a lower bound on runs."""
    tries = max_repairs + 1
    s = 1.0 - (1.0 - overall_pass) ** (1.0 / tries)
    return sum((1.0 - s) ** k for k in range(tries)), s


def reserved_hours_per_accepted(overall_pass, agent_to_s, max_repairs=MAX_REPAIRS):
    runs, _ = validations_per_candidate(overall_pass, max_repairs)
    per_run_s = BUILD_TO_S + agent_to_s + VERIFY_TO_S
    return runs / overall_pass * per_run_s / 3600.0


# (1) About a third of attempts die at the cheap deterministic gates, before any
#     sandbox is allocated, and that share is stable across the reported rounds.
gate_kill = {k: 1.0 - candidates_per_attempt(YIELD_PER_1K[k], CAND_PASS[k])
             for k in ("R1", "R15")}
assert all(0.27 < v < 0.34 for v in gate_kill.values())

# (2) Edge case: if every candidate passed the sandbox, the gate kill share would
#     be exactly one minus the yield. The formula degenerates correctly.
assert np.isclose(1.0 - candidates_per_attempt(530.0, 1.0), 0.470)

# (3) "Attempt" is not "seed": R1 turned 639 seeds into 2,820 accepted tasks at a
#     published 551.6 per 1,000 attempts, which needs about eight attempts a seed.
#     The paper never states this multiplier.
attempts_r1 = R1_ACCEPTED / (YIELD_PER_1K["R1"] / 1000.0)
assert 7.0 < attempts_r1 / BOOTSTRAP_SEEDS < 9.0

# (4) Sandbox reservation per accepted task, at the median and p95 agent timeout.
runs, s = validations_per_candidate(CAND_PASS["R15"], MAX_REPAIRS)
res_med = reserved_hours_per_accepted(CAND_PASS["R15"], AGENT_TO_S)
res_p95 = reserved_hours_per_accepted(CAND_PASS["R15"], AGENT_TO_P95_S)
assert res_p95 > res_med > 1.0

# (5) Invert the published cost: what would a 1-vCPU, 2-GB container-hour have to
#     cost for sandbox time alone to consume the whole $0.05 per accepted task?
break_even = {f"{int(f * 100)}% of ceiling": CLAIMED_USD_PER_TASK / (res_med * f)
              for f in (1.0, 0.5, 0.25, 0.1)}
assert break_even["100% of ceiling"] < 0.05

# (6) The released bundles disagree with their own difficulty: three quarters are
#     still labelled easy on a pool whose late rounds pass at 2.5%.
n_lab = sum(DIFFICULTY.values())
assert DIFFICULTY["easy"] / n_lab > 0.70 and DIFFICULTY["hard"] / n_lab < 0.002

# (7) The 180-word instruction gate is described as a rejection, but the shipped
#     pool contains instructions past it. A hard gate would have left none.
assert INSTR_OVER_180_WORDS > 0

print("deterministic-gate kill share:", {k: f"{v:.1%}" for k, v in gate_kill.items()})
print(f"R1 implies {attempts_r1:,.0f} attempts from {BOOTSTRAP_SEEDS} seeds "
      f"= {attempts_r1 / BOOTSTRAP_SEEDS:.1f} attempts per seed")
print(f"repair model: per-run success {s:.3f}, {runs:.2f} sandbox runs per candidate, "
      f"{runs / CAND_PASS['R15']:.2f} per accepted task")
print(f"reserved sandbox-hours per accepted task: {res_med:.2f} at median timeouts, "
      f"{res_p95:.2f} at the p95 agent timeout")
print("break-even container price ($ per vCPU-hour) if real time is:",
      {k: f"${v:.3f}" for k, v in break_even.items()})
print("difficulty labels on the released pool:",
      {k: f"{v / n_lab:.1%}" for k, v in DIFFICULTY.items()})
print(f"instructions past the 180-word gate: {INSTR_OVER_180_WORDS} of {TOTAL_ACCEPTED}")

Executed output:

deterministic-gate kill share: {'R1': '28.8%', 'R15': '32.1%'}
R1 implies 5,112 attempts from 639 seeds = 8.0 attempts per seed
repair model: per-run success 0.396, 1.97 sandbox runs per candidate, 2.52 per accepted task
reserved sandbox-hours per accepted task: 1.68 at median timeouts, 2.31 at the p95 agent timeout
break-even container price ($ per vCPU-hour) if real time is: {'100% of ceiling': '$0.030', '50% of ceiling': '$0.059', '25% of ceiling': '$0.119', '10% of ceiling': '$0.297'}
difficulty labels on the released pool: {'easy': '75.0%', 'medium': '18.4%', 'hard': '0.2%', 'unlabelled': '6.5%'}
instructions past the 180-word gate: 56 of 37484

Three results from that run matter for anyone building this. The text-only gates absorb roughly 30% of attempts for nearly no cost, and that share is stable from R1 to R15, so the gate is not a startup filter that stops mattering. An accepted task costs about 2.5 sandbox validations, not one, once the sandbox failures and the bounded repair rounds are counted. And the published price is thin: at the median timeouts an accepted task reserves 1.68 sandbox-hours, which exhausts the whole $0.05 at about $0.030 per vCPU-hour. The paper publishes no cost breakdown anywhere, so the honest reading is that $0.05 is a model-token figure and the container bill is separate.

The word "attempt" also does real work in that arithmetic and is never defined. R1 turned 639 bootstrap seeds into 2,820 accepted tasks at a published 551.6 per 1,000 attempts, which requires about eight attempts per seed. Whether that is eight operators tried against each seed or eight cohorts of the same seed is not stated. The gate-kill ratio is unaffected because the multiplier cancels, but any absolute capacity plan built from "seeds" rather than "attempts" will be eight times too small.

How to use it

The pipeline is unreleased; the output is not. The authors' HuggingFace collection carries three CC BY 4.0 datasets and three checkpoints. The two synthesis datasets were checked here against their own shard manifests.

Artifact Contents Verified how
Zhongzhi1228/Recursive-Task-Synthesis 37,484 task bundles, 8 tar shards plus a 124 MB metadata parquet Shard manifest task counts sum to 37,484; parquet has 37,484 rows, all validation_status = passed, zero duplicate content hashes
Zhongzhi1228/Recursive-Task-Synthesis-Trajectories 327,189 agent trajectories, 66 tar shards, 24.1 GB Shard manifest trajectory_count fields sum to 327,189
Zhongzhi1228/Terminal-Bench-Hard 100-task evaluation subset in Harbor bundle layout 100 distinct tasks/<id>/ directories in the repo file list
Qwen3.5-27B-SFT, Qwen3.5-122B-A10B-SFT, Qwen3.5-27B-RL The three trained checkpoints Present in the collection; not downloaded or evaluated here

The metadata parquet is the practical entry point because it holds the instruction, task.toml, solve.sh, and Dockerfile inline, so a pool can be filtered before pulling multi-hundred-megabyte tars. What it does not hold is a synthesis round label. Public identifiers are opaque by design, so the R1 to R15 curves that carry the paper's entire difficulty argument cannot be reproduced from the release. Treat those curves as reported, not as reproducible.

What the released pool measurably contains, computed here over all 37,484 rows:

Property Value
solve.sh length median 227 lines, 196 non-empty; p25 134, p90 388, max 689
Instruction length median 108 words, p90 144, max 237
Files per bundle median 11, p90 14, max 592
Uncompressed bundle size median 39.9 kB, mean 90.1 kB, max 16.7 MB; 3.38 GB total
Requested resources 1 vCPU / 2048 MB / 6144 MB on 37,459 of 37,484 tasks
Agent timeout median 900 s, p95 1800 s, max 3600 s
Build timeout median 600 s, p95 900 s
Categories 49 distinct, led by scripting-automation (5,987) and system-administration (5,150)
Exact duplicate bundles 0, by content SHA256; 169 repeated solution bodies and 217 repeated instructions

How to develop with it

Four rules in RST's implementation are the ones worth copying, because each one closes a specific hole that a naive generator falls into.

Write the contract before the code. The operator is turned into an explicit transformation contract first: what behaviour must be preserved, what new requirement is added, what evidence is discoverable, what artifacts appear at each stage, and which shortcuts the verifier must reject. The contract demands at least four distinguishable checks covering evidence discovery, intermediate state, final semantics, and shortcut rejection. Without this the verifier ends up mirroring the solution's incidental commands rather than the task's outcome, and any agent that solves the task differently is marked wrong.

Derive the verifier from the contract, never from the solution transcript. The verifier checks semantic content and state, keeps relevant parent checks, and rejects placeholder artifacts, stale outputs, and hard-coded answers. This is what lets the task credit a solution path the reference never took, which is the property that makes it usable as an RL reward rather than an imitation target.

Select operators by affordance, not by preference. A local scan reads bounded slices of the seed's instruction, solution, verifier, config, and environment, and scores the 40 operators against observable signals such as package manifests, structured data formats, build scripts, archives, databases, logs, permissions, and services. The score is combined with a family-balance term and an inverse-frequency penalty for operators already used in the batch. A model-based ranker may then swap the preferred operator for one of the recorded alternatives, but it cannot introduce an operator the local scan excluded. The result across fifteen rounds is 36 of 40 operators still represented at R15 with the most frequent accounting for 8.0% of tasks.

Protect lineage explicitly. Recursively generated task names outgrow filesystem limits, so RST compacts them to 96 characters while preserving the bootstrap-parent identifier and appending a stable hash, and it requires at least 80% of records in any manifest of 20 or more to retain recoverable ancestry, checked before selection, after selection, and after materialization. A failed check aborts the round before generation. This is not bookkeeping fussiness: losing parent identity silently disables the per-parent cap, and every diversity statistic computed afterwards becomes meaningless.

The five families each carry exactly eight operators. The table below reproduces the implementation-level identifiers from the paper's Appendix C, not the shortened ring labels in its Figure 5, which are a separate conceptual taxonomy with different family names.

Family What it makes harder Operators
environment runtime substrate Conditions that must hold before anything runs dependency_version_alignment, path_workdir_alignment, permission_executable_alignment, environment_variable_resolution, toolchain_availability_check, container_build_alignment, service_process_lifecycle, resource_cleanup_and_limits
build/test execution workflow The command sequence and program behaviour compile_link_package_workflow, unit_test_failure_repair, integration_test_workflow, cli_argument_behavior, lint_format_static_check, runtime_error_debug_loop, build_artifact_generation, performance_or_benchmark_smoke
data/artifact report processing Producing or validating concrete outputs format_conversion_validation, schema_content_validation, aggregation_summary_report, artifact_inventory_reconciliation, archive_compression_extraction, dedup_sort_normalization, checksum_hash_provenance, media_or_binary_metadata_processing
configuration state migration Cross-file state that must stay consistent config_data_consistency, manifest_lockfile_reconciliation, state_migration_transform, cache_index_regeneration, database_or_file_state_initialization, template_render_consistency, profile_feature_flag_selection, backup_rollback_idempotency
diagnostics audit forensics Inferring the action from system evidence log_error_diagnosis, trace_event_correlation, verifier_failure_interpretation, security_permission_audit, data_quality_anomaly_investigation, process_endpoint_inspection, git_history_diff_forensics, evidence_bundle_generation

The two taxonomies are worth keeping straight when reading the paper. Section 4.2 introduces the 40 operators as grouped into "Configuration and Control State; Data, Manifest, and Schema State; Filesystem and Resource Binding; Build, Cache, and Artifact State; and Runtime, Tooling, and Diagnostics", which are Figure 5's ring labels. Figure 5's own caption states those labels are shortened and that the implementation families are defined separately in Appendix C, where they carry the five names in the table above. The operator counts agree; the family names do not.

How to maintain it

Watch the near-duplicate tail, not the mean. RST's within-round nearest-neighbour similarity median rises from 0.223 at R1 to 0.464 at R15, which the paper reads as still below half. The p95 reaches 0.703. The median is the reassuring statistic and the p95 is the actionable one: a pool can stay diverse on average while accumulating a cluster of near-copies that inflate the apparent size of the RL pool without adding signal. The released pool ships task_group_id, and grouping by it gives 12,010 groups over 37,484 tasks, sized 1 to 10 with 2,811 singletons. That field is the dedup handle the paper says future scaling will need.

Watch lineage attrition. Bootstrap-seed coverage falls from 98.3% at R2 to 60.1% at R15, so two fifths of the original seed lineages stop producing viable descendants. Nothing in the pipeline notices this, because the per-round diversity entropy stays flat. If a specific domain matters to you, track its lineage survival directly rather than trusting pool-level entropy.

Re-derive metadata after every rewrite, and audit that it happened. This is where the released pool visibly failed. task.toml is one of the six files the generator may modify, and the paper states metadata is updated when the environment needs different resources or timeouts. Measured over the release, 75.0% of the pool is still labelled difficulty = "easy" and 58 tasks in 37,484 are labelled hard, on a pool whose late rounds a frontier model passes 2.5% of the time. Requested resources are equally frozen: 37,459 of 37,484 tasks ask for exactly 1 vCPU, 2 GB, and 6 GB, unchanged while the median solution grew more than fivefold. Do not build a curriculum on these labels, and do not schedule against these requests.

Treat the deterministic gates as advisory until you have audited their output. The 180-word instruction rule is described as a rejection, yet 56 shipped tasks exceed it and the longest is 237 words. Borderline cases are documented as retained with warnings, which is a defensible design, but it means the gate's thresholds describe intent rather than an invariant you can rely on downstream.

How to run it in production

Synthesis and rollout compete for the same sandbox capacity, and only one of them is elastic. Sizing follows from three measured facts. A validation reserves the sum of the build, agent, and verifier timeouts, which at the pool medians is 2,400 seconds. An accepted task costs about 2.5 such validations under the repair model above. And the container itself is small and uniform, so a sandbox slot is one vCPU and 2 GB, making an ordinary CPU node CPU-bound at roughly one slot per core.

Practical consequences:

  • Reserve by timeout, schedule by observation. The timeouts are ceilings, not runtimes, but a scheduler that admits work against observed averages will thrash when a batch of slow builds lands. Admit against the ceiling and reclaim early on completion.
  • Separate the synthesis queue from the rollout queue. Synthesis is throughput-bound and interruptible; rollout is latency-bound and feeds a trainer that stalls without it. Sharing one pool lets a synthesis round starve the RL step. The rate-matching framing in rollout fleet sizing applies here with the sandbox pool as the third resource.
  • Fail candidates fast and cheaply. The 30% killed by text-only gates cost nothing. The remainder is what the pipeline actually spends, so any gate you can move earlier pays roughly its rejection rate times the full 2,400-second reservation.
  • Cache the base images. Bundles request 6 GB of storage each and build a Dockerfile per validation. Layer reuse across a round is the difference between a build-bound and a run-bound pipeline; the pool's median build timeout of 600 seconds is the budget being defended.
  • Record provenance per accepted task. RST writes provenance.json alongside each accepted bundle and keeps parent, operator, family, cohort, repair count, and initial and final validation result in the round manifest. When a pool later turns out to teach the wrong thing, this is the only way to find and remove the lineage responsible.

Where the paper and the release disagree

Four discrepancies surfaced from reading the paper against its own artifacts. None of them invalidates the method; all of them change what a reader should quote.

The conclusion contradicts Table 4 on the headline RL result. Table 4 and the abstract both report Qwen3.5-27B-RL at 49.44 on Terminal-Bench 2 for a +20.00% relative gain over the 41.20 base. The conclusion instead states 46.07 and +11.82%. The two are internally consistent within their own paragraphs (41.20 x 1.1182 = 46.07), so this is a stale figure in one place rather than an arithmetic slip. The abstract, the intro's absolute gain of 8.24 points, and Table 4 all agree on 49.44; the conclusion is the outlier.

Fifteen rounds are claimed and eleven are reported. R11, R12, R13, and R14 appear nowhere in the paper: every figure and table steps from R10 straight to R15. The "no observed ceiling" claim rests on yield and pass-rate stability across a series with four consecutive unreported points in it.

The PPO result is not a clean base-model comparison. The actor starts cold from released Qwen3.5-27B base weights, but the PPO value head is warm-loaded from a prior terminal-agent critic checkpoint and given two critic-only warm-up steps. Some of the +20.0%, +41.2%, and +21.9% relative gains is carried by a critic trained on terminal-agent data outside this pipeline. The run is also short: the reward curve spans about 60 steps and moves the five-step moving average from roughly 0.11 to above 0.14.

The released trajectory corpus is not the corpus the paper describes. The paper describes self-collected Qwen3.5 rollouts. The released 327,189 trajectories were produced by four distinct models: qwen35-27b-iter0000161-hf (145,848), gpt-oss-120b (83,271), Qwen3.5-27B (59,259), and Qwen3.6-27B-base (38,811). The latter two names appear nowhere in the paper. The gpt-oss-120b slice is close to unusable on its own terms, with a 74.5% exception rate and a 6.9% pass rate. Filter by model_name before training on this corpus.

Failure modes

  • Instruction-verifier drift. The verifier tests something the instruction never stated and the workspace never revealed. The task then measures guessing. RST's contract-validity condition and its requirement-discoverability rules exist for this; its own audit shows the problem is real, with weakly grounded tasks at 32.8% in R1 falling to 1.2% by R15 only after explicit attention.
  • Verifier mirrors the solution. Checks copied from the reference transcript reject valid alternative solutions, converting an RL reward into an imitation loss. Derive checks from the contract and assert outcomes, not command sequences.
  • Shortcut acceptance. A candidate passes because the solution writes a placeholder the verifier happens to accept. Every operator card must name the shortcuts to reject, and the contract must require a check that rejects empty, stale, and hard-coded artifacts.
  • Silent metadata rot. Difficulty and resource fields inherited from the seed and never re-derived, as measured above. A curriculum or a scheduler reading them will be wrong in the same direction for the whole pool.
  • Repair loops that regenerate instead of repairing. Unbounded repair turns a failed candidate into an unrelated new one, and the round's diversity and lineage statistics silently describe something else. Bound the rounds, restrict writes to the allowlisted files, and forbid repairs that weaken semantic checks or move requirements into the instruction.
  • Lineage collapse under a broken parent cap. If parent identity is lost through path truncation or manifest churn, the cap stops binding and the pool concentrates without any statistic noticing. Verify recoverable ancestry before selection, after selection, and after materialization.
  • Mistaking a low pass rate for good difficulty. Pass rate falls both when tasks get harder and when they get broken or under-specified. Pair it with partial credit and with the fraction of failures that are near-misses; RST's near-miss share collapsing from 86.4% to 1.2% is what distinguishes genuine difficulty from noise.
  • Treating synthesis cost as total cost. The per-task generation price is the smaller number. The recurring container cost of rolling out that task, every epoch, for the length of a training run, is sized in the rollout sandbox fleet and is where the money goes.

References

  • Li et al., "Recursive Synthesis for Long-Horizon Terminal Tasks" (RST), arXiv:2608.05466v1, 2026-08-05. https://arxiv.org/abs/2608.05466
  • RST project page and lineage case viewer. https://zhongzhi660.github.io/recursive-verified-synthesis-site/
  • RST artifact collection (datasets and checkpoints, CC BY 4.0). https://huggingface.co/collections/Zhongzhi1228/recursive-task-synthesis
  • Recursive-Task-Synthesis dataset (37,484 task bundles). https://huggingface.co/datasets/Zhongzhi1228/Recursive-Task-Synthesis
  • Recursive-Task-Synthesis-Trajectories dataset (327,189 trajectories). https://huggingface.co/datasets/Zhongzhi1228/Recursive-Task-Synthesis-Trajectories
  • Harbor: a framework for evaluating and optimizing agents and models in container environments. https://doi.org/10.5281/zenodo.20953922
  • Merrill et al., "Terminal-Bench: Benchmarking agents on hard, realistic tasks in command line interfaces", arXiv:2601.11868. https://arxiv.org/abs/2601.11868
  • Li et al., "Long-Horizon-Terminal-Bench", arXiv:2607.08964. https://arxiv.org/abs/2607.08964
  • Ivison et al., "TMax: A simple recipe for terminal agents", arXiv:2606.23321. https://arxiv.org/abs/2606.23321
  • Chu et al., "TerminalWorld: Benchmarking agents on real-world terminal tasks", arXiv:2605.22535 (RST's bootstrap seed source). https://arxiv.org/abs/2605.22535
  • Shen et al., "SETA: Scaling environments for terminal agents", arXiv:2607.10891. https://arxiv.org/abs/2607.10891
  • Gandhi et al., "Endless Terminals: Scaling RL environments for terminal agents", arXiv:2601.16443. https://arxiv.org/abs/2601.16443
  • Hua et al., "CLI-Universe: Towards verifiable task synthesis engine for terminal agents", arXiv:2606.22883. https://arxiv.org/abs/2606.22883
  • Tu et al., "ScaleEnv: Scaling environment synthesis from scratch for generalist interactive tool-use agent training", arXiv:2602.06820. https://arxiv.org/abs/2602.06820
  • Raoof et al., "OpenThoughts-Agent: Data recipes for agentic models", arXiv:2606.24855. https://arxiv.org/abs/2606.24855

Related: Rollout sandbox fleet · Agentic & tool-use RL · RLVR · Reward design · Synthetic data generation · Autodata · Training-data curation · Evaluation integrity · DevOps-Gym · Rollout fleet sizing · Post-training system map