sift-sync.mjs (loadStrLookup, lines 100-115) builds its stamping map keyed by taxpayer_number alone, then stamps every receipt row of that taxpayer with that one STR number. A taxpayer that files hotel tax for multiple properties (management companies, owners with 2+ hotels) gets ALL of its locations stamped with a single hotel's STR number. str_hotel_summary then sums those receipts into one hotel's TTM and divides by that one hotel's rooms, inflating estimated_revpar.
Proven case: STR 54889 (Hilton Garden Inn Beaumont, 100 rooms). Its TTM receipts of $8,107,857 include $3,782,347 from Hilton Garden Inn Houston/Clear Lake NASA (750 W Texas Ave, Webster, 126 rooms), filed by shared taxpayer EPIC Hotel Group LLC (32065202775). Displayed RevPAR $222.13; Beaumont-only receipts of $4,325,510 give the true $118.51 (receipts query, 2026-07-14).
Joining every receipt row to hotel_tax_summary on (taxpayer_number + normalized location_address), which is the correct location-grained key (query run 2026-07-14, 960,124 receipt rows total):
| Bucket | Rows | Re-stamp action |
|---|---|---|
| Stamp agrees with the location-grained summary | 919,612 | none |
| No summary row exists for the (taxpayer, address) pair (address spelling variants, dead filings) | 33,589 | none (fail-safe: leave untouched, log) |
| Stamped, but the summary says that specific location is unmatched (inherited another location's STR) | 6,811 | strip stamp to NULL (this is the contamination) |
| Unstamped, but the summary has an STR for that location | 104 | gain correct stamp |
| Stamped with a different STR than the summary assigns | 8 | move to correct STR |
So the write touches 6,923 rows of 960,124 (0.7%). The Clear Lake NASA rows fall in the strip-to-NULL bucket; they regain their own STR (59355) later through the existing match-str-to-tax pipeline, which is unchanged by this work.
Sizing by two detectors: a name-aware cross-city classifier confirms 30 hotels with $27,074,410 of foreign TTM receipts (lower bound; it misses same-brand cases like Beaumont itself, where "Hilton Garden Inn" tokens defeat name matching). The city-only classifier finds 39 hotels with $58,373,834 foreign (upper bound; it false-positives on mailing-city quirks like The Westin at The Woodlands, whose own receipts file under Spring, TX). The exact per-hotel deltas fall out of the re-stamp dry run, which uses the authoritative key and no fuzzy matching. The 30 name-aware-confirmed hotels, worst first:
| STR | Hotel (census identity) | Rooms | TTM shown | Foreign $ | RevPAR now | RevPAR clean | Foreign locations (city, TTM $) |
|---|---|---|---|---|---|---|---|
| 32784 | Heritage Inn & Suites The Woodlands, Trademark by Wyndham | 150 | $7,796,314 | $4,671,375 | $142.40 | $57.08 | DRURY INN AND SUITES HOUSTON GALLERIA (HOUSTON, $4,671,375) |
| 33720 | Sonesta Select Austin North | 120 | $4,796,859 | $3,437,843 | $109.52 | $31.03 | AMERISUITES - DN (DALLAS, $3,437,843) |
| 39328 | Sonesta Select Dallas Richardson | 123 | $5,087,922 | $2,611,072 | $113.33 | $55.17 | SONESTA ES SUITES SAN ANTONIO DOWNTOWN (SAN ANTONIO, $2,611,072) |
| 21472 | Fairfield Inn & Suites San Antonio Airport/North Star Mall | 120 | $5,761,936 | $2,223,223 | $131.55 | $80.79 | RESIDENCE INN AUSTIN ROUND ROCK (ROUND ROCK, $2,223,223) |
| 21268 | Days Inn by Wyndham Shenandoah | 93 | $2,538,197 | $1,991,389 | $74.77 | $16.11 | HOLIDAY INN EXPRESS HOTEL & SUITES (SPRING, $1,991,389) |
| 36555 | HomeTowne Studios Houston West Oaks | 145 | $2,987,340 | $1,596,933 | $56.44 | $26.27 | CROSSLAND - DALLAS - MESQUITE - 3544 (MESQUITE, $954,812); CROSSLANDS AUSTIN - WEST #3545 (AUSTIN, $642,121) |
| 57792 | Aloft Las Colinas | 136 | $8,680,455 | $1,559,462 | $174.87 | $143.45 | HYATT PLACE FORT WORTH/TCU (FORT WORTH, $1,559,462) |
| 67861 | Homewood Suites by Hilton Harlingen | 89 | $5,726,497 | $1,100,412 | $176.28 | $142.41 | HOLIDAY INN EXPRESS (ZAPATA, $1,100,412) |
| 58934 | Fairfield by Marriott Inn & Suites Texarkana | 83 | $2,195,427 | $1,031,347 | $72.47 | $38.42 | COMFORT SUITES FRISCO (FRISCO, $1,031,347) |
| 33191 | SpringHill Suites Houston Hobby Airport | 122 | $2,889,745 | $886,067 | $64.89 | $45.00 | FAIRFIELD & SUITES DALLAS MEDICAL/MARKET CENTER (DALLAS, $886,067) |
| 31431 | Spark by Hilton San Antonio Northwest near Six Flags | 124 | $3,464,715 | $831,159 | $76.55 | $58.19 | HOLIDAY INN EXPRESS (BAY CITY, $831,159) |
| 82140 | Home2 Suites by Hilton Denton | 120 | $4,879,862 | $629,062 | $111.41 | $97.05 | FAIRFIELD INN & SUITES WICHITA FALLS, TX (WICHITA FALLS, $629,062) |
| 57527 | Best Western Littlefield Inn & Suites | 45 | $1,690,648 | $556,965 | $102.93 | $69.02 | WESTON INN & SUITES (RUSK, $556,965) |
| 58502 | Overton Hotel & Conference Center | 303 | $13,228,223 | $469,256 | $119.61 | $115.37 | INN OF THE HILLS RESORT & CONFERENCE CENTER (KERRVILLE, $438,756); SOUTH SHORE HARBOUR RESORT (LEAGUE CITY, $30,500) |
| 74211 | WoodSpring Suites Round Rock-Austin North | 122 | $1,199,686 | $450,960 | $26.94 | $16.81 | ECHO SUITES-FORT WORTH ALLIANCE (FORT WORTH, $210,804); ECHO SUITES-DALLAS PLANO RICHARDSON (PLANO, $190,828); ECHO SUITES-HOUSTON PASADENA (PASADENA, $49,328) |
| 3533 | Americas Best Value Inn Bonham | 53 | $1,171,422 | $344,350 | $60.55 | $42.75 | SAGAMAR INN (SEYMOUR, $344,350) |
| 33474 | Best Western Lindale Inn | 55 | $761,744 | $338,847 | $37.94 | $21.07 | CANDLEWOOD SUITES (GRAND PRAIRIE, $338,847) |
| 54732 | Courtyard Austin Airport | 151 | $7,826,144 | $297,119 | $142.00 | $136.61 | FAIRFIELD INN SAN ANTONIO DOWNTOWN (SAN ANTONIO, $297,119) |
| 62936 | Comfort Inn Midland | 61 | $1,582,823 | $286,081 | $71.09 | $58.24 | ECONOMY INN (BROWNFIELD, $286,081) |
| 54385 | Comfort Suites University Drive | 79 | $1,310,087 | $270,173 | $45.43 | $36.06 | HOME 2 SUITES (LUFKIN, $270,173) |
| 61983 | Sleep Inn & Suites Center | 55 | $1,539,733 | $244,288 | $76.70 | $64.53 | EXECUTIVE INN & SUITES (LUFKIN, $244,288) |
| 54867 | Red Roof PLUS+ San Antonio Northeast | 57 | $455,927 | $228,968 | $21.91 | $10.91 | BUDGET INN (BIG SPRING, $228,968) |
| 30970 | Hotel O Luxury Inn Channelview Baytown I-10 | 30 | $411,991 | $220,602 | $37.62 | $17.48 | ROSE GARDEN (HOUSTON, $220,602) |
| 57199 | Days Inn by Wyndham Salado | 41 | $1,267,084 | $144,380 | $84.67 | $75.02 | 817 E VETERANS MEMORIAL BLVD (KILLEEN, $144,380) |
| 82077 | Kasa at The Waller Austin | 19 | $593,189 | $137,195 | $85.54 | $65.75 | KASA MOCKINGBIRD FLATS DALLAS APARTMENTS (DALLAS, $109,008); BAYOU PARK (HOUSTON, $28,187) |
| 45597 | Hotel Lucine | 61 | $2,720,194 | $129,773 | $122.17 | $116.34 | MUSTANG INN (HUMBLE, $129,773) |
| 5959 | Motel 6 North Richland Hills | 82 | $1,719,433 | $122,755 | $57.45 | $53.35 | GARDEN INN & SUITES (DALLAS, $122,755) |
| 45736 | Economy Inn By OYO Lamesa, TX | 27 | $207,828 | $91,886 | $21.09 | $11.76 | ROTAN INN (ROTAN, $91,886) |
| 40169 | Comfort Suites Fort Worth North Fossil Creek | 75 | $818,801 | $90,116 | $29.91 | $26.62 | INN OF THE CONCHO'S (SAN ANGELO, $90,116) |
| 67428 | Studio 6 Corpus Christi, TX - North | 71 | $1,563,096 | $81,352 | $60.32 | $57.18 | MANSA DALLAS HOTEL (DALLAS, $81,352) |
Add Beaumont (54889, $3,782,347 foreign, $222.13 to $118.51) which the name detector misses but the authoritative key catches. A same-city review bucket of 69 locations across 53 STR numbers (e.g. Drury Inn Houston Galleria receipts of $4,671,375 sitting under Heritage Inn & Suites The Woodlands, STR 32784) resolves mechanically in the dry run via the same key.
scripts/sift-sync.mjs (upstream, prevents recurrence). loadStrLookup keys change from taxpayer_number to taxpayer_number + normalized address (upper, collapse whitespace), reading taxpayer_number, location_address, str_number from hotel_tax_summary. Fallback: taxpayer-only match allowed ONLY when the taxpayer has exactly one location in the summary, which preserves stamping through minor address drift for single-property taxpayers.scripts/backfill-receipt-str-number.mjs (the re-contamination trap). This script fills NULL stamps from hotel_tax_summary by taxpayer. Run after the re-stamp, it would put the bad stamps straight back. It gets the same composite-key change, in the same PR, before any nightly-cron wiring of it happens.scripts/restamp-receipt-str-by-location.mjs (the data pass). Recomputes the stamp for every receipt row from the (taxpayer + address) summary join. Dry-run by default: prints per-hotel before/after RevPAR deltas, row counts per bucket, and a list of any THM-owned or deal-pipeline STR numbers touched. --execute applies in batches with keyset pagination (id-ordered; PostgREST range-without-order silently drops rows). Idempotent: second run reports zero changes.