Skip to content

Make the three-state measurement runnable against a layer this repository does not ship #78

Description

@ChelseaKR

What

The three-state measurement is already generic: Cell/CellState in cells.py know nothing about fire, FieldSpec.classify and field_coverage in coverage.py operate on any FieldSpec, and require_columns refuses any file per row. What is not generic is everything around them — FRAP_FIELDS and DINS_FIELDS are Python literals inside schema.py, perimeters.py and dins.py are bespoke report builders, render.py is 1,132 lines of two hand-written pages, and cli.py takes exactly --frap and --dins.

This makes the reviewed field registry a declared file rather than a Python literal, and adds a verb that measures a tabular file against one: registry in, three-state coverage artifact out, with the same fail-closed behaviour (SchemaDriftError, SentinelDriftError, outside_published_domain, marker_basis, integer-only shares). The two shipped registries are re-expressed in that format as the proof it is a seam rather than a second branch, and the acceptance test is that site/data/perimeters-coverage.json and site/data/dins-coverage.json come out byte for byte unchanged.

Why this project

The repository's whole argument is that publishers already state where their data is incomplete and nobody publishes the arithmetic behind those sentences. That argument is not about wildfire. It is true of every civic dataset with a coded-value domain and a nullable column, and this is the only implementation of it that counts a published Unknown code as a third state rather than folding it into either neighbour.

Right now that method is locked to two files. Somebody who wants it pointed at their own layer has to fork schema.py. A declared registry turns the reviewed judgment — the domains, the markers, the basis, the finding-of-absence values — into the artifact it already is in docs/MARKERS.md, and makes "a third layer" a registry plus a page rather than a fourth module.

It is also the test ADR-0006 asks for in the sibling project's idiom: a seam is proven by a new source landing without the existing outputs moving.

Concrete seams

  • src/perimeter/schema.pyFieldSpec (line 70), Basis (line 51), require_columns (line 194), FRAP_FIELDS (line 286), DINS_FIELDS (line 453). The dataclass is already the format; this gives it a serialization and a loader that refuses an unreviewed registry.
  • src/perimeter/records.py — classification at the edge is already registry-driven and should not need to change; if it does, that is the finding.
  • src/perimeter/coverage.pyfield_coverage/field_coverages are generic; perimeter_report and dins_report are not, and the generic path needs a report that is only the field table plus whatever the registry declares.
  • src/perimeter/artifacts.py, src/perimeter/render.py, src/perimeter/cli.py — where the bespoke half lives.
  • docs/MARKERS.md — a registry file makes the audit and the registry the same object, which is the current promise ("schema.py is the registry itself; this document is the audit of it").

What must be decided first

  1. What perimeter is. Two shipped measurements published as pages, or a measurement engine with two shipped registries. The distribution question is already live and unresolved: the PyPI name is taken (noted as out of scope in Make acquire a reusable library: caller User-Agent, geometry, post-walk checks #64, and misdiagnosed in The PyPI name this repo would publish under belongs to yunojuno/django-perimeter #76), and a tool nobody can install is not a tool.
  2. Where the generic path stops. The JSON artifact generalizes cleanly. A page does not: render.py carries the three-state key, the marker lists, the counterfactuals and the publisher's quoted caveats, and a generic page that renders a registry with no quotes would publish a coverage number stripped of the sentence it answers — the thing this project exists to refuse. It may be right for the generic path to stop at the artifact and say so.
  3. Whether a registry is publishable without a marker audit. Every FieldSpec here carries a basis and a note because somebody reviewed it. A loader that accepts a registry with basis: unreviewed makes it easy to produce a well-formed measurement nobody has reviewed.

Relationship to open work

This is not the schema work in #63, which publishes a contract for the artifacts this code already emits, and not the library work in #64, which is about the acquisition path. Both are compatible with it and #63 in particular becomes the output contract of the generic path.

Size

Multi-session, and the largest of the current proposals. The registry format and loader are a couple of days; the honest cost is render.py and deciding item 2.

Proposed with AI assistance.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestfeatureMajor new capability proposal

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions