Skip to content

v0.2.0 — read formatVersion 3, and refuse what it cannot read

Choose a tag to compare

@sylvesterdamgaard sylvesterdamgaard released this 16 Sep 10:52
· 2 commits to main since this release

Breaking. JurisdictionAssignment::$authorities is now list<Authority>|null, was list<string>|null.

The bug this fixes

An authority set in the boundary format is [{level, code, type?}]. BoundaryData::sets() kept an entry only when it was a bare string — the v2 shape — so against a v3 artifact every entry was dropped and every set came out empty.

An empty set is not nothing in this format. It means a row covers this address and no local authority levies here. So v0.1.0 reported no local tax, with no error, for every address in all twenty-four Streamlined states. Against the real Kansas artifact that is 1029 sets, every one of them wrong.

The suite was green throughout, because every fixture had been hand-built to match the parser's assumption rather than what the register publishes. tests/Fixtures/ks.v3.json is now a genuine slice of boundaries/KS from register release 2026.09.15-202, so that cannot happen the same way twice.

Changed

  • BoundaryData::resolveZip(), resolveStreet() and Geometry::authoritiesAt() return Authority objects.
  • BoundaryData::fromArtifacts() and Geometry::fromFeatureCollection() throw UnsupportedFormatVersion for a version they do not implement, instead of reading what they can. A version this code cannot read has to stop: the alternative is a confident answer that is wrong in the direction nobody audits.

Added

  • Authority — level, code, optional type (the state's own district-type code: 79 and 26 in North Carolina, 63 in Minnesota, 02 in Vermont), and jurisdiction, filled only by the geometry layer, which publishes the register's own code.
  • Authority::key() and is(). Identity is level and code together, never the bare number — a county and a special district can file under the same number and levy separately, so matching on 209 alone merges two bodies that each want their own share.
  • UnsupportedFormatVersion.

Upgrading

Anywhere you read $assignment->authorities as strings, read $authority->key() ('county:209') or join on {stateFips, $authority->level, $authority->code}. Use $assignment->resolved() rather than checking for an empty array — null and empty mean opposite things.