docs: retire the SZZ-pool metric, add the valid-benchmark brief, plain-English pass

The frozen SZZ-labeled pool is not a valid benchmark. SZZ puts the vulnerability in
the wrong place about half the time, and ~76% of entries are library-internal (not
reachable in-repo), so its catch/false-alarm numbers (record 07's "3/92") are noise.
Retired as a metric and a gate across the roadmap and briefs; no pool-scored number
is quoted anywhere.

Added .kiro/specs/valid-detection-benchmark/brief.md: positives labelled from the
fix (not SZZ), kept only when app-shaped and reachable in-repo, each verified;
ordinary negatives from the same repos; unseen repos; the whole tool measured; plain
metrics (catch rate, false-alarms per 100 pushes, precision, proved-catch).

Ran a plain-English pass (the humanizer rules) on the roadmap, README, and the two
active briefs via Codex, then reviewed the diff: only these files changed, every
number/path/hash/command is unchanged, and the retired-metric decision is intact.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Marius Ciepluch
2026-10-12 16:38:18 +02:00
co-authored by Claude Opus 4.8
parent c3f610f9f4
commit b08f372022
4 changed files with 455 additions and 371 deletions
@@ -0,0 +1,85 @@
# Valid detection benchmark
Created 2026-10-12. This brief **supersedes the frozen-SZZ-pool evaluation** (`unseen-repo-evaluation`), which is retired.
SZZ guessed its labels and placed the vulnerability in the wrong location about half the time (audit: path 1/7,
output-encoding 1/8 sound). Also, ~76% of entries are library-internal (reachability not in-repo). Catch /
false-alarm numbers from that pool are not valid benchmark results. This brief defines a benchmark with
sound ground truth and metrics that engineers and managers can understand.
## The two questions the benchmark answers
A pre-push security check must answer two questions:
1. **If I'm about to push a real vulnerability, does it stop me?**
2. **If my change is fine, does it leave me alone?**
The benchmark runs the **whole tool** as a developer uses it at push time. Quick rules and the engine are on,
as is the LLM-guided arm. The repositories are ones the tool has **never seen**.
## What makes the data valid
### Positive cases — "should catch" (real, verified, reachable vulnerabilities)
- Take the label from the security fix, never from SZZ. Use the code *just before* the fix, at the file/function
the fix changed. The fix establishes the vulnerability location without a blame walk-back or mislocation.
- Keep only app-shaped cases where the vulnerability is reachable from an entry point *inside the repo*. This
can be a web route or a request handler; a CLI argument also qualifies. Drop library-internal vulnerabilities
that depend on a caller outside the repo. A pre-push tool cannot see that trust boundary, so scoring it is unfair.
- Verify each case through LLM adjudication and a human spot-check. Confirm (a) that the pre-fix code is
vulnerable and (b) that the fix addresses it. Also confirm (c) that it is reachable in-repo.
### Negative cases — "should not alarm" (ordinary safe changes)
- Use everyday commits from the **same** application repositories that are not security fixes. They are plentiful
and cheap to collect. Almost every real push is ordinary, so false alarms determine whether developers keep
using the tool. The negative set must be much larger than the positive set.
### Held out
- None of these repositories were used to build, train, tune, mine or harvest the tool.
A few dozen vulnerabilities with sound labels and in-repo reachability, each verified, are better than hundreds
of SZZ guesses. SZZ is wrong about half the time. Curating this set takes manual work, supported by the LLM-audit
tooling that found the SZZ pool's defects. Dozens of good cases are enough.
## Metrics someone understands
| Metric | Plain-English question | Example |
|---|---|---|
| **Catch rate** (recall) | Of real vulnerabilities, how many did it flag? | "Caught 18 of 20 (90%)" |
| **False-alarm rate** | On safe changes, how often does it cry wolf? | "Flagged 3 of 100 ordinary pushes" |
| **Precision** | When it alarms, how often is it real? | "When it speaks up, it's right 8 of 10 times" |
| **Proof rate** | Of what it flags, how much can it *demonstrate*, not just suspect? | "12 of 18 catches came with a working demonstration" |
Report false alarms as **"per 100 pushes"**, which reflects a developer's experience. Vulnerabilities are rare
among real pushes. Precision depends on that ratio, so a 50/50 test set overstates it.
Report the first two metrics together:
> "On unseen application repos: catches 90% of real vulnerabilities while false-alarming on 3% of ordinary
> pushes."
This wording is easier for a manager to understand than "3/92, Wilson95 1.1–9.2%".
## The gold-standard metric — and the project's edge
**Proved catches** do not depend on labels. The search-with-proof / differential-exploit arm demonstrates the
bad effect on the vulnerable code and its absence on the fixed code. The demonstration *is* the ground truth.
The headline to aim for is:
> "On unseen app repos: caught and *proved* N of M real vulnerabilities, with K false alarms per 100 ordinary
> pushes."
The architecture is built to produce this kind of benchmark.
## Why the old approach was not feasible (recorded so it is not re-tried)
- SZZ ground truth is machine-guessed and ~half wrong → every catch/FP number off it is noise.
- Library-heavy corpus → most cases are unreachable in-repo, so a pre-push tool is scored on things it
structurally cannot see.
- Scoring regex-only (engine and LLM off) measured a tier nobody would ship alone.
- Automated harvesting prioritized pool size over sound ground truth and in-repo reachability.
## Open questions for the spec phase
- How many verified positives are enough for a credible catch-rate interval (target set size)?
- The negative:positive ratio that reflects real push traffic (for an honest false-alarms-per-100-pushes).
- Curation workflow: how LLM adjudication + human spot-check are recorded and frozen per case.
- Language/framework coverage of the positive set (be explicit about what is and isn't represented).