This site is a public-data reference. Analyses use public datasets and are presented for reference, not as financial, tax, legal, or underwriting advice.

Working paper · Colorado housing data infrastructure · Living document

Instrumenting Housing Need

How a seven-month public data system learned to say “we don’t know,” and why that turns out to be the precondition for building the right housing in the right place.

00 Executive summary

What this is
A continuously rebuilt public dataset covering 546 Colorado geographies64 counties, 272 incorporated places and 210 census-designated places — free at cohoanalytics.com. Built in seven months across 6,654 commits.
The central claim
The system’s contribution is not data volume but epistemology: an enforced discipline separating a measured zero from an unknown. 39 of its 288 test files carry named assertions — 120 of them — whose declared subject is absence.
Why that matters for money
A coerced zero does not merely misinform — it misallocates. Every program dollar committed on a false zero is committed to the wrong income band, the wrong tenure, and the wrong timeline.
What it enables
Place-level targeting that county data cannot produce. Two towns with near-identical burden rates can need opposite programs; only municipal-scale tenure data reveals which.
Worked result — Palisade, Mesa County
419 households below 80% AMI are cost burdened. Severe burden is an owner problem by 5.1 to one — 113 owners against 22 renters — which a renter-led assessment would miss entirely.
Shortage versus mismatch
Cost burden and unit deficit are different measurements licensing different actions. At 31–50% AMI, 96 of 97 renter households are burdened while the unit gap at that band is zero. A construction problem and an allocation problem are different problems.
The sequencing result
Ordering by intervention type rather than defaulting to construction reaches 113 households within a year and reserves the slow, expensive instrument for the one band that requires it.
Where investment would land
The binding constraint is not analysis but development cost data. The system can size need precisely and cannot cost it at all. See §07.
What a commissioned build would cost
6.111.1 person-years, or 1221 months for a team of 7, over 338,104 hand-written lines and 63 data sources. An analyst estimate, declared as one in §07.
What it cost in energy
Built with heavy AI assistance. Measured: 71,384 model turns and 40,000 CI jobs over a partial window. Published per-query energy spans 140×, giving 172,399 kWh. No midpoint is offered, and no net benefit is claimed. §09.

01 Emergence

The first commit is dated 2026-02-20. By 2026-09-15 the repository held 6,654 commits — sustained across seven months. The shape of that growth is not the smooth ramp of a planned build.

Figure 1. Commits per month on origin/main. The final month (2026-09) is partial. The peaks correspond to build-out of the place-level pipeline and the ownership model; the troughs are consolidation, not pause.

What the curve conceals is a change in kind. Early months added surface — pages, charts, fetchers. Later months increasingly added constraint: tests, guards, and documented rules about what the system may claim.

Table 1 · Repository inventory, measured at build time
ComponentCountNote
Geographies54664 counties · 272 places · 210 CDPs
Places with apportioned CHAS detail482Tenure × AMI band × cost burden
Data files1,653Tracked JSON, CSV, GeoJSON and Parquet
HTML pages55662 top-level; 484 generated per place
Build & fetch scripts258Python and Node, run on schedule
Workflows7159 carry a live cron — also the system’s main liability, §08
Test files28839 carry assertions named for absence
LIHTC features926CHFA portfolio, refreshed weekly
Two corrections, recorded rather than quietly fixed

An earlier draft reported 498 test files. That figure was wrong: the shell glob used to count them matched every *.test.js twice, once under each pattern. The generator described in §10 now counts a resolved set of paths, which cannot double-count.

The same draft reported 52 absence-handling test files, from a scan that looked for the word null anywhere in a file alongside any absence-related word. That matched files which merely mention a missing key in passing. The count now requires an assertion’s own name to declare absence as its subject, which is a claim about intent rather than vocabulary. Both errors were caught by applying this paper’s argument to the paper itself.

1.2 · What grows without anyone touching it

“Continuously rebuilt” is a claim worth making specific. 59 of the repository’s 71 workflows carry a live cron, on cadences from every thirty minutes to annually. They divide into three kinds, and the distinction matters: one kind brings in new facts, one computes over them, and one watches the other two.

Everything derived is generated, never hand-edited — the jurisdiction digests, the 484 place pages, the ranking index and its scenarios, the homepage snapshot, and the file manifests. A change to any of them is made by changing its generator and re-running it; CI fails a pull request whose generated files drift from what their sources would produce. That is the mechanism by which a one-person project stays internally consistent across 1,653 data files.

The third category — the watchers — exists because the first two run unattended. It is also, as §08 records, the least finished part of the system.

02 What it does well

2.1 · Place-level resolution, not county proxy

Housing policy is made at municipal scale — inclusionary ordinances, zoning, fee waivers, land contribution — while most published housing data is county-level. A county figure is almost never the figure a practitioner needs. Boulder County’s severe renter burden is one number; Louisville and Boulder city sit far apart inside it, and the spread is the product.

The system resolves 482 Colorado places by apportioning tract-level CHAS data through tract-to-place membership, then capping the result against the ACS occupied-household count. Every metric carries a geography_level, so a figure that is really the county’s own says so rather than masquerading as local.

2.2 · An explicit epistemology of absence

An unmeasurable quantity is null, never 0.

A zero and an unknown are indistinguishable once rendered, so nothing downstream can catch the difference.

AGENTS.md, repository root

The rule exists because the same defect recurred five times: a zero-household AMI band; $0 and −$320,000 equity figures; an absent Proposition 123 commitment rendered as non-participation; a flat chart line standing in for a dead data series; and $0 resale price and owner equity shown for 53 of 482 places.

Why this is the load-bearing idea

Every other quality depends on it. Place-level resolution is worthless if a place with no data renders as a place with no need. A recommendation built on a coerced zero recommends the wrong program. Marking absence is what makes the output actionable rather than merely available.

2.3 · Verification treated as a first-class artifact

The repository distinguishes green from verified: a passing check list answers “did the jobs report success,” not “did the work happen.” The documented tests for a real pass are whether the check actually ran, whether the passing test asserted anything, whether a cancelled outcome is being misread as neutral, and whether main is green at that moment.

03 The method in use: Palisade

Palisade, Mesa County (GEOID 0856970): 1,297 households, 433 renter and 864 owner. The town filed its Proposition 123 commitment on 2024-08-29, so it is eligible for that funding stream.

Table 2 · Palisade households by tenure and AMI band, with cost burden
AMI band RentersBurdened
>30%
Severe
>50%
OwnersBurdened
>30%
Severe
>50%
≤30% AMI5129171138756
31–50%979651327051
51–80%142103090346
81–100%40006500
>100%10300464238
≤80% AMI subtotal29022822335191113
Finding 1 · The severe-burden problem here is an owner problem

113 owner households below 80% AMI pay more than half their income for housing, against 22 renter households — 5.1 to one. A conventional assessment leading with renter cost burden and rental production would direct the entire program at the smaller population.

Finding 2 · Burden and shortage do not coincide

At 31–50% AMI, 96 of 97 renter households are cost burdened while the measured unit gap at that band is zero — the units exist and are occupied by households who cannot afford them. At the deepest band the gap is real. Both are true and they describe different problems: a shortage at the bottom, a mismatch above it.

Methodological caveat — stated, not smoothed over

The gap series is computed against household-size-adjusted income limits while the affordability comparison uses area medians. A one-person household at 50% AMI in Mesa County has an income limit of $35,200 (affordable rent ≈ $880), against a median gross rent well above it. “Zero gap at 50%” means zero deficit of units priced at the four-person 50% AMI level, not “no affordability problem at 50% AMI.” The burden column is the better guide to household experience; the gap column to production targets. Either alone produces a wrong program.

3.1 · The resulting program path

Track 1 · fastest

Preserve owners already in place

113severely burdened owners ≤80% AMI

Owner-occupied rehabilitation, weatherization, tax relief. No land, no entitlement, no construction financing. The households are already housed; the intervention keeps them housed.

Horizon: months

Track 2 · mismatch

Income-target existing stock

96burdened renters, 31–50% AMI

The gap at this band is zero, so the units exist. Acquisition with deed restriction, or project-based subsidy on existing buildings, reaches these households without new construction.

Horizon: 6–18 months

Track 3 · genuine shortage

Build the deep-tier units

116burdened households ≤30% AMI

The band where units do not exist at any occupancy. Requires the deepest subsidy and longest timeline — competitive credit, Proposition 123, and project-based rental assistance layered.

Horizon: 3–5 years

A strategy beginning with new construction reaches its first household in three to five years. A strategy sequenced by the data reaches 113 households within a year, 96 more within eighteen months, and reserves the slow instrument for the one band that requires it. Same total need, different order, materially different time-to-relief.

04 Generalizing: which jurisdictions are tractable

Of 482 places, 231 have at least 400 households and confident data. Within those, 63 meet a tractability test — acute burden concentration combined with an absolute target a single program could plausibly close. These are the jurisdictions where “eliminate the need” is a coherent sentence rather than a slogan.

Table 3 · Tractable jurisdictions, ranked by burden concentration
PlaceHouseholds Burdened ≤80% AMIShare Renter / ownerProgram implication
Air Force Academy65726640.5%266 / 0renter only
Rocky Ford1,36953238.8%249 / 283even split
Dolores51118536.2%159 / 26renter-dominant
Cripple Creek59120634.9%123 / 83both tenures
Snowmass Village1,75859533.9%344 / 251both tenures
Georgetown59619933.3%99 / 100even split
Mountain Village1,56252033.3%455 / 66renter-dominant
Pagosa Springs77525032.3%171 / 79renter-dominant
Winter Park47715432.2%81 / 73even split

The renter/owner column is the operationally important one, and it is the column county-level analysis cannot produce. Two towns can share a burden share to within a tenth of a point and have opposite tenure profiles. They need different programs. A regional analysis would assign them the same one.

05 Policy decisions, and why

These are choices about what the system is for. They are not derived from the data; they shape what the data is permitted to say.

5.1 · Free, public, no account

The audience — municipal staff in towns of 500 to 5,000 households — is precisely the audience that cannot commission a consultant study or buy a data subscription. The marginal town in Table 3 does not have a housing department. A paywall would exclude exactly the jurisdictions where the tractability screen says the need is most closeable.

5.2 · The place, not the county, is the unit of analysis

This is a policy choice before it is a technical one. Regional analysis is cheaper, more statistically stable, and matches how most housing data is published. It is also unusable for the instruments that actually get built: an inclusionary ordinance, a fee waiver, a land donation, and a deed restriction are all municipal acts. Choosing the place as the unit accepts wider error bars in exchange for matching the scale at which decisions happen.

5.3 · Publishing absence rather than suppressing it

A system may handle a missing value three ways: omit the row, substitute a default, or publish the absence. Omission makes a place look uncovered; substitution is the coerced-zero failure; publishing absence is noisier and less impressive and is the only one that leaves the reader correctly informed. Four sources are currently displayed as stale on the dashboard rather than having their age limits quietly raised to silence them.

5.4 · Burden and gap kept as separate policy instruments

Most assessments report a single “units needed” figure. This system reports cost burden and unit gap separately because they license different actions — burden licenses preservation and subsidy, gap licenses production. The Finding 2 result is only derivable because the two were not merged.

5.5 · Preservation ordered before production

Where the data supports both, the sequencing in §03.1 places preservation first. The justification is time-to-relief rather than cost: a household severely burdened today is at risk this year, and the fastest unit to secure is one that already exists and is already occupied by the household that needs it. This ordering is a judgment, and a jurisdiction prioritising long-run stock growth over near-term stability could defensibly reverse it.

5.6 · Declining a paid data subscription

Commercial parcel data was evaluated and deferred: the cost could not be justified at current use. The consequence is recorded rather than hidden — parcel coverage is marked inactive, and the integration is left wired so it resumes if the decision changes. §07 revisits this as the clearest case where modest money would buy a capability the system cannot otherwise have.

06 Mathematical decisions, and why

Every decision below had a simpler alternative, rejected for a stated reason.

6.1 · Area-weighted apportionment over county inheritance

HUD CHAS — the only public source crossing cost burden with AMI band and tenure — publishes at tract level, and municipal boundaries do not follow tracts. The rejected alternative assigns each place its primary county’s rates; the repository documents why it fails: a parcel in Erie would see Weld County rates though half of Erie’s tracts sit in Boulder County. The error is systematic and largest exactly where towns straddle county lines.

The method: for each tract T overlapping place P the weight is max(area_share, population_share), and each CHAS count is multiplied by that weight and summed. (Area share alone collapsed small towns embedded in large rural tracts; the file header still documents the superseded rule.) The assumption is uniform household distribution within a tract — poor for slivers under roughly 10% of tract area, which are flagged rather than silently included.

6.2 · The ACS occupied-household cap

Area weighting is geometrically sound and demographically optimistic: summed across overlapping tracts it overcounts households in roughly 31% of Colorado places, because population clusters in the built-up portion rather than spreading across tract area. The correction anchors each place’s total to the ACS occupied-unit count and scales the tiers to fit.

The property that matters: rates are preserved, only levels change. A cost-burden percentage — the number a policy is written against — survives the correction, while the household count becomes defensible against an independent source. The ordering is load-bearing: summary caches must regenerate before the CHAS build, or the cap silently no-ops.

6.3 · A PITI model rather than the 3× income rule

The conventional price = income × 3 heuristic is rejected because it ignores interest rates — where rate movement alone shifts affordability 20–40% — ignores property taxes and insurance, worth roughly $500–$1,500 per month in Colorado, ignores mortgage insurance, and substitutes a single multiple for the debt-to-income threshold lenders underwrite to.

The model is not one model. The registry defines 7 of them — conservative screening, first-time buyer, conventional lender DTI, FHA, USDA, a Proposition 123 eligibility gate, and a fully custom one — differing in ratio convention, down payment, mortgage insurance and its loan-to-value gate. The default is conservative_screening, which screens at a 30% front-end ratio. A 43% back-end DTI is one of the others, and it answers a different question: what a lender will approve, rather than what a household can carry without being cost burdened. On the documented Denver illustration the heuristic and a PITI model agree on the affordable price and disagree entirely on the gap — roughly 117% over affordable against roughly 206% — so a town using the heuristic would size its subsidy at about half what a buyer needs. Which PITI model it uses changes the answer again. The full specification, with every parameter read out of the registry, is in the computational methods companion.

An earlier version of this section described a single model at “20% down, 6.5%, 0.65% tax, 0.85% insurance, 43% back-end DTI.” That is a recognisable description of conventional_dti — one of the seven, and not the default. Writing out the methods companion is what found it.

A threshold worth naming explicitly

43% back-end DTI is the lender’s maximum, not the affordability standard. HUD calls a household cost burdened above 30% of income. A model solving at 43% answers “what will a bank approve,” which is a larger number than “what can this household carry without being cost burdened.” Both are legitimate questions and they are not the same question; a subsidy sized to the first leaves the buyer burdened on the day they move in.

6.4 · Absence handled before coercion, not after

The numeric core of the epistemology is one line of language semantics: Number(null) === 0, and 0 is finite. A coercion two layers upstream therefore produces a value passing every downstream Number.isFinite() check. Three consequences are enforced: do not coerce a possibly-null value that feeds arithmetic or a renderer; guard <= 0 rather than < 0 wherever zero is not meaningful, since a price or rent of exactly zero means unknown rather than free; and carry the reason with the data. Fixing the renderer is insufficient — a renderer that formats null correctly never sees the null if the coercion happened upstream.

6.5 · Published shares derived from published counts

Rates were originally computed from unrounded accumulators while counts were rounded separately for display. Several shares land on exact .00005 ties, where a one-ulp difference in the denominator flips the fourth decimal — producing files that differed between machines and a freshness checker reporting five places as stale whose inputs had not changed in months. Deriving each share from the published counts makes the computation reproducible and, as a side effect, internally consistent: a reader dividing the published count by the published total now obtains the published share, which was previously not true.

6.6 · The tractability screen is analyst-constructed

Declared: not repository methodology

The screen in §04 — burden share ≥ 20%, target ≤ 600 households, on places of ≥ 400 households with confident data — was constructed for this paper. It is not a published standard and carries no validation beyond face plausibility. The thresholds encode a judgment that one municipal program can address several hundred households but not several thousand. They live in the generator as named constants so a reader can change them and re-run rather than having to trust them. These cut points are the weakest part of this paper and are stated so a reader can reject them without rejecting the approach.

07 What a little real investment would buy

The system is labour-rich and capital-poor. Its limits are not analytical — they are four specific missing inputs, each of which costs less than a single consultant housing needs assessment.

Priority 1 · Development cost benchmarks — the binding constraint

The system can size need to the household and cannot cost it at all. That is why §03.1 sequences three tracks and attaches no budget: the repository holds no validated Colorado cost-per-unit figures for new construction, acquisition-rehabilitation, or owner-occupied repair, and inventing them would violate the discipline the whole system is built on.

This is the single highest-return investment available. The data largely exists — in CHFA’s credit allocations, in DOLA’s Proposition 123 awards, in completed project cost certifications. It needs assembling into a published series by intervention type and region, not inventing. With it, every jurisdiction in Table 3 gets a costed strategy instead of an ordered one.

Priority 2 · A preservation pipeline that refreshes

The National Housing Preservation Database extract has had no refresher since it was loaded — it is stale and displayed as such. This matters more than its size suggests: preservation is the fastest track in every sequencing above, and expiring affordability restrictions are time-bound in a way that need estimates are not. A unit lost to expiry costs far more to replace than to preserve. This is engineering time, not subscription cost.

Priority 3 · Parcel and zoning coverage

Commercial parcel data was deferred on cost. The capability it would unlock is specific: moving from “this town needs deep-tier units” to “here are the parcels where they could go, with their zoning, ownership and size.” Need analysis identifies the target; parcel data is what converts a target into a site. This is a genuine annual subscription cost and the clearest case where modest money buys a capability that cannot be substituted with effort.

Priority 4 · Roughly two weeks of engineering

59 scheduled workflows now run with no map of themselves, alerting that covers failures but not silence, and at least one quality gate that runs only after merge. On 2026-09-14 the public site stopped deploying for thirty-two hours and three separate monitoring mechanisms each failed to report it. None of this is hard; it is unglamorous consolidation work that no volunteer evening prioritises over a new feature, and it is currently the largest tax on the maintainer’s attention.

What replacing this would cost

A funder deciding whether to support an existing system is really asking what it would cost to commission one. The scope is measurable: 338,104 hand-written lines — excluding 226,824 generated lines of place pages — 62 pages of which 25 are substantial interactive tools, and 63 declared data sources feeding 59 scheduled pipelines.

Decomposed by workstream and summed, that comes to 6.111.1 person-years, or 1221 months of calendar time for a team of 7 after allowing 12% for coordination and 25% for the fact that a team of seven is not seven times one person.

Declared: an estimate, and not a claim about this project

The scope above is measured; the person-weeks are a judgment, with no validation beyond face plausibility, and they live in the generator as named constants so a reader can change them and re-run rather than having to trust them.

More important is what the figure does not say. It is what a commissioned team would plausibly bill to build this from a specification. It is not a claim that 6.111.1 person-years of human effort were expended here. A commissioned build carries requirements negotiation, review cycles, sign-off and status reporting that a single maintainer does not, and the domain expertise that a team would spend two to four months acquiring was already resident. Treating the two as equivalent would be precisely the unsupported claim this paper spends nine sections arguing against.

One cross-check is worth recording because it initially looked like a contradiction. A line-count sanity test against defence-software productivity benchmarks implies roughly twenty-six person-years — two to four times the workstream estimate. The discrepancy is an artefact of the benchmark, not of the estimate: only 39% of the page markup is distinct rather than shared boilerplate, and modern web and data work sustains line rates several times those benchmarks assume. Adjusted for both, the two methods agree.

The line item a fixed-bid team cuts first

After front-end and data engineering, the largest workstream is quality assurance — and it produces nothing a demonstration would show. 39 of the 288 test files exist only to stop the system asserting things it cannot know. That is exactly the line a fixed-price bid removes, and exactly the line this system's argument rests on. A commissioned build of this site would very likely ship sooner, look more consistent, and be better documented — and publish coerced zeros, because nothing in a statement of work asks for the discipline that prevents them.

Deliberately no dollar figures

Priorities 1–4 are stated as capabilities and rough magnitude classes rather than budgets. Quoting costs for a cost-data-assembly project, a commercial subscription, or contract engineering without sourcing them would reproduce precisely the error this paper documents. A funder should expect to size these with the vendors and agencies involved; the argument here is about ordering and return, not price.

08 Limits, honestly stated

A paper describing a system built around admitting what it does not know owes the same treatment to itself.

The automation has outgrown its operator

59 scheduled workflows run against this repository, and the system offers no map of itself. Failures are discovered by inspection rather than notification. This is the limit most likely to end the project — not through technical failure but through maintainer exhaustion.

Gates that do not run where decisions are made

The deploy artifact guard runs on push, never on pull_request; the inventory check sits outside the main suite. A pull request can be genuinely green and still break production. The gates are correct; their placement is not.

Alerting covers failure, not silence

Failure and recovery notifications work. A job killed by its own timeout (recorded as cancelled, rendered grey rather than red) and a scheduled job that quietly stops firing remain uncovered. One instance went fifteen days undetected and was found by hand. As of 2026-09-15 the staleness sweep is explicitly disabled rather than running as a green placeholder that checks nothing.

Uneven data vintage across sources

ACS figures are 2020–2024 five-year estimates; home values are Zillow ZHVI; income limits are HUD FY2026. Each metric carries its own as_of, which is the right design, but a reader comparing two metrics is comparing two vintages and the interface does not always make that prominent.

Single-state, and single-maintainer

Everything here is Colorado. The methods generalise; the data wiring does not, and no second state has been attempted. The bus factor is one.

09 What this cost to build

This repository was built with substantial AI assistance over roughly nine months. That is a material fact about how the work was produced, it carries an energy cost, and a paper arguing that systems should publish what they would rather omit cannot omit it.

Two things are measurable. Continuous integration has run 40,000 workflow jobs, about 1,509 runner-hours of hosted compute. Model inference across the surviving session transcripts comes to 71,384 turns generating 68,149,998 output tokens.

This is a floor, not a total

Session transcripts rotate. The surviving window runs 2026-06-05 to 2026-09-15 — roughly a third of the project. Everything below counts only that window, and the earlier and heavier build-out months are simply gone. The honest reading is “at least this much,” and the true figure is materially larger.

Turning turns into kilowatt-hours is where the accounting stops being measurement. The published per-query energy figures span 140×, from 0.24 Wh for a short prompt on optimised serving to 33.6 Wh for a reasoning model on a long one. Agentic coding turns carry longer context than the high-end benchmark, so neither end is a bound so much as a signpost.

Table 4 · Inference energy, bounded rather than estimated
AssumptionPer queryEnergyCO2e
Short prompt, optimised serving 0.24 Wh 17 kWh 7 kg
Reasoning model, long prompt 33.6 Wh 2,399 kWh 936 kg
No midpoint is offered, deliberately

The obvious move is to split the difference and publish one number. That number would be the most-cited figure in this paper and the least defensible thing in it — a precision manufactured by averaging away a disagreement in the literature. It is the same move as filling an unmeasured cell with a zero, and this system exists because that move is wrong. The range is the finding. At the high end the surviving window alone approaches a tonne of CO2e; at the low end it is a rounding error. Both readings are currently supportable, which is itself worth knowing.

What is not claimed here is a net benefit. It would be easy and self-serving to set this against the travel and staff time of the consultant studies the system substitutes for, and declare the trade favourable. That comparison has not been measured, the counterfactual is unknown, and asserting it would be exactly the unsupported claim this paper spends nine sections arguing against. The cost is stated because it was incurred, not because it has been justified.

The narrower point stands on its own: scripts/paper/measure-compute-footprint.mjs computes these figures the same way every other figure here is computed, and they update with the rest. A project that discloses its compute cost once, in a launch post, has disclosed a historical curiosity. One that recomputes it has disclosed a number.

10 How to use it

  1. Read the tenure split before anything else. It determines which half of the toolkit applies, and it is the finding most often lost in county-level work.
  2. Separate the shortage from the mismatch. Compare unit gap by band against burden counts by band. Where the gap is zero and burden is high, the units exist — acquisition and income targeting beat construction on both cost and time.
  3. Find the severe-burden concentration. Households above 50% of income are at risk this year, and preserving an occupied unit is consistently faster than producing a new one.
  4. Check the funding gate before designing the program. Proposition 123 eligibility turns on a filed commitment; 217 jurisdictions’ filings are tracked, so this is a lookup rather than a phone call.
  5. Treat any “0” with suspicion and look for a stated reason. Where the system says “not published,” that is information about the source, not about the place.
The claim in one sentence

A housing data system earns its keep not by how much it publishes but by how reliably it refuses to publish what it cannot support — because every program dollar committed on a coerced zero is committed to the wrong band, in the wrong tenure, on the wrong timeline.

11 Provenance, and how this paper stays current

Every figure in this paper is written by scripts/paper/build-paper-figures.mjs and injected by scripts/paper/inject-paper-figures.mjs. None is typed. Re-running them refreshes the paper against whatever the repository then says, and test/paper-figures-fresh.test.js fails continuous integration if this page and the data ever disagree — so the paper cannot silently go stale while the site beneath it moves.

A figure the generator cannot read is emitted as null with a stated reason and rendered here as that reason, never as 0 — the same rule the system applies to itself. That generator is what caught both corrections recorded in §01.

Authorship and standing. COHO Analytics is the sole product of pggLLC, built and maintained by one person. It has no institutional funding, no research grant, and no staff. That is relevant to reading this paper in two directions: it explains the limits catalogued in §08, and it is the reason the investment argument in §07 is framed around four specific inputs rather than a programme — the labour is already being supplied.

  • Household and cost-burden detaildata/hna/place-chas.json, from HUD CHAS tract data apportioned to places and capped at the ACS occupied-household count.
  • Geography coveragedata/hna/geography-registry.json.
  • Income, rent, vacancy, AMI gap — ACS 2020–2024 five-year estimates.
  • Median home value — Zillow ZHVI city index.
  • Income limits and Fair Market Rents — HUD FY2026. Mesa County four-person AMI $100,600; 30% $30,200; 50% $50,300; 60% $60,350; 80% $80,500.
  • Proposition 123 commitments — 217 jurisdictions from CDOLA commitment filings. Town of Palisade filed 2024-08-29; Mesa County 2023-08-07.
  • LIHTC portfoliodata/chfa-lihtc.json, CHFA, refreshed weekly.
  • Compute footprintdata/paper/compute-footprint.json, written by scripts/paper/measure-compute-footprint.mjs; CI counts from the GitHub Actions API, inference counts from local session transcripts (partial window, stated in §09). Per-query energy from Jegham et al., How Hungry is AI? (arXiv:2505.09598) and Google's reported Gemini median; grid intensity from EPA eGRID; household comparison from US EIA.
  • Repository statistics — git history of origin/main, counted at build time.

Figures reflect the repository at commit 0e3579672. The underlying data refreshes continuously; a reader reproducing these tables later should expect vintage drift and is encouraged to check each metric’s own as_of field rather than this page’s date.