A field note is published output, and two of them were false - #26
Merged
Conversation
This was referenced Aug 28, 2026
ChelseaKR
force-pushed
the
fix/notes-that-claim-a-denominator
branch
from
August 29, 2026 00:28
19c17c5 to
f290572
Compare
`FieldSpec.note` is copied verbatim into site/data/*-coverage.json beside the counts it describes, so it is published output. Two of them were false. WHEREFIRESTARTEDONSTRUCTURE said its blank rate was "read against the affected subset rather than the whole file". `field_coverage` derives a field's total from the records it is handed and `dins_report` hands it all 132,522, so the sentence described a measurement that has never existed, and it shipped inside the same JSON object as the 2.8% it was describing. The note now quotes what D4 actually says, "Only recorded for Affected 1-9% damage category", names it as CAL FIRE's restriction on collection rather than this project's on counting, and says the counts are over every record. WHATDIDFIRESTARTFROM carried the shorter form, "Collected only where the structure was affected", with nothing in this repository behind it. docs/MARKERS.md:182 gives its source as D3 and D4 and records only that D4 adds "May not be reliably determined". The note now says that. It does not borrow the other field's restriction: D4's "Affected 1-9%" is a named damage band recorded on one field's audit line and not the other's, and writing it as "where the structure was affected" would widen it to four damage bands under CAL FIRE's name. That is why PR #25 was closed and why the false clause was still here to remove. Nothing in this repository read a field note until now. tests/test_field_notes.py adds four gates, each also run against input it must reject, per ADR-0004: 1. a note may not claim a population narrower than the file 2. every field's total is every record it was handed, in the code, in the published artifacts, and per incident 3. a quotation in a note must be transcribed in docs/MARKERS.md on that field's own line 4. the note in schema.py and the note in the published artifact are one sentence Measured on this tree. Against the pre-change registry, gate 1 fails with [('WHEREFIRESTARTEDONSTRUCTURE', 'affected subset'), (..., 'rather than the whole file'), (..., 'read against the')] and the other thirteen tests in the file pass. With schema.py corrected and site/ not yet rebuilt, gate 4 fails naming both fields. After `make site`, all fourteen pass; `make verify` exits 0 with 577 tests and 100% branch coverage. site/ is rebuilt from the acquired files in data/raw/, and the rebuild changes exactly two lines of site/data/dins-coverage.json: the two notes. Nothing else in the published measurement moved, which also re-verifies that the committed site/ was current. Closes #23. See ADR-0005. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ChelseaKR
force-pushed
the
fix/notes-that-claim-a-denominator
branch
from
August 29, 2026 16:42
f290572 to
ddf067c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #23. Adds ADR-0005.
What was wrong
FieldSpec.notereads like a comment and is not one.artifacts.pycopies it verbatim intosite/data/*-coverage.jsonbeside the counts it describes, so it is the only prose a machine reader of these artifacts gets handed with the numbers.WHEREFIRESTARTEDONSTRUCTUREtold a reader its blank rate was "read against the affected subset rather than the whole file". There is no such subset anywhere in this pipeline.field_coverage(src/perimeter/coverage.py:66) derives a field's total from the records it is handed anddins_report(coverage.py:237) hands it all 132,522. The false sentence shipped inside the same JSON object as the 2.8% it purported to describe.WHATDIDFIRESTARTFROMcarried the shorter form, "Collected only where the structure was affected", with nothing in this repository behind it.Why not PR #25's replacement
PR #25 removed the false clause correctly and replaced it with "Collected only where the structure was affected per CAL FIRE's dictionary", on both fields.
docs/MARKERS.md:181records a collection restriction for one of the two, quoting D4 asOnly recorded for Affected 1-9% damage category: a named damage band, not "affected" generally.grep -rn "Only recorded for"returns that line and no other, anddocs/MARKERS.md:182givesWHATDIDFIRESTARTFROM's source asD3, D4. D4 adds "May not be reliably determined"with no restriction at all.So this PR quotes D4's actual wording, on the one field whose audit line records it, and says for the other field what the audit actually records. Both notes now say separately what CAL FIRE restricts and what this project counted, because those are two different facts.
The gates
Nothing mechanical read a field note until now. Every other published fact here has a reader: provenance has
test_provenance.py, the standards pin hastest_standards_conformance.py, a number in page prose hastest_pages_html.py.tests/test_field_notes.pyadds four, each written as a function that is separately run against input it must reject, per ADR-0004:docs/MARKERS.mdon that field's own line. Double quotes mean source text, single quotes mean a value. This is the gate that rejects PR docs(schema): clarify affected structure note for fire origin fields (fixes #23) #25's attribution.schema.pyand the note in the published artifact are one sentence.Proof each new check can fail
Measured on this tree.
Against the pre-change registry (
tests/test_field_notes.pyadded,schema.pyuntouched), gate 1 fails and the other thirteen pass:With
schema.pycorrected andsite/not yet rebuilt, gate 4 fails on real drift:Gates 2 and 3 pass in both states, so each is additionally run against input it must reject, in the same file:
test_the_total_gate_rejects_a_field_counted_over_less_than_the_filetest_the_quotation_gate_rejects_the_attribution_pr_25_proposedtest_the_quotation_gate_rejects_a_quotation_nobody_transcribedtest_the_drift_gate_rejects_an_artifact_left_behind_by_an_editPositive controls, passing identically before and after:
test_the_prose_gate_rejects_the_sentence_it_exists_to_catch,test_every_field_is_counted_over_every_record_it_was_handed[dins], andtest_every_published_field_total_is_the_published_record_count[dins-coverage.json].Gate output
Exit code read from
$?, not from the tail.The published artifact
site/is rebuilt withmake sitefrom the acquired files indata/raw/. The rebuild changes exactly two lines ofsite/data/dins-coverage.json, both of them notes:Nothing else in the published measurement moved, which also re-verifies that the committed
site/was current againstfirep25_1and the 2026-08-07 DINS retrieval.🤖 Generated with Claude Code