Surname resolution ladder

upnaam.resolve_name_records produces three typed tables: resolutions, candidates, and evidence. Surname selection belongs in Upnaam. Instate consumes eligible evidence and keeps any coverage adjustment in a separate module.

from pathlib import Path
from upnaam import LadderPolicy, NameRecord, resolve_name_records

records = [
    NameRecord(
        record_id="roll:1",
        source_revision="roll-2017-sha256",
        state="karnataka",
        year="2017",
        name="Asha",
        relative_name="Ravi Shastri",
        relationship="husband",
    )
]
result = resolve_name_records(
    records,
    policy=LadderPolicy("example-relative-policy-v1", relative_position="last"),
)
print(result.resolutions.to_pylist())
result.write(Path("surname-example"))

This explicitly enabled relative-position policy returns a relative_candidate, keeps the recorded-surname decision null, and leaves p_correct null. No state has that policy enabled automatically. Matching scores are not accuracy estimates.

Ladder

Priority

Rule

Meaning

1

explicit_surname_field

Surname explicitly written for the person.

2

household_and_relation, household, relation, house

Own-name token selected with the existing corroboration precedence. House evidence requires a source policy.

3

initials_single_token

Existing narrowly scoped Kannada initials exception.

4

position_policy

Own-name token under an explicit source position policy.

5

same_person_external

Surname on a uniquely linked record of the same person.

6

relative_recorded_surname

Candidate from the independently resolved named relative.

7

relative_position_policy

Candidate from the relative’s full name under an explicit position policy.

8

abstain

Insufficient or unsupported evidence.

Priority is not a claim about accuracy. Conflicting evidence blocks fallback. An external disagreement preserves the written candidate while accepting neither value. Differences may represent variants or changes over time; they are exposed rather than silently resolved.

Relative candidates never feed the recorded-name pass or propagate recursively. Unknown relationships, one-word relative names and ambiguous relative matches abstain. Father, mother, husband, wife and spouse relationships stay explicit. Confirming Ravi Shastri’s surname does not establish that Asha uses it.

LinkedSurname requires source identity and revision, original text, surname span, linkage basis and reference kind. explicit_field and adjudicated references can produce external_recovered; a position_policy reference remains an external_candidate. This interface checks link uniqueness; the caller must establish person links independently. These statuses do not certify link accuracy.

Three-table contract

Table

Preserved information

resolutions

One row per source record in input order. Provenance, state/year/household, raw own and relative fields, relationship, recorded and selected candidate IDs, status, rule, reasons, policy revisions and nullable accuracy/calibration fields.

candidates

Eligible own/relative tokens and supplied explicit/external surnames. Source text and exact spans, origin, native normalization, original and normalized transliteration, comparison representation, canonical spelling, cluster identity/revision, selection status and rule.

evidence

Attempted household spelling comparisons, including rejections, and selection decisions. Exact compared representations, uncensored scores, thresholds, parameters, edit operations, metric/policy revisions and decision details.

Offsets are zero-based Python Unicode code-point indices, with exclusive end. Every source substring must equal the candidate’s raw token. IDs are deterministic hashes; source revisions must identify immutable inputs. Household grouping is scoped by source revision, state and year. Unknown household IDs must be null. Process complete households together when batching a larger source.

comparison_value records what was matched. Kannada uses the aligned token lookup while surname_source_normalized retains native script. A lookup requires a revision. The original Latin transcription is retained separately from its normalization. Clustering never overwrites raw or normalized source variants.

Household spelling anchors are contextual and separately versioned. The existing national variant clusterer retains its complete-link evidence requirement: A-B and B-C similarity alone cannot merge A and C. These are different cluster scopes.

upnaam.matching.compare_strings supports exact matching, Levenshtein distance and Jaro-Winkler similarity through MatchPolicy. Each has explicit score direction and threshold. Jaro-Winkler records prefix weight; Levenshtein records edit operations. Rejected scores are not cutoff-clipped. Household resolution continues to use its constrained Levenshtein policy; it does not silently switch metrics.

ResolutionBundle.write atomically creates a new directory with three Parquet files and a manifest containing hashes, row counts, policy and span convention. It refuses to overwrite an existing artifact. Use resolve_name_records for this complete audit; the positional resolve_electors and parsed-roll artifact builder remain narrower interfaces. The parsed-roll builder also exposes separate relative-candidate fields. Corroboration v2 removes unconditional inheritance from recorded surnames.

Validation

Bihar land and Rajasthan ration adapters currently label final tokens from full names. These are provisional references, not independent surname boundaries. Bihar’s exact-name-and-relative links cannot evaluate naturally omitted surnames using those links alone.

Evaluate linkage, token selection, transliteration and clustering separately. Retain disagreements and independently adjudicate a stratified sample. Measure incremental coverage, precision, abstention and conflict by rung, source, state, relationship, recorded sex and naming pattern. Separate households and linked people across development, calibration and test partitions. Hold out surname variants when measuring generalization to new names. Freeze clustering first.

Recovery evaluation needs links that do not require the proposed surname to agree. Include naturally missing cases; artificially masked names have a different missingness mechanism. Landholders and ration members also differ from the population of electors. Results from Bihar or Rajasthan do not establish Karnataka accuracy. Some people may have no evidenced family surname.

These are restricted person-level artifacts. Public releases should contain reviewed aggregates and reproducibility manifests, not the row-level tables.