TL;DR
RucTangle is a method that turns one enormous diff from a coding agent into a tidy chain of small commits. Each commit serves one purpose: a feature, a refactor, or a bug fix. Here's the mechanics: the agent first proposes a plan (which edits go into which commit, and in what order), then the commits are applied one at a time. After each one, the system checks that the project still runs. If the check fails, the plan gets repaired along a ladder: reorder the commits, regroup the edits, and as a last resort merge neighboring commits.
The headline finding: existing patch-splitting methods don't think about whether the code runs. The model slices the diff "by meaning," and the intermediate versions often break. Classic example: one commit removes an old name from a dictionary, and the next commit updates the reference to it. In between, the project crashes. With the baseline methods, anywhere from a fifth to a third of histories contained at least one broken intermediate version. A history like that can't be reviewed properly, can't be rolled back piece by piece, and can't be run through git bisect.
The core of the method: plan → run check after every commit → repair the plan along the ladder, while the final code stays exactly the same. A side benefit: on a working history you can run git bisect and find the commit that broke the tests. Hand that history and the culprit commit to another agent tasked with fixing the regression, and it succeeds noticeably more often. The relative gain in successful fixes averages about 17%, versus 4–6% for competitors.
Want this for your own case? Apply this study to your task: AI adapts its method to what you are working on and writes the prompt.
Method Blueprint
STEP 1: Agent reads the diff → commit plan: [group of edits + message stating the purpose], in dependency order
(if a single edit mixes purposes, the agent splits it into parts)
STEP 2: Plan check: every edit lands in exactly one commit, no empty commits
→ apply commits one by one from the base version, run check after each
STEP 3: If the check fails, climb the repair ladder:
3a. reorder commits (a module shows up later than it's used)
3b. fresh agent session: plan + error text → regroup/reorder (up to 2 times)
3c. still failing → merge the failing commit with the next one until the version runs
STEP 4: The final result must match the original patch byte for byte
STEP 5 (optional): git bisect on failing tests → candidate commit → into the repair agent's context
Steps 1–3 can run in a single agent session with git and terminal access. The author of the method drove them with an orchestrator written in code.
Example in Practice
Task: In one evening, Claude Code rewrote payment processing in a Django store to go through Stripe. The result is one diff across 40 files: a new integration, renamed models, and, "while it was in there," a fix for a sales-tax bug. Reviewing this is impossible. If something breaks in production, you can only roll back everything at once, and you definitely don't want to roll back the sales-tax fix.
Prompt:
You are a meticulous developer preparing a large patch for review.
Context: the feature/stripe branch contains all my changes relative to main.
They're mixed together: the Stripe integration, renaming models Order → Payment,
and a fix for the sales-tax calculation.
<Task>
Split the changes into a sequence of commits where each commit
serves ONE purpose (feature / refactor / bug fix).
</Task>
<Rules>
1. First draft a plan: a list of commits, each with a message stating its purpose and a list of files/hunks.
Every edit goes into exactly one commit. Show me the plan before executing.
2. The order respects dependencies: a module must appear before anything imports it.
3. If a single hunk mixes several purposes, split it.
4. After EACH commit, run `pytest --collect-only`. The version must run.
5. If the check fails:
a) first try reordering the commits;
b) if that doesn't help, regroup the edits (2 attempts max);
c) if it still fails, merge this commit with the next one.
6. Edits that can't be separated without breaking things stay together.
7. At the end, compare the final state with the original branch (`git diff`).
There must be no difference. If there is, fix it.
8. Do not fix correctness tests. The job is only to reorganize the history.
</Rules>
At the end, output: the list of commits, the run-check result after each,
and the result of the final comparison.
Result: first the model shows a plan of several commits with purpose messages: say, the model refactor on its own, the integration on its own, the sales-tax fix on its own. Then it makes the commits one at a time and runs test collection after each. If an import breaks somewhere, you'll watch it reorder or merge commits. At the end you get a report: the list of commits, the run status of every version, and confirmation that the final code matches the original.
Why It Works
The weakness: the model slices the patch "by meaning" and can't see which edits are coupled at runtime. A rename in one place and a usage in another look like two different purposes. In reality they can't be split, or you get a KeyError or ImportError between commits. Judging "by meaning" won't reveal that. You need an actual check.
The strength: the model is good at grouping edits by purpose and writing clear messages. It can also read an error message and figure out what's wrong: "the module shows up later than it's imported." That gives it a precise repair signal.
How the method exploits this: the model does the grouping, while the environment does the run check. The error stops being a hunch and becomes a fact. The repair ladder (reorder → regroup → merge) goes from the cheapest, safest move to the bluntest one. Merging always preserves a working state, even if the price is bigger commits.
Control knobs:
- The check command. pytest --collect-only verifies that the project loads. For your stack, swap in a build, a linter, tsc --noEmit, go build. The stricter the check, the fewer commits you'll end up with.
- Number of regrouping attempts (2 in the paper). Raise it for large patches, lower it to save tokens.
- Granularity of purpose. Want big chunks? Say "5 commits max." Want finer slices? Say "every functional purpose separately."
- Show the plan before executing. Keep this if you want control over the grouping. Drop it if you want full automation.
Prompt Template
<Role>
You are a developer preparing a large patch for review. Your goal is a history
of small commits where each serves one purpose and the project runs after every one.
</Role>
<Input>
Base version: {base_branch_or_commit}
Final version (the whole patch): {branch_with_changes}
Run-check command: {check_command}
</Input>
<Step_1_Plan>
Read the diff and the surrounding code. Draft a commit plan:
- a message describing the PURPOSE (not a list of files),
- the list of edits in each commit,
- an order that respects dependencies.
Every edit goes into exactly one commit. If an edit mixes several purposes, split it into smaller ones.
Show the plan and wait for approval: {approval_needed: yes/no}.
</Step_1_Plan>
<Step_2_Check>
Apply the commits one by one starting from the base version.
After each, run: {check_command}.
Record the result: RUNS / FAILS + error text.
</Step_2_Check>
<Step_3_Repair>
If a version fails, climb the ladder:
1. Reorder the commits (use the error text to figure out which commit must come earlier).
2. If that doesn't help, regroup/split the edits based on the error. {max_attempts} attempts max.
3. If that doesn't help, merge the failing commit with the next one. Repeat until the version runs.
After every change, re-check ALL intermediate versions.
</Step_3_Repair>
<Constraints>
- The state after the last commit must exactly match {branch_with_changes}. Verify with git diff.
- Don't change code, don't fix bugs, don't improve anything: only reorganize.
- If the final version doesn't run on its own, stop and report it.
</Constraints>
<Output>
Table: commit # | purpose | runs (yes/no) | was it merged.
Then: the result of the final comparison.
</Output>
What to fill in: {check_command} is whatever actually tells you "the project is alive" (tests, build, linter). {max_attempts} is usually 2. {approval_needed} should be "yes" for important branches.
🚀 Quick start: paste this into a chat or an agent (Claude Code, Cursor):
Here's the RucTangle template. Adapt it to my task: {describe: repository, stack, what the patch is}.
Ask me questions to fill in the fields.
[paste the template above]
The LLM will ask how to check that the project runs in your stack, which branches to compare, and whether you want to approve the plan manually. Without a check command the method doesn't work: the model has no way of knowing an intermediate version is broken.
Limitations
⚠️ "Runs" ≠ "works correctly": in the paper, the check is just
pytesttest collection. A commit can pass it and still contain a regression. The method doesn't guarantee that every intermediate commit is correct.
⚠️ The original patch has to run on its own: if the final version fails the check, you can't both preserve the final code and make every step runnable.
⚠️ The price of reliability is bigger commits: if edits can't be separated without breaking things, they get merged. Worst case, you end up with a single commit. So look not only at "does it run" but also at the number of commits.
⚠️ Misleading messages do damage: in the trajectory analysis, the repair agent sometimes second-guessed a correct fix over and over because it contradicted an inaccurate commit message. A bad description is worse than no description.
⚠️ Narrow evaluation: everything was tested on Python projects from a single benchmark and with two small open repair models. The effect is modest: single-digit percentage points in absolute terms.
⚠️ Full automation needs code: the paper implemented the method as an orchestrator. In a regular agent you can reproduce it with instructions, but with weaker guarantees.
How They Tested It
The idea was simple: see what happens to patches with a method that doesn't just group edits but actually runs the code after each commit. For the first test, the researchers took 131 patches from a Claude Opus 4.6 agent across 100 Python repositories (the SWE-CI benchmark). They compared RucTangle with four baseline methods. With RucTangle, every history passed the run check at every step. With the baselines, 20.6% to 37.4% of histories contained at least one broken intermediate version.
The second test was practical. The researchers collected 453 patches from six different agents, each of which broke previously passing tests. Then two other agents (running Qwen3-Coder-Next and Nemotron) were asked to fix those regressions. Three conditions were compared: no history, a history from competitors (Armchr, Atomizer), and a RucTangle history plus the git bisect result (a hint about which commit broke the tests). The first-attempt fix rate rose from 26.9% to 31.3% for Qwen and from 30.8% to 36.0% for Nemotron. The average relative gain was 16.6%, versus 4.5% and 6.1% for the competitors.
The surprise: splitting into commits helps even without bisect, but much less. That makes it unclear how much of the gain comes from the working history and how much from pointing at the "guilty" commit. Another unexpected result: a bad commit message can throw the agent off the right track. The practical takeaway: a history is useful context for an agent, but only if the messages honestly describe the purpose of the edits.
Adaptations and Extrapolations
🔧 Technique: add bisect context when fixing → the agent finds the source of a regression faster
When tests break after an agent's work, don't just ask it to "fix everything." First split the patch with the template above, then give a fresh session this:
Tests {failing_test_names} passed on {base_branch} but fail on {agent_branch}.
The history is split into commits. Run git bisect on these tests, find the commit
after which they fail, and fix the regression. Don't break other tests.
Commit messages are a hint about intent, but check against the diff itself, not just the message.
The last line guards against what the paper observed: the agent getting stuck on an inaccurate message.
🔧 Technique: add a rule to the agent's instructions file → splitting becomes a habit
In CLAUDE.md or AGENTS.md:
When working on a task that touches more than 5 files, commit in small steps.
One commit, one purpose (feature, refactor, or bug fix). After each commit,
run {check_command}. Never leave a version in the history that doesn't run.
The commit message describes the purpose, not a list of files.
This turns the method from a one-off patch cleanup into prevention: patches come out "clean" from the start. The paper's author did not test this adaptation directly.
Resources
- Paper: Runnable Commit Untangling for Coding Agents (Jiang et al., preprint). Methods: RucTangle (splitting) and TangleEval (evaluation via regression repair).
- Benchmark: SWE-CI, which measures how agents evolve and maintain existing code over several iterations.
- Compared against: Armchr, Atomizer (commit-splitting methods).
- Repositories/tools:
git bisect,pytest, mini-swe-agent (the harness for the repair agents). - An idea from industry: the Linux kernel rule that the build must work after every patch in a series. The paper's epigraph is from Martin Fowler: "Change programs in small steps."
- University names and the full author list were not given in the provided excerpt.