Houston Real Estate AI Visibility: the 500-Query Study. Analysis code. CiteVantage, September 2026. CC BY 4.0. https://citevantage.com/research/houston-study-code/ WHAT IS HERE study500_findings.py name extraction (named, canon) and the first-pass analysis. Prints FINDINGS.txt. Every filter that decides what counts as a named firm lives in this file, with the reason for each. study500_market.py the market-facing cuts: concentration, brands vs individuals, the citation layer, own-domain and referral-network tests with the exposure control and the base rate. Prints MARKET.txt. study500_sensitivity.py recomputes every headline figure under a strict filter built to break the findings. Prints SENSITIVITY.txt. study500_validation.py 400 extracted names labelled by hand, blind to their outcomes, post-stratified and bootstrapped. Estimates every headline figure for real agents, teams and brokerages only. Prints VALIDATION.txt. The labels are published beside the data as houston-validation-sample-2026.csv. WHAT THEY READ The scripts read study500/answers.json, the raw engine output (one record per answer with the full answer text). That file is not published. The published CSV is the output of the extraction step: one row per answer with the canonical names extracted and the domains cited, so every aggregate on the page can be recomputed from it. RECOMPUTE THE HEADLINE FROM THE CSV (Python 3.10+, standard library only) import csv, io, collections rows = [r for r in csv.DictReader(io.open("houston-real-estate-ai-visibility-2026.csv", encoding="utf-8")) if r["geo_clean"] == "1"] areas = collections.defaultdict(set); mentions = collections.Counter() for r in rows: for name in filter(None, r["firms_named"].split("|")): mentions[name] += 1; areas[name].add(r["area"]) firms = len(mentions); one = sum(1 for f in areas if len(areas[f]) == 1) print(len(rows), "answers;", firms, "firms;", sum(mentions.values()), "mentions;", one, "in one area =", round(100 * one / firms, 1), "%") Expected on the published file: 1940 answers; 4841 firms; 11100 mentions; 4100 in one area = 84.7%. NOTES Name extraction is heuristic, so the page gives every headline figure three ways: as extracted, under a strict filter, and as a hand-checked estimate from the labelled sample. The 60 rows with geo_clean=0 are answers about the wrong place; they are listed for completeness and excluded from every figure.