Harness published — no results yetThe apparatus is built and reproducible. It has not been run here, so there are no measurements on this page and none are implied.

Original research

A Seeded-Defect Corpus for Testing AI Code Review

Ten deliberately planted defects with a machine-readable answer key and a scoring harness, so claims about what AI review catches can be checked rather than asserted.

The question

Of ten deliberately seeded defects across seven categories, how many does an AI code reviewer find — and how many of its findings come with a reproduction rather than a hedge?

Written by
The Copilot Stack Editorial Team
Published
Last verified
Reading time
3 min

Environment

  • Python 3.11+
  • Corpus: projects/copilot-code-review-corpus/ in this repository
  • Answer key: ANSWER_KEY.json, 10 defects, 7 categories
/research/code-review-corpus/

The problem with “it catches security issues”

Claims about AI code review are almost always unfalsifiable. There is no corpus, no ground truth, and no way to check. This is the apparatus that makes the claim testable, and it is deliberately unglamorous.

What is in the corpus

src/orders.py — one file, ten seeded defects across seven categories:

IDCategorySeverityDefect
D01InjectionCriticalSQL built by concatenation
D02Path traversalCriticalUser segment joined into a path unchecked
D03CryptoHighSecret compared with ==
D04InjectionCriticalShell command concatenated with shell=True
D05CryptoMediumMD5 for an identifier — arguable, scored separately
D06ConcurrencyHighCheck-then-act without the lock
D07CorrectnessHighDecimal round-tripped through float
D08ResourceMediumFile handle leaked on the error path
D09AuthorizationCriticaluser_id accepted and never checked
D10Error handlingMediumBare except returns a valid-looking 0

Every trigger in the answer key has been executed and confirmed, except D06, which is timing-dependent by nature and marked as such.

That verification mattered. The first draft of D07 claimed Decimal("0.70") less 10% would not yield exactly 0.63. Running it showed it does — the float round-trip happens to land exactly there. A sweep of 4,000 values found real divergences, and the key now reads Decimal("0.01") less 10% → 0.009000000000000001. A corpus with an untriggerable answer is a broken corpus, and the only way to know is to run it.

The three scoring rules

A finding without a trigger is not a hit. “This may be vulnerable to SQL injection” and “an order_id of ' OR '1'='1 returns every row” cost the same to produce and differ entirely in usefulness. Requiring the trigger is what separates a review from a list of adjectives.

Recall and precision are reported separately, never folded into one score. A reviewer that finds everything and flags twelve non-issues is a different problem from one that finds half and flags nothing, and one number hides which you have.

D05 is excluded from the headline. Whether MD5 for a non-security identifier is a defect is genuinely arguable. Counting it either way bakes an opinion into the ground truth, so it is reported on its own line.

Running it

cd projects/copilot-code-review-corpus
# point your reviewer at src/orders.py, then:
python score.py findings.json

Sample output, from a deliberately partial run used to test the scorer itself:

Scored defects: 9  (excluded as arguable: ['D05'])

  Found with a trigger   4 / 9   recall 44%
  Reported without one   1   ['D03']
  Not in the answer key  1   (false positives, or real defects we did not seed)
  Precision              80%

That output is the scorer being tested, not a code review tool being measured. It used hand-written findings to confirm the harness rejects an unsupported claim and flags an unknown id.

What a good result looks like

Not 10/10. The interesting signal is which defects a reviewer misses.

The three injection and traversal defects are pattern-matchable and most tools find them. D06, D07 and D08 are the discriminating ones — a race condition, a decimal round-trip and a leaked handle on an error path all require reasoning about execution rather than recognising a shape.

A reviewer that finds the four critical defects and misses all three of those is a reviewer that reads code the way a linter does, which is worth knowing before you put it in front of a pull request.

Limitations

What this cannot tell you. Stated in the frontmatter so it cannot be dropped in an edit.

  • No results are published on this page. GitHub Copilot is not available in the environment where this site is built, so the experiment has not been run here and no measurement is implied.
  • Ten defects in one file is a small corpus. It is enough to distinguish a reviewer that reads error paths from one that does not; it is not enough to rank two reviewers that both score well.
  • The defects are seeded, which means they are findable by construction. Real defects hide in code someone believed was correct.
  • D06 (a check-then-act race) is timing-dependent and may not reproduce on a single run. It is marked as such in the answer key.
  • Scoring rewards a stated trigger. A reviewer that identifies a real defect but cannot say how to reproduce it scores as unsupported, which is a deliberate editorial choice rather than a neutral measurement.

Reproduce this

Clone projects/copilot-code-review-corpus/, point any reviewer at src/orders.py, ask for findings with a concrete triggering input for each, write them to findings.json in the documented shape, then run `python score.py findings.json`. The harness reports recall and precision separately and lists what was missed.

Raw results: /research-data/code-review-answer-key.json

Sources

Every version-sensitive claim on this page was checked against first-party documentation. Only sources actually used are listed.

Primary sources

Cite this research

Free to use, republish and build on. A link back is appreciated and is not a condition. The one thing we ask is that theverification date travels with the numbers — several of these figures change monthly, and a table with no date on it becomes misinformation within a quarter however accurate it was when copied.

Plain citation

The Copilot Stack. "A Seeded-Defect Corpus for Testing AI Code Review." Version 2026-09-04. https://thecopilotstack.com/research/code-review-corpus/ (verified 2026-09-04).

Markdown

[The Copilot Stack — A Seeded-Defect Corpus for Testing AI Code Review](https://thecopilotstack.com/research/code-review-corpus/) (verified 2026-09-04)
BibTeX
@misc{copilotstack_research-code-review-corpus_2026,
  title        = {A Seeded-Defect Corpus for Testing AI Code Review},
  author       = {{The Copilot Stack}},
  year         = {2026},
  note         = {Version 2026-09-04. Maintained research, not peer reviewed},
  howpublished = {\url{https://thecopilotstack.com/research/code-review-corpus/}},
  urldate      = {2026-09-04}
}

@misc rather than @article or@dataset, and the note records that this is a maintained research that has not been peer reviewed. Formatting it as a journal reference would imply a review process that did not happen.

Licence. Free to reuse for any purpose, including commercially. This is original work by The Copilot Stack — the method, the harness and the results — and it has not been peer reviewed.

All research