Repository navigation
Tie source-conflict markers to the register shared with Axiom - #10287
Merged
Merged
Conversation
PolicyEngine-US follows the statute when it conflicts with a regulation, form, instructions, manual or stated agency practice; forms and instructions fill gaps where the statute is silent. Each such choice is an entry in the source-conflict register in TheAxiomFoundation/rulespec-us, and the deciding line carries `# Source conflict <jurisdiction>:<id>`. - Vendor the register at a pinned rulespec-us commit (pin.yaml: commit and sha256), refreshed by scripts/update_source_conflicts.py. - tests/code_health/test_source_conflicts.py: markers resolve to entries that list their file; registered files keep their marker; comments that read like a source conflict may not grow, file by file. - Document the rule and the steps in README, CONTRIBUTING, CLAUDE.md, AGENTS.md and the PR template. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e register at rulespec-us ff213980 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From a sweep of PolicyEngine-US pull request bodies since July 2026, the claimable-filer and claim-of-right research, the review notes and the code comments, deduplicated and triaged. 60 are cases where the model follows the non-statutory source against the statute. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…2025 booklet page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ow registers from the backlog Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
marked this pull request as ready for review
October 19, 2026 22:18
…ntries in the backlog The vendored copy is #1710's register (it moves to rulespec-us main when #1710 merges). The seven entries drafted in rulespec-us#1825 stay in the backlog until they register there, so the guard removes them then. The Maryland marker waits for its entry. Captures now hold only pdftotext output or official HTML. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…est tree
CI: the selective runner treats every .yaml under policyengine_us/tests/ as
a policy test, and the dead-URL check flagged one backlog link. The
register, pin, backlog and captures move to source_conflicts/ at the
repository root (out of the wheel too), which the dead-URL check now scans;
the Texas HHS link uses its live host.
Review findings:
- Markers: any comment starting like a marker, in any spacing or case
("# Source conflict", "# Source\tconflict", "#Source conflict"), must be
exactly "# Source conflict <id>" on its own line; inline markers are
rejected.
- Comments are read syntax-aware: Python comment tokens (text in strings is
not a comment), and YAML comments outside quoted and block scalars,
inline ones included.
- The backlog privacy scan reads the raw file, so comments and mapping keys
are covered; code paths must be relative and resolve to a file inside the
repository; years need a whole-year from, an optional to not before it, a
type, and a note for a program year.
- Backlog: us:eitc-table-versus-continuous-formula is dropped (IRC 32(f)
prescribes the tables; a model approximation, not a source conflict); the
Oregon and Wisconsin duplicates merge into their claim-of-right items; the
Puerto Rico EITC maxima are unknown pending the Hacienda bulletin on
indexing; Kansas 2021-2023 is unknown because the code does not do what the
item said. 225 candidates.
- CONTRIBUTING lists every check, says they are structural, and that a
marked file is exempt from the ratchet.
Every probe from the review now fails the guard; a docstring that contains a
marker-like line no longer does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Markers: "source conflict" is found with any number of #s and with spaces, a hyphen, an underscore or nothing between the words, and when an id-shaped token follows a colon or "conflicts"; the ratchet counts "Source-conflict" prose too. Marker classification is one tested helper. - YAML comments: a quote opens a scalar only at the start of a value, '' and \" escapes are honoured, quoted scalars and flow collections carry across lines, and lines break where PyYAML breaks them. Across all 6,383 scanned YAML files, cutting each found comment leaves PyYAML's value unchanged and every skipped # is inside a scalar; a test asserts no file ends inside a quote. - Python comments: inline detection reads the token's own line, so form feeds and Unicode separators no longer shift rows. - Backlog privacy: scan the raw text and every parsed value and key, ignoring case and allowing six-digit ids; code paths must be files git tracks, with no hidden component. - The dead-URL check skips the byte-pinned vendored register. - Backlog: drop the Puerto Rico EITC item (Hacienda's bulletin gives the Schedule CT maxima as the indexed statutory amounts, so the sources agree), and keep only the merged Oregon and Wisconsin items' linked sources. 224 items. - Docs give the marker as <jurisdiction>:<id> and the guard's full path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…P3 gaps The third review of the guard approved 1b81648 and listed seven P3 items. This commit addresses all of them. - YAML comments are now the ones PyYAML's scanner skips, recorded by a BaseLoader subclass. The hand-written scanner missed comments glued to a token, trailing comments under a block scalar, tagged or anchored headers, plain continuation lines and a BOM. Over all 6,383 scanned files the recorder finds exactly the comments the old scanner did, and the BaseLoader soundness and completeness checks still hold. - A file whose comments cannot be read (it doesn't tokenize or parse) is reported as malformed, rather than read as having no comments. - Privacy: - decision ids joined by an underscore are caught; - bare UUIDs are caught (but not UUIDs in URL paths); - more local-path forms are caught; - bytes, sets, tuples and keys in parsed values are scanned. - A marker attempt may have any separator between the words, including a dash, a dot or a zero-width space. - Kill inputs for the reviewer's surviving mutants: escaped keys and list members in the privacy scan, years.to's type, a block-scalar header comment, and unscannable files. - Backlog, 223 items: - drop us-mt:disability-income-exclusion-cap: MCA 15-30-2110 caps the exclusion at $100 a week, so the instructions follow the statute; - restate 16 items' "the input" wording in terms of the sources. - The docstring and CONTRIBUTING describe the git-tracked, hidden-component and parsed-value rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…one qualifier Review of c5b524b (approved; three P3s): - R4-1: PyYAML's reader does not advance its column over U+FEFF, so a BOM inside a line shifted the extracted comment. Record the absolute index and slice the text by it. Regression: two embedded BOMs before an inline marker. - R4-2: a test runs a real unscannable file through _cached_comments, _comments and _markers, so a cache hand-off that swallowed the error would fail. - R4-3: us:filing-claimable-spouse-unearned-income-additions says the same-year instructions for the separate 2026 issues are not cited here, rather than that none exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TheAxiomFoundation/rulespec-us#1710 landed. Its register has eight entries: two Social Security entries that pinned an unmerged microcosm-dynamics draft (#530) were deferred, and the saving-clause entry keeps only shipped texts. No PolicyEngine-US marker or candidate refers to them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
to TheAxiomFoundation/rulespec-us
that referenced
this pull request
Oct 20, 2026
… the runner runs YAML The pin runner executes Python pins only, so the entry's one test pin, a PolicyEngine-US YAML case, would fail the pinned-tests job. A followed reading must be pinned by a test, so the implementation goes with it. The entry stays, open, recording the conflict. Both come back in the follow-up that runs PolicyEngine-US YAML cases through policyengine-core's runner (eng hub ruling). Removed, to restore then: - implementation: PolicyEngine/policyengine-us@79249f0 policyengine_us/variables/gov/states/md/tax/income/credits/poverty_line/md_poverty_line_credit_potential.py (follows and adopts `reading`; PolicyEngine ruling 2026-10-19, PolicyEngine/policyengine-us#10287) - test: the same commit, policyengine_us/tests/policy/baseline/gov/states/md/tax/income/credits/poverty_line/md_poverty_line_credit_earned_income.yaml case "A farm loss is netted against wages like other self-employment (2024)", pinning `reading` Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Merge audit (admin merge: the only block is the required approving review; this is Max's own PR, which no one can self-approve). Head merged: Independent reviews (GPT-6.1 Sol):
Gates at this head: |
MaxGhenis
added a commit
to TheAxiomFoundation/rulespec-us
that referenced
this pull request
Oct 20, 2026
PolicyEngine/policyengine-us#10287 squash-merged as ce9377ee, and its captures moved to source_conflicts/sources/ at the repository root. The ten pending_texts snapshot refs (eight distinct files) now name that commit and path. Each file at ce9377ee hashes to the sha256 already recorded; the re-pin script refuses to write otherwise. GitHub reports every external commit in the register as shipped: canonical, not a fork, and on its default branch (the PolicyEngine-US one is `identical` to main). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hua7450
added a commit
that referenced
this pull request
Oct 21, 2026
The new source-conflict guard (#10287) registers statute conflicts only; this NOTE compares a regulation with another regulation or a handbook. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hua7450
added a commit
that referenced
this pull request
Oct 21, 2026
…stered The new guard (#10287) reserves the phrase for registered markers. The NOTE's content is unchanged; the conflict goes to source_conflicts/candidates.yaml when main is merged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hua7450
added a commit
that referenced
this pull request
Oct 21, 2026
The new source-conflict guard (#10287) registers statute conflicts only; this NOTE compares a regulation with another regulation or a handbook. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 21, 2026
MaxGhenis
added a commit
that referenced
this pull request
Oct 21, 2026
…seline (#10334) The guard (#10287) took its KNOWN_UNMARKED baseline at 0f21a63. FL LIHEAP (#9969) and SD LIHEAP (#9974) merged before the guard did, each with comments the ratchet reads as unmarked source conflicts, so test_conflict_sounding_comments_do_not_grow fails on main from ce9377e. These five files were on main when the guard landed, so they belong in the baseline, one comment each. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Oct 26, 2026
…9879) * Model the IRS local sales tax worksheet with official locality rates Replace the 20%-of-state-amount approximation in local_sales_tax with the State and Local General Sales Tax Deduction Worksheet: the Optional Local Sales Tax Tables (with the selector's named counties and cities) in the line-2 states and the ratio method elsewhere, with line 3 the combined rate above the state table heading rate. Combined rates come only from official state files: the Streamlined Sales Tax rate and boundary files, New York Publication 718 and 718-A, and Virginia Tax's rates by locality, weighted to Census 2020 places. Where no official file exists the combined rate defaults to the heading rate, so the local amount is 0 unless the user enters a combined or local rate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Resolve locality codes to Census places and use the state rate for line 3 Address the independent review of #9879: - Recode SST place codes that match no Census place or county subdivision of their county by the records' postal city, or by the code the same ZIP codes carry after the records end. This fixes the DeKalb County part of Atlanta (Georgia code 05000; 8.0% before, 8.9% now) and Vermont's 2022 town taxes (older town codes, and town-wide taxes under CDP codes). In Vermont codes resolve to towns. The build logs every recoding and every code left unmatched. - Write each covered state's general sales tax rate (state_rates.csv) and take worksheet line 3 as the combined rate less it, so rounded or averaged headings (Minnesota 6.88%, South Dakota 2023 4.35%) no longer shift line 3. California and Nevada keep the IRS rule: the combined rate above the heading. - List New York City's five counties as Table A counties. - Accept 7-digit place GEOIDs; document the county_fips/place_fips keys, the pre-2022 backdating and the remaining limits (Iowa, Vermont towns outside places, a few Utah codes). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Regenerate the build manifest with the final log wording Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix Rutland city sales tax timing and audit Vermont dates * Document missing official locality coverage and Houston regression * Correct sales tax worksheet and table PDF references * Test normalized county and place codes in sales tax lookups * Record Mississippi proxy and ratio readings without choosing a method * Resolve local sales tax geography consistently from supplied inputs * Document special district contributions to locality rate averages * Explain dated state sales tax components and parameter limitations * Audit SST boundary inputs and preserve recoverable trailing fields * Fix county carryover in local sales tax geography * Test the county carryover refusal paths * Use the ratio method for Mississippi localities without IRS tables * Give each Mississippi worksheet example its own tax unit * Separate direct and combined rate inputs in Mississippi worksheet tests * Fix absent local table routing in state sales tax tests * Address review round 2 (N2, N3, suggestions 4 and 5) - Drop local_sales_tax.py from KNOWN_UNMARKED: on the tree merged with main it has no unmarked conflict-sounding comment, so the #10287 ratchet's shrunk check failed. - Default-table descriptions follow the IRS-led house form (a-d, none.yaml). - Shipped text cites the 2025 Schedule A instructions (p. 5) instead of decision ids. - The changelog fragment states the $0 local amount in the 16 uncovered local-sales-tax states (was 20% of the state amount). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Hash every shipped local sales tax CSV in the build manifest (review round 2, suggestion 3) build.py now records output_sha256 for rates.csv, populations.csv, place_counties.csv and state_rates.csv, and manifest.json carries the current files' hashes (place_counties.csv unchanged). test_manifest_hashes_every_shipped_csv checks that the manifest names exactly the shipped CSVs and that each hash matches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lands after TheAxiomFoundation/rulespec-us#1710 (the register), which merged on 2026-10-20 as 5796861e; the pin is now that merge commit on rulespec-us main. TheAxiomFoundation/rulespec-us#1825 (PolicyEngine-US fields and first entries) lands after this, because its entries pin the source captures this PR adds.
What and why
PolicyEngine US's rule, from 2026-10-19: when a statute conflicts with a regulation, a tax form, form instructions, an agency manual or stated agency practice, the model follows the statute. Forms and instructions fill gaps where the statute is silent.
This PR makes that rule hard to skip and tracks every such conflict we know of:
The register. It is shared with Axiom:
known-source-conflicts.yamlin TheAxiomFoundation/rulespec-us (introduced in DC Keep Child Care Affordable Tax Credit #1710, extended for PolicyEngine US in Add NYC taxes and refundable credits to relevant household variables #1825). An entry records:pinned-testsjob runs each pinned Python test at its commit and passes onlypassed. It does not run YAML cases yet, so a PolicyEngine US YAML case can't pin a reading until it does;This PR vendors the register byte for byte at a pinned commit (
source_conflicts/pin.yamlholds the commit and sha256).scripts/update_source_conflicts.py --commit <sha>refreshes it, and--checkcompares it with upstream. CI never reads a moving branch.The marker. Code that decides such a reading carries
# Source conflict <jurisdiction>:<id>at the deciding line, the convention PolicyEngine/microcosm-dynamics already uses. This PR adds no markers. The first entries register after it lands. The Maryland marker follows once the register lists Maryland's implementation, which needs a runnable test pin, and so waits for the runner to execute YAML cases.The backlog.
source_conflicts/candidates.yamlholds 228 known conflicts (including the five entries drafted in rulespec-us#1825, under the ids they will register with, so the guard removes them when they do) that are not yet registered. Each has its sources, what PolicyEngine US does today (statute,conflicting-source,gap-fill,not-modelledorunknown), the code and PRs, and whether PolicyEngine has ruled. A candidate leaves the backlog by becoming a register entry, and the guard fails if a registered id is still listed.conflicting-source, with a note.Source captures.
source_conflicts/sources/holds pdftotext or official-HTML captures of the forms, instructions and statutes that register entries cite but the Axiom corpus does not yet hold (pending_texts, pinned there by this repository, commit and sha256).The guard.
policyengine_us/tests/code_health/test_source_conflicts.py; see Invariants.Docs. README, CONTRIBUTING ("Source conflicts"), CLAUDE.md, AGENTS.md and the PR template state the rule and the steps: register, mark, refresh the pin.
Scope. The register and backlog hold conflicts between official sources. A model that departs from the statute with no official source behind it is a bug, not an entry. An example is the North Carolina Special Assistance 2027 rate, which used the unrounded CPI-W ratio where G.S. 108A-42.1 says the approved COLA. Such bugs go to fixes and issues.
Drafted in rulespec-us#1825. Hawaii and Arkansas claim-of-right floors, Ohio's federal-claim condition, Colorado OAP interest, and Maryland Worksheet 18B. The register records only shipped code: the open PolicyEngine US PRs (#10200, #10195, #10203, #10176) are listed there as
proposed. Maryland's implementation is onmain, but its only test is a YAML case, so the entry lists neither until the runner executes YAML cases.Invariants
These hold on every run of the guard:
policyengine_us/{variables,parameters,reforms}is well formed. It names an entry in the pinned register, and that entry lists the marker's file as aPolicyEngine/policyengine-usimplementation.PolicyEngine/policyengine-usimplementation that the register says cites its entry (cites_entry: true) exists here and still carries its marker.KNOWN_UNMARKEDis lowered. The starting count is 45 files and 46 lines. This is a count ratchet, not a ban.rulespec-us checks the other half at pinned commits. Its verify job checks that every excerpt is verbatim, each listed file and test exists, and each
cites_entryclaim matches its file. Itspinned-testsjob checks that each pinned test's commit is on its repository's default branch, then runs the test there and passes onlypassed. TheAxiomFoundation/rulespec-us#1825 extends verify's default-branch check to every external commit.Test plan
pytest policyengine_us/tests/code_health/test_source_conflicts.pygives 103 passed. CI runs it in Rest Python'sremaininggroup. It costs about 1 s and 180 MB RSS (file scan and YAML only, no model build).ruff formatandruff check(0.15.5, as locked).changelog.d/.axiom: n/a: tooling. The register itself is TheAxiomFoundation/rulespec-us#1825.
🤖 Generated with Claude Code