← back to the brainstorm

WholeATX × Austin Chronicle

24 prompts to merge Best of Austin into every subdomain

Run them roughly in order — acquire, normalize, match, map, enrich, publish, market. Each is written to hand straight to an agent with access to the droplet (/opt/wholeatx/, /opt/wtdirectory/) and the roster. Swap the year as new polls drop.

Working set. Data lands in /opt/wholeatx/bestof/winners.json as records of {name, category, year, poll, url, place, notes}. The roster of real businesses is /opt/wtdirectory/osm-travis.json (via roster.records()), scores are in audit-austin.json, sector assignment is sectors.assign(), and the site generator is /opt/wholeatx/wholeatx.py. Every prompt below assumes attribution to and links back to the Austin Chronicle.

Phase A · Acquire

01 Map the poll's URL surface scrape

Know exactly what to fetch before fetching anything.

Best of Austin publishes at austinchronicle.com/best-of-austin/year:YYYY/poll:PPP/category:CCC/.
Enumerate every valid (year, poll, category) tuple for years 2024 and 2025, polls "readers"
and "critics", across all 8 sections (Food & Drink, Services, Shopping, Nightlife, Arts &
Entertainment, Sports & Recreation, Kids & Family, Politics & Media). Output a checklist of the
exact index URLs to crawl. Do NOT fetch yet — just produce the target list and confirm the URL
pattern against two live pages.

02 Fetch winners politely scrape

Pull the raw winner lists without hammering the Chronicle.

Fetch each index URL from prompt 01 with a 3-second delay between requests, an honest
User-Agent ("WholeATX-research/1.0 (+https://wholeatx.com; walhus@gmail.com)"), and on-disk
caching so a re-run never re-fetches. Save raw HTML to /opt/wholeatx/bestof/raw/. Stop and
report if you see 403/429 — do not retry in a loop.

03 Parse to structured winners parse

Turn HTML into clean records.

Parse the cached Best of Austin pages into /opt/wholeatx/bestof/winners.json — one record per
award: {name, category (the exact Chronicle category label), section, year, poll, place
(1st/finalist), url (the specific poll page), blurb_excerpt (<=160 chars, for attribution
context only)}. Preserve the Chronicle's exact business name string verbatim in raw_name.

04 Capture the runners-up too parse

Finalists are leads too.

Extend the parser to also capture finalists / runners-up where listed, tagged place:"finalist".
A finalist with a broken website is nearly as good an outreach lead as a winner — we want them.

Phase B · Normalize

05 Canonicalize business names normalize

Names must match across two very different datasets.

Add a canonical_name to every winner: lowercase, strip legal suffixes (LLC, Inc, Co), collapse
"&"/"and", drop location qualifiers ("- South Lamar"), normalize whitespace and punctuation.
Keep raw_name untouched. This canonical form is the join key against the roster.

06 Detect chains & multi-location winners normalize

One award, many storefronts.

Some winners are chains with many roster rows (e.g. a coffee brand with 12 Austin locations).
Flag winner records as multi_location and decide the rule: the badge attaches to the BRAND
(dedupe by canonical_name) not to each storefront. Document the rule in the file.

Phase C · Match to real businesses

07 Exact-match pass match

Get the easy, high-confidence joins first.

Load the roster via roster.records(osm-travis.json). For every winner, attempt an exact
canonical_name match against roster canonical names. Write matches to
/opt/wholeatx/bestof/matched.json with match_confidence:"exact" and the roster osm_id + website.
Report the exact-match rate per category.

08 Fuzzy-match pass with a human gate match

Catch the near-misses without inventing bad joins.

For unmatched winners, run token-set fuzzy matching (rapidfuzz) against the roster, restricted
to the same sector where possible. Auto-accept >=92 similarity; queue 80–92 into
/opt/wholeatx/bestof/review.csv for a human yes/no; discard <80. Never guess a match into
production — a wrong badge on the wrong business is worse than no badge.

09 Reconcile the misses match

A winner with no roster row is itself a signal.

List winners that matched NO roster business. Split them: (a) genuinely missing from our
roster — queue for a targeted add; (b) present but under a different name — fix the alias.
Producing this "beloved businesses WholeATX doesn't even know about yet" list is a deliverable.

Phase D · Map to sectors

10 Category → sector crosswalk map

Send each award to the right subdomain.

Build /opt/wholeatx/bestof/crosswalk.json mapping every distinct Chronicle category string to
one or more of the 85 WholeATX sector slugs (see the brainstorm's starter table). Where a
category is ambiguous ("Best Local Business"), map to a network-wide bucket, not a wrong sector.
Flag any category that maps to zero sectors for review.

11 Attach awards to sector records map

The join, finalized.

Produce /opt/wholeatx/bestof/by_sector.json: for each sector slug, the list of its matched
winners with {business, category, year, poll, place, website_score (from audit-austin.json),
chronicle_url}. This single file is what the generator will read.

Phase E · Enrich & find the stories

12 The “Beloved but Broken” list enrich

The single most valuable output of the whole project.

Join every matched winner to its website score. Emit /opt/wholeatx/bestof/beloved_but_broken.csv
sorted by lowest score: {business, award, year, score, top 3 failed checks, phone, sector}.
This is the outreach call sheet — an award-winner losing customers to a bad website is the
warmest lead we will ever have.

13 Dynasties, risers, and fallers enrich

Two years turns a list into a narrative.

Compare 2024 vs 2025 winners per category. Tag each business: "hall_of_fame" (won both years),
"rising" (2025 only), "faded" (2024 not 2025). Add these tags to by_sector.json. They drive
badges and a genuinely interesting trends page.

14 Award-winner with no website at all enrich

The most quotable cross-section.

Intersect the winner list with roster tier-1 (no website found). Any Austin favorite the crawler
couldn't find a website for is a headline. Output the list; it feeds both a press angle and the
highest-priority "get online" outreach.

15 Write the pairing blurbs enrich

Warm + useful, in our own words.

For each matched winner, draft a 2-sentence WholeATX blurb: sentence 1 acknowledges the award
(our words, attributing the Chronicle, never copying their prose); sentence 2 gives the honest
one-line website read and the single highest-impact fix. Neutral, non-snarky, genuinely helpful.

Phase F · Publish across the subdomains

16 Award badge in the generator publish

Make it appear everywhere automatically.

In /opt/wholeatx/wholeatx.py, load by_sector.json and render a gold "🏆 Best of Austin YEAR —
CATEGORY" badge next to any matched business in the sector worksheet and at the top of its
/biz/ detail page, linking to the specific Chronicle poll URL. Idempotent; safe on re-run.

17 Award JSON-LD publish

Let AI answer engines cite us.

On each winner's per-business page, add schema.org "award" to its LocalBusiness node
("Best [Category], Austin Chronicle Best of Austin YEAR"). This makes WholeATX the structured,
machine-readable source for "who won Best X in Austin" — exactly what LLM answer engines quote.

18 Sort winners to the top publish

Reward the businesses people came for.

Add an optional "Best of Austin" view/filter on each sector page that pins matched winners to the
top of the worksheet, badge visible, score visible. Default sort stays by score; this is a toggle.

19 The network Best-of hub publish

One page to rule the award terms.

Generate wholeatx.com/bestof/index.html: every matched winner, both years, filterable by
category and sector, each with live website score and Chronicle link. Plus wholeatx.com/bestof/
beloved-but-broken/ as a public teaser of the gap (names shown, full report gated). Rich
ItemList + Award JSON-LD.

20 Per-category landing pages publish

Own "best [thing] in austin" search.

For high-value categories (Best Coffee, Best Tacos, Best Plumber, Best Realtor...), generate a
focused page pairing the winner + finalists with their website scores and a short guide. These
target the exact high-volume searches and are prime AI-citation bait.

Phase G · Market it

21 Outreach copy from the badge market

Turn the flattery into a conversation.

For each beloved-but-broken business, draft a short, warm outreach email: congratulate them on
the Best of Austin win (specific category + year), note one concrete thing their website is
missing, offer the free full report. No pressure, no pricing. Save as drafts per your rules
(never auto-send).

22 The press one-pager market

A story journalists will actually run.

From the joined data, write a factual findings one-pager: "N of Austin's Best-of winners have
websites that score under 45," "M award winners have no website at all," the most surprising
gaps. Numbers only, attributed, with methodology. This is link-worthy and AI-quotable.

23 The partnership pitch to the Chronicle market

Do the biggest version of this the right way.

Draft a partnership proposal to the Austin Chronicle: they own the love and the brand; WholeATX
brings the website-readiness layer they don't have. Offer co-branded "Best of Austin — is your
site ready?" reports, proper licensing of the marks, and traffic back to them. Frame it as
additive to their franchise, not competitive.

Phase H · Keep it honest

24 Attribution & freshness guardrail qa

Never drift from correct and fair.

Add a build-time check: every rendered badge and winner card must carry "Austin Chronicle Best
of Austin YEAR" attribution and a link to the source poll page; no Chronicle editorial prose may
appear verbatim beyond a short attributed quote. On each new annual poll, re-run 01–19 and mark
prior-year badges as "20XX" (historical), never silently dropping them. Fail the build if any
winner card is missing attribution.