policyengine 6.3.2__tar.gz → 6.3.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. {policyengine-6.3.2 → policyengine-6.3.4}/CHANGELOG.md +20 -0
  2. {policyengine-6.3.2/src/policyengine.egg-info → policyengine-6.3.4}/PKG-INFO +1 -1
  3. {policyengine-6.3.2 → policyengine-6.3.4}/docs/bundles.md +3 -1
  4. policyengine-6.3.4/docs/engineering/runbooks/us-legacy-spm-sources.md +122 -0
  5. {policyengine-6.3.2 → policyengine-6.3.4}/docs/microsim.md +61 -9
  6. {policyengine-6.3.2 → policyengine-6.3.4}/docs/outputs.md +6 -0
  7. {policyengine-6.3.2 → policyengine-6.3.4}/docs/regions.md +9 -4
  8. {policyengine-6.3.2 → policyengine-6.3.4}/docs/release-bundles.md +2 -1
  9. {policyengine-6.3.2 → policyengine-6.3.4}/examples/us_budgetary_impact.py +12 -3
  10. {policyengine-6.3.2 → policyengine-6.3.4}/pyproject.toml +1 -1
  11. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/run_record.py +9 -0
  12. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/simulation.py +8 -0
  13. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/data/bundle/manifest.json +12 -12
  14. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/data/bundle/uk.trace.tro.jsonld +5 -5
  15. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/data/bundle/us.trace.tro.jsonld +5 -5
  16. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/poverty.py +21 -1
  17. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/common/model_version.py +23 -0
  18. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/us/datasets.py +178 -38
  19. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/us/legacy_inputs.py +230 -12
  20. policyengine-6.3.4/src/policyengine/tax_benefit_models/us/legacy_spm/__init__.py +120 -0
  21. policyengine-6.3.4/src/policyengine/tax_benefit_models/us/legacy_spm/declarations.py +218 -0
  22. policyengine-6.3.4/src/policyengine/tax_benefit_models/us/legacy_spm/independence.py +121 -0
  23. policyengine-6.3.4/src/policyengine/tax_benefit_models/us/legacy_spm/model_adapter.py +337 -0
  24. policyengine-6.3.4/src/policyengine/tax_benefit_models/us/legacy_spm/record.py +167 -0
  25. policyengine-6.3.4/src/policyengine/tax_benefit_models/us/legacy_spm/registry.py +76 -0
  26. policyengine-6.3.4/src/policyengine/tax_benefit_models/us/legacy_spm/runs.py +140 -0
  27. policyengine-6.3.4/src/policyengine/tax_benefit_models/us/legacy_spm/universe.py +137 -0
  28. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/us/model.py +54 -4
  29. {policyengine-6.3.2 → policyengine-6.3.4/src/policyengine.egg-info}/PKG-INFO +1 -1
  30. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine.egg-info/SOURCES.txt +13 -0
  31. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_poverty_run.py +4 -3
  32. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_release_manifests.py +19 -5
  33. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_us_legacy_inputs_integration.py +198 -0
  34. policyengine-6.3.4/tests/test_us_legacy_spm.py +565 -0
  35. policyengine-6.3.4/tests/test_us_legacy_spm_integration.py +772 -0
  36. policyengine-6.3.4/tests/test_us_legacy_spm_model_contract.py +243 -0
  37. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_us_regions.py +11 -8
  38. policyengine-6.3.4/tests/test_us_year_file_stamp.py +1021 -0
  39. {policyengine-6.3.2 → policyengine-6.3.4}/uv.lock +1 -1
  40. {policyengine-6.3.2 → policyengine-6.3.4}/.claude/policyengine-guide.md +0 -0
  41. {policyengine-6.3.2 → policyengine-6.3.4}/.claude/quick-reference.md +0 -0
  42. {policyengine-6.3.2 → policyengine-6.3.4}/.github/CONTRIBUTING.md +0 -0
  43. {policyengine-6.3.2 → policyengine-6.3.4}/.github/bump_version.py +0 -0
  44. {policyengine-6.3.2 → policyengine-6.3.4}/.github/capture-version.sh +0 -0
  45. {policyengine-6.3.2 → policyengine-6.3.4}/.github/changelog_template.md +0 -0
  46. {policyengine-6.3.2 → policyengine-6.3.4}/.github/check-changelog.sh +0 -0
  47. {policyengine-6.3.2 → policyengine-6.3.4}/.github/copilot-instructions.md +0 -0
  48. {policyengine-6.3.2 → policyengine-6.3.4}/.github/dispatch-policyengine-release.sh +0 -0
  49. {policyengine-6.3.2 → policyengine-6.3.4}/.github/fetch_version.py +0 -0
  50. {policyengine-6.3.2 → policyengine-6.3.4}/.github/get-changelog-diff.sh +0 -0
  51. {policyengine-6.3.2 → policyengine-6.3.4}/.github/has-functional-changes.sh +0 -0
  52. {policyengine-6.3.2 → policyengine-6.3.4}/.github/is-version-number-acceptable.sh +0 -0
  53. {policyengine-6.3.2 → policyengine-6.3.4}/.github/publish-git-tag.sh +0 -0
  54. {policyengine-6.3.2 → policyengine-6.3.4}/.github/wait-for-pypi.sh +0 -0
  55. {policyengine-6.3.2 → policyengine-6.3.4}/.github/workflows/draft-pdf.yml +0 -0
  56. {policyengine-6.3.2 → policyengine-6.3.4}/.github/workflows/pr_code_changes.yaml +0 -0
  57. {policyengine-6.3.2 → policyengine-6.3.4}/.github/workflows/pr_docs_changes.yaml +0 -0
  58. {policyengine-6.3.2 → policyengine-6.3.4}/.github/workflows/push.yaml +0 -0
  59. {policyengine-6.3.2 → policyengine-6.3.4}/.gitignore +0 -0
  60. {policyengine-6.3.2 → policyengine-6.3.4}/.python-version +0 -0
  61. {policyengine-6.3.2 → policyengine-6.3.4}/AGENTS.md +0 -0
  62. {policyengine-6.3.2 → policyengine-6.3.4}/CITATION.cff +0 -0
  63. {policyengine-6.3.2 → policyengine-6.3.4}/CLAUDE.md +0 -0
  64. {policyengine-6.3.2 → policyengine-6.3.4}/CODE_OF_CONDUCT.md +0 -0
  65. {policyengine-6.3.2 → policyengine-6.3.4}/CONTRIBUTING.md +0 -0
  66. {policyengine-6.3.2 → policyengine-6.3.4}/LICENSE +0 -0
  67. {policyengine-6.3.2 → policyengine-6.3.4}/Makefile +0 -0
  68. {policyengine-6.3.2 → policyengine-6.3.4}/README.md +0 -0
  69. {policyengine-6.3.2 → policyengine-6.3.4}/architecture.png +0 -0
  70. {policyengine-6.3.2 → policyengine-6.3.4}/architecture.svg +0 -0
  71. {policyengine-6.3.2 → policyengine-6.3.4}/changelog.d/.gitkeep +0 -0
  72. {policyengine-6.3.2 → policyengine-6.3.4}/docs/.gitignore +0 -0
  73. {policyengine-6.3.2 → policyengine-6.3.4}/docs/_generator/README.md +0 -0
  74. {policyengine-6.3.2 → policyengine-6.3.4}/docs/_generator/build_reference.py +0 -0
  75. {policyengine-6.3.2 → policyengine-6.3.4}/docs/_quarto.yml +0 -0
  76. {policyengine-6.3.2 → policyengine-6.3.4}/docs/countries.md +0 -0
  77. {policyengine-6.3.2 → policyengine-6.3.4}/docs/data-publishing-design.md +0 -0
  78. {policyengine-6.3.2 → policyengine-6.3.4}/docs/decile-impact-spec.md +0 -0
  79. {policyengine-6.3.2 → policyengine-6.3.4}/docs/dev.md +0 -0
  80. {policyengine-6.3.2 → policyengine-6.3.4}/docs/engineering/runbooks/acs-local-wic-replacement.md +0 -0
  81. {policyengine-6.3.2 → policyengine-6.3.4}/docs/engineering/runbooks/build-m-us-populace-certification.md +0 -0
  82. {policyengine-6.3.2 → policyengine-6.3.4}/docs/engineering/runbooks/canonical-spm-development.md +0 -0
  83. {policyengine-6.3.2 → policyengine-6.3.4}/docs/engineering/skills/README.md +0 -0
  84. {policyengine-6.3.2 → policyengine-6.3.4}/docs/engineering/skills/data-certification.md +0 -0
  85. {policyengine-6.3.2 → policyengine-6.3.4}/docs/engineering/skills/github-prs.md +0 -0
  86. {policyengine-6.3.2 → policyengine-6.3.4}/docs/engineering/skills/repository-guidance.md +0 -0
  87. {policyengine-6.3.2 → policyengine-6.3.4}/docs/examples.md +0 -0
  88. {policyengine-6.3.2 → policyengine-6.3.4}/docs/getting-started.md +0 -0
  89. {policyengine-6.3.2 → policyengine-6.3.4}/docs/households.md +0 -0
  90. {policyengine-6.3.2 → policyengine-6.3.4}/docs/impact-analysis.md +0 -0
  91. {policyengine-6.3.2 → policyengine-6.3.4}/docs/index.md +0 -0
  92. {policyengine-6.3.2 → policyengine-6.3.4}/docs/methodology/index.md +0 -0
  93. {policyengine-6.3.2 → policyengine-6.3.4}/docs/methodology/model-architecture.md +0 -0
  94. {policyengine-6.3.2 → policyengine-6.3.4}/docs/methodology/us-health-costs.md +0 -0
  95. {policyengine-6.3.2 → policyengine-6.3.4}/docs/programs/index.md +0 -0
  96. {policyengine-6.3.2 → policyengine-6.3.4}/docs/programs/us-chip.md +0 -0
  97. {policyengine-6.3.2 → policyengine-6.3.4}/docs/reference/index.md +0 -0
  98. {policyengine-6.3.2 → policyengine-6.3.4}/docs/reforms.md +0 -0
  99. {policyengine-6.3.2 → policyengine-6.3.4}/docs/run-records.md +0 -0
  100. {policyengine-6.3.2 → policyengine-6.3.4}/docs/trace-case-study.md +0 -0
  101. {policyengine-6.3.2 → policyengine-6.3.4}/docs/visualisation.md +0 -0
  102. {policyengine-6.3.2 → policyengine-6.3.4}/examples/belgium_axiom_pilot.py +0 -0
  103. {policyengine-6.3.2 → policyengine-6.3.4}/examples/employment_income_variation_uk.py +0 -0
  104. {policyengine-6.3.2 → policyengine-6.3.4}/examples/employment_income_variation_us.py +0 -0
  105. {policyengine-6.3.2 → policyengine-6.3.4}/examples/household_impact_example.py +0 -0
  106. {policyengine-6.3.2 → policyengine-6.3.4}/examples/income_bands_uk.py +0 -0
  107. {policyengine-6.3.2 → policyengine-6.3.4}/examples/income_distribution_us.py +0 -0
  108. {policyengine-6.3.2 → policyengine-6.3.4}/examples/paper_repro_uk.py +0 -0
  109. {policyengine-6.3.2 → policyengine-6.3.4}/examples/policy_change_uk.py +0 -0
  110. {policyengine-6.3.2 → policyengine-6.3.4}/examples/speedtest_us_simulation.py +0 -0
  111. {policyengine-6.3.2 → policyengine-6.3.4}/paper-preview.html +0 -0
  112. {policyengine-6.3.2 → policyengine-6.3.4}/paper.bib +0 -0
  113. {policyengine-6.3.2 → policyengine-6.3.4}/paper.md +0 -0
  114. {policyengine-6.3.2 → policyengine-6.3.4}/scripts/bundle.py +0 -0
  115. {policyengine-6.3.2 → policyengine-6.3.4}/scripts/certify_data_release.py +0 -0
  116. {policyengine-6.3.2 → policyengine-6.3.4}/scripts/check_data_staleness.py +0 -0
  117. {policyengine-6.3.2 → policyengine-6.3.4}/scripts/check_native_spm_loading.py +0 -0
  118. {policyengine-6.3.2 → policyengine-6.3.4}/scripts/check_release_credentials.py +0 -0
  119. {policyengine-6.3.2 → policyengine-6.3.4}/scripts/export_bundle_release_assets.py +0 -0
  120. {policyengine-6.3.2 → policyengine-6.3.4}/scripts/generate_bundle_artifacts.py +0 -0
  121. {policyengine-6.3.2 → policyengine-6.3.4}/scripts/generate_trace_tros.py +0 -0
  122. {policyengine-6.3.2 → policyengine-6.3.4}/scripts/prepare_package_bundle_update.py +0 -0
  123. {policyengine-6.3.2 → policyengine-6.3.4}/scripts/release_lock.py +0 -0
  124. {policyengine-6.3.2 → policyengine-6.3.4}/scripts/spm_bundle.py +0 -0
  125. {policyengine-6.3.2 → policyengine-6.3.4}/setup.cfg +0 -0
  126. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/__init__.py +0 -0
  127. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/bundle.py +0 -0
  128. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/cli.py +0 -0
  129. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/__init__.py +0 -0
  130. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/cache.py +0 -0
  131. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/dataset.py +0 -0
  132. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/dynamic.py +0 -0
  133. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/output.py +0 -0
  134. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/parameter.py +0 -0
  135. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/parameter_node.py +0 -0
  136. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/parameter_value.py +0 -0
  137. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/policy.py +0 -0
  138. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/region.py +0 -0
  139. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/scoping_strategy.py +0 -0
  140. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/spm.py +0 -0
  141. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/tax_benefit_model.py +0 -0
  142. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/tax_benefit_model_version.py +0 -0
  143. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/core/variable.py +0 -0
  144. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/countries/__init__.py +0 -0
  145. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/countries/uk/__init__.py +0 -0
  146. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/countries/uk/regions.py +0 -0
  147. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/countries/us/__init__.py +0 -0
  148. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/countries/us/data/__init__.py +0 -0
  149. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/countries/us/data/districts.py +0 -0
  150. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/countries/us/data/places.py +0 -0
  151. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/countries/us/data/states.py +0 -0
  152. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/countries/us/regions.py +0 -0
  153. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/data/__init__.py +0 -0
  154. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/data/schemas/trace_tro.schema.json +0 -0
  155. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/data/uk_geography_assets.py +0 -0
  156. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/derivations/__init__.py +0 -0
  157. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/derivations/narrate.py +0 -0
  158. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/derivations/trace.py +0 -0
  159. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/graph/__init__.py +0 -0
  160. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/graph/extractor.py +0 -0
  161. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/graph/graph.py +0 -0
  162. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/__init__.py +0 -0
  163. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/aggregate.py +0 -0
  164. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/change_aggregate.py +0 -0
  165. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/cliff_impact.py +0 -0
  166. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/congressional_district_impact.py +0 -0
  167. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/constituency_impact.py +0 -0
  168. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/decile_analysis.py +0 -0
  169. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/decile_grouping.py +0 -0
  170. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/decile_impact.py +0 -0
  171. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/extra_variables.py +0 -0
  172. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/inequality.py +0 -0
  173. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/intra_decile_impact.py +0 -0
  174. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/labor_supply_response.py +0 -0
  175. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/local_authority_impact.py +0 -0
  176. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/program_statistics.py +0 -0
  177. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/uk_geography_assets.py +0 -0
  178. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/outputs/uk_geography_impact.py +0 -0
  179. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/provenance/__init__.py +0 -0
  180. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/provenance/bundle_validation.py +0 -0
  181. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/provenance/certification.py +0 -0
  182. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/provenance/dataset_materialization.py +0 -0
  183. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/provenance/manifest.py +0 -0
  184. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/provenance/pyproject_pins.py +0 -0
  185. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/provenance/spm_configuration.py +0 -0
  186. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/provenance/trace.py +0 -0
  187. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/provenance/verify.py +0 -0
  188. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/provenance/zenodo.py +0 -0
  189. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/results/__init__.py +0 -0
  190. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/results/schema.py +0 -0
  191. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/results/trace_tro.py +0 -0
  192. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/results/tracking.py +0 -0
  193. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/be/__init__.py +0 -0
  194. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/be/datasets.py +0 -0
  195. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/be/model.py +0 -0
  196. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/common/__init__.py +0 -0
  197. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/common/axes.py +0 -0
  198. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/common/extra_variables.py +0 -0
  199. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/common/household.py +0 -0
  200. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/common/reform.py +0 -0
  201. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/common/result.py +0 -0
  202. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/uk/__init__.py +0 -0
  203. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/uk/analysis.py +0 -0
  204. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/uk/budget_reform_contract.py +0 -0
  205. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/uk/budget_reform_population.py +0 -0
  206. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/uk/datasets.py +0 -0
  207. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/uk/household.py +0 -0
  208. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/uk/model.py +0 -0
  209. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/us/__init__.py +0 -0
  210. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/us/analysis.py +0 -0
  211. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/us/household.py +0 -0
  212. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/tax_benefit_models/us/spm.py +0 -0
  213. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/utils/__init__.py +0 -0
  214. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/utils/dates.py +0 -0
  215. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/utils/design.py +0 -0
  216. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/utils/entity_utils.py +0 -0
  217. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/utils/errors.py +0 -0
  218. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/utils/hashing.py +0 -0
  219. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/utils/household_validation.py +0 -0
  220. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/utils/parameter_labels.py +0 -0
  221. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/utils/parametric_reforms.py +0 -0
  222. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine/utils/plotting.py +0 -0
  223. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine.egg-info/dependency_links.txt +0 -0
  224. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine.egg-info/entry_points.txt +0 -0
  225. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine.egg-info/requires.txt +0 -0
  226. {policyengine-6.3.2 → policyengine-6.3.4}/src/policyengine.egg-info/top_level.txt +0 -0
  227. {policyengine-6.3.2 → policyengine-6.3.4}/tests/__init__.py +0 -0
  228. {policyengine-6.3.2 → policyengine-6.3.4}/tests/conftest.py +0 -0
  229. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/__init__.py +0 -0
  230. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/filtering_fixtures.py +0 -0
  231. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/household_calculator_snapshots/uk_couple_two_kids.json +0 -0
  232. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/household_calculator_snapshots/uk_model_surface.json +0 -0
  233. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/household_calculator_snapshots/uk_single_adult_employment_income.json +0 -0
  234. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/household_calculator_snapshots/uk_single_adult_no_income.json +0 -0
  235. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/household_calculator_snapshots/uk_single_parent_one_child.json +0 -0
  236. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/household_calculator_snapshots/us_married_two_kids_high_income.json +0 -0
  237. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/household_calculator_snapshots/us_model_surface.json +0 -0
  238. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/household_calculator_snapshots/us_single_adult_employment_income.json +0 -0
  239. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/household_calculator_snapshots/us_single_adult_no_income.json +0 -0
  240. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/household_calculator_snapshots/us_single_parent_one_child.json +0 -0
  241. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/parameter_labels_fixtures.py +0 -0
  242. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/parametric_reforms_fixtures.py +0 -0
  243. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/poverty_by_demographics_fixtures.py +0 -0
  244. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/region_fixtures.py +0 -0
  245. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/release_tros/README.md +0 -0
  246. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/release_tros/country-models.lock.toml +0 -0
  247. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/release_tros/manifest.json +0 -0
  248. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/release_tros/us-release_manifest.json +0 -0
  249. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/release_tros/us.trace.tro.jsonld +0 -0
  250. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/spm_development.py +0 -0
  251. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/us_reform_fixtures.py +0 -0
  252. {policyengine-6.3.2 → policyengine-6.3.4}/tests/fixtures/variable_label_fixtures.py +0 -0
  253. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_aggregate.py +0 -0
  254. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_be_axiom_pilot.py +0 -0
  255. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_budgetary_impact.py +0 -0
  256. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_bump_version.py +0 -0
  257. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_bundle.py +0 -0
  258. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_bundle_metadata.py +0 -0
  259. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_cache.py +0 -0
  260. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_certify_data_release.py +0 -0
  261. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_change_aggregate.py +0 -0
  262. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_cliff_impact.py +0 -0
  263. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_cliff_impact_analysis.py +0 -0
  264. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_congressional_district_impact.py +0 -0
  265. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_constituency_impact.py +0 -0
  266. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_dataset_materialization.py +0 -0
  267. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_dataset_persistence.py +0 -0
  268. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_dataset_runtime.py +0 -0
  269. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_dataset_source.py +0 -0
  270. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_decile_grouping.py +0 -0
  271. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_derivations.py +0 -0
  272. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_dict_reforms_on_simulation.py +0 -0
  273. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_entity_mapping.py +0 -0
  274. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_entity_utils.py +0 -0
  275. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_entity_utils_characterization.py +0 -0
  276. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_errors.py +0 -0
  277. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_extra_variables.py +0 -0
  278. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_filtering.py +0 -0
  279. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_generate_trace_tros.py +0 -0
  280. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_graph/__init__.py +0 -0
  281. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_graph/conftest.py +0 -0
  282. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_graph/test_extractor.py +0 -0
  283. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_household_calculator_snapshot.py +0 -0
  284. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_household_impact.py +0 -0
  285. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_inequality.py +0 -0
  286. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_intra_decile_impact.py +0 -0
  287. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_labor_supply_response.py +0 -0
  288. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_local_authority_impact.py +0 -0
  289. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_manifest_version_mismatch.py +0 -0
  290. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_models.py +0 -0
  291. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_pandas3_compatibility.py +0 -0
  292. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_parameter_labels.py +0 -0
  293. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_parameter_nodes.py +0 -0
  294. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_parameter_values.py +0 -0
  295. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_parametric_reforms.py +0 -0
  296. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_poverty.py +0 -0
  297. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_poverty_by_demographics.py +0 -0
  298. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_preservation_mirror.py +0 -0
  299. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_program_statistics.py +0 -0
  300. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_pyproject_pins.py +0 -0
  301. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_region.py +0 -0
  302. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_region_group_strategy.py +0 -0
  303. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_release_credentials.py +0 -0
  304. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_release_lock.py +0 -0
  305. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_release_tro_generation.py +0 -0
  306. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_results.py +0 -0
  307. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_run_record.py +0 -0
  308. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_scoping_strategy.py +0 -0
  309. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_small_follow_ups.py +0 -0
  310. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_spm_bundle_bootstrap.py +0 -0
  311. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_spm_bundle_tooling.py +0 -0
  312. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_spm_decile.py +0 -0
  313. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_spm_development_authentication.py +0 -0
  314. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_spm_household.py +0 -0
  315. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_spm_model.py +0 -0
  316. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_spm_selection.py +0 -0
  317. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_spm_selection_schema.py +0 -0
  318. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_trace_tro.py +0 -0
  319. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_trace_tro_verify.py +0 -0
  320. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_uk_analysis.py +0 -0
  321. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_uk_budget_reform_contract.py +0 -0
  322. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_uk_dated_reforms.py +0 -0
  323. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_uk_geography_assets.py +0 -0
  324. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_uk_program_statistics.py +0 -0
  325. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_uk_regions.py +0 -0
  326. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_uk_year_file_data_year.py +0 -0
  327. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_us_legacy_inputs.py +0 -0
  328. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_us_long_term_datasets.py +0 -0
  329. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_us_microsim_structural_reforms.py +0 -0
  330. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_us_native_alignment.py +0 -0
  331. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_us_native_hdf_weights.py +0 -0
  332. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_us_program_statistics.py +0 -0
  333. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_us_reform_application.py +0 -0
  334. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_variable_labels.py +0 -0
  335. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_variable_metadata.py +0 -0
  336. {policyengine-6.3.2 → policyengine-6.3.4}/tests/test_zenodo_mirror.py +0 -0
@@ -1,3 +1,23 @@
1
+ ## [6.3.4] - 2026-10-13
2
+
3
+ ### Changed
4
+
5
+ - `Poverty.rate` is now `None`, not `0.0`, when nobody in the group is measured (its weighted population with a poverty status is zero), for every source. `headcount` and `total_population` are unchanged, and the poverty DataFrame helpers report the rate as missing. Callers that format or subtract rates must handle `None`.
6
+ - US state and congressional-district regions temporarily filter the certified national dataset again, as in 6.2.1, because the ACS-local release lacks source-backed SPM independence and canonical SPM refused every state-region run (PolicyEngine/microcosm#1263).
7
+
8
+ ### Fixed
9
+
10
+ - Measure SPM poverty on legacy certified US sources that predate SPM measurement-scope declarations, such as the ACS-local Build O release. Following Census's ACS household-population method, ACS group-quarters units are outside the SPM measurement: their SPM results are missing instead of stopping the run with `SPM_COMPOSITION_REQUIRED`, and poverty rates count measured people only. Ordinary tax and benefit results are unchanged.
11
+ - Supply the certified SPM independence role from the national release for the ASEC and PUF records of legacy certified US sources, so independent-teen SPM units are measured instead of stopping SPM calculations.
12
+
13
+
14
+ ## [6.3.3] - 2026-10-13
15
+
16
+ ### Changed
17
+
18
+ - `create_datasets` stamps each US year file with the register of renamed stored inputs it was cut under and a year-file format revision. `ensure_datasets` creates a year file again, and `load_datasets` refuses it, unless its stamp matches this release exactly. A year file cut before a rename was registered, which may have lost that input, is therefore not reused. `load_datasets` says why it refuses a file, naming the renames the file may lack when its register is the reason. Year files written before the stamp existed are created again once.
19
+
20
+
1
21
  ## [6.3.2] - 2026-10-12
2
22
 
3
23
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: policyengine
3
- Version: 6.3.2
3
+ Version: 6.3.4
4
4
  Summary: A package to conduct policy analysis using PolicyEngine tax-benefit models.
5
5
  Author-email: PolicyEngine <hello@policyengine.org>
6
6
  License: GNU AFFERO GENERAL PUBLIC LICENSE
@@ -147,7 +147,9 @@ python scripts/bundle.py certify-data \
147
147
  --model-version <policyengine-us-version>
148
148
  ```
149
149
 
150
- US state and congressional-district regions scope one certified ACS-local
150
+ **Temporarily**, state and congressional-district regions filter the certified national dataset (`populace_us_2024`), as in policyengine 6.2.1. The certified ACS-local release has no source-backed SPM independence, so canonical SPM refuses thousands of its SPM units (PolicyEngine/microcosm#1263, PolicyEngine/policyengine-sim-api#747). They move back to the ACS-local dataset with a release whose SPM units all have a classified adult.
151
+
152
+ When they use it, US state and congressional-district regions scope one certified ACS-local
151
153
  Populace dataset with row filters. Pass its non-default local-area release
152
154
  manifest through `--regional-manifest-uri`; certification adds the pinned H5
153
155
  artifact to `data_releases.us.datasets` and writes both regional path templates
@@ -0,0 +1,122 @@
1
+ # Legacy US SPM sources (backward compatibility)
2
+
3
+ > **Backward compatibility, not a data contract.** This layer exists so that
4
+ > certified US sources published *before* SPM declarations existed can still
5
+ > be measured. New certified sources must carry the declarations themselves;
6
+ > do not register them here.
7
+
8
+ policyengine-us measures SPM poverty only when a source declares two things:
9
+
10
+ - **Measurement scope:** which SPM units are inside the SPM measurement
11
+ population (`spm_unit_spm_universe_status`: `INCLUDED`/`OUTSIDE`, the
12
+ contract of PolicyEngine/microcosm#977).
13
+ - **Independence role:** which 15–17-year-olds independently head their own
14
+ SPM unit (`is_spm_independent_minor_role`).
15
+
16
+ A legacy source carries neither. Every SPM calculation on it stops with
17
+ `SPM_COMPOSITION_REQUIRED` ("SPM unit has no classified adult").
18
+
19
+ The package
20
+ [`policyengine.tax_benefit_models.us.legacy_spm`](../../../src/policyengine/tax_benefit_models/us/legacy_spm/__init__.py)
21
+ derives the missing declarations from evidence a legacy source does carry.
22
+ Nobody is dropped: memberships, weights and every ordinary tax, benefit and
23
+ income calculation are unchanged.
24
+
25
+ ## Registered sources
26
+
27
+ Sources are keyed by certified SHA-256 in `legacy_spm/registry.py`.
28
+
29
+ | Source | Scope rule | Independence list |
30
+ | --- | --- | --- |
31
+ | `populace_us_2024_acs_local` Build O (`769756c3…a7ec`) | `acs_household_population` | national release `source_spm_person_independence.csv` (`22b5968d…3ce5`) |
32
+
33
+ ### Scope rule `acs_household_population` (#597)
34
+
35
+ Census's ACS SPM methodology measures the household population only.
36
+
37
+ - ACS records with `TYPEHUGQ` 2 or 3 and the matching `RELSHIPP` (37 or 38) are
38
+ group quarters, so their SPM units are `OUTSIDE`.
39
+ - ACS housing-unit records are `INCLUDED`.
40
+ - Records from the `asec` and `puf_tax_detail` channels are `INCLUDED`.
41
+
42
+ Age is never used. Unknown channels, missing or inconsistent evidence, and SPM
43
+ units that mix group quarters with household people all raise.
44
+
45
+ On the full Build O source, 183,206 SPM units are outside the measurement.
46
+
47
+ ### Independence list (#598)
48
+
49
+ The source's ASEC/PUF records are the same records the certified national
50
+ release carries with a source-backed independence role. The list is joined by
51
+ `person_id` and `person_spm_unit_id`, and the join must be exact:
52
+
53
+ - every ASEC/PUF person appears once, in the same SPM unit;
54
+ - the list names nobody else.
55
+
56
+ ACS people keep the model's own fallback (household head or spouse).
57
+
58
+ On Build O, exactly 28 units change: Census-built units around an independent
59
+ teen.
60
+
61
+ ## How runs get the declarations
62
+
63
+ | Path | What happens |
64
+ | --- | --- |
65
+ | `create_datasets` / `ensure_datasets` | Year files store the declarations as ordinary input columns, with a `policyengine_legacy_spm_declarations` record. |
66
+ | `Simulation.run()` over a raw legacy source file | The declarations are derived in memory. The file is recognised by its evidence columns and then its SHA-256, which is hashed once per process. |
67
+ | `managed_microsimulation` | The declarations are derived from the loaded tables and set on the simulation and its branches. |
68
+
69
+ `legacy_spm.model_adapter` then makes the model honour the scope:
70
+
71
+ - **native:** policyengine-us defines `spm_unit_spm_universe_status`
72
+ (PolicyEngine/policyengine-us#9462). The declaration is just an input, and
73
+ nothing is overridden.
74
+ - **override:** policyengine-us 2.2.1 cannot read it. A variable-only reform:
75
+ - adds the input;
76
+ - wraps the installed SPM chain so that outside units never reach the
77
+ canonical provider and every SPM outcome for them is NaN ("not measured").
78
+
79
+ Poverty indicators become floats (1.0, 0.0 or NaN). The override wraps the
80
+ installed formulas, with one exception: `spm_unit_income_decile` ranks
81
+ units, so it is recalculated over measured units with the installed method.
82
+ A contract test pins that method. The override refuses to run if the SPM
83
+ chain no longer has the verified shape.
84
+
85
+ Poverty rates count measured people only. A group with nobody measured has a
86
+ rate of `None`, not 0.
87
+
88
+ ## Staleness
89
+
90
+ The record holds a `LegacySPMProfile`: the registry entry plus
91
+ `ADAPTER_REVISION`. Checks are total and exact, like the year-file stamp:
92
+
93
+ - `ensure_datasets` cuts a year file again, and `load_datasets` refuses it, when
94
+ its record is absent, unreadable, malformed, or from another profile.
95
+ - `Simulation.load()` refuses an output saved under a different profile.
96
+
97
+ Raise `ADAPTER_REVISION` whenever a change to derivation or the override means
98
+ existing files must be made again.
99
+
100
+ ## When the model or data changes
101
+
102
+ - **policyengine-us gains native scope (#9462).**
103
+ `test_the_installed_model_does_not_read_scope_natively_yet` fails. Check that
104
+ the native members include `INCLUDED` and `OUTSIDE`, and confirm the native
105
+ runs in the integration tests. Then delete the override half of
106
+ `model_adapter` (keep the derivation) and raise `ADAPTER_REVISION`.
107
+ - **The SPM chain changes.** `test_the_override_covers_every_variable_that_depends_on_the_provider`,
108
+ `test_the_installed_chain_has_the_verified_shape` or
109
+ `test_the_recalculated_decile_matches_the_installed_method` fails. Re-verify
110
+ the override against the new chain before bumping the pin.
111
+ - **A rebuilt source declares scope and role itself.** Point the bundle at it.
112
+ Keep the Build O registry entry only while anything still reads Build O,
113
+ then delete it.
114
+
115
+ ## Related
116
+
117
+ - Microcosm #977: scope classification at source.
118
+ - Microcosm #959: independence role in US builds.
119
+ - policyengine-us #9462: native scope.
120
+ - policyengine.py #519: nullable SPM summaries.
121
+ - The year-file and output stamps (#590, and #593/#594 in flight). Fold this
122
+ record into them once those land.
@@ -87,6 +87,24 @@ before passing them to the country model. Generic adult/child counts remain
87
87
  separate. The native source enrichment is not recalibration, and inherited
88
88
  schema-5 calibration diagnostics cannot be relabeled as schema 6.
89
89
 
90
+ **Legacy SPM sources (backward compatibility).** Some certified US sources
91
+ predate SPM declarations, for example the ACS-local Build O release. For those
92
+ sources, .py derives the declarations from evidence they carry, following
93
+ Census's ACS SPM methodology, which measures the household population only:
94
+
95
+ - ACS group-quarters SPM units are outside the SPM measurement. Their SPM
96
+ thresholds, resources and poverty indicators are missing (NaN).
97
+ - Poverty indicators on those sources are floats: 1.0, 0.0, or NaN for "not
98
+ measured".
99
+ - Poverty rates count measured people only. A group with nobody measured has
100
+ a rate of `None`, not 0.
101
+ - The sources' ASEC and PUF records take the certified SPM independence role
102
+ from the national release.
103
+
104
+ Ordinary tax, benefit and income results are unchanged. Outputs record the
105
+ declarations they used under `legacy_spm_declarations`. See
106
+ [the runbook](engineering/runbooks/us-legacy-spm-sources.md).
107
+
90
108
  The wrapper's native pandas HDF loader accepts files that store only calibrated
91
109
  `household_weight`. It maps missing person and group weight columns through
92
110
  native membership IDs in memory, retaining supplied weight columns and the
@@ -401,15 +419,49 @@ The renames applied are recorded as `{old: new}` (`{}` when none applied):
401
419
  carried the draw: data that stores neither name runs with the new input's
402
420
  default, which for WIC is full take-up.
403
421
 
404
- Files written before this mapping existed have no record, and are not reused:
405
-
406
- - A saved US output may have been calculated without the mapped inputs.
407
- `load()` refuses it, and `ensure()` runs the simulation again and saves the new
408
- output.
409
- - A year file that `ensure_datasets` or `create_datasets` wrote stores
410
- neither name, so the draw is lost from it. `ensure_datasets` creates such
411
- year files again, and `load_datasets` refuses them. A year file opened
412
- directly, as `PolicyEngineUSDataset(filepath=...)`, is not checked.
422
+ A saved US output written before this mapping existed has no record and may
423
+ have been calculated without the mapped inputs, so `load()` refuses it, and
424
+ `ensure()` runs the simulation again and saves the new output. Saved outputs
425
+ are checked only for the record, not for the register they were calculated
426
+ under.
427
+
428
+ ### Year files
429
+
430
+ `create_datasets` stamps each year file, after writing everything else, with
431
+ the register it was cut under and a year-file format revision
432
+ (`legacy_inputs.YEAR_FILE_STAMP_H5_DATASET`, UTF-8 JSON
433
+ `{"register": {old: new, ...}, "revision": 1}`). `ensure_datasets` reuses a
434
+ year file only when its stamp equals this release's exactly and its renames
435
+ record lists only renames in the stamped register. Otherwise it logs why and
436
+ creates the year files again; `load_datasets` refuses the file with the same
437
+ reason, which names the renames the file may lack when its register is the
438
+ cause. To decide, both read only the stamp and the record, never the tables
439
+ or the source dataset. `legacy_inputs.year_file_stamp_problem(path)` returns
440
+ the reason, or `None` for a current year file.
441
+
442
+ A year file is out of date when:
443
+
444
+ - it has no stamp. Year files that policyengine.py 6.1.1 and earlier cut,
445
+ with policyengine-us 2.x, from a source storing the draw only as
446
+ `would_claim_wic` (such as the certified default) store neither WIC take-up
447
+ name, so the draw is lost from them. Year files written with the renames
448
+ record but before the stamp are cut again once, and so is a year file whose
449
+ write did not finish;
450
+ - its register lacks an entry this release has. It may have lost that
451
+ entry's input, as the older files lost the WIC draw;
452
+ - its register has an entry this release lacks, or maps an old name to
453
+ another new name. It may store a new name this release's engine does not
454
+ define, and a run skips any column the engine does not define;
455
+ - it has another format revision, or its stamp or record cannot be read.
456
+
457
+ So any change to `LEGACY_INPUT_RENAMES` makes every year file out of date,
458
+ and two policyengine.py versions with different registers that share a data
459
+ folder cut each other's year files again. `ensure_datasets` raises, rather
460
+ than cutting the files again, when a year file cannot be opened for reasons
461
+ unrelated to its contents: no permission, a directory at its path, or a lock
462
+ held by another process. A year file opened directly, as
463
+ `PolicyEngineUSDataset(filepath=...)`, is not checked, and saving it again with
464
+ `save()` drops its stamp.
413
465
 
414
466
  ## Pinned model versions
415
467
 
@@ -167,6 +167,12 @@ rate.run()
167
167
  rate.headcount, rate.total_population, rate.rate
168
168
  ```
169
169
 
170
+ `rate.rate` is `None` when nobody in the group is measured: the group's
171
+ weighted population with a poverty status is zero, for example a filter that
172
+ matches no one, or a group whose members are all outside the SPM measurement
173
+ on a legacy source. That is not a measured rate of zero, so handle `None`
174
+ before formatting or subtracting rates.
175
+
170
176
  For all canonical poverty measures over one simulation:
171
177
 
172
178
  ```python
@@ -23,9 +23,13 @@ ca_snap = Aggregate(
23
23
  ca_snap.run()
24
24
  ```
25
25
 
26
- Each state is a region in the US registry. State regions load the ACS-local
27
- Populace dataset declared by `region.dataset_path`, then scope its rows by
28
- `state_fips`. They do not require separate state H5 files:
26
+ Each state is a region in the US registry. State regions load the Populace
27
+ dataset declared by `region.dataset_path`, then scope its rows by `state_fips`.
28
+ They do not require separate state H5 files.
29
+
30
+ **Temporarily**, state and congressional-district regions filter the certified national dataset (`populace_us_2024`), as in policyengine 6.2.1. The certified ACS-local release has no source-backed SPM independence, so canonical SPM refuses thousands of its SPM units (PolicyEngine/microcosm#1263, PolicyEngine/policyengine-sim-api#747). They move back to the ACS-local dataset with a release whose SPM units all have a classified adult.
31
+
32
+ For example:
29
33
 
30
34
  ```python
31
35
  states = pe.us.model.region_registry.get_by_type("state")
@@ -52,7 +56,8 @@ for row in impacts.district_results:
52
56
 
53
57
  `district_geoid` is the SSDD integer (state FIPS × 100 + district number;
54
58
  at-large districts use `00`). Congressional district regions load the same
55
- ACS-local file and scope its rows by `congressional_district_geoid`.
59
+ file as state regions (for now the national dataset; see above) and scope its rows by
60
+ `congressional_district_geoid`.
56
61
 
57
62
  ## UK countries
58
63
 
@@ -207,7 +207,8 @@ python scripts/bundle.py certify-data --country us --data-producer populace \
207
207
 
208
208
  That produces one US bundle manifest entry containing the Populace national
209
209
  default dataset and the certified ACS-local regional dataset. State and
210
- congressional-district simulations use the latter with runtime row filters, so
210
+ congressional-district simulations use one of them with runtime row filters (for now the
211
+ national dataset; see [Regions](regions.md)), so
211
212
  derived `states/*.h5` or `districts/*.h5` files are not vendored into
212
213
  `data_releases.us.datasets`.
213
214
 
@@ -32,6 +32,11 @@ from policyengine.tax_benefit_models.us import (
32
32
  )
33
33
 
34
34
 
35
+ def _format_rate(rate: float | None) -> str:
36
+ """Format a poverty rate; ``None`` means nobody in the group is measured."""
37
+ return "n/a" if rate is None else f"{rate:.4f}"
38
+
39
+
35
40
  def main():
36
41
  year = 2026
37
42
 
@@ -147,11 +152,15 @@ def main():
147
152
  analysis.reform_poverty.outputs,
148
153
  strict=True,
149
154
  ):
155
+ # A rate is None when nobody in the group is measured.
156
+ change = (
157
+ "n/a" if bp.rate is None or rp.rate is None else f"{rp.rate - bp.rate:+.4f}"
158
+ )
150
159
  print(
151
160
  f" {bp.metric:30s} "
152
- f"baseline={bp.rate:.4f} "
153
- f"reform={rp.rate:.4f} "
154
- f"change={rp.rate - bp.rate:+.4f}"
161
+ f"baseline={_format_rate(bp.rate)} "
162
+ f"reform={_format_rate(rp.rate)} "
163
+ f"change={change}"
155
164
  )
156
165
 
157
166
  print("\n=== Inequality ===")
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "policyengine"
7
- version = "6.3.2"
7
+ version = "6.3.4"
8
8
  description = "A package to conduct policy analysis using PolicyEngine tax-benefit models."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -193,6 +193,15 @@ def build_simulation_run_record_payloads(
193
193
  if renames is not None:
194
194
  results_payload["legacy_input_renames"] = dict(renames)
195
195
 
196
+ # BACKWARD COMPATIBILITY (#597, #598): the legacy SPM declarations a
197
+ # US run applied, which change its SPM results. See
198
+ # ``policyengine.tax_benefit_models.us.legacy_spm``.
199
+ legacy_spm_declarations = (
200
+ getattr(simulation.output_dataset, "metadata", None) or {}
201
+ ).get("legacy_spm_declarations")
202
+ if legacy_spm_declarations is not None:
203
+ results_payload["legacy_spm_declarations"] = dict(legacy_spm_declarations)
204
+
196
205
  return {"reform": reform, "input": input_payload, "results": results_payload}
197
206
 
198
207
 
@@ -258,4 +258,12 @@ class Simulation(BaseModel):
258
258
  )
259
259
  if renames is not None:
260
260
  result["legacy_input_renames"] = dict(renames)
261
+ # BACKWARD COMPATIBILITY (#597, #598): the legacy SPM declarations a
262
+ # US run applied, which change its SPM results. See
263
+ # ``policyengine.tax_benefit_models.us.legacy_spm``.
264
+ legacy_spm_declarations = (
265
+ getattr(self.output_dataset, "metadata", None) or {}
266
+ ).get("legacy_spm_declarations")
267
+ if legacy_spm_declarations is not None:
268
+ result["legacy_spm_declarations"] = dict(legacy_spm_declarations)
261
269
  return result
@@ -1,10 +1,10 @@
1
1
  {
2
- "bundle_version": "6.3.2",
2
+ "bundle_version": "6.3.4",
3
3
  "citation": {
4
4
  "publisher": "PolicyEngine",
5
- "title": "PolicyEngine bundle 6.3.2",
5
+ "title": "PolicyEngine bundle 6.3.4",
6
6
  "type": "software-bundle",
7
- "version": "6.3.2"
7
+ "version": "6.3.4"
8
8
  },
9
9
  "countries": {
10
10
  "uk": {
@@ -17,7 +17,7 @@
17
17
  "data_releases": {
18
18
  "uk": {
19
19
  "build_id": "policyengine-uk-data-1.58.0",
20
- "bundle_id": "uk-6.3.2",
20
+ "bundle_id": "uk-6.3.4",
21
21
  "certification": {
22
22
  "built_with_model_git_sha": "4cbedbecca352f52dffe47e849553c2759a00711",
23
23
  "built_with_model_version": "2.122.2",
@@ -99,7 +99,7 @@
99
99
  "version": "2.126.12",
100
100
  "wheel_url": "https://files.pythonhosted.org/packages/4f/3f/0a611e9b25beb75db3f2f521d359eabad0d1ab4e50194b866663355591ff/policyengine_uk-2.126.12-py3-none-any.whl"
101
101
  },
102
- "policyengine_version": "6.3.2",
102
+ "policyengine_version": "6.3.4",
103
103
  "region_datasets": {
104
104
  "national": {
105
105
  "path_template": "enhanced_frs_2024_25.h5"
@@ -112,7 +112,7 @@
112
112
  },
113
113
  "us": {
114
114
  "build_id": "populace-us-2024-spm-20260915",
115
- "bundle_id": "us-6.3.2",
115
+ "bundle_id": "us-6.3.4",
116
116
  "certification": {
117
117
  "built_with_model_version": "2.2.1",
118
118
  "certified_by": "policyengine.py bundle certification",
@@ -217,16 +217,16 @@
217
217
  "version": "2.2.1",
218
218
  "wheel_url": "https://files.pythonhosted.org/packages/5e/09/86ac3968d2a18e28fce3ba1186fc21728e9f299b96a806d6f204f1e0aaed/policyengine_us-2.2.1-py3-none-any.whl"
219
219
  },
220
- "policyengine_version": "6.3.2",
220
+ "policyengine_version": "6.3.4",
221
221
  "region_datasets": {
222
222
  "congressional_district": {
223
- "path_template": "populace_us_2024_acs_local.h5"
223
+ "path_template": "populace_us_2024.h5"
224
224
  },
225
225
  "national": {
226
226
  "path_template": "populace_us_2024.h5"
227
227
  },
228
228
  "state": {
229
- "path_template": "populace_us_2024_acs_local.h5"
229
+ "path_template": "populace_us_2024.h5"
230
230
  }
231
231
  },
232
232
  "regional_release_manifest_uri": "https://huggingface.co/datasets/policyengine/populace-us/resolve/populace-us-2024-buildo-acs-local-767312d60-20260923T074941Z/releases/populace-us-2024-buildo-acs-local-767312d60-20260923T074941Z/release_manifest.json",
@@ -295,10 +295,10 @@
295
295
  "packages": {
296
296
  "policyengine": {
297
297
  "import_name": "policyengine",
298
- "install_requirement": "policyengine==6.3.2",
298
+ "install_requirement": "policyengine==6.3.4",
299
299
  "name": "policyengine",
300
300
  "role": "bundle_carrier",
301
- "version": "6.3.2"
301
+ "version": "6.3.4"
302
302
  },
303
303
  "policyengine-core": {
304
304
  "import_name": "policyengine_core",
@@ -334,6 +334,6 @@
334
334
  "wheel_url": "https://files.pythonhosted.org/packages/19/a0/c484f69a0ebf88a9b9fd0ac28176f8df66550f46714f1b0c5ec0b360824e/spm_calculator-1.0.0-py3-none-any.whl"
335
335
  }
336
336
  },
337
- "policyengine_version": "6.3.2",
337
+ "policyengine_version": "6.3.4",
338
338
  "schema_version": 2
339
339
  }
@@ -18,18 +18,18 @@
18
18
  "schema:url": "https://policyengine.org"
19
19
  },
20
20
  "schema:dateCreated": "2026-10-07T17:31:53.189867Z",
21
- "schema:description": "TRACE TRO for certified runtime bundle uk-6.3.2 covering the bundle manifest, the certified dataset artifact, the country model wheel, and the country data release manifest when it is available.",
21
+ "schema:description": "TRACE TRO for certified runtime bundle uk-6.3.4 covering the bundle manifest, the certified dataset artifact, the country model wheel, and the country data release manifest when it is available.",
22
22
  "schema:name": "policyengine uk certified bundle TRO",
23
23
  "trov:createdWith": {
24
24
  "@type": "schema:SoftwareApplication",
25
25
  "schema:name": "policyengine",
26
- "schema:softwareVersion": "6.3.2"
26
+ "schema:softwareVersion": "6.3.4"
27
27
  },
28
28
  "trov:hasArrangement": [
29
29
  {
30
30
  "@id": "arrangement/1",
31
31
  "@type": "trov:ArtifactArrangement",
32
- "rdfs:comment": "Certified arrangement for bundle uk-6.3.2.",
32
+ "rdfs:comment": "Certified arrangement for bundle uk-6.3.4.",
33
33
  "trov:hasArtifactLocation": [
34
34
  {
35
35
  "@id": "arrangement/1/location/bundle_manifest",
@@ -75,7 +75,7 @@
75
75
  "@type": "trov:ResearchArtifact",
76
76
  "schema:name": "policyengine.py bundle manifest for uk",
77
77
  "trov:mimeType": "application/json",
78
- "trov:sha256": "323b86d959539ed3fbde416387d136161e58483d8f7b6c7b9dc47936e3e18f9b"
78
+ "trov:sha256": "594d1c8ba2539df9c26ca2b6a7170ab20fb03176e5428b3963dd42e61b707564"
79
79
  },
80
80
  {
81
81
  "@id": "composition/1/artifact/data_release_manifest",
@@ -102,7 +102,7 @@
102
102
  "trov:hasFingerprint": {
103
103
  "@id": "composition/1/fingerprint",
104
104
  "@type": "trov:CompositionFingerprint",
105
- "trov:sha256": "c00ff93e4fc59e689bb7c50b979ca5efc749c2eee6c66a3931135fe196c385a1"
105
+ "trov:sha256": "42003f5d9bda251119c325e8d61d22c05edb1c75bf5b9ad80ab3e497fe548439"
106
106
  }
107
107
  },
108
108
  "trov:hasPerformance": {
@@ -17,18 +17,18 @@
17
17
  "schema:name": "PolicyEngine",
18
18
  "schema:url": "https://policyengine.org"
19
19
  },
20
- "schema:description": "TRACE TRO for certified runtime bundle us-6.3.2 covering the bundle manifest, the certified dataset artifact, the country model wheel, and the country data release manifest when it is available.",
20
+ "schema:description": "TRACE TRO for certified runtime bundle us-6.3.4 covering the bundle manifest, the certified dataset artifact, the country model wheel, and the country data release manifest when it is available.",
21
21
  "schema:name": "policyengine us certified bundle TRO",
22
22
  "trov:createdWith": {
23
23
  "@type": "schema:SoftwareApplication",
24
24
  "schema:name": "policyengine",
25
- "schema:softwareVersion": "6.3.2"
25
+ "schema:softwareVersion": "6.3.4"
26
26
  },
27
27
  "trov:hasArrangement": [
28
28
  {
29
29
  "@id": "arrangement/1",
30
30
  "@type": "trov:ArtifactArrangement",
31
- "rdfs:comment": "Certified arrangement for bundle us-6.3.2.",
31
+ "rdfs:comment": "Certified arrangement for bundle us-6.3.4.",
32
32
  "trov:hasArtifactLocation": [
33
33
  {
34
34
  "@id": "arrangement/1/location/bundle_manifest",
@@ -74,7 +74,7 @@
74
74
  "@type": "trov:ResearchArtifact",
75
75
  "schema:name": "policyengine.py bundle manifest for us",
76
76
  "trov:mimeType": "application/json",
77
- "trov:sha256": "323b86d959539ed3fbde416387d136161e58483d8f7b6c7b9dc47936e3e18f9b"
77
+ "trov:sha256": "594d1c8ba2539df9c26ca2b6a7170ab20fb03176e5428b3963dd42e61b707564"
78
78
  },
79
79
  {
80
80
  "@id": "composition/1/artifact/data_release_manifest",
@@ -101,7 +101,7 @@
101
101
  "trov:hasFingerprint": {
102
102
  "@id": "composition/1/fingerprint",
103
103
  "@type": "trov:CompositionFingerprint",
104
- "trov:sha256": "d7ecd868e200c681d97acb264562877d2f86cb9820e2cee5e48b5f3426cae693"
104
+ "trov:sha256": "0c5fdf655befb2e64f5511bfefd9dc022041a526f1505ac9ee31a7058e349bff"
105
105
  }
106
106
  },
107
107
  "trov:hasPerformance": {
@@ -81,6 +81,21 @@ class Poverty(Output):
81
81
 
82
82
  # Map poverty variable to target entity if needed
83
83
  if poverty_var_obj.entity != target_entity:
84
+ source = getattr(
85
+ self.simulation.output_dataset.data, poverty_var_obj.entity
86
+ )
87
+ if (
88
+ target_entity != "person"
89
+ and pd.isna(source[self.poverty_variable]).any()
90
+ ):
91
+ # Mapping onto a group sums members and fills empty groups
92
+ # with zero, which would count a person outside the SPM
93
+ # measurement as measured and not poor.
94
+ raise ValueError(
95
+ f"{self.poverty_variable} is missing for units outside "
96
+ f"the poverty measurement; summarise it for people, not "
97
+ f"for {target_entity} units."
98
+ )
84
99
  mapped = self.simulation.output_dataset.data.map_to_entity(
85
100
  poverty_var_obj.entity,
86
101
  target_entity,
@@ -121,8 +136,13 @@ class Poverty(Output):
121
136
  # Calculate results using weighted counts
122
137
  self.headcount = float((poverty_series == True).sum()) # noqa: E712
123
138
  self.total_population = float(poverty_series.count())
139
+ # ``None`` means nobody in the group is measured (for example, every
140
+ # member is outside the SPM measurement population), which is not
141
+ # a measured rate of zero.
124
142
  self.rate = (
125
- self.headcount / self.total_population if self.total_population > 0 else 0.0
143
+ self.headcount / self.total_population
144
+ if self.total_population > 0
145
+ else None
126
146
  )
127
147
 
128
148
 
@@ -417,6 +417,14 @@ class MicrosimulationModelVersion(TaxBenefitModelVersion):
417
417
  RENAMES_RECORD_KEY,
418
418
  read_renames_record,
419
419
  )
420
+ from policyengine.tax_benefit_models.us.legacy_spm import (
421
+ RECORD_KEY as LEGACY_SPM_RECORD_KEY,
422
+ )
423
+ from policyengine.tax_benefit_models.us.legacy_spm import (
424
+ expected_profile_for_dataset,
425
+ read_record_value,
426
+ record_problem,
427
+ )
420
428
 
421
429
  with h5py.File(filepath, "r") as stream:
422
430
  raw = (
@@ -442,6 +450,17 @@ class MicrosimulationModelVersion(TaxBenefitModelVersion):
442
450
  "Saved US simulation predates the mapping of renamed "
443
451
  "stored inputs (it has no record of them); run it again"
444
452
  )
453
+ # BACKWARD COMPATIBILITY (#597, #598): an output is reused only
454
+ # under exactly the legacy SPM declarations its input needs.
455
+ recorded_legacy_spm = read_record_value(filepath)
456
+ legacy_problem = record_problem(
457
+ recorded_legacy_spm, expected_profile_for_dataset(simulation.dataset)
458
+ )
459
+ if legacy_problem is not None:
460
+ raise ValueError(
461
+ f"Saved US simulation output is out of date: {legacy_problem}; "
462
+ "run it again"
463
+ )
445
464
 
446
465
  if self.country_code == "uk":
447
466
  from policyengine.tax_benefit_models.uk.datasets import (
@@ -474,6 +493,10 @@ class MicrosimulationModelVersion(TaxBenefitModelVersion):
474
493
  simulation.output_dataset.metadata.update(
475
494
  {"spm_config": recorded["config"], RENAMES_RECORD_KEY: recorded_renames}
476
495
  )
496
+ if isinstance(recorded_legacy_spm, dict):
497
+ simulation.output_dataset.metadata[LEGACY_SPM_RECORD_KEY] = (
498
+ recorded_legacy_spm
499
+ )
477
500
 
478
501
  if os.path.exists(filepath):
479
502
  simulation.created_at = datetime.datetime.fromtimestamp(