# Ohio homepage comparison: source record and limits **Record date: 25 September 2026 (UTC).** This record supports a possible Snapshot sentence about a named website feature among sampled Ohio business homepages. It does not establish a statewide rate for all businesses. ## Where the businesses came from The business list came from the **Overture Maps Places release 2026-09-23.0**. We used its listed website, primary category and location. The nine counties were Franklin, Cuyahoga, Hamilton, Summit, Montgomery, Lucas, Stark, Butler and Lorain. County assignment used the US Census 2025 county boundaries. [LICENCES.md](LICENCES.md) records the Overture source terms and field notices: the contributing Overture records carry CDLA Permissive 2.0, Apache 2.0 or CC0 1.0. Overture provides source labels; Cowerx made the homepage observations. Foursquare OS Places was **not** ingested in this benchmark. ## How sites were selected We included only Overture primary categories explicitly mapped to the 18 types in [overture_categories.yml](overture_categories.yml). A broad category family did not qualify. Within each type and county, we sorted listings by a repeatable hash of the source ID, then took turns among counties. We sought 80 listed website domains per type, excluding social networks and listing directories. A domain could enter only once across types. If a type had fewer than 50 successful homepage observations, we drew disjoint reserves with the same rule until it reached 50. For this rendered extension, low-yield types drew further disjoint county-balanced reserves. The resulting static pools contain 100 landscaping, 95 retail boutique, 85 exercise gym, 80 cleaning and 53 real estate homepages; attempted counts can therefore exceed 80. Retail boutique's first 80-candidate reserve tranche was exhausted at 95 usable static sites. No type, county or ownership label was hand-selected. The same input release and mapping produce the same selection. This is a balanced sample of **listed sites we could observe**, rather than a random sample of all Ohio businesses. [BENCHMARKS.md](BENCHMARKS.md) shows attempted and usable counts by type. ## What was observed The first check fetched each site's homepage as **CowerxBot**, obeyed robots.txt and allowed no more than one request per second per host. Failed, blocked and non-HTML checks were excluded from the rate denominator. Some previously checked Columbus homepages were reused. The original successful static checks ran from **08:06 to 09:16 UTC on 25 September 2026**; additional reserve checks ran later the same day. From each type's successful static checks, we selected the first 20 by the same repeatable ID hash for a headless **Chromium** revisit, then continued the hash order toward 30 usable renders per type. Newly added static reserves enter after the original pool, sorted by that same hash. CowerxBot checked robots.txt again, blocked private hosts, paced document and script requests to at most one per second per host, and allowed at most six concurrent hosts. Images, styles, fonts, frames and service workers were blocked; third-party scripts were allowed. The extension waited for network idle (up to five seconds), scrolled to the page bottom, waited two seconds, and returned to the top before inspecting the rendered DOM. A timed-out or blocked revisit does not enter the rendered denominator. The type-specific rendered samples represent eight or nine of the nine source counties; they are not county-level estimates. A group of technical timeouts from an interrupted observer revision received one retry; only the final successful observation enters the rate. [SIGNAL-ACCURACY.md](SIGNAL-ACCURACY.md) records the attempt and usable counts, rates and UTC dates. The detector counted **tap-to-call** when the homepage contained a link with `tel:` and at least ten digits. A **form** required a form element with a message box or contact-like fields. **Booking** meant a link or resource matching known scheduling words or booking services; we did not click through or complete a booking. A visible phone meant phone-shaped text in the page. HTTPS used the final page address, and a mobile viewport meant a viewport setting in the page. These are automated presence checks, not manual tests of whether a call, message or booking actually works. The booking detector was expanded before the rendered check, so its change from the static result reflects both rendering and detector changes. [SIGNAL-ACCURACY.md](SIGNAL-ACCURACY.md) shows the paired comparison and failures. ## How to read a claim The denominator for a feature is the number of **successful rendered homepages for that type**, not the number of businesses in Overture, the 80 initial attempts or the larger static sample. We count homepages where the feature was detected and divide by that denominator, then round to the nearest whole percent. The sentence names the sample, method, date and n. For example, “47% of sampled Ohio plumber homepages had a tap-to-call link” would mean that about 47 of every 100 **observed plumber homepages in this sample** had a detected `tel:` link at the time checked. It would **not** mean 47% of all Ohio plumbers, 47% of all plumber websites, that the link was visible on the first phone screen, that calling succeeded, or that the other sites had no phone number elsewhere. **47% is an illustration from the original static check and is not an approved rendered claim.** `claim()` refuses a feature unless at least 20 rendered observations support it, the local manifest matches the current benchmark, and the feature is not static-only. At n=20–49, treat a result as exploratory; n≥50 is the target for grading. Overture type labels can be wrong, a single domain does not prove independent ownership, sites can change, and a homepage may hide a feature found on a contact or booking page. ## Audit trail without publishing business names [manifests/](manifests/) contains one CSV per type. Each successful static homepage has a row; a successful Chromium revisit adds a second row for the same site. Rows contain the UTC observation time, method, yes/no signals and a **keyed SHA-256 hash of the listed URL**. There are no names, URLs, source IDs, phone numbers, page bodies or raw captures in the local manifests. The random key (salt), original URLs, audit checkpoint and Parquet remain only in ignored `work/`. A person with the private salt and source data can link a hash back to the checked URL; the local CSV alone supports arithmetic checks but cannot establish what a specific named site showed. The manifest ID returned with an allowed claim is the SHA-256 digest of that type's CSV. It identifies the exact file used for the calculation. To reproduce the record from the local private inputs, run: ```sh python3 -m lines.data.ohio_identity.observation_manifest publish \ --source lines/data/ohio_identity/work/benchmarks.parquet \ --audit lines/data/ohio_identity/work/signal_accuracy.jsonl \ --salt lines/data/ohio_identity/work/manifest_salt.hex python3 -m lines.data.ohio_identity.observation_manifest verify \ --source lines/data/ohio_identity/work/benchmarks.parquet \ --salt lines/data/ohio_identity/work/manifest_salt.hex ``` `verify` compares every per-type percentage, method, denominator and site hash to the private benchmark. Without the salt, it can still recompute percentages from the CSVs and compare them with a local benchmark file. Keep the original salt: changing it changes every URL hash and manifest ID. ## Where the manifests live The per-type observation manifests are captured data, so they are kept out of git (gitignored, in `manifests/` next to this file on the machine that ran the audit). Their SHA-256 checksums are committed in `MANIFEST-CHECKSUMS.sha256`, so any manifest produced later for an audit can be proven unchanged.