How Kiro fixes itself, and how we keep the fixes honest

By
MY

Myeongsoo Kim

Applied Science

PA

Patrick Chapman

Applied Science

CH

Chang Liu

Applied Science

MI

Mingwei Zheng

Applied Science

BE

Berk Cirisci

Applied Science

SA

Sam Mayers

Applied Science

JO

Joe Hsu

Applied Science

LI

Liam Bouayad-Agha

Engineering

SH

Shweta Garg

Applied Science

One sentence in Kiro’s file-read and search tool descriptions made a false claim about how the harness behaves, and it reached the model 61,742 times across a few hundred sessions. It did nothing useful, and it’s unlikely anyone would have filed a ticket for it. Problems like that are hard to find, and fixes are just as hard to judge. For example, we nearly shipped one that passed more tasks and used fewer tokens, until we split the results by host and found almost all of the gain came from a single run where the agent happened to escape a loop it usually gets stuck in. Without that host, the fix was slower, more expensive, and less accurate. A loop that grades its own fixes would have shipped it.

Loading image...Bar chart in two panels. Left, labeled ‘The average’: a single dashed light-purple Aggregate bar sits slightly above zero, annotated ‘looks like a win’. Right, labeled ‘Broken out by host’: host h1 sits slightly below zero, h2 is a tall bar far above zero annotated ‘one lucky run’, and h3 and h4 sit well below zero, showing the aggregate gain came from one host.

We built an internal tracing system to improve Kiro, our AI coding agent, from its own runs. This system, internally named kiro-trace, reads those runs to find where Kiro fails or wastes effort, proposes a change, and keeps the change only if it survives checks built to reject it. Those checks are also there to catch results like the earlier one, which looked like a win and was really just luck. Models can propose changes, but we use deterministic code to accept or reject them.

In its first week, kiro-trace opened eight pull requests to Kiro. Seven merged, one was closed for unrelated reasons, and none were sent back in code review. All seven changed Kiro’s prompts or tools, its tool descriptions, argument checks, and response behavior. Each one targeted a specific pattern we had measured, and each improved it. A few of these appear later in this post with their results. The rest never surfaced as an error a user could see, and no ticket pointed at them. A person could have found them, but only by reading far more runs than anyone has time for. This is how Kiro keeps self-improving.

The following numbers come from Kiro’s internal usage at Amazon, and each figure is a delta from an internal experiment, not a production estimate. A companion post, Continuous Prompt Evaluation, covers how we evaluate human-written prompt changes with LLM judges and live A/B tests. This one picks up after that, at automated trajectory mining, change generation, and harness self-improvement.

Why self-improvement is hard

Kiro’s core modules took 141 bug-fix commits last month, close to five a day, and those are only the failures a human caught and had time to fix. The worst problems usually don’t show up as clean bug reports. They hide in how the agent recovers from an error, how it sequences tools, and how it holds a long context, and you only notice them after reading through thousands of real runs. The tool-description sentence at the top of this post is one example of that kind of problem.

So the job is to find the places where Kiro fails or wastes effort and change Kiro so it improves without seeing regressions in other places. That is broader than bug fixing. It covers misleading tool descriptions, tool calls rejected on a technicality, wasted calls and tokens, poor recovery after errors, and instructions that no longer help. Nobody can read thousands of runs by hand to find these, so the work has to be automatic. The obvious shortcut, letting the agent improve itself end to end, is where most self-improving loops go wrong. A loop that grades its own fix will ship narrow hacks and move on. To do this properly, kiro-trace has to solve four problems at once: 1) find a failure worth fixing, 2) write a change that targets its cause, 3) keep the solution general instead of tuned to the benchmark, and 4) prove the change improved the results. The self-grading loop skips step 4, and without it there’s no way to know whether steps 1 to 3 were worth doing.

Loading image...Flow diagram of a naive self-improvement loop with five steps left to right: The agent under test; Reads its own trace, finds a ‘problem’; Patches itself, edits its prompt or tools; Re-runs once; and a dashed final box reading ‘Number went up!’, self-graded, ship it. A dashed arrow returns to the agent, labeled ‘ship the update, then do it all again, on top of whatever the last one broke’.

The loop in that picture checks each change only against the problem it was aimed at, so a new change can break something an earlier one fixed without this loop noticing. That happens when nothing outside the loop checks for regressions. In kiro-trace, candidates are also run on tasks Kiro already passes, the final candidate is compared on the full benchmark against an unchanged control, and the pull request still goes through normal code review before it merges.

How kiro-trace works

The diagram below shows one pass through the loop. Evidence from benchmarks and human-approved live-traffic complaints comes in on the left. The loop reads the failure, diagnoses it, picks one lever to change, and runs the same Kiro with the change off and on. A three-stage gate (screen, regression, confirmation) then decides. Changes that pass become a pull request for a human to merge, and rejected ones go back to diagnosis.

Loading image...Flow diagram of the kiro-trace loop. Two evidence sources, Benchmarks and Live-traffic complaints (in-pod, human-approved), feed four steps grouped as ‘the loop proposes’: Read failure (full trajectory), Diagnose (the mechanism), Hypothesize (one lever, pre-registered), and Controlled trial (same Kiro, OFF vs ON). Next comes the 3-stage gate (screen, regression, confirmation), labeled ‘the gate decides’. A pass leads to Open PR, labeled ‘human merges’, and a dashed reject arrow returns to Read failure with the note ‘a rejected fix is a success of the loop; re-diagnose’.

We always keep three roles separate, because anything that both writes a change and decides whether it worked will tend to accept it. We learned that partly from our own mistake. While judging comparisons by hand, we once read the same run three different ways, which is why the decision now sits in code. Kiro is the agent being improved. The loop proposes and applies a change. The gate, which is plain code, decides whether the change survives. Outside those three, a human makes the final merge call. Here is what we learned building each part.

Iterate on the failures, not the whole benchmark

Running a full benchmark every round is slow and expensive, and the tasks are not interchangeable. Some time out, some are really long-context tests, and some fail because of the eval harness rather than the agent. So the loop uses what it already knows about each task, its failure mode and its cost and whether it is even a real signal, to assemble a small set that covers the failure modes without the dead weight. It iterates against that small set, which is fast and cheap. The full benchmark runs only once, at the end, after a candidate has passed on the small set. We compare a full run with the change against an unchanged control run, and a change that wins on the small set but not on the full run does not ship.

Ground the reviewer in Kiro’s own history

A generic LLM judge can read any run and suggest a fix, but it does not know your agent loop or your inference layer. The kiro-trace reviewer reads Kiro’s real code-review comments and developer discussions, so it flags what this team would flag. The ideas it reviews come from evidence. When Kiro fails a task, the loop runs a panel of reference agents on the same task, with the same inputs and budget. Reference agents are other strong models and agents whose runs we compare with Kiro’s. When one succeeds where Kiro failed, that difference is a concrete behavior the loop can turn into a fix. A separate research step pulls in outside work, from papers to public code to how peer agents solve the same problem, for the general technique. It doesn’t copy their code. Every candidate records the failure it diagnosed and the evidence it cited, so any fix traces back to its reasoning. By default it tries removing something before adding a rule, because modern models already know a lot.

Decide on the delta, through checks the model cannot reach

Running a fix a few times and reading the average won’t give you consistent results, because one outlier can swing it, as in the earlier example. So a change runs an escalating set of checks, starting with the cheapest, and most candidates fail before the expensive checks run. Unlike a plain A/B test, we measure the delta rather than the score, re-running the control every time, so a reported gain is the contribution of that specific change, and not something a new model would have produced anyway. And a change has to show it did not regress against a set margin, because at small samples a weak test finds nothing significant by default but can introduce real harm. Finally, a contamination check throws out any pass where the agent used the network to look up the task’s answer, for example by fetching its source or tests, instead of working it out.

Loading image...Funnel of three stacked gate stages. Stage 1, Mechanism screen, runs on every candidate and is cheap: on the target failures only, 10 runs per arm, it requires ON = 10/10 and OFF ≤ 4/10 for a false-positive rate of at most 0.5%, and most rejected candidates stop here. Stage 2, Regression screen, also runs on every candidate and is cheap: the permanent suite of everything ever fixed must hold 100% of assertions, and one failure is a hard stop that sends the candidate back to re-diagnose. Stage 3, Offline confirmation, runs pre-ship only at about 890 runs: the full 89-task benchmark at 5 runs per arm requires superiority on target and held-out tasks and non-inferiority within a margin δ elsewhere. A candidate that passes all three is ship-eligible, and a human does the final merge.

Some example fixes

Because weak candidates get cut early, the changes that reach a human reviewer are usually meaningful ones. Here are some of them. Each number is an internal experiment delta.

  • A false claim about harness behavior on every file read and search. The model saw it 61,742 times across 445 samples and nothing acted on it, so we removed it.
  • Guidance that told the model to call a tool that does not exist. We deleted it.
  • Redundant file re-reads cut about 83 percent, by not re-reading a file that has not changed.
  • Two over-strict or under-documented tool contracts fixed, clearing 205 read-tool schema rejections and 167 sub-agent orchestration rejections across 80 sampled sessions.
  • Type-strict tool inputs stopped failing on a technicality. When the model returned a boolean as the string “true” instead of a raw boolean, the call was rejected and the work stalled. Now those values are corrected on retry, so a call with the right intent goes through.
  • A silent file-search bug that broke roughly one in eight searches. A bad spec returned empty results with no error, so the agent searched and found nothing. Fixed.
  • Over-eager delegation cut at the source, by removing the blanket delegate instructions that spun up sub-agents for simple direct edits, while keeping delegation where it helps.

Adding a stricter reference agent to the panel turned up something we weren’t looking for. A relative-path bug that failed 13–17 percent of that agent’s reads showed up on the model we ship as only 1.6 percent. The more tolerant model was absorbing a spec defect without error. Correcting the spec helps every model that shares the contract, and that fix is in review now.

The largest change has not shipped yet and is under team review. By removing aggressively, the loop built a smaller tool-and-prompt profile that scored 82.4 percent against 77.5 percent pass@1 (the share of tasks solved on the first attempt) on Terminal-Bench 2.1, at roughly a third of the token cost. It hasn’t shipped because it would change the default tools and prompt for every Kiro user, which is a product decision more than a benchmark one. The team is deciding whether to make the smaller profile the default, and which of the removed tools should come back only when a task needs them.

How kiro-trace raised compaction retention from 0.82 to 0.92

The fixes above are small and separate. Compaction is worth walking through from start to finish, because it shows the loop working on one problem from diagnosis to release. Compaction is how Kiro summarizes a long conversation when it reaches its context limit, so a fresh instance can continue. The diagram below shows how we picked the fix: 11 candidate summary prompts (two from Kiro, five from outside work, four of our own), tested head to head on whether a fresh agent could continue from the summary alone.

Loading image...Diagram titled ‘The 11 candidate prompts’. Three source boxes, each reproduced as a prompt, feed one funnel: Kiro’s own, 2 (Kiro IDE summarization and Kiro CLI); From outside work, 5 (production coding agents and public handoff practices); and Our own variants, 4 (retention, efficiency, narrative). The funnel holds 11 candidates tested head to head on the question ‘can a fresh agent continue from the summary alone?’, and most are rejected. It narrows to 1 winner, a terse template plus kept recent turns, which leads to a final box reading 0.82 to 0.92 retention, shipped.

Diagnose

After a compaction, Kiro could abandon its current task and confidently resume a much older one. The old design appended a raw, newest-first dump of past user messages to the end of every summary, so the summary ended on the oldest question, right before the “resume from the summary above” cue. When the triggering message was a bare “yes, do that,” its context had been summarized away while the old thread survived word for word, so the agent tied the confirmation to the wrong request.

Measure

kiro-trace built a benchmark from real internal conversations that had actually hit compaction, in four size bands up to about 1.5 million tokens. It judged a summary by whether someone could pick up the work from it: it handed each candidate summary to a fresh model with nothing else and scored whether it could carry on. The score covered four things: was the summary actionable, were the file paths there, were earlier decisions stated, and did the thread hold together.

Draw on outside work

kiro-trace read how a range of production agents and public handoff practices compact, and rebuilt each as a prompt, 11 in all. Five came from outside our own code. They varied widely. Some were long multi-section templates, some were terse fixed ones, one was a situation-action-result narrative, and one was a step that decides when to compact.

Find the lever, and ship

The fix was to carry less context across a compaction. Before, Kiro kept the summary plus a raw list of old user messages, and that list was what sent the agent back to the wrong task, so reordering it would not have been enough. The outside prompts showed us the range of designs to compare against, but the idea to drop the list came from our own diagnosis of the failure. The fix removed it. A compaction now keeps a short summary, written to a terse fixed template and capped in size, plus the most recent turns. It ends with a static resume cue that names no task. A session can have several tasks in flight, and guessing one is what caused the wrong binding. Retention went from 0.82 to 0.92, and it shipped.

The loop keeps that same record for every candidate: the failure it diagnosed, the evidence and outside technique it used, the one thing it changed, and the result it measured. No part touches the task’s own answer.

How we keep improving the loop

kiro-trace keeps improving the loop alongside Kiro. It started from checks drawn out of our own prior research and code, then grew from mistakes. Each new check exists because the loop or the team was fooled once in some specific way, and most of those were mistakes in how we measured, not bugs in Kiro.

In one case, a correct fix scored 0 out of 5 because its output never reached the scorer. In another, a task’s setup gave the agent a way to undo its own mistakes, which hid a real bug. And a stale checkout once led us to open a pull request for something upstream had fixed the day before. Each of these became a check in the loop’s code.

Every confirmed fix also adds a deterministic assertion about its run to a permanent regression suite, so a failure fixed once does not return without being caught. Those checks come from one team today. The next step is to build them based on Amazon’s own resolved incidents and tickets, lessons the company has already paid for, so customers end up with a more reliable agent and a better developer experience.

Conclusion

kiro-trace finds problems in Kiro’s own runs, proposes one change at a time, and keeps a change only when deterministic checks and then a human reviewer accept it. In one week that produced seven merged fixes, and the compaction work raised retention from 0.82 to 0.92.

Takeaways

  • Separate the proposer from the judge. Put accept and reject in code the model cannot reach.
  • Decide on deltas, and re-run a control every time, because a new model alone can move the score.
  • “No significant harm” is not the same as no harm, and small harms add up quickly across changes. Test against a margin.
  • Turn every confirmed fix into a permanent regression check on the mechanism that failed, so it covers more than the one outcome.
  • Try removing before adding. The safest wins are often removals, especially as models get better.

For Kiro users, that means fewer wasted calls and more careful delegation, and Kiro keeping better track of what it was doing in long conversations. No change from kiro-trace reaches users until it has passed the gate, with checks built to reject it, and then a human code review. We judge an agent experiment first by what it rejects. Most candidates never make it past the gate, and the ones that do are the changes that can meaningfully improve the developer experience.

You can try Kiro today.