Repository navigation
v0.2.0 — read formatVersion 3, and refuse what it cannot read
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()andGeometry::authoritiesAt()returnAuthorityobjects.BoundaryData::fromArtifacts()andGeometry::fromFeatureCollection()throwUnsupportedFormatVersionfor 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, optionaltype(the state's own district-type code: 79 and 26 in North Carolina, 63 in Minnesota, 02 in Vermont), andjurisdiction, filled only by the geometry layer, which publishes the register's own code.Authority::key()andis(). 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 on209alone 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.