Apply to my task
arXiv:2610.34098 ★ 79 Oct 25, 2026

TRACER: reviewing an agent's code through its development history, plus a "memory" of fixes

Key result

Of 354 agent patches, 350 passed regular tests, yet only 69 passed security tests. The code is green, but a check is missing. TRACER lets you catch such holes from the agent's work history and lock in the fix so the next task doesn't roll it back. The trick: agent statements like "the input is already validated upstream" aren't facts but hypotheses. The process takes each such claim, checks it against the current code, and after the fix writes a boundary test (a check that fails if the rule is broken). A scanner (CodeQL, Semgrep) points at the right function in only 13% of cases.

01

TL;DR

TRACER is a workflow (you can load it into an agent as a skill) that reviews agent-written code not from the final snapshot but from its edit history. It looks at which functions the agent changed and pulls in the agent's own explanations ("the input is already validated upstream," "the calling code is trusted"). Those explanations are treated as claims to be checked against the current code, not as facts. When a vulnerability is fixed, the workflow writes a note: what must stay true and which test checks it. Every subsequent edit has to pass that test.

The core pain: the agent writes a patch, all the tests are green, but a check or constraint is missing. Of 354 patches examined, 350 passed functional tests, yet only 69 passed the security tests. A security scanner (CodeQL, Semgrep) points at the right function in only 13% of cases. It sees the final code and has no idea what the agent assumed or dropped along the way. The second problem: a fix gets quietly broken by the next task. A request to "add compatibility with legacy configs" brought back unsafe file loading, and every test stayed green, because a test doesn't remember why the constraint was there.

The method tackles both. First: track changes by function, not by diff lines, and hand the reviewing model the agent's explanations as hypotheses. Second: after a fix, store next to the code an invariant (what must stay true) and a boundary test (a check of that condition), both triggered on every subsequent edit. A fix is accepted only through a "gate": a confirmed assessment, a check of the calling code, and passing functional tests.

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.

02

Method Blueprint

PHASE A — reviewing the agent's patch
STEP 1: Find the changed functions and classes (not diff lines) → list of symbols
STEP 2: Attach the agent's explanations from its log to each function → quote + verification plan
STEP 3: Check each claim against the CURRENT code → confirmed / refuted
STEP 4: Record the hypothesis in the decision log (JSON): function, input boundary, suspected flaw
STEP 5: Repair gate: confirmed assessment + check of the function's callers + functional tests
        → patch accepted / rejected

PHASE B — protecting the fix going forward
STEP 6: Write a note: invariant + reason + boundary test (stored separately from the main code)
STEP 7: Every new edit must pass: regular tests + new-feature tests + the boundary test
STEP 8: Failure → return the failed check to the agent, one attempt; if that fails → keep the protected code

All of this is one workflow inside the host agent. Separate prompts are needed only for phases A and B.

03

Example in Practice

Task: An online store's Python team accepts payments via Stripe webhooks. Claude Code rewrote the webhook handler. Tests are green, but the agent noted in its log: "the signature is already verified in middleware." A month later a manager asks: "Support the legacy notifications from our partner; they don't have a signature." You need to review the patch and make sure that request doesn't open the handler up to forgeries.

Prompt:

You are a security reviewer for code written by an agent. Follow the process below.

Changed function: handle_payment_webhook (file payments/webhooks.py)
The agent's task: "Rewrite the Stripe webhook handler, add idempotency by payment_id"
From the agent's log (its explanations, not code):
  "The request signature is already verified in middleware, so in the handler
   it's enough to parse the JSON and update the order status."

1. Treat the agent's words as a HYPOTHESIS, not a fact. Write out the claim verbatim.
2. Draw up a verification plan: which functions and calling code you need to read
   to confirm that the signature really is verified BEFORE handle_payment_webhook
   on every call path.
3. Check against the current code. Verdict: confirmed / refuted / insufficient data.
4. If refuted, propose a minimal patch. Accept it only if
   (a) the assessment is confirmed by the code, (b) every call site of the function has been checked,
   (c) functional tests pass.
5. After the fix, write a note: the invariant ("the handler never runs without a valid signature"),
   the reason, and a boundary test that sends a request without a signature and expects a rejection.
6. For any later edit to this file: run this test. If it fails,
   don't change the invariant; extend the solution within it.

Result: The model writes out the agent's quote and a verification plan. Then it walks the call chain, and for each path you'll see whether the signature is checked there. If there's a hole, you get a minimal patch marked with the checks it passed. At the end comes a note with the invariant and the boundary test code. When the "legacy partners without a signature" request arrives later, that note and test won't let anyone simply switch off verification. The model will have to propose another route, such as a separate locked-down endpoint or an IP allowlist.

04

Why It Works

The weakness. Scanners and tests see only the final code. Code with a missing check often looks fine and sails through every test. A reviewing model with no hint doesn't know where to look either: the repo is huge, with hundreds of functions.

The strength. The agent left a history: what it changed and what it said along the way. Its explanations contain assumptions: "the input is already validated," "the calling code is trusted." Each assumption becomes a concrete verification task: open the validator and see who calls the function. A model reads code well and checks it against a claim when the claim is stated explicitly. On its own it's weaker at hunting for "what could possibly be wrong here."

How the method uses this. It narrows the search to the changed functions (the right function lands in review scope almost every time, not in 13% of cases). It turns the agent's words into verifiable commitments. And the boundary test, stored separately, remembers the reason for the constraint, which regular tests don't capture.

Control knobs: - What to give the reviewer. The agent's explanations worked best (configuration C2). Adding the function's change history (parse tree, AST) made results worse on both models. More context isn't better. - Number of attempts. The study allows one attempt after a failed check. More attempts cost more; fewer may leave no chance to fix things. - The repair gate. You can tighten it: require the boundary test to run before a patch is accepted, not just after. - Who owns the boundary test. It's stored separately so the agent can't rewrite it along with the code.

05

Prompt Template

The paper has no ready prompt text, only a description of the process. The original skill lives in the authors' repository (link under Resources). The template below is a reconstruction from the process description, not a copy of the original. I kept the structure: decision log, repair gate, invariant note, maintenance gate.

<Role>
You are a security reviewer for code written by an agent. Your job is to find
missing checks and protect fixes from future edits.
</Role>

<Input>
Changed functions: {function_list}
Agent's task: {task_description}
Agent's explanations from the log (text only, no code): {agent_explanations}
Scanner warnings (if any): {scanner_warnings}
Current code of the functions: {code}
</Input>

<ReviewLogic>
For each changed function:
  1. Write out the agent's claims about inputs, calling code, libraries.
  2. Every claim is a hypothesis. Draw up a verification plan: what to read.
  3. Check against the CURRENT code (not against the explanation!). Verdict:
     confirmed / refuted / insufficient data.
  4. If data is insufficient, propose running verification code on normal
     and boundary inputs.
  5. Add an entry to the decision log:
     {"hypothesis": ..., "function": ..., "input_boundary": ..., "suspected_flaw": ..., "verdict": ...}
</ReviewLogic>

<RepairGate>
Accept a patch only if ALL conditions are met:
  - the flaw assessment is confirmed by the current code;
  - every function that calls the one being fixed has been checked;
  - functional tests pass.
Otherwise leave the code unchanged and explain what was missing.
</RepairGate>

<RepairNote>
After an accepted patch, create a note:
  - invariant: {what_must_stay_true}
  - reason: {why_the_constraint_is_needed}
  - boundary_test: an executable check that fails if the invariant is violated.
Store the test separately from the main code.
</RepairNote>

<MaintenanceGate>
On any later edit to these functions:
  1. Run the regular tests, the new-feature tests and the boundary test.
  2. If the boundary test fails, show the failed check and allow ONE attempt
     to rework the implementation without weakening the invariant.
  3. If that fails, keep the previous protected code and report it.
</MaintenanceGate>

What to fill in: the list of functions from the diff, the task text, the agent's verbatim explanations (from the log, minus code blocks), scanner results if any, and the code itself. The key field is the agent's explanations: without them the method loses its core.

🚀 Quick start — paste into the chat:

Here is the TRACER template for reviewing an agent's code. Adapt it to my task: [your task].
Ask me questions to fill in the fields.

[paste the template above]

The LLM will ask which functions the agent changed, where its explanations live, what the function does and who calls it. Without that it can't check the agent's claims against the code. Then it will write the invariant and boundary test for your project on its own.

06

Limitations

⚠️ The review effect is small: The best configuration (code + agent explanations) produced about 5 extra fixes per 345 patches on the main test. The confidence interval includes zero. The authors themselves say it's a consistent ordering across two models, not a big gain.

⚠️ More context makes it worse: Adding the function's change history on top of the agent's explanations reduced the number of fixes on both models. Don't bury the reviewer in everything you've got. The authors didn't pin down the mechanism: "context dilution" is just a hypothesis.

⚠️ The agent's explanations are unreliable: They don't reflect the model's actual reasoning. They're only hypotheses, can't be taken on faith, and the method is built on checking them.

⚠️ You need access to the agent's log: Without explanations and edit history, the method loses its main idea. It doesn't work for code written by humans.

⚠️ No gain for already-protected code: Where protection was already in place, the agent's explanations added almost nothing (0 and 2 fixes out of 110).

⚠️ A lot gets lost along the way: For one of the models, only a small fraction of cases reach a confirmed assessment (15–32 of 387). I don't see a full breakdown of the reasons in the text; it's truncated. The method doesn't catch everything.

⚠️ Narrow testing: Experiments were on Python projects only. The second study (fix memory) covers 5 components and 10 tasks — a small sample. A single type of protection (the boundary test) doesn't cover every kind of vulnerability.

07

How They Studied It

The researchers used the public SusVibes benchmark: 186 tasks in real Python repositories where an agent writes code, and functionality and security are checked separately. Of 1302 patches from seven "author" agents (Claude 4 Sonnet, GLM-5, Kimi K2 and others), 387 remained after filtering. Two reviewer models, DeepSeek-V3.2 and Gemini-3.8-Flash, tried to fix the vulnerabilities in these patches under four configurations. The baseline saw the code and scanner warnings. The others added the function's change history, the agent's explanations, or both. That came to 3096 runs in total. Hidden security tests were used only for scoring and never shown to the reviewers.

First they checked whether the right function landed in review scope: tracking changed functions gave 94.6% versus 13.4% for the scanner's primary warning. Then they counted verified fixes. The agent's explanations gave the best result on both models: 76 and 75 passing security tests versus the original 68. Adding AST history lowered the result. Functional regressions were rare, at most one case. The comparison with the baseline isn't clean: C1–C3 also get "audit hints," so what's being compared is data bundles, not individual channels.

The second experiment tested memory. Five components of structural-biology software (industry partner Nostrum Biodiscovery) were first fixed, then run through 10 feature-addition tasks. A 2×2 design: with or without a fix note, with or without a boundary test. With functional-test feedback alone, 4 of 10 tasks succeeded with 3 protection violations. With a note and a boundary test, 9 of 10 succeeded with zero violations. The surprise is that functional tests themselves nudge the model toward weakening protection: for the sake of "compatibility" it brought back weights_only=False, and every test passed.

08

Adaptations and Extrapolations

🔧 Technique: add a "boundary test" to the agent's instruction file → protection against rollbacks

In CLAUDE.md or AGENTS.md:

Before editing any file on the protected list ({file_list}), read
SECURITY_NOTES.md. For every invariant listed there, run its check
after the edit. If a check fails, don't weaken the invariant; find another way.

This is my adaptation of the invariant-note idea; the paper has no such file. The notes live in a plain file that the agent reads on every edit. Test it on your own project.

🔧 Extrapolation: the agent reviews its own PR before submitting

Before submitting the PR, write out every claim you made about input data
("this is already validated," "the calling code is trusted"). For each one, find
the place in the code that guarantees it. If you can't find it, add a check or
explicitly flag the assumption in the PR description.

This transfers the "an explanation is a hypothesis" principle to the agent's self-check. In the paper the check is done by a separate reviewer, not the author itself, and the authors didn't test self-review.

09

Resources

  • Paper: Securing Agent-Generated Code with Its Own Development History
  • Authors: Prateek Kumar Rajput, Shahzeb Khan, El-Hacen Diallo, Yewei Song, Jacques Klein, Tegawendé F. Bissyandé. University of Luxembourg, Nostrum Biodiscovery S.L. (Barcelona).
  • TRACER skill: https://github.com/pkrajput/TRACER_skill
  • Benchmark: SusVibes (evaluates functionality and security with separate test suites)
  • Related: ReAct, SWE-agent, CodeQL, Semgrep, GumTree, PAGENT

Research digest

Key result

Of 354 agent patches, 350 passed regular tests, yet only 69 passed security tests. The code is green, but a check is missing. TRACER lets you catch such holes from the agent's work history and lock in the fix so the next task doesn't roll it back. The trick: agent statements like "the input is already validated upstream" aren't facts but hypotheses. The process takes each such claim, checks it against the current code, and after the fix writes a boundary test (a check that fails if the rule is broken). A scanner (CodeQL, Semgrep) points at the right function in only 13% of cases.

Principle

The process runs in two phases. Phase A, patch review: find the changed functions (not diff lines) → pull the agent's explanations from the log → check each one against the CURRENT code → record the verdict in a log → accept the patch only through a "gate." Phase B, future-proofing: after the fix, record the invariant (what must stay true), the reason and a boundary test. Every new edit must pass that test. Repair gate: assessment confirmed by the code, all call sites checked, functional tests green — otherwise the patch is rejected. Picture a detective. They read a witness statement but don't take it at face value. Every sentence becomes a task: go and check. And the boundary test is a note in the safe: "don't touch, here's why."

Why it works

Scanners and tests see only the final code. A missing check looks normal in it. A reviewing model with no hint gets lost too: hundreds of functions, no idea where to look. The agent left a trail: what it changed and what it said. Its words carry assumptions: "the calling code is trusted," "the data is already sanitized." Each assumption is a ready-made assignment: open the validator, see who calls the function. A model is bad at hunting for "what's wrong here" but good at checking code against a clearly stated claim. Part two: regular tests don't remember why a constraint existed. A request to "add compatibility with legacy configs" brought back unsafe file loading, and every test stayed green. The boundary test stores the reason separately from the code, so the agent won't rewrite it along with the code. The funny part: they also gave the reviewer the function's change history (parse tree, AST). Things got worse on both models. Code plus the agent's explanations worked best. Honestly, about scale: the review gain is small, roughly 5 extra fixes per 345 patches. The confidence interval includes zero. The authors themselves call it a consistent ordering, not a big jump.

When to apply

Code security → reviewing patches from Claude Code, Cursor and other agents, especially when tests are green and the log contains phrases like "this is already checked upstream." Also: projects where tasks come one after another and new features might quietly roll back old fixes. NOT a fit for code written by humans: there's no log with explanations. No gain for already-protected code: the agent's explanations added 0 and 2 fixes out of 110. Experiments were Python-only. For the second part (fix memory) the sample is small: 5 components and 10 tasks.

Mini recipe

1. Gather the evidence: the list of changed functions, the task text, the agent's explanations from the log. Drop the code blocks from the log; you only need the words. 2. Write out the agent's words verbatim: "the signature is already verified," "the input is safe." Every phrase is a suspect. 3. Draw up a verification plan: which functions and calling code to read to confirm or refute the phrase. 4. Reach a verdict from the CURRENT code: confirmed, refuted or insufficient data. Not enough data — run verification code on normal and boundary inputs. 5. Keep a log: function, input boundary, suspected flaw, verdict. JSON works best. 6. Run the patch through the gate: assessment confirmed, all call sites checked, functional tests green. Didn't pass — patch rejected. 7. Write a fix note: invariant, reason, boundary test. Store the test separately from the main code. 8. Cover future edits: every new task runs the regular tests, the new-feature tests and the boundary test. 9. Allow one attempt on failure: show the agent the failed check and let it redo the work without weakening the invariant. Didn't work — keep the previous protected code.

Examples

[BAD]: `Check this code for vulnerabilities` [GOOD]: `You are a security reviewer for code written by an agent. Function: handle_payment_webhook. In its log the agent said: "The request signature is already verified in the middleware layer." 1) Write out this claim verbatim as a hypothesis. 2) Draw up a plan: which call sites to read to confirm the signature is verified BEFORE the function on every path. 3) Check against the current code: confirmed / refuted / insufficient data. 4) If refuted — a minimal patch. Accept it only if every call site has been checked and functional tests pass. 5) Write the invariant and a boundary test: a request without a signature must be rejected.` [BAD]: `Add support for partner notifications without a signature` [GOOD]: `Add support for partner notifications. First run test_webhook_rejects_unsigned. If it fails, don't weaken signature verification. Propose another route: a separate locked-down endpoint or an IP allowlist.`

In plain words

AI agents write code that looks perfect, tests are green, but inside lurks an epic hole. Ordinary scanners blow it because they only look at the final snapshot of the code. The real mess hides in the model's lazy assumptions: the model confidently writes in its logs that "authorization is already checked somewhere upstream" and just skips validation. The TRACER tool breaks this blindness: it judges the agent's work not by the final file but by the history of its reasoning.

It's like a slick contractor who patched a hole in a load-bearing wall with drywall and swears "it's all held up by the neighboring beam in there". A regular inspector knocks on it — looks level, wallpaper's up, sign-off done. But TRACER pulls the log of concealed work, holds the builder to their own words and demands they open up the wall: prove it with the numbers right now, no fairy tales.

The mechanics are simple: the system turns every excuse the agent makes into a hypothesis under strict audit. The bot wrote that "the input is filtered"? The pipeline digs into the architecture and fact-checks that claim. As soon as the bug is fixed, the process records a security invariant and generates a targeted test. Every later edit has to pass it: if a new request breaks the old rule, the deploy gets blocked cold.

The study ran payment gateways through the wringer, but the principle is universal. It's critical for any microservices, parsers and backends where you let autonomous coders like Claude Code or Devin loose. Anywhere a model tries to cut a corner and counts on "the neighboring module", checking the development context keeps production from disaster.

Stop blindly trusting code just because all the tests are lit up green. Make the agent validate its own guesses and turn every bug you find into a permanent barrier against regressions. Either you keep a tight grip on the model's train of thought, or one day your payment gateway will accept Monopoly money instead of a million dollars.

AI lab assistant

Apply this study to your task

Describe what you're working on. AI takes the method and the measured results from this paper and adapts them to your case, or writes a ready prompt built on them.

Based on TRACER: reviewing an agent's code through its development history, plus a "memory" of fixes arXiv:2610.34098

Sign in to apply this study New accounts get 50 free credits.