From a county tax roll to a ranked call list
territory-gtm finds every commercial property with grounds worth servicing in a county, traces each one to the company that actually signs the contract, and scores it 0–100 with a one-line outreach angle. Built for tree care; adaptable to landscaping, roofing, paving and other field services.
Harris County, TX with the bundled config: about 2½ minutes on a laptop after the download, $0 in data costs.
A list your team can work on Monday
Everything lands in runs/<county>/output/. Every value carries a source column, so anyone can check where a number came from.
Lead workbook
One tab per segment, management companies, managed communities, contacts, method, rules and sources.
Top-50 call sheet
Who to call, why them, and the angle, written from each account's own acreage, canopy and storm history.
Clay-ready tables
Companies, people and communities as linked CSVs with stable ids, ready to import as Clay tables.
Contact shortlist
The accounts worth researching, with the credit estimate you approve before anything is spent.
Storm alerts
A weekly list of accounts with a property near a new National Weather Service damage report.
Checkpoints
Samples and top-buyer lists to review with Claude before you trust the list.
Install, then run the demo
You need Claude Code (the desktop app or the CLI) and Python 3.10+. For contacts you'll also want Apollo and/or Clay connected in Claude; they're optional and never used without your approval.
- Add the marketplace and install the plugin. In Claude Code, type:
/plugin marketplace add qdevery/territory-gtm /plugin install territory-gtm@territory-gtm - Open a working folder. Start Claude Code in an empty folder where your county projects will live, for example
~/territory. Projects go inruns/inside it. - Run the demo. It builds a synthetic county end to end, offline, in about a minute, and walks you through the output. Claude sets up the Python tool the first time.
/territory-gtm:demo - Build a real county. Ask in plain words, or use the command:
“Build a tree-care account list for Harris County, TX.”
/territory-gtm:build Harris County, TX
- Clone and install.
git clone https://github.com/qdevery/territory-gtm cd territory-gtm python3 -m venv .venv && source .venv/bin/activate pip install -e . - Run the demo.
Opentgtm demo runs/demoruns/demo/output/: start with the workbook andcall_sheet_top50.md. - Build a county.
tgtm init runs/harris --county harris_tx --trade tree_care tgtm fetch runs/harris # ~650 MB from HCAD + canopy + storm reports tgtm run runs/harris - Optional: use Claude anyway. Open Claude Code in the repo folder.
CLAUDE.mdpoints it at the same skills, so it can review checkpoints and research contacts with you.
Install trouble with GDAL or rasterio? See the FAQ.
Seven steps, two checkpoints
The pipeline is plain Python driven by two YAML files: county.yaml (where the data is and what the local codes mean) and trade.yaml (segments, rules, scoring, angles). Claude runs it, stops at the checkpoints, and explains the result.
Classify rules over tags + keywords
Each county uses its own codes, so county.yaml translates them into shared tags (state class B1 → multifamily, building type 4389 → country_club_golf). The trade's rules only talk about tags and name keywords, so the same rules work in any county. For tree care the order is:
Districts → Cemeteries → Golf → Senior living → Churches → HOAs → Apartments → Other commercial, with cities, school districts, condo units and homes excluded.
Checkpoint 1 writes counts, 25 samples per segment and the 200 largest uncertain parcels. Claude reviews them with you and fixes misfires, such as “HOA” read as a given name.
Who buys the step that makes the list useful
The owner on the deed is often an LLC that never hires anyone. Each parcel's buyer is the first of:
- the district itself (utility districts buy by board vote and public bids)
- the operator on a state licensed-facility list (senior living)
- a verified management firm from your
firm_aliases.csv - the care-of business on the tax bill's mailing line
- the firm most often named at that mailing address county-wide
- an owner group sharing a mailing address, or the owner
Checkpoint 2 lists the top buyers and the firms worth verifying. Claude checks the largest on their own websites and adds them to firm_aliases.csv.
Storms NWS Local Storm Reports
Each county lists its defining storms with a method: gust_field estimates the peak gust at each property from measured gusts (hurricanes); nearest_report uses the distance to the nearest damage report (derechos, tornado outbreaks, ice); recent keeps the score current with the last 12 months. Reports come from the Iowa Environmental Mesonet archive, because the Storm Prediction Center's pages disallow automated access.
The score
Every weight, cap and cut-off is in trade.yaml. A paving trade sets canopy to 0 points; roofing raises storm and adds a hail event.
What the top of the list looks like
From the synthetic demo county. Every name is invented. A real county looks the same, with real management firms, operators and clubs.
| Account | Segment | Score | Props | Acres | Canopy ac | Storm |
|---|---|---|---|---|---|---|
| Pinecone Community Management | HOA – Managed | A 86 | 8 | 322 | 150 | 69 |
| Bluebird Association Services | HOA – Managed | A 86 | 8 | 289 | 129 | 70 |
| Copperleaf HOA Management | HOA – Managed | A 83 | 7 | 238 | 103 | 65 |
| Heron Landing Country Club | Golf | A 80 | 1 | 258 | 132 | 77 |
| Summit Hollow Residential | Apartments | A 77 | 15 | 266 | 35 | 65 |
“Pinecone Community Management manages 8 associations here with about 150 canopy acres of common area, many inside the Spring 2024 derecho damage area, so pitch one portfolio-wide tree-risk inventory and pre-storm-season pruning bid its managers can take to each board this budget season.”
The outreach angle for the top account, written from its own row.
A real county, step by step
With the plugin, Claude drives these steps and pauses where your judgment matters. The commands are shown so you can run or rerun any step yourself.
- Pick the county and trade. Bundled:
harris_txand the demo county; tree care. For anything else Claude writes the configs with you (see New county / trade).tgtm init runs/harris --county harris_tx --trade tree_care - Download the public data. The roll and parcel outlines from the appraisal district, a tree-canopy clip from MRLC, and storm reports from the Iowa Mesonet. Claude checks each source's terms first.
tgtm fetch runs/harris - Checkpoint 1: is every parcel in the right bucket? Review the samples with Claude and fix the rules until they read right.
tgtm normalize runs/harris && tgtm classify runs/harris # output/checkpoints/1_classification_counts.csv, 1_samples_by_segment.csv - Checkpoint 2: who actually buys? Verify the biggest management firms on their own sites, add them to
firm_aliases.csv, rerun.tgtm canopy runs/harris && tgtm rollup runs/harris # output/checkpoints/2_top_buyers_by_acres.csv, 2_firm_alias_candidates.csv - Score and export.
tgtm storms runs/harris && tgtm score runs/harris && tgtm export runs/harris # or everything from a step on: tgtm run runs/harris --from score
Cheapest first, and no yes, no spend
Contacts are optional. Claude uses your own Apollo and Clay connections, starts with the free lookups, and stops for your approval before every paid batch. The result is one file, contacts.csv, merged into the workbook on the next export.
tgtm shortlist“Find contacts for the tier-A accounts. Show me the count and the credit estimate first.”
Self-managed HOAs and public districts are never people-searched: their boards are volunteers. They get public websites, office lines and the operator firm instead.
Know which accounts got hit this week
The alert pulls the National Weather Service's Local Storm Reports for your county's offices and keeps the ones that matter for grounds: wind damage, tornadoes, ice, tropical damage, 50+ mph gusts, large hail, and remarks that mention trees. Every property within 3 km is listed, closest damage and highest score first.
Run it after a storm, replay a past one to test your pitch, or ask Claude to schedule it every Monday.
tgtm alert runs/harris # last 7 days
tgtm alert runs/harris --days 14 --km 3
tgtm alert runs/harris --start 2024-05-16 --end 2024-05-18
/territory-gtm:storm-alert runs/harris
Two YAML files, written with Claude
A new county
Ask “Add Travis County, TX.” Claude finds the appraisal district's bulk download, inspects the columns and code tables, and writes county.yaml. Most counties fit the generic adapter: one parcel layer with owner and land-use fields.
adapter: generic
source: {parcels: parcels.shp}
columns: {parcel_id: PROP_ID, owner_name: OWNER,
mailto: MAIL_NAME, acreage: ACRES}
code_columns: {state_class: STATE_CD}
codes:
state_class: {B1: multifamily, X3: religious_exempt}
nws_offices: [EWX]
A new trade
Ask “Make a roofing version.” Claude interviews you (who buys, what makes a good prospect, the smallest job worth a visit) and edits a copy of tree_care.yaml: segments, fit, buyer titles, angles and score weights.
score:
canopy: {points: 0} # roofs, not trees
storm: {points: 35} # + a hail event in county.yaml
acreage: {points: 20, full_at: 50}
segments:
- id: apartments
fit: 0.9
buyer_titles: Regional Maintenance Director
Research and files only
These are written into the skills, and the plugin's hook enforces the first and last while Claude works in a project folder.
The hook only acts inside a folder that holds county.yaml and trade.yaml. TGTM_GUARDRAILS=always applies it everywhere; TGTM_ALLOW_OUTREACH=1 lifts the outreach block in a session where you've decided to do outreach.
Where every number comes from
| Source | Used for | Cost |
|---|---|---|
| County appraisal district / assessor bulk data (e.g. HCAD) | Owners, mailing and care-of lines, acreage, land and building codes, parcel outlines | Free |
| USFS / NLCD Tree Canopy Cover (30 m) | Canopy share and canopy acres per parcel | Free |
| NWS Local Storm Reports, Iowa Environmental Mesonet | Storm exposure and weekly alerts | Free |
| State licensed-facility lists (e.g. Texas HHSC) | Senior living operators and facility phones | Free |
| Organizations' own websites | Verifying management firms; office lines | Free |
| Apollo, Clay (your accounts) | Business contacts, behind the credit gate | Your credits |
Terms change. Check each source's terms of use before commercial use.
When something doesn't work
pip install fails on GDAL, rasterio or pyogrio
Recent Python versions on macOS, Windows and Linux get prebuilt wheels. If yours doesn't, install the geo stack from conda-forge, then the package without dependencies:
conda install -c conda-forge geopandas rasterio rasterstats pyyaml openpyxl
pip install --no-deps -e .“MRLC returned an error instead of a GeoTIFF”
The county's box is too large for one request, or the service is down. Shrink bbox in county.yaml, or download Tree Canopy Cover from mrlc.gov/data and save it as raw/canopy.tif. Any canopy raster with 0–100 values works.
My county's data is behind a login or an order form
The pipeline never gets around a login, paywall or CAPTCHA. Many districts provide the same files free through a public records request; once you have them, put them in raw/ and point county.yaml at them.
Small HOA parcels have no canopy reading
The canopy raster is 30 m, so parcels smaller than about one pixel get no reading. Those accounts score on canopy from their other parcels. A finer raster fixes it: convert a 1 m canopy height map (Meta and WRI publish one free on AWS Open Data) to percent cover and save it as raw/canopy.tif.
Lots of “Unnamed Manager @ …” accounts
That's an office mailing address shared by five or more associations whose care-of line doesn't name the firm. Open 2_firm_alias_candidates.csv, check the firm on its website, add a row to firm_aliases.csv, and rerun from rollup.
Can I use it outside Texas?
Yes. Canopy and storm data are national. You need the county's parcel data and a county.yaml mapping its codes; the add-county skill walks Claude through it. Texas-only extras (HHSC, TCEQ) are simply left out.
Where is my data stored?
On your machine, in the project folder. runs/, contacts.csv and firm_aliases.csv are git-ignored, so lead lists don't end up in a repository by accident.