Delimited address joins for the record-matcher — the #694 flip evidence
ingestRows space-joins a multi-column address mapping into one concatenated run
("214 JONES RD ELKHART TX 75839"). That strips the parser's only segmentation signal, and it
interacts badly with the #690 all-caps case-normalization: on comma-less input, title-casing craters
the geocode. #699 shipped the fix as a default-OFF capability (IngestOptions.addressSeparator). This
is the evidence for flipping the record-matcher's geocode callers onto it.
The setup
The cross-dataset correlation (scripts/record-matcher/cross-dataset-correlation.ts, #618) ingests four
TX-scoped sources (TX HHSC facilities, FCC RHC posted-services, NPPES org NPIs, FCC RHC commitments —
1200 records, 300/source) through the real parser + resolverresolverThe component that converts parsed address components (locality, region, postcode) into coordinates by looking them up in the gazetteer. The resolver ranks candidates by name match, population, and proximity, and returns the best-matching place with its centroid or polygon., and counts entities spanning ≥2 sources.
Four configs, same script, same data:
| config | join | #690 case-norm | geocode rate | rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few metres — the highest tier of the geocode cascade. Sourced from address-point and situs data. (Σ of 1200) | entities | cross-source links |
|---|---|---|---|---|---|---|
| A | space | off | 100% | 579 (baseline) | 839 | 23 |
| A+ | space | on | 39.2% ⚠ | — | — | 12 |
| B | comma | off | 100% | 610 (+5%) | 837 | 23 |
| C | comma | on | 100% | 667 (+15%) | 825 | 25 |
What it says
1. Space-join + #690 craters (config A+). Title-casing a concatenated all-caps run
("214 JONES RD ELKHART TX 75839" → "...Rd Elkhart Tx 75839") destroys segmentation: geocode rate
falls 100% → 39.2%, cross-source links 23 → 12. This is the #694 trap, and it's why #690 was never
wired into the geocoder.
2. Comma-join eliminates the crater (B, C). With addressSeparator: ", ", the parser gets delimited
input and the geocode rate holds at 100% with or without #690. The crater is gone.
3. Comma-join + #690 is the best config (C) — +15% rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few metres — the highest tier of the geocode cascade. Sourced from address-point and situs data. over the current baseline. Delimiting alone lifts rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few metres — the highest tier of the geocode cascade. Sourced from address-point and situs data. 579 → 610 (the parser segmentssegmentA punctuation-bounded chunk of the normalized input — the comma-separated parts of 'Portland, OR' — used to give downstream stages structural context. better with commas); adding #690 lifts it again to 667 (+15% over A), and cross-source links 23 → 25 (fcc-rhc ↔ nppes 1 → 3). The gain is concentrated exactly where it should be — the all-caps sources, where #690 fixes the OOD parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates.:
| source | A (space) | B (comma) | C (comma + #690) | A→C |
|---|---|---|---|---|
| txhhsc-nursing (100% all-caps) | 135 | 145 | 167 | +32 |
| nppes | 107 | 110 | 150 | +43 |
| fcc-rhc (~46% all-caps) | 132 | 147 | 142 | +10 |
| fcc-rhc-commitments (mixed-case) | 205 | 208 | 208 | +3 (byte-stable) |
The mixed-case source barely moves (detection doesn't fire), and the all-caps sources gain the most rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few metres — the highest tier of the geocode cascade. Sourced from address-point and situs data. precisionprecisionOf the spans the model labeled as a given tag, the fraction it got right. High precision means few false positives. Paired with recall to compute F1. — exactly the #690 contract, delivered cleanly once the input is delimited.
The flip
The evidence supports flipping the record-matcher's geocode ingest to addressSeparator: ", " +
normalizeCase: no crater, +9% rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few metres — the highest tier of the geocode cascade. Sourced from address-point and situs data. precisionprecisionOf the spans the model labeled as a given tag, the fraction it got right. High precision means few false positives. Paired with recall to compute F1., links hold/improve. Caveats for the migration
(tracked on #694):
- It changes the parsed address string → re-validate the matcher's clusteringclusteringThe final stage of entity resolution: resolve non-transitive pairwise match decisions (A↔B, B↔C, but not A↔C) into canonical entities via union-find with path compression. Each cluster of records becomes one resolved entity. at scale (here, links held at 23 and rose to 25, but a larger run should confirm).
- The dedup GBTGBT (Gradient Boosted Trees). A non-linear machine learning model that combines many weak decision trees into a strong predictor. Mailwoman uses a GBT as an optional learned scorer for single-dataset dedup, improving F1 by 5–7 percentage points over the Fellegi-Sunter baseline. was trained on space-joined strings; anything that feeds GBTGBT (Gradient Boosted Trees). A non-linear machine learning model that combines many weak decision trees into a strong predictor. Mailwoman uses a GBT as an optional learned scorer for single-dataset dedup, improving F1 by 5–7 percentage points over the Fellegi-Sunter baseline. trainingtrainingThe process of adjusting a model's parameters so its predictions match labeled examples, by repeatedly measuring error and nudging the weights to reduce it. Distinct from inference, when the trained model is run on new input./evalevalRunning the model against a held-out golden dataset and computing per-component F1, exact-match, calibration, and resolved-coordinate error. must be re-baselined if flipped. The capability stays default-OFF until that migration is deliberate.
Source: scripts/record-matcher/cross-dataset-correlation.ts (--comma-address, --normalizenormalizeStage 1 of the runtime pipeline: deterministic input preprocessing (Unicode NFC, punctuation normalization, whitespace collapse). Returns a NormalizedInput with an offsetMap that maps normalized positions back to the raw input.-case);
diagnostic scripts/eval/geocode-case-diag.ts (comma-less title-case 150→17 vs delimited 150/150).