ADR-040: Candidate corpus¶
Status¶
Accepted; amended by ADR-041 (D7, D11).
Decision¶
D1 -- A candidate is what the scanner reported on, not what a search matched¶
A repository joins only when the shipped obelize scan reports a legacy usage in it at the pinned
SHA, not when code search matches it: the scanner is what the benchmark measures. Candidates come
from the gate-1 sample and D5's draws.
D2 -- No SPDX identifier, no candidate¶
none and NOASSERTION are rejected, because unlicensed code cannot be quoted in an error
analysis (BENCHMARK.md). The licensed share of
the frame is published beside the corpus.
D3 -- archived is recorded and never disqualifies¶
A frozen repository reproduces; the flag only tells a reader of a wrong tier the code is frozen.
D4 -- A pack surface is a change id together with the legacy symbols its parameters name¶
A repository exercises a change when a finding at its SHA carries a symbol the change's pack.yaml
parameters name (or, for flag-legacy-module-reached-indirectly, a pattern). Not grep, which says
whether a token appears, not whether the scanner resolved a usage.
D5 -- An unexercised surface is resolved three ways, in order, and never by silence¶
Each change no candidate exercises gets a dated, verbatim code-search query with its hit count and
one outcome: drawn (an eligible, scan-confirmed result becomes a candidate), no_eligible_result
(with the number examined and why each was passed over) or absent (zero hits).
D6 -- The anti-inflation rules are enforced here, one step before the case¶
Rejected: forks, a third repository of one owner, development-corpus owners, repositories over
150,000 KB or gone at their SHA, and any that vendors the legacy SDK's own source (a finding
under google/generativeai/), since its case would migrate the library. Every rejection is
published with its reason.
D7 -- A candidate is not a case¶
A candidate is a repository, SHA, licence, owner and scan; pins, install, tests, split and ground
truth belong to the case (ADR-041). A candidate is retired, with a reason and never deleted, only
when it cannot be a case: gone, unresolvable SHA or changed licence. One with no runnable suite
stays, capped at patched_unverified.
D8 -- Whether a repository has a test suite at all is measured now¶
A repository with no tests cannot reach verified_success, and the file list shows it. It is
recorded as path evidence (test_files, test_config, ci_workflow, manifest), never as a
judgement of the tests.
D9 -- pattern_key is computed now¶
The scanner's pattern_key (sorted legacy symbols of the call sites) is computed per candidate, so
the second headline denominator is visible before any case runs.
D10 -- The table is generated, checked, and recomputed by a test that does not read it¶
bench/candidates.py renders the bench/CANDIDATES.md block and --check fails when it is stale.
tests/unit/test_candidates.py recomputes the selection from the frame, the scans and pack.yaml.
D11 -- The negative controls are T27's, and what the scans already know about them is written down here¶
A repository declares google-genai only with a row at a position no legacy pin holds; a row at a
legacy pin's position is manifest-dependency's proposed add. The list is computed from the scans.
Declaring with legacy call sites makes a manifest/code-mismatch candidate; without, control
material. The controls are drawn by ADR-041 D9.
Consequences¶
- Every candidate's findings,
pattern_keyand surfaces recompute offline from its scan record. - The licence rule removes over half the frame and biases the corpus toward licensed repositories.
- Every
pack.yamlchange appears once in the surface table; a pack change that moves its symbols needs D5 again.