Candidate
data class Candidate<K>(val key: K, val nameWords: List<String>, val aliasWords: List<String> = emptyList())
A candidate container = a stable key (buildingId or category name) + its CMS name words.
nameWords are the DISTINCTIVE / primary name words (the CMS long name). They alone form the coverage denominator and the genericWords population, so a candidate is neither ranked lower on the coverage tiebreak nor has its generic-word share skewed just because it happens to carry a CMS short name (#62).
aliasWords are additional matchable words (e.g. a CMS short name) that a container token may hit to engage the candidate, but which do NOT count toward coverage and are NOT fed to the generic-word derivation. They are matchable aliases only; default empty preserves the prior behaviour for callers that carry no aliases.