Skip to content

An average over the file describes no year, no county and no structure class - #80

Merged
ChelseaKR merged 1 commit into
mainfrom
feat/dins-completeness-cuts
Sep 7, 2026
Merged

ChelseaKR merged 1 commit into
mainfrom
feat/dins-completeness-cuts

Conversation

@ChelseaKR

Copy link
Copy Markdown
Owner

What was wrong

DINS completeness was published for the whole file and per incident. The file spans a decade of inspection practice, fifty-two counties and several classes of structure, and completeness differs along every one of those axes: fields that exist only in later inspection forms, the two spellings of Not Applicable that occupy non-overlapping eras, construction attributes that apply to a residence and not to a shed.

A reader planning to use EAVES needs to know it is blank in the early years and not the late ones. One number across the file describes no year in particular.

What changed

completeness_by_year, completeness_by_county and completeness_by_structure_category in site/data/dins-coverage.json, and three tables on the DINS page.

Each cohort carries its own record count as its denominator, the same assessed / inaccessible / neither split the whole file carries, the three-state count for every registered field, and each field's completeness inside each of those three access populations. Nothing compares one cohort against another: no county is ranked, no year is called better than another, and no row is a rate of anything but its own cells.

fields_by_access publishes [present, total] pairs rather than shares. A population with no records has no share, and a 0 where the share would go reads as "the field is empty here" rather than "nothing was counted here". The consumer divides, and sees the denominator it divided by.

Three refusals, each with a test

An absence is not a value. A record whose county cell is empty and a record whose county cell carries a marker are two cohorts, and neither is a county. Their published value is null while label is prose, so a consumer grouping by value never acquires a county named the empty string. The same for a year that was never recorded.

An out-of-domain category does not get a row. A recorded STRUCTURECATEGORY the publisher's domain does not describe goes to one outside_published_domain cohort. A row would publish a category CAL FIRE does not define as though this project had found one (ADR 0002). Two different unpublished spellings land in the same cohort, and a test uses two.

An empty denominator is never a share. The page's field columns are shares of each cohort's assessed records, because that is the population the construction attributes describe. A cohort made entirely of structures nobody could reach therefore reads no records rather than 0.0%: there is no denominator to divide by, and a zero would say the inspectors found nothing when what happened is that nobody could walk up to anything.

Cohorts with no records are not published at all. A row of zeros reads as a finding about a county the file simply never names.

test_each_cut_sums_back_to_the_file_totals asserts the partition per field and per state across the whole 37-field registry, in all three cuts, and a second test does the same for the access split. A cut that lost or double-counted a record fails there rather than publishing a quietly smaller denominator.

The marker era claim is measured now, not remembered

docs/MARKERS.md section 2 rests the decision to count both NA and N/A as CAL FIRE's own finding on the observation that the two spellings fall on opposite sides of one year. Both counts and that boundary were numbers a person measured once and typed into the document. That is precisely the shape the document exists to be suspicious of.

They are published now, per incident-start year, under not_applicable_spellings_by_year, and a test holds the paragraph to the artifact in both directions: the sentence has to state the counts the file produces, and the boundary the sentence claims has to still hold in the file.

Measured against the acquired file, at the values the document already carried:

incident start year records N/A NA
2018 28,403 5,167
2019 2,108 1,377
2020 28,626 2,147
2021 12,253 1,348
2022 3,010 229
2023 229 12
2024 8,117 1,295
2025 32,293 7,203
total 6,544 12,234

6,544 and 12,234 are exactly the numbers docs/MARKERS.md already stated, and no year carries both spellings. The document was right; nothing was keeping it right.

Every existing number is unchanged

Rebuilt with make site against the acquired FRAP and DINS files. make diff against origin/main:

24,947 additions
0 changes
0 removals
56,242 leaves before, 81,189 after

The artifact grows from 1.5 MB to 2.5 MB and dins.html from 191 KB to 218 KB.

How it was verified

Full suite 947 passed. html-validate clean; tools/a11y.mjs reports three pages with no violation and nothing undecided beyond the four declared rules, against six rule sets; the Chromium Playwright gate (axe plus reflow at 320px) 8 passed. The three new tables are focusable scroll regions with captions and scope headers, which is what the browser half of that gate caught on the existing tables once before.

The artifact schema landed in #79 caught this change automatically, which is the point of it: adding the keys to the writer without adding them to the contract failed test_the_committed_artifact_validates until both moved together.

Six negative controls, each committed first, baselined with git hash-object, sabotaged, asserted changed, asserted the sabotaged file still parses, run with __pycache__ cleared under PYTHONDONTWRITEBYTECODE=1, restored and re-hashed:

sabotage expected red result
_absent publishes its label as the cohort value the absence-is-not-a-value checks 3 red
the out-of-domain test in the category key disabled the one-cohort check 1 red
group_by_cohort drops the last record the partition 12 red
present_tenths_of_percent returns 0 for an empty denominator the empty-assessed-denominator check 1 red
docs/MARKERS.md: 6,544 changed to 6,540 the document-to-artifact binding 1 red
the published artifact given a N/A in 2021 the era boundary 1 red

All six restored to their exact baseline blob hashes.

One assertion was wrong on its first run and it is worth recording, because it would have passed for the wrong reason on a different day. The empty-denominator check first asserted "0.0%" not in <the whole year table>. "0.0%" is a substring of "100.0%", so a healthy cohort failed it, and a percentage anywhere else in the table would have failed it too. The claim is about one cohort's cells, so it is scoped to that row now and asserts no % appears in it at all.

Scope

Part of #61. Cross-tabulations of two cuts at once and any per-county damage figure stay out of scope, as the issue says.

One thing the artifact carries beyond the issue's Scope list, deliberately: fields_by_access per cohort, the per-field assessed / inaccessible / neither split. The issue's own "Done when" asks that a year with only inaccessible records publish its assessed denominator as none in words, and there is no such denominator to publish without it.

Prepared with AI assistance; reviewed before submission.

…e class

DINS completeness was published for the whole file and per incident, and it
differs along every axis this file has: fields that exist only in later
inspection forms, the two spellings of Not Applicable that occupy
non-overlapping eras, construction attributes that apply to a residence and not
to a shed.

Adds completeness_by_year, completeness_by_county and
completeness_by_structure_category to the artifact and three tables to the page.
Each cohort carries its own denominator, the assessed / inaccessible / neither
split, the three-state count for every registered field, and each field's
completeness inside each access population as [present, total] pairs rather than
shares. Nothing compares one cohort against another.

An absence is not a value: a blank county and a county carrying a marker are two
cohorts and neither publishes a `value`. A recorded structure category outside
the published domain goes to one outside_published_domain cohort rather than
getting a row. A cohort with no assessed records reads "no records" rather than
0.0%, because there is no denominator to divide by.

Also publishes the NA against N/A era claim per year. docs/MARKERS.md rests a
marker decision on it and it was a number measured once and typed in; a test now
holds the paragraph to the artifact in both directions.

Every existing number is unchanged: 24,947 additions, 0 changes, 0 removals.
@ChelseaKR
ChelseaKR merged commit c069a17 into main Sep 7, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant