Skip to content

Derive US local-jurisdiction flags (Denver, Yonkers, Wilmington, Philadelphia) from household geography - #1453

Open
MaxGhenis wants to merge 10 commits into
mainfrom
us-local-jurisdiction-flags
Open

MaxGhenis wants to merge 10 commits into
mainfrom
us-local-jurisdiction-flags

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Problem

policyengine-us 2.2.1 (and 2.60.4) declares four local-tax jurisdiction flags as inputs with no formula, so each stays False unless the dataset writes it:

Flag Entity Engine consumer
in_denver household Denver property-tax relief eligibility
in_yonkers household ny_yonkers_income_tax (resident surcharge)
in_wilmington household de_wilmington_earned_income_tax
pa_philadelphia_wage_tax_resident person pa_philadelphia_wage_tax (resident rate)

engine_abi.lock.json listed all four as declared_absent_engine_input, so every built dataset charged no Yonkers surcharge or Wilmington earned-income tax and gave no Denver relief.

Change

Registry. us_runtime/us_local_jurisdictions.json maps each flag to one 2020 geography, and local_jurisdictions.py pins its SHA-256 (45035eee…).

  • County rule: Denver county 08031 and Philadelphia county 42101. Each is the same area as its city.
  • Place rule: Yonkers 36/84000 and Wilmington 10/77580. Place codes repeat across states, so the rule matches the state from county_fips[:2] as well.

tools/verify_us_local_jurisdiction_registry.py checked every code against Census data; none came from memory:

Flag Geography P1_001N Blocks (ladders 7ba39b95 and 6840b990)
in_denver county 08031 = place 08/20000 715,522 8,394 (same blocks as the city)
in_wilmington place 10/77580 70,898 1,071
in_yonkers place 36/84000 211,569 1,336
pa_philadelphia_wage_tax_resident county 42101 = place 42/60000 1,603,797 14,549 (same blocks as the city)
  • The 2020 Gazetteer place and county files (zip SHA-256s recorded) match each GEOID, ANSI code, name and land/water area. Each county-rule county has the same ALAND/AWATER as its city.
  • The 2020 P.L. 94-171 P1_001N from the Census API matches each population.
  • Both block ladders sum to the national 331,449,281 and to each jurisdiction's population exactly.

Lines.

  • Block ladder (with_household_us_geography_ladder): writes all four flags from the block's county and place. Person flags go through person_household_id.
  • PUMA ladder (with_household_us_puma_ladder, used by the stacked pool): writes the county-rule flags from the drawn county. It never writes in_yonkers or in_wilmington and removes any stale column. The summary records them as unavailable with the reason.
  • Pooled ACS line (with_optional_acs_spine): re-derives the county-rule flags for every row from the final county and removes the place-rule flags. Without a PUMA ladder it removes every flag, since ACS rows then have no county.
  • Block-location draw (US household location v1: one census block per household, every geography derived #1047): nothing here edits that branch. It can call with_us_local_jurisdiction_flags(frame, rungs=BLOCK_GEOGRAPHY_RUNGS).
  • Gates: both ladder gates fail:
    • a flag that disagrees with its rule;
    • a null or non-boolean flag;
    • a place-rule flag on a table with no complete place_fips; and
    • a missing flag that the line's own rung decides. The block gate requires in_denver, in_yonkers and in_wilmington; the PUMA gate requires in_denver.

Existing artifacts. A base H5 built before this change carries no flags. Two steps start from an existing artifact, and both now call rederive_us_local_jurisdiction_flags first:

  • export_us_l0_refit_h5 records the receipt as local_jurisdiction_flags in its summary.
  • tools/build_us_fiscal_refresh_release.py runs it on the loaded base or prepared pool, before frozen-support selection. It records the receipt under support_value_repairs in the calibration diagnostics and as a telemetry stage.

The helper reads the rungs from the artifact's own geography. The county rung needs county_fips on every row. The place rung also needs place_fips and a 15-digit block_geoid on every row, so a pooled artifact whose ACS rows have no block gets only the county-rule flags. A carried household or person flag that disagrees with the geography stops the step.

The next certified build from main therefore carries the flags even from a pre-change base, which moves its Yonkers, Wilmington and Denver aggregates. The running Route A build pins its own commit and is untouched.

Pooled ACS receipt. build_optional_acs_multispine records provenance["local_jurisdiction_flags"] with or without a PUMA ladder. Without one, every flag is listed as unavailable with its reason; before this change that receipt was dropped.

Philadelphia. pa_philadelphia_wage_tax_resident exists in the pinned engine, so the registry decides it from county 42101 and records the rule. The tax also needs pa_philadelphia_wage_tax_taxable_wages, which no line supplies yet, so built datasets still compute zero Philadelphia wage tax. That follow-up is split out.

Engine ABI lock. tools/repin_us_pool_engine_contracts.py re-derived the pool manifest:

  • in_denver and pa_philadelphia_wage_tax_resident → geography_derived_jurisdiction_input, available at assembled.
  • in_yonkers and in_wilmington → unavailable_place_jurisdiction_input, with fallback policyengine_default_no_place_rung.
  • county_fips and congressional_district_geoid → preserved_stacked_engine_input. The stacked PUMA operator writes both, and the lock had them as declared absent; without this the new rows would cite a source the lock calls absent.
  • Counts: declared absent 762 → 756, preserved 4 → 6. Manifest digest d4fb9de9 → 6e70bc74.

The stacked checkpoint identity binds that digest, so a checkpoint built before this change is refused on resume rather than reused without flags.

Spec pins. repin_us_spec_engine_contracts.py, generate_us_bundle_from_constants.py and spec_engine_coverage.py re-derived the spec bundle (US spec_sha256 0b5c29e4…), inventory digests, coverage evidence and test pins. --check passes on all three tools (42305/42305 fields, 41/41 checks).

Invariants

These hold for every input. Hypothesis tests in tests/engine_free/us/test_us_local_jurisdictions.py check them:

  1. Iff. A county-rule flag is true exactly when county_fips equals the registry county. A place-rule flag is true exactly when county_fips[:2] and place_fips equal the registry state and place. The oracle is the four codes written out in the test, independent of the registry file. Generated codes include near misses: place 84000 in other states, 08032, 18031 and random codes.
  2. Population conservation. Over any block ladder, the flag-weighted population of the blocks the ladder assigns equals the population of the jurisdiction's blocks. The test runs the real assign_us_geography_ladder on random ladders. The verification tool checks the same identity on the real ladders against P1_001N.
  3. Determinism and order invariance. Derivation repeats exactly and commutes with any permutation of household rows. Each person carries its household's value under any person order.
  4. Never silently False. A line without a place rung never writes a place-rule flag. Missing, null, numeric or malformed county or place codes raise.
  5. Registry pin. The packaged bytes must match the pinned SHA-256. The recorded ladder checks must equal the registry populations and the national total.
  6. Re-derivation round trip. For any set of block-line households, dropping every flag and re-deriving restores the block line's household and person tables exactly. Re-deriving again changes nothing. A carried flag that disagrees with the artifact's geography is refused.
  7. Required where decidable. A ladder gate fails exactly the absent flags whose rule its line supplies. The block gate requires all three household flags; the PUMA gate requires only in_denver.

Mutation check, with the Hypothesis DB cleared for each mutant:

  • dropping the state match from the place rule: 6 failures;
  • leaving stale place flags in place: 4 failures;
  • matching only the last three digits of the county: 2 failures.

Tests

  • engine_free/us/test_us_local_jurisdictions.py (new): registry pin and validation, the Hypothesis properties above, the block and PUMA lines end to end, the frame helper for US household location v1: one census block per household, every geography derived #1047, and gate failures. It also covers rung inference, person-flag checks, and re-derivation of pre-flag, corrupt, pooled and county-less artifacts.
  • engine_free/us/test_us_l0_refit_export.py: a pre-flag base on a Yonkers block exports in_yonkers=True and records the receipt. A base whose carried flag disagrees is refused before the writer runs.
  • engine_free/us/test_us_fiscal_refresh_builder.py: the builder derives the flags once, after the base load and before selection and export, and the diagnostics carry the receipt.
  • engine_free/us/test_us_acs_multispine.py: the pooled receipt is recorded with and without a PUMA ladder.
  • engine_free/us/test_us_geography_ladder.py, test_us_puma_ladder.py and test_us_county_vintage_ladders.py: gate fixtures carry the flags, and new cases cover a missing required flag.
  • engine_free/us/test_us_spine_blindness.py: classifies local_jurisdictions.py and pins the pool tool's runtime graph at 82 modules.
  • engine_contract/us/test_spec_engine_engine_abi.py: repins the remaining-stage manifest digest to 6e70bc74….
  • engine_free/us/test_us_base_pool.py: the pooled line re-derives the county flags for every row and drops the place flags, or drops every flag without a ladder.
  • engine_free/us/test_us_multispine_pool.py: new provision counts and rows.
  • engine_contract/us/test_us_local_jurisdiction_engine_contract.py (new): each flag is an input-only boolean (default False) on its entity in the pinned engine, and each listed consumer reads it.
  • engine_scenario/us/test_us_local_jurisdiction_flags_engine.py (new): synthetic households placed by with_household_us_geography_ladder and run through PolicyEngineUSEngine.materialize.
    • Yonkers surcharge, Wilmington earned-income tax and Denver renter relief are nonzero for residents and zero for the neighbour.
    • The Philadelphia resident pays the resident rate and the Delaware County neighbour the nonresident rate.
    • Stripping the derived flags returns each resident to the neighbour's result.

Review

GPT-6.1 Sol reviewed b7b3e21 and asked for two changes (review-r1.md). Both are fixed in 84ab874:

  1. Older artifacts exported with silently false flags. The gate let every flag be absent, and l0_refit_export wrote the artifact without deriving them. Now both the export and the release builder re-derive the flags from the artifact's own geography, and both ladder gates fail a missing flag their rung decides.
  2. The no-ladder ACS pool dropped the unavailable-flags receipt. build_optional_acs_multispine now records it on both paths.

Commit 84ab874 also fixes the two CI failures on b7b3e21: the spine-blindness module classification and the stale ABI manifest digest pin. Sol's round-2 review of 84ab874 is pending.

Out of scope

PUMA×county×CD atom tables, congressional-district plans (#1041, #1043), making the block canonical (#1047), and every running Route A build.

Follow-up: Yonkers city may cover exactly 2020 residence PUMA 36-03112. If block-level checks confirm that, the PUMA line could decide in_yonkers instead of recording it unavailable. That needs its own Census verification, registry rule and repin, so it is a separate change.

Axiom: n/a (data/infra).

🤖 Generated with Claude Code

MaxGhenis and others added 3 commits November 2, 2026 08:02
policyengine-us declares in_denver, in_yonkers, in_wilmington and
pa_philadelphia_wage_tax_resident as inputs with no formula. The registry maps
each to its 2020 county (Denver 08031, Philadelphia 42101) or place (Yonkers
36/84000, Wilmington 10/77580), checked against the 2020 Gazetteer, P.L.
94-171 P1_001N and block ladders 7ba39b95 and 6840b990.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The block-ladder frame wrapper derives all four flags from the block's county
and place. The PUMA ladder and the pooled ACS line derive the county-rule
flags (in_denver, pa_philadelphia_wage_tax_resident) from the final county
and remove the place-rule flags (in_yonkers, in_wilmington), which no place
rung can decide there. Summaries record each flag as derived or unavailable,
and both ladder gates fail a present flag that disagrees with its rule.

The pool manifest moves the county-rule flags to
geography_derived_jurisdiction_input (assembled) and the place-rule flags to
unavailable_place_jurisdiction_input, and relabels county_fips and
congressional_district_geoid, which the stacked PUMA operator writes, as
preserved_stacked_engine_input. tools/repin_us_pool_engine_contracts.py
re-derived the provision counts, the manifest digest and the engine ABI lock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Re-derived with the repo tools after the derivation change:
repin_us_spec_engine_contracts.py (--legacy-only, then full),
generate_us_bundle_from_constants.py (US spec_sha256 0b5c29e4...) and
spec_engine_coverage.py (42305/42305 fields, 41/41 checks); --check passes
on all three and on repin_us_pool_engine_contracts.py.

Tests: Hypothesis properties for the iff rule, block-population
conservation, row-order invariance and person broadcast, plus registry pin,
gate, PUMA and pooled-line cases (engine_free); an engine contract that each
flag is an input-only boolean its consumers read; and an engine scenario in
which Yonkers, Wilmington, Denver and Philadelphia residents placed by the
block ladder get their local tax or benefit and their neighbours do not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review November 2, 2026 13:40
…, record the no-ladder pool receipt

Answers the round-1 review of b7b3e21:

- rederive_us_local_jurisdiction_flags reads the rungs an existing
  artifact's own geography supports (county when county_fips is on every
  row; place only when place_fips and a 15-digit block_geoid are too),
  refuses carried household or person flags that disagree, and writes
  the flags. export_us_l0_refit_h5 and the fiscal refresh release
  builder call it on the loaded base and record the receipt.
- Both ladder gates fail an absent flag whose rule the line's own rung
  decides (block: county and place; PUMA: county), so a missing column
  can no longer reach the engine as an all-False default.
- build_optional_acs_multispine records the pool's flag receipt in
  provenance on both the ladder and no-ladder paths.

Also classifies local_jurisdictions.py for the spine-blindness test,
repins the engine ABI digest, and reruns the US spec repin chain
(seed-kernel attestations hash geography_ladder.py and puma_ladder.py).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 6 commits November 2, 2026 12:24
…nifest

tools/graph_us_post_transfer_fixture.py rewrote the pinned graph's
remaining_stage_manifest_sha256 (d4fb9de9 -> 6e70bc74) after this PR
moved the pool manifest. Sources and expected.csv are byte-identical;
the tool's own graph-parity check passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review r2 finding 1: the rungs belong to the whole household table, so a
pooled base whose ACS rows carry no block decides only the county-rule
flags, and a frozen support or L0 support that keeps only block-anchored
donors would export without in_yonkers and in_wilmington even though its
own geography decides them.

The fiscal refresh builder now derives the flags on the loaded base, again
right after frozen-support selection (before target materialization), and
again on the export frame once the L0 or full-pool weights attach, before
any export gate or the H5 write. The calibration diagnostics record each
receipt under support_value_repairs.local_jurisdiction_flags, keyed base,
frozen_support and export.

rederive_us_local_jurisdiction_flags returns the same frame when it already
carries exactly the flags its rungs decide, as boolean columns, so the
repeat calls copy no tables. Property tests check that deriving a selection
of a derived support equals deriving the kept rows from scratch and leaves
every decidable flag, and that the unchanged return equals a rewrite.

The commit also adds us_local_jurisdiction_artifact_receipt, which refuses
an artifact that lacks or contradicts a flag its own geography decides and
otherwise returns the receipt a derivation would give. The selection
property test uses it as its completeness oracle; the annual static-aging
lane uses it in the next commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review r2 finding 2: the annual lane copies its parent's base year byte for
byte and keeps the parent's columns in every projected year, and the annual
publication validator requires exactly those columns. A parent built before
the local-jurisdiction flags therefore produced annual files without
in_yonkers or in_denver, and the engine defaulted every resident to False.

The lane cannot write the flags without breaking that preservation
contract (the base-year file is the certified release's own base
artifact), so it now requires them. build_annual_static_aging reads the
parent before the model provenance check and calls
us_local_jurisdiction_artifact_receipt, which refuses a parent that lacks
a flag its own geography decides, carries one that disagrees with it, or
carries one its rungs cannot decide. The candidate manifest records the
receipt as base.local_jurisdiction_flags.

Engine-free tests write parents with block, county-only and no geography:
six refusal cases fail before the model provenance check and before the
output directory exists, and complete parents reach it. Against the
previous lane the six refusal cases and the three receipt cases fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review r2 nit 3: an assembled checkpoint from before the local-jurisdiction
flags holds a passing PUMA-ladder receipt but no in_denver. The live gate
now fails it, and the rejection said only that the receipt changed from
live output, so the operator could not see the repair it needs.

_validate_stacked_geography_assignment_receipt now appends the live gate's
failure lines (in_denver: absent, but this table's county rung decides
it, ...) when the live gate fails, and says that the live gate passes but
its details differ otherwise. It still fails closed in both cases.

Engine-free tests build an assembled pool with every column the live gate
reads, with and without in_denver, and check that the message names the
missing flag only when it is missing; a third test covers the passing,
changed-details branch. All three fail against the previous message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in main at 3a9c808. Both sides moved the US seed-protocol
digests: main through housing_inputs (microcosm#1444), this branch through
geography_ladder and puma_ladder. The four conflicts were all digest pins,
so none keeps either side's value: tools/repin_us_spec_engine_contracts.py
recomputed EXPECTED_HASHES seed_map and seed_protocol, the loader golden
spec_sha256 and the pool-tool run_config spec_sha256, and
tools/spec_engine_coverage.py rewrote docs/evidence/spec-engine/
us-f0-coverage.json (42210/42210 configuration fields, 41/41 inventory
checks). A comment above the seed pins names both causes.

same_edit.py found no edit both sides made identically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rewrites six passive comments and docstring lines from the support
re-derivation commit so each names its actor (_main, the derivation, the
release engine). Comments only; the builder tests pass unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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