Skip to content
Draft
1 change: 1 addition & 0 deletions changelog.d/us-local-jurisdiction-flags.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Derive the policyengine-us input-only local flags `in_denver`, `in_yonkers`, `in_wilmington` and `pa_philadelphia_wage_tax_resident` from household geography through a SHA-pinned registry checked against the 2020 Gazetteer, P.L. 94-171 counts and the block ladder. The block-ladder line writes all four; the PUMA and pooled ACS lines write the county-rule flags and record the place-rule flags (Yonkers, Wilmington) as unavailable instead of writing `False`. The L0/refit export and the fiscal refresh release builder re-derive the flags from an existing base's own geography, so a base built before this change no longer exports without them, and both ladder gates now fail a table that lacks a flag its geography decides.
1 change: 1 addition & 0 deletions changelog.d/us-local-work-location-inputs.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Derive `pa_philadelphia_wage_tax_taxable_wages` for Philadelphia residents as `max(employment_income_before_lsr, 0)` and 0 for everyone else, through a SHA-pinned registry (`us_local_tax_inputs.json`), so `pa_philadelphia_wage_tax` no longer returns 0 for every microcosm household. The block-ladder line, the stacked pool's derive stage, the pooled ACS line, the L0/refit export, the fiscal refresh release builder and annual static aging all write or re-derive it. The registry records the nonresident component and the other four work-location and refund inputs (`ny_yonkers_nonresident_earnings`, `de_wilmington_nonresident_earnings`, `co_denver_employee_occupational_privilege_tax_months`, `pa_philadelphia_wage_tax_reduced_rate_eligible`) as unavailable, with the reason no source decides each. They are never written, so the engine default applies, and the pool manifest shows them as unavailable instead of declared-absent.
44 changes: 22 additions & 22 deletions docs/evidence/spec-engine/us-f0-coverage.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"country": "us",
"documentation_sha256": "811b4091f9419c8792e5cc7fcda30d54c5a7c6d35dd944c26c005e32cae06647",
"field_usage": {
"authored_normative_field_count": 32524,
"authored_normative_field_count": 32525,
"claim_count": 49,
"claims": [
{
Expand Down Expand Up @@ -530,8 +530,8 @@
],
"mode": "legacy_behavior",
"pointer_class": "all",
"pointer_count": 88,
"pointer_sha256": "ae011455154bae0df3913ca9a056058a909d913439b292cb46c11f63c7d0d9a3",
"pointer_count": 89,
"pointer_sha256": "68a3a00171aca397db079a7482b68aba8a7b4ad88235ac1bd0d8d7061a242e1c",
"rationale": null,
"relative_sink_prefix": null,
"source_prefix": "/authored/spec~1spine.yaml/pipeline_contract",
Expand Down Expand Up @@ -722,8 +722,8 @@
],
"mode": "legacy_behavior",
"pointer_class": "all",
"pointer_count": 8614,
"pointer_sha256": "66eec1216f404e43f7605fba0016d1cd666e71ebe3e35278d14a944d8b48b619",
"pointer_count": 8655,
"pointer_sha256": "e8cb4e455fd52f3ff9fffe402fc5a943dea81c89a1310a458649c7561b286ccb",
"rationale": null,
"relative_sink_prefix": null,
"source_prefix": "/resolved/generated_authorities",
Expand Down Expand Up @@ -772,21 +772,21 @@
"verifier": "vintages"
}
],
"configuration_field_count": 42305,
"consumed_field_count": 42305,
"configuration_field_count": 42347,
"consumed_field_count": 42347,
"generation0_effect_counts": {
"legacy_behavior": 38608,
"legacy_behavior": 38650,
"no_generation0_effect": 3697
},
"mode_counts": {
"compiler_semantic": 27751,
"front_end_validation": 354,
"identity_only": 103,
"legacy_behavior": 14097
"legacy_behavior": 14139
},
"multiple_primary_use_field_count": 0,
"pointer_inventory_sha256": "d51087fd950f99fb7fcf126c31419374172d946f5846d2bf9d0eb0e7a6dfca46",
"resolved_binding_field_count": 9781,
"pointer_inventory_sha256": "29f7f23ad778c4a3f56f5cc269bd8868739fd39470e5f9fbb0089c205ad56238",
"resolved_binding_field_count": 9822,
"unused_field_count": 0
},
"inventory_coverage": {
Expand Down Expand Up @@ -1656,13 +1656,13 @@
"compiler_ir.node_slices"
],
"expected": {
"map_sha256": "488846129a3c47e89d54538515e6bdbd7ce849e4081ab8a4ad40c9ce2a1e1391",
"protocol_sha256": "87f44e60621c45d2fc1448fda11cd446960d6bab7cb51b346367339e2e751e1f"
"map_sha256": "42c141d97283222723e0e96822f62f49829d3c5a1904fac950b1110af85028da",
"protocol_sha256": "cf465029597523a6efc4fd411601180ff811d26614f3af85ef0e0610f8b8f24d"
},
"failures": [],
"observed": {
"map_sha256": "488846129a3c47e89d54538515e6bdbd7ce849e4081ab8a4ad40c9ce2a1e1391",
"protocol_sha256": "87f44e60621c45d2fc1448fda11cd446960d6bab7cb51b346367339e2e751e1f"
"map_sha256": "42c141d97283222723e0e96822f62f49829d3c5a1904fac950b1110af85028da",
"protocol_sha256": "cf465029597523a6efc4fd411601180ff811d26614f3af85ef0e0610f8b8f24d"
},
"status": "covered"
},
Expand All @@ -1677,7 +1677,7 @@
"compiler_ir.seed_stream_map"
],
"expected": {
"implementation_sha256": "87f44e60621c45d2fc1448fda11cd446960d6bab7cb51b346367339e2e751e1f",
"implementation_sha256": "cf465029597523a6efc4fd411601180ff811d26614f3af85ef0e0610f8b8f24d",
"protocol": "legacy-v1",
"streams": [
"build_model",
Expand All @@ -1698,7 +1698,7 @@
},
"failures": [],
"observed": {
"implementation_sha256": "87f44e60621c45d2fc1448fda11cd446960d6bab7cb51b346367339e2e751e1f",
"implementation_sha256": "cf465029597523a6efc4fd411601180ff811d26614f3af85ef0e0610f8b8f24d",
"protocol": "legacy-v1",
"streams": [
"build_model",
Expand Down Expand Up @@ -1887,7 +1887,7 @@
"alpha",
"zeta"
],
"sha256": "69c2e4fcc8ebdf1e6257ccaf4787b736c4822beda5f4ee01a53ffa3945f4936f"
"sha256": "668073673f214f8cee24f435a5b8f6167831ac6a4e303fc6420b8ad3684ed9db"
},
"failures": [],
"observed": {
Expand All @@ -1911,7 +1911,7 @@
"alpha",
"zeta"
],
"sha256": "69c2e4fcc8ebdf1e6257ccaf4787b736c4822beda5f4ee01a53ffa3945f4936f"
"sha256": "668073673f214f8cee24f435a5b8f6167831ac6a4e303fc6420b8ad3684ed9db"
},
"status": "covered"
},
Expand Down Expand Up @@ -1975,7 +1975,7 @@
"take_up_contract",
"us_qbi_reconciliation_contract"
],
"sha256": "7248c2831f3772e75f22dbabcbb5794d5d81138009a38aae8410e43aa05fb656"
"sha256": "cac308466b897bc5652fc1cd9e6304fcc98c3b4bd5141b03268c4b5f1139de5d"
},
"status": "covered"
},
Expand Down Expand Up @@ -2599,7 +2599,7 @@
"country": "us",
"schema_id": "country_spec",
"schema_version": 1,
"spec_sha256": "9b4b3260c20c30ba71a0947f8d78e322c8d2a3e6cb94e87a3729be950a9e90f2"
"spec_sha256": "c69a4313618e0099e9d4dbeb18af80e7a3aac797c0263bd0e4f16d87b442af0e"
}
},
"report_schema_version": 3,
Expand All @@ -2609,7 +2609,7 @@
"country": "us",
"schema_id": "country_spec",
"schema_version": 1,
"spec_sha256": "9b4b3260c20c30ba71a0947f8d78e322c8d2a3e6cb94e87a3729be950a9e90f2"
"spec_sha256": "c69a4313618e0099e9d4dbeb18af80e7a3aac797c0263bd0e4f16d87b442af0e"
},
"status": "pass"
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
)
from .schemas import load_schema_registry

EXPECTED_AUTHORED_FIELD_COUNT = 32_524
EXPECTED_RESOLVED_BINDING_FIELD_COUNT = 9_781
EXPECTED_CONFIGURATION_FIELD_COUNT = 42_305
EXPECTED_AUTHORED_FIELD_COUNT = 32525
EXPECTED_RESOLVED_BINDING_FIELD_COUNT = 9822
EXPECTED_CONFIGURATION_FIELD_COUNT = 42347


class FieldUsageError(AssertionError):
Expand Down Expand Up @@ -377,8 +377,8 @@ def _path_inventory(rows: Sequence[tuple[str, object]]) -> tuple[int, str]:
"8a186065f5b8ffc59bc3f62fe927975e6f36bc1e913aa61761652c9a8aa67988",
),
"generated_authorities": (
8_614,
"66eec1216f404e43f7605fba0016d1cd666e71ebe3e35278d14a944d8b48b619",
8655,
"e8cb4e455fd52f3ff9fffe402fc5a943dea81c89a1310a458649c7561b286ccb",
),
"geography_assignment": (
28,
Expand Down Expand Up @@ -505,8 +505,8 @@ def _path_inventory(rows: Sequence[tuple[str, object]]) -> tuple[int, str]:
"cf0000464013118955571dac2691d4cc1b900c97c6570349b489678cdf937649",
),
"spine_pipeline_contract": (
88,
"ae011455154bae0df3913ca9a056058a909d913439b292cb46c11f63c7d0d9a3",
89,
"68a3a00171aca397db079a7482b68aba8a7b4ad88235ac1bd0d8d7061a242e1c",
),
"spine_sampling": (
17,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@
"acs_person_predictors": "878c788a6f037d7aca12b3586ea034eff04f3034ffa11935a736493042551f25",
"authority": "bf681aedc859fd6398ef72da68f584069b7d25b3085c549bf99f82e8324c4eef",
"early_families": "4aa9f736fd76e83955477ad1667e58f48f264783f05bdc7f0102cd32d61323bd",
"full_checkpoint": "69c2e4fcc8ebdf1e6257ccaf4787b736c4822beda5f4ee01a53ffa3945f4936f",
"full_checkpoint": "668073673f214f8cee24f435a5b8f6167831ac6a4e303fc6420b8ad3684ed9db",
"gap_fill_schedule": "1c31f9868f7884347cc19cf1ff65da43f950b9114941a715bab168246db414a7",
"graph_nodes": "1b208fff4f6d878c522147fee617177544bda8963c940f98a4bfc37507cb955f",
"geography_assignment": "44e7f5f6cd7ceeef85d532c3d46306d03de4f4ee811376e6d29920742746b65c",
Expand Down Expand Up @@ -380,8 +380,8 @@
# microcosm#1259's recipient-share bound in puf_support moves both again.
# microcosm#1261 moves both again: the PUF donor carries partnership and
# S-corp line-32 shares as separate leaves.
"seed_map": "488846129a3c47e89d54538515e6bdbd7ce849e4081ab8a4ad40c9ce2a1e1391",
"seed_protocol": "87f44e60621c45d2fc1448fda11cd446960d6bab7cb51b346367339e2e751e1f",
"seed_map": "42c141d97283222723e0e96822f62f49829d3c5a1904fac950b1110af85028da",
"seed_protocol": "cf465029597523a6efc4fd411601180ff811d26614f3af85ef0e0610f8b8f24d",
"source_manifest": "608c3621b1e4a108e57c8f83d16a184acdb3d05e16a81b895f7f72dbf5d0685c",
"take_up": "9522ce40f7dea569312dd7a7beb474e5a5afadd2f1e10cb5876534c3ef623d35",
"tail": "071d418bb49976f4682c320d2ad07097c177de3a645d2524f235e1ff33ac83f9",
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pipeline_contract:
- with_us_education_inputs
derive_operator_order:
- _complete_schedule_d_input
- with_us_local_tax_inputs
- with_us_qbi_input_reconciliation
auxiliary_operations:
- calibrate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@

import pandas as pd

from microcosm.build.us_runtime.local_jurisdictions import (
LOCAL_TAX_INPUT_RESIDENT_WAGES,
packaged_us_local_tax_input_registry,
us_local_tax_input_failures,
us_resident_wage_tax_values,
)
from microcosm.calibrate import (
SeriesProjection,
ssa_population_projection,
Expand All @@ -36,6 +42,32 @@
)


def _rederive_local_tax_inputs(
tables: Mapping[str, pd.DataFrame],
) -> tuple[dict[str, pd.DataFrame], list[str]]:
"""Re-derive each resident wage-tax base the tables carry from their wages.

Static aging factors a column only by its own uprating series, and these
inputs have none: carried unchanged, a resident's taxable wages would stay
at base-year wages while the wages themselves grow. A base that does not
carry an input gains none.
"""

out = dict(tables)
rederived: list[str] = []
for item in packaged_us_local_tax_input_registry().with_provision(
LOCAL_TAX_INPUT_RESIDENT_WAGES
):
table = out[item.entity]
if item.variable not in table:
continue
table = table.copy()
table[item.variable] = us_resident_wage_tax_values(table, item)
out[item.entity] = table
rederived.append(item.variable)
return out, rederived


def _sha256(path: Path) -> str:
with path.open("rb") as stream:
return hashlib.file_digest(stream, "sha256").hexdigest()
Expand Down Expand Up @@ -269,6 +301,12 @@ def build_annual_static_aging(
}
_verify(base, base_tables, base_year)
del tables, base_tables
local_tax_input_failures = us_local_tax_input_failures(frame.table("person"))
if local_tax_input_failures:
raise ValueError(
"Base local-tax inputs disagree with its residence flags and wages: "
+ "; ".join(local_tax_input_failures)
)
demographics = ssa_population_projection(ssa, age_top=85, age_bands={80: 84})
for year in range(base_year, end_year + 1):
demographics.for_year(year)
Expand Down Expand Up @@ -363,6 +401,9 @@ def build_annual_static_aging(
entity: getattr(dataset, entity).loc[:, columns[entity]]
for entity in frame.entities
}
projected_tables, rederived_inputs = _rederive_local_tax_inputs(
projected_tables
)
round_trip = _write_year(path, projected_tables, year)
receipt_path = output / f"projection_{year}.json"
_json(
Expand All @@ -388,6 +429,7 @@ def build_annual_static_aging(
"fraction_within_10pct": float(
projection.fraction_within_10pct
),
"rederived_local_tax_inputs": rederived_inputs,
},
)
projection_receipt = {
Expand Down
Loading
Loading