Method

What this calculates, what it refuses to guess at, and how to check any number it produces.

Scope

One-time fees to entitle and build. Entitlement and environmental review, building and trade permits, site and engineering permits, connection and impact fees, and posted assurances.

Not recurring costs: no utility service, property tax, business licence renewals, or annual inspections. And not construction cost — a state DOT access permit is a few hundred dollars, while the turn lanes it requires are not a fee at all.

Every dollar carries a citation

Each line records a source URL, document, section, adoption date, and a confidence level:

Confidence is reported in dollars, not line counts. "48 of 60 lines verified" sounds reassuring while hiding that the twelve unverified ones are most of the money.

Three things that are easy to get wrong

A $0 line is not an answer

A plumbing permit priced per fixture, run against a project where nobody filled in the fixture count, returns $0 — and $0 looks like a result. Those are surfaced as explicit warnings, and the command-line runner exits non-zero so it can sit in a pipeline that fails loudly.

A bond is not a fee

A letter of credit is posted as security and released on acceptance, so its face value never enters the fee total. Its premium does — roughly 1–2% per year of face value, which the fee schedule never mentions.

An estimate is not a budget

Fees are paid at permit issuance, not at underwriting, so they are escalated forward to the expected permit date. A contingency is then applied to fees plus bond premium together. Both conventions come from real fee checklists; on the worked example they move the number by about 31%.

Redevelopment credits

The existing meter earns a water and sewer capacity credit; the prior use's traffic offsets the transportation impact fee. Both are routinely left on the table, because claiming them requires knowing the existing meter size and the prior use. A credit is capped at the gross so a fee can never go negative.

Valuation is not one number

Jurisdictions variously take the contractor's declared price, compute it themselves from a $/sf table, or take the greater of the two. Each schedule declares which. The table is the published ICC Building Valuation Data, with an explicit escalation factor rather than adjusted numbers baked in invisibly.

Trip generation

Transportation impact fees are assessed on net new PM peak hour trips. ITE rates here are editable defaults drawn from publicly filed traffic studies, not a licensed reproduction of the Trip Generation Manual. Where an ordinance names a specific ITE edition and land use code, that frozen rate governs and should be set as an override.

Reading Confirm Entities

The roster is fixed. [5] lists thirteen categories — Zoning, Building, Water, Sewer, Stormwater, Electric, Gas, Schools, Roads, Parks, Fire, Health, Housing — every one of them, whether or not anything was found. An empty Water row reading LOW is the tool advertising a gap; an absent row would hide one. Anything the resolution ladder found that fits none of the thirteen, and any service the schedule prices that none of them covers, is listed below Housing rather than dropped, and a conservation line at the foot of the section says so in numbers.

Confidence is derived, never authored. There is no confidence field in any data file. Each row's level is a pure function of statuses the engine already computes: LOW — no specific body named, or a body named whose status is unresolved or absent, so nothing sourced establishes it bills here; MODERATE — a body named and sourced, but either no governing document was located or the strongest source form is an API, HTML or rendered-DOM capture, a live page that can change under its own citation after it is cited; HIGH — a governing document located as a fixed artefact, a regulator filing or an archived PDF, which cannot. Source forms rank regulator filing > archived PDF > API > HTML > rendered DOM, and the strongest decides. The dividing line is whether the document can change under its own citation.

Existence is not assignment. "Which county, township and incorporated place contain this point" is precisely what the Census Geocoder determines, so in [4] a geocoder match is the governing determination and reads HIGH — there is no separate document to go and find. In [5] the same geocoder establishes only that a body exists here and says nothing about who bills, so that tier alone never reaches HIGH on a service. Township is listed in [4] on purpose: every point in Indiana and Ohio falls in one and it genuinely governs, most often fire protection and poor relief.

A decision is recorded against the body, not the category. Confirm, Modify and Not Applicable confirm who a body is and that it is present here. One body can sit in two categories — Hancock County is both the County row and the Health row — and deciding it once decides it in both. That is not a claim that the body bills for the category; no button on the page can make one. The link identifies the body, not the document: a utility's homepage is a valid confirmation, and a confirmed body with a blocked document is a normal state, which is why entity status and document status are kept separate. Entity confirmation stays out of verified_pct, because confirming who bills is not the same act as confirming a number.

Two bodies on one row: alternatives or co-billers. A city MS4 utility and a county drainage board can both bill the same parcel for different pieces; a town sewer department and a private utility that split the town by territory are candidates for one slot. The page says which, and derives it rather than assuming: from the jurisdiction's record when it states the relationship (McCordsville's sewer, from Aqua Indiana's own service-territory page), otherwise from the bodies' kinds (several of one kind are candidates for one slot by construction), otherwise from the service registry's multiple_common flag — whether the service is normally billed by more than one body on the same parcel. When none of those settles it, the row says so instead of picking. On an alternatives row confirming one body resolves the others — a derived state, not a stored decision, so undoing the confirmation makes them candidates again; on a co-billers row each is confirmed on its own.

What the reader can add. A body, by link, on any row — the link identifies the body, so a pasted URL plus a name (pre-filled from the page title when one can be fetched, always editable) is a complete claim. It renders attested: no document produced it, it sits below anything cited, and the row's confidence is still derived from cited evidence alone. After ingest it also lands on the jurisdiction's artifact as an attested entity, which is exactly what document discovery needs to start a fee-schedule hunt. And an empty row can be marked not applicable — "no body bills this here" — recorded at the category level because there is no body to record it against. Neither claim can hide a finding: a not-applicable mark over a row where the ladder or the artifact names a body is shown as a conflict, both sides visible, until the reader withdraws one. Both travel through the same export → ingest path as every other decision, and corrections never auto-apply.

Two engines, held together

The estimator exists twice: Python for the CLI and reports, JavaScript so this site computes instantly in the browser. The JS holds logic only — every fee schedule, rate table, and default is exported from Python, and expressions are parsed in Python and shipped as a syntax tree, so the browser never parses anything. The two are held to each other by a line-by-line parity check.

What it cannot do