Expand description
Structural-mode candidate generation: the inverted-index seed layer.
Verifying every pair of fragments in a corpus is quadratic and hopeless at
scale, so detection never starts from pairs. It starts from an inverted
index: statement-window and subtree feature hashes (crate::features)
map to the fragments that produced them, and only fragments that landed in
the same posting list — an exact structural match under the feature recipe
— become candidate pairs. The approximate near-match layer (characteristic
vector nearest-neighbour, MinHash/LSH) plugs in behind the same
candidate-emitting interface later; this layer is the exact-match seed.
Candidate-explosion control is a first-class concern, not an afterthought
(AGENTS.md invariant 10). Two controls act here, both before any pair
leaves the stage, and both count what they drop into CandidateStats
rather than letting it vanish:
- high-frequency suppression — a hash whose posting list exceeds
CandidateConfig::posting_capis boilerplate-shaped noise that would dominate the pair budget; it is dropped whole and counted; - a global candidate upper bound — posting lists are paired
rarest-first, so when
CandidateConfig::pair_budgetruns out the high-frequency, low-signal lists are the ones sacrificed, and the set records that it was truncated.
§A list is paired whole or not at all
The ceiling stops between posting lists, never inside one. That costs coverage — the list the allowance could not hold entirely is skipped rather than half-paired — and it is worth the cost because of what grouping does with a half-paired list.
Grouping treats a pair nothing proposed as a pair that is not similar
(crate::grouping), which is sound while the stage above it ran to
completion and is not sound once a ceiling cut a list in two. A family whose
members were compared to each other only in part looks, from there, like a
family whose members mostly disagree: the complete-linkage floor ejects them
and the surviving comparisons come back out one by one, as pairs no group
holds both halves of. One duplication that a whole list states once is then
restated as many times as the ceiling happened to leave edges.
Measured against the labelled corpora with the ceiling lowered until it bites, cutting inside a list turned one twenty-seven-member family into a hundred and fifty-five pairs, and made the report grow as the ceiling came down — seven times its untruncated size at one setting, while the findings anybody had ruled on stayed exactly the same. Stopping between lists costs a few per cent of those findings at the same ceiling and leaves the rest of the report the size it was. It also makes the ceiling monotone: a run given more allowance can no longer report more.
Output is a pure function of the input: the emitted pairs are sorted
deterministically, so file order only moves the file indices inside the
fragment references and never changes which pairs appear or in what order.
Structs§
- Candidate
Config - Tuning for candidate generation.
- Candidate
Pair - An exact-hash candidate pair: two fragments that share one feature hash.
- Candidate
Set - The candidate stage’s output: exact-hash pairs plus funnel statistics.
- Candidate
Stats - Counters describing what candidate generation saw and dropped: the head of
the detection funnel, recorded for the
doctor/verbose view. - Fragment
Ref - One occurrence of a hashed fragment (a statement window or a subtree) at a
source location.
fileindexes the slice given togenerate;unitindexes that file’sFileFeatures::units. - Statement
Run - Where a statement-window fragment sits in its unit’s statement sequences.
Constants§
- DEFAULT_
PAIR_ BUDGET - Default global candidate-pair upper bound. Provisional, as above.
- DEFAULT_
POSTING_ CAP - Default posting-list cap. Provisional; the corpus funnel measurement calibrates it against real high-frequency structure.
Functions§
- generate
- Generate exact-hash candidate pairs across
files.