How this deployment grew
districtry Illinois began as a Chicago-only explorer and grew outward one county at a time — 91 of the state's 102 counties as of 26 August 2026. The counties that are missing are missing for reasons this project measured and wrote down, not for want of looking, and every one of them is on the record in the app's own data-gaps panel. This page is the deployment's record: what it ships, what it re-reads on a schedule, and the corrections its own machinery has caught.
The numbers below are measured from the app's own shipped data files every time anything changes — they cannot drift from what the map actually serves. The changelog entries are dated snapshots: each was true on its own date, and none is edited after the fact.
The changelog
- 26 August 2026
Clay, the ninety-first county
Clay County's board districts ship, taking the map to 91 of Illinois's 102 counties. Its fourteen lettered districts were never the problem — the county publishes them, and seven are independently confirmed by certified 2026 primary returns — but the census draws one Clay City precinct where the county runs two districts through it, and nothing published said where that line falls. County Clerk Amy Britton answered in two sentences: District A is the village of Clay City, District B the unincorporated area around it. That is a corporate-limits line the census already draws, so the split was resolved block by block — 833 people inside the village, 333 outside, every block sorting cleanly to one side, and the two blocks that sit on the margin shipped with their measured precinct rather than absorbed quietly. Two of the districts sit further from equal population than this project's own ceiling normally allows; they ship only because the Clerk confirmed the map is current, and the measured figure is recorded rather than smoothed. Clay ships its board and not its precincts, because its two published surfaces disagree on how many precincts it has, and a precinct card would state more than is known.
- 25 August 2026
A county held back by its own numbers, and the letter that released it
Wayne County's district composition had been corroborated twice over for weeks: the county's own board page states which precincts make up each of its seven districts, and its certified November 2024 General — 27 precinct-level results grouped by which board candidate each precinct actually voted on — partitions the same 27 precincts the same way, agreeing on six of seven exactly. The one difference is the county seat, where the board page omits two Fairfield precincts that the certified ballot shows voting in District 7, so the page is incomplete rather than the ballots wrong. None of that was the blocker. Composed against an ideal of 2,311 people, one district deviates by 32.4% — past the 30% ceiling this project enforces everywhere else, and both sources agreed on that district exactly, which is what made the number worth pausing over instead of dismissing as a mis-transcription. Clerk Elizabeth Woodrow was asked directly whether the plan had moved since it was drawn. Her reply: “Nothing has changed with this map for years.” The ceiling was raised for Wayne alone rather than widened for the fleet, and the measured deviation is on the record.
- 23 August 2026
Four counties in one day, and a lesson about labels
Richland, Jackson, Douglas and Vermilion all join. Vermilion is the largest county the project has added and the one that teaches the most: it publishes three board-district layers, and the best-labelled one is obsolete. A service described as “Governmental and school districts” holds the 2011 plan, while the plan actually in force is an undocumented layer with a typo in its own name. Currency is a measurement, not a reading of a name — a plan drawn to a census balances on that census, and across 4,943 census blocks the layer that ships runs within 0.7% of equal population where the better-labelled one runs 15.1% out. The builder now refuses to write if that comparison ever reverses, in either direction. Jackson, the same day, is the first county in the fleet whose districts split precincts and are built anyway: its adopted map is a vector PDF whose districts are real polygon objects, so the shapes were read from the file rather than sampled from its pixels, and nothing traced ships — the map only ever chose between the two districts the county's own canvass had already named.
- 22 August 2026
A county is not blocked because its website is
Knox County had been recorded as refusing every request. That was true of knoxcountyil.gov, and it was written down as though it described the county. Knox has four hosts: the website denies, a public GIS server answers and carries the county's twenty townships, the same site's document store has no block at all, and a fourth is simply dead. Knox joined as the 80th county — the first to join by closing an enclave rather than extending the frontier, filling a hole that seven already-served neighbours had left around it. Its districts come from the county's own adopted map, checked against certified returns and census population before shipping; a faster method that read the map's fill colours was tried first, produced a clean and confident answer, and was wrong, which is why that arithmetic check is now a gate in the builder rather than a comment in it. The frontier probe now sweeps GIS subdomains too, because the old host list could never have found the one host that mattered.
- 21 August 2026
Two counties that were never dark
Gallatin's record said the county was dark on every route tried, and that its clerk's own mail domain did not resolve. Neither was true. The site answers normally and serves an incomplete certificate chain — the leaf without its intermediate — which every automated client reports as a hard failure and which no browser notices, so a misconfiguration had been recorded as an absence. Coles County had taught the pattern days earlier; sweeping all 27 frontier counties' clerk domains for the same shape found Gallatin and confirmed Vermilion, whose record had likewise said its site could not be reached. The fix supplies the missing intermediate and pins its fingerprint, rather than turning certificate verification off.
- 20 August 2026
Four websites that answer perfectly and serve nothing
A sweep of all 406 municipal websites this app links found four that return a clean HTTP 200 and no content a reader wants: one is a domain-for-sale lander, one an entirely empty body, and two the default placeholder page that ships with an unconfigured web server. This is the one failure a status-code check cannot see — the link gate asks whether a URL answers, and all four answer flawlessly. The tell is content, and the cheap version of it is size: every one of the four came back under 1,200 bytes, against real municipal front pages that run to tens of thousands. None of them entered the roster by mistake — each is the website that municipality's own county publishes for it, so the counties are citing them too, and this project does not overwrite a county's published value on its own initiative. They are recorded as gaps instead.
- 18 August 2026
A clerk said there were no maps, and the county built anyway
Clark County's Clerk answered the standing request with one sentence: the County Board is elected by districts, and no maps are available. That settles the board's form and refuses its geometry in the same breath. But Clark's districts are unions of whole precincts, so the county's own certified canvasses describe them completely, and the census voting districts — matching all 23 precinct names and summing to the county's exact population — supply the polygons. It is the first county in the fleet built entirely from election returns, with no map at any stage. Because a certified return names who won an election rather than who holds the seat today, every roster row names the election that seated the member instead of claiming a currency the returns cannot show.
- 17 August 2026
The first boundary this project ever bought
Jo Daviess County's GIS office sells its data under a signed licence rather than publishing it. Its board-district shapefile ships displayed under the county's written permission — a purchased licence plus the IT/GIS Director's authorisation by e-mail — with the raw file retained offline rather than committed to this repository. It is the 63rd county, and the first whose boundary was paid for.
- 8 August 2026
The guard that measured the wrong thing
Brown County's website switched on Cloudflare's e-mail obfuscation, so every published mailto: link became an encoded attribute instead. The scraper read mailto: only, and returned seven board members carrying zero e-mail addresses — while the guard that checks Brown seats seven passed happily. Seven rows in, seven rows out, guard satisfied, contact gone, on a tool whose whole purpose is telling residents how to reach their board. A count guard answers “did this break?” and cannot answer “does this still say what it said?”, so a retention gate now compares every roster field against the same file before the change and fails when a field stops being published — no threshold to configure, and a field is protected the moment it first ships. The obvious follow-up question was then measured rather than reassured about: all 25 other scrapers reading mailto: were fetched and checked, and none served the obfuscated form.
- 7 August 2026
A dead link nobody was checking
Macon County's precinct card had been linking maconcountyil.gov for three days. That hostname has no DNS record at all — not a redirect, not a 404, no address associated with it. The county is at maconcounty.illinois.gov. Nothing in this repository checked the URLs the cards actually render: one gate covered the dataset endpoints the layers fetch and another covered layer counts, while the link a reader clicks was unguarded. A link gate now extracts its own surface from the app's authored pages and every roster file — around 1,200 URLs across some 540 hosts — so a new county is covered the day it ships, with nothing to remember to update. It separates links this project chose, which fail, from links carried through from a county's own published roster, which warn.
- 4 August 2026
Contiguity stops deciding which county comes next
Until this day a county could only join if it touched the area already served, which meant the shape of the map was choosing the work rather than the data being available. That gate was retired: a county now joins whenever a county-keyed layer answers in it, wherever it sits. The served area is allowed to be disjoint, and has been four times since — each island rejoining as soon as a neighbouring county filled the gap, and the outline recomputed from the counties themselves every time rather than patched by hand. The honesty test it replaced is unchanged: a county is never joined just because some statewide layer happens to give a rich answer there.
Checked on a schedule
Every scheduled job lands its changes as a reviewed pull request — data about real officeholders never ships without a person looking at the diff.
| What it refreshes | Job | When |
|---|---|---|
| IL Senate + House rosters (ilga.gov scrape) | update-ilga-roster.yml | Mon 13:00 UTC |
| U.S. House roster (unitedstates/congress-legislators) | update-congress-roster.yml | Mon 13:00 UTC |
| CPD district info (Cloudflare-challenged scrape via Playwright) | update-cpd-roster.yml | Tue 13:00 UTC |
| CCPSA district council rosters | update-ccpsa-roster.yml | Wed 13:00 UTC |
| Will County Board roster (willcountyboard.com scrape) | update-will-county-board-roster.yml | Thu 13:00 UTC |
| Kendall County Board roster (kendallcountyil.gov scrape, Playwright fallback for the Akamai front) | update-kendall-county-board-roster.yml | Thu 14:00 UTC |
| McHenry County Board roster (mchenrycountyil.gov scrape, Playwright fallback) | update-mchenry-county-board-roster.yml | Thu 15:00 UTC |
| Kane County Board roster (the county's SharePoint list API) | update-kane-county-board-roster.yml | Thu 16:00 UTC |
| Lake County Board leadership tags (directory scrape, Internet Archive fallback) | update-lake-county-board-roles.yml | Thu 17:00 UTC |
| Board of Review commissioner roster (cookcountyboardofreview.com scrape) | update-ccbr-roster.yml | Fri 13:00 UTC |
| Illinois county clerk roster (ISBE election-authority directory scrape) | update-county-clerk-roster.yml | Sat 13:00 UTC |
| DuPage County Board roster (dupagecounty.gov directory scrape) | update-dupage-county-board-roster.yml | Sun 13:00 UTC |
| Winnebago County Board contact (wincoil.gov board page; base64 e-mails, name-matched to the county GIS) | update-winnebago-county-board-roster.yml | Wed 15:00 UTC |
| Sangamon County Board roster (29 per-district member pages on sangamonil.gov) | update-sangamon-county-board-roster.yml | Tue 15:00 UTC |
| Livingston County Board roster (livingstoncountyil.gov directory scrape) | update-livingston-county-board-roster.yml | Tue 14:00 UTC |
| Municipal governing bodies (Cook Clerk DOEO API, Will Clerk directory, DMMC directory, Kane/McHenry/Kendall/Ogle/Carroll clerk yearbooks, Stephenson county villages page, Lake county GIS contact, LaSalle clerk PDF, Winnebago WinGIS officeholder layers, Freeport city site) | update-municipal-officials.yml | Wed 14:00 UTC |
| DeKalb County Board roster (dekalbcounty.org members page; Sucuri-fronted, the scraper retries past the captcha stub) | update-dekalb-county-board-roster.yml | Fri 14:00 UTC |
| Ogle County Board roster (oglecountyil.gov staff directory) | update-ogle-county-board-roster.yml | Sat 14:00 UTC |
| Stephenson County Board roster (stephensoncountyil.gov board page) | update-stephenson-county-board-roster.yml | Sun 14:00 UTC |
| Carroll County Board roster (carrollcountyil.gov board directory) | update-carroll-county-board-roster.yml | Mon 14:00 UTC |
| Lee County Board roster (leecountyil.com Member Contact List PDF) | update-lee-county-board-roster.yml | Tue 16:00 UTC |
| Rock Island County Board roster (rockislandcountyil.gov board page) | update-rock-island-county-board-roster.yml | Wed 16:00 UTC |
| source freshness + redistricting watch (opens/updates one tracking issue) | validate-sources.yml | 1st of month 14:00 UTC |
| LaSalle County Board roster (county directory scrape) | update-lasalle-county-board-roster.yml | Sun 15:00 UTC |
| Henry County precinct polling places (Clerk's own directory) | update-henry-precinct-polling.yml | Fri 18:00 UTC |
| Woodford County Board roster (woodford-county.org directory scrape) | update-woodford-county-board-roster.yml | Mon 16:00 UTC |
| Logan County Board roster (logancountyil.gov board-page scrape) | update-logan-county-board-roster.yml | Mon 17:00 UTC |
| Boone County Board roster (boonecountyil.gov board-page scrape) | update-boone-county-board-roster.yml | Tue 17:00 UTC |
| Grundy County Board roster (grundycountyil.gov board-page scrape) | update-grundy-county-board-roster.yml | Wed 17:00 UTC |
| Jefferson County Board roster (jeffersoncounty.illinois.gov board-table scrape) | update-jefferson-county-board-roster.yml | Thu 19:00 UTC |
| Macon County Board roster (maconcounty.illinois.gov board-members scrape) | update-macon-county-board-roster.yml | Fri 17:00 UTC |
| Menard County commissioners roster (menardcountyil.gov board-table scrape) | update-menard-commissioners-roster.yml | Thu 20:00 UTC |
| Montgomery County Board roster (montgomerycountyil.gov board-page scrape) | update-montgomery-county-board-roster.yml | Wed 18:00 UTC |
| Henry County Board roster (henrycty.com district-keyed directory scrape) | update-henry-county-board-roster.yml | Thu 18:00 UTC |
| Peoria County Board roster (GIS roster spine + per-member page contact scrape) | update-peoria-county-board-roster.yml | Fri 15:00 UTC |
| Tazewell County Board roster (tazewell-il.gov member-page scrape) | update-tazewell-county-board-roster.yml | Fri 16:00 UTC |
| Iroquois County Board roster (county board-page table scrape) | update-iroquois-county-board-roster.yml | Sat 15:00 UTC |
| At-large county boards (Monroe, Randolph) rendered on the County card | update-county-commissioners-roster.yml | Sat 16:00 UTC |
| De Witt County Board roster + district-composition drift check | update-dewitt-county-board-roster.yml | Sun 15:00 UTC |
| Washington County Board roster + township-composition drift check | update-washington-county-board-roster.yml | Sun 16:00 UTC |
| Cass County Board roster + seat-count tripwire on the derived boundary | update-cass-county-board-roster.yml | Sun 17:00 UTC |
| Marshall County Board roster + township-composition drift check | update-marshall-county-board-roster.yml | Mon 18:00 UTC |
| McDonough County Board roster + derived-boundary drift check (mcg.mcdonough.il.us, HTTP-only) | update-mcdonough-county-board-roster.yml | Mon 19:00 UTC |
| Fulton County Board roster (fultoncountyil.gov Members page — a district-grouped grid joined by name to hidden per-member popups for the e-mails) | update-fulton-county-board-roster.yml | Tue 20:00 UTC |
| Stark County Board roster (starkco.illinois.gov) + drift check on the five boundaries built from the Clerk's archived Google My Maps export | update-stark-county-board-roster.yml | Tue 19:00 UTC |
| Mason County Board roster SOURCE watch (scan — transcribed by hand, never scraped) | watch-mason-roster-source.yml | Tue 18:00 UTC |
| Shelby County Board roster (shelbycounty-il.gov coboard.aspx scrape, cross-checked against contacts.aspx) + drift check on the composition the derived boundary is dissolved from | update-shelby-county-board-roster.yml | Wed 19:00 UTC |
| White County Board roster + precinct polling (whitecounty-il.gov board + elections pages; district join from the certified-canvass table, any unplaceable name fails) + watch on the Elections page's adopted-map link | update-white-county-board-roster.yml | Sat 17:00 UTC |
| Jo Daviess County Board roster (jodaviesscountyil.gov board page + 16 member directory pages, paced; seats counted named+vacant = 17, districts 1..17 asserted against the purchased boundary's constant, home addresses never parsed) | update-jodaviess-county-board-roster.yml | Sat 18:00 UTC |
| Coles County Board roster (colesco.illinois.gov board page + committees page cross-check; the fetch supplies the intermediate certificate the county's server omits, pinned by hash. Exists because the county's own board-district GIS layer carries a 2022 member column that gets six of twelve names wrong — the app takes geometry from the service and people from here) | update-coles-county-board-roster.yml | Sun 18:00 UTC |
| Clark County Board roster (the Clerk's own certified canvasses at il-clark.accessliberty.com — the county's only board document is a scan, so the roster is the election returns; the run also re-checks each district's tabulated precinct list against the shipped dissolve, which is the re-precincting/redistricting tripwire) | update-clark-county-board-roster.yml | Mon 20:00 UTC |
| Crawford County Board roster (the county's own board page) + the redistricting tripwire: the same run re-reads the district composition from the Clerk's results feed at il-crawford.pollresults.net and fails if it stops matching the shipped dissolve | update-crawford-county-board-roster.yml | Tue 21:00 UTC |
| Mercer County Board roster (the county's own board table — party, term, home town, chairman) + the redistricting tripwire: the same run re-reads the district composition from the Clerk's results feed at il-mercer.pollresults.net and fails if it stops matching the shipped dissolve | update-mercer-county-board-roster.yml | Wed 20:00 UTC |
| Edgar County Board roster (edgarcountyillinois.COM board page — the .gov does not carry it) + the redistricting tripwire: the same run re-reads the district composition from the Clerk's results feed at il-edgar.pollresults.net and fails if it stops matching the shipped dissolve | update-edgar-county-board-roster.yml | Thu 21:00 UTC |
| Franklin County Board roster (the county's own members page, read as a GRID by cell coordinate — a flattened text read pairs every role with the wrong member, and its Address column is every member's home) + the redistricting tripwire: the same run re-checks the county's live precinct count and each numbered board contest's precinct count against the shipped dissolve, and fails if either moves | update-franklin-county-board-roster.yml | Fri 19:00 UTC |
| Clinton County Board roster (the county's own board page — 15 members with phone, e-mail and term year, Chairman and Vice Chairman badged by name match) + the redistricting tripwire: the same run re-checks the county's live precinct count and each numbered board contest's precinct count against the shipped dissolve | update-clinton-county-board-roster.yml | Sat 19:00 UTC |
| Adams County Board roster (certified canvasses on platinumelectionresults.com; also the backfill tripwire for the unread November 2022 canvass) | update-adams-county-board-roster.yml | Sat 20:00 UTC |
| Knox County Board roster (roll call of the county's own board minutes on its Revize CMS; its member page is a 403) | update-knox-county-board-roster.yml | Sun 20:00 UTC |
| Warren County Board roster (the county's own board page — 15 members with committees, term year, phone and e-mail, Chairman of the Board badged; home addresses seen and dropped) + the strongest drift check in the fleet: the same run re-reads the precinct-map legend the shipped boundary was dissolved from | update-warren-county-board-roster.yml | Sun 19:00 UTC |
| Daily watchdog over every roster refresh: reports the ones that are FAILING or have gone longer than their own cadence without a successful run, on one standing issue. Exists because a builder that refuses the write opens no PR and files nothing — the shipped roster silently freezes at its last good run | roster-health.yml | daily 23:00 UTC |
| Hancock County Board roster (county board page scrape) | update-hancock-county-board-roster.yml | Fri 20:00 UTC |
| Richland County Board roster (the county's own board page) + the redistricting tripwire: the same run re-reads the board-district and voting-precinct layers from the county's own GIS at richlandil.wthgis.com and fails if either stops matching the shipped dissolve | update-richland-county-board-roster.yml | Mon 21:00 UTC |
| Jackson County Board roster (the county's own board page, grouped by district heading, plus each member's staff-directory entry for the e-mail); the builder fails if the page's grouping and the directory disagree about a member's district | update-jackson-county-board-roster.yml | Tue 22:00 UTC |
| Douglas County Board roster, assembled from the county's annual Yearbook (people and e-mail) and its certified election summary reports (district numbers), joined on surname; the builder fails if the two county documents stop naming the same seven people | update-douglas-county-board-roster.yml | Wed 21:00 UTC |
| Vermilion County Board roster (27 members across 9 districts) from the county's own members page, fetched with the intermediate certificate its server omits supplied by AIA and pinned by hash | update-vermilion-county-board-roster.yml | Thu 22:00 UTC |
| Wayne County Board roster (14 members across 7 districts) from the county's own board page + the redistricting tripwire: the same run re-reads the per-district precinct list from that page and fails if it stops matching the shipped dissolve (District 7 checked as the known page/canvass subset, since the page never names Fairfield 1 or Fairfield 2) | update-wayne-county-board-roster.yml | Fri 21:00 UTC |
| Clay County Board roster (14 members across 14 lettered districts, 14/14 with a phone) from the county's own members page + the redistricting tripwire: the same run re-reads the county-board page's Districts section letter by letter and fails if it stops matching the shipped composition (the village-limits line splitting Clay City between districts A and B came from the Clerk's e-mail, and no page re-read can trip a line the page never draws) | update-clay-county-board-roster.yml | Sat 21:00 UTC |
Back to the map Sources & data layers Common questions Privacy Why this exists