Skip to content

Fix formula reads with ADD or DIVIDE in a Microsimulation - #647

Merged
MaxGhenis merged 9 commits into
masterfrom
fix-microsim-add-divide-decode-enums
Nov 15, 2026
Merged

MaxGhenis merged 9 commits into
masterfrom
fix-microsim-add-divide-decode-enums

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

On master, a formula that reads a variable with options=[ADD] or options=[DIVIDE] raises this error in a Microsimulation, though a Simulation computes it:

TypeError: Microsimulation.calculate_add() got an unexpected keyword argument 'decode_enums'

A policyengine-us impact run hit the error on spm_unit("snap", period, options=[ADD]) in mt_liheap_categorical_eligibility.py (PolicyEngine/policyengine-us#10141 (comment)). At PolicyEngine/policyengine-us@2b39b88e2f, 21 formulas read with options=[ADD], and each raised the error whenever it ran in a Microsimulation. In PolicyEngine/policyengine-us#10730, we replaced the options=[ADD] reads in 26 formula files with plain reads to avoid the error. No formula on policyengine-us main (PolicyEngine/policyengine-us@61c743cb61) reads with an option.

Cause

Population.__call__ has passed decode_enums=False with every read since 0abe43a (December 2022), and it sends reads with options=[ADD] or options=[DIVIDE] to calculate_add or calculate_divide (population.py#L147-L163). Simulation.calculate_add and calculate_divide take it (simulation.py#L2057-L2062, #L2126-L2131), but Microsimulation overrides both without it (microsimulation.py#L84-L118), so Python rejects the call.

Plain reads go to calculate, which takes the flag. When a formula reads a monthly flow over a year or a yearly flow over a month, Simulation._calculate calls calculate_add or calculate_divide without the flag (simulation.py#L1741-L1753), so those reads kept working.

Fix

Both overrides now take decode_enums: bool = False, Simulation's default, and pass it to super(). Simulation.calculate_add and calculate_divide accept the flag without using it, and the new parameter goes last, so every direct call that worked before returns the same values.

Population.__call__ now returns a NumPy array for a read with ADD or DIVIDE, as a Simulation does (population.py#L167-L178). In a Microsimulation the overrides return a MicroSeries, and pandas pairs two Series' values by index label. GroupPopulation.project labels each member's value with its group's position (group_population.py#L366-L370). So a formula that projected such a read and combined it with another Series would raise microdf's ValueError: Cannot propagate weights unambiguously to these rows (microdf 1.5.11 and 1.5.12). If every group had one member and the data listed them out of group order, members would get other groups' values without an error. At 2b39b88e2f, policyengine-us's wi_wheap_categorically_eligible projected its ADD read of snap to members and combined it with a person-level read of ssi (lines 33-44).

Population.__call__ copies the values with numpy.array, which returns a new writable array that shares no memory with the Series and keeps its dtype for numbers and booleans. pandas 3 returns a read-only view of a Series' values, so a formula that wrote to the view would raise ValueError: assignment destination is read-only, while the same formula computes in a Simulation. From version 1.2.2, microdf warns on every MicroSeries.to_numpy or .values call that the result carries no weights (PolicyEngine/microdf#278), and numpy.array calls neither.

On master these reads raised the TypeError in every core Microsimulation, and a Simulation already returns arrays, so the conversion moves no value that computed before. Converting in Population.__call__ leaves direct calls alone: calculate_add(..., use_weights=False) worked on master and still returns a MicroSeries.

Invariants and tests

tests/fixtures/microsimulation_option_reads.py adds to the country template ten formulas that read with ADD or DIVIDE. They read person and household variables, sum members' values into households, compare a sum with zero and call the add helper. One of them, fee_or_all_paid, projects a household's ADD read to its members. tests/core/test_microsimulation_option_reads_property.py checks three properties with Hypothesis:

  • A Microsimulation's unweighted reads equal a Simulation's, or both raise the same type of error. Hypothesis generates one to four households of one to three people, listed in any order, with weights, whole monthly amounts, yearly amounts in multiples of 12 and up to six reads in sequence. The reads cover the formulas, the population calls they make, calculate_add and calculate_divide with either value of decode_enums, and plain reads at months, years and a three-month period. Population calls and the two methods read each variable over periods its option accepts. hypothesis.event records each read's outcome, and in two 200-example runs, population calls returned nonzero values in 19% and 31% of examples and raised in none.
  • Each of those population calls returns a NumPy array that a formula can write to, in both classes (50 examples).
  • Each formula returns what the same arithmetic gives in NumPy, in both classes (25 examples for each formula). The test compares them without a tolerance, since whole monthly amounts and yearly amounts in multiples of 12 keep every sum and monthly share exact in float32.

Example tests in tests/core/test_microsimulation_option_reads.py check hand-computed values for each formula and for fee_or_all_paid on two one-person households listed in reverse order. They also check that seven population calls return writable arrays equal to a Simulation's, the weights on a weighted result, and both methods with each value of decode_enums. Four more check that population calls raise no microdf warning about MicroSeries values. microdf added that warning in 1.2.2, so those four can fail only under microdf 1.2.2 or later. uv.lock pins 1.2.1, and CI installs without the lock and resolves 1.5.12.

tests/core/test_microsimulation_calculate_signatures.py binds each keyword call that Simulation.calculate, calculate_add, calculate_divide and calculate_dataframe take, with every subset of their optional arguments, to Microsimulation's override. It checks only that each keyword call binds. So it can't catch an override that takes a flag and ignores it, a default that differs from Simulation's, a keyword that Population.__call__ passes and Simulation lacks (unweighted), or an override in a country package. Positional calls already differed on master: Microsimulation.calculate_add takes map_to third, where Simulation.calculate_add takes decode_enums. So Microsimulation.calculate_add(name, period, True) passes True as map_to and raises KeyError: True. No caller in core or on policyengine-us main passes a third argument by position.

On master, 44 of the 62 new tests fail (21 signature tests, 29 example tests and 12 property tests). In 40 the Microsimulation raises the TypeError, and the signature test rejects the 4 keyword calls that pass decode_enums to calculate_add or calculate_divide. The other 18 check the NumPy reference alone or bind calls that master's overrides already take. Without the conversion in Population.__call__, 10 fail, and Hypothesis falsifies all three properties. fee_or_all_paid raises the ValueError, the reverse-order case returns [True, False] against the Simulation's [False, True], and five population calls return a MicroSeries. With .to_numpy() in place of the copy, 10 fail: five population calls return read-only arrays, four calls raise microdf's warning, and Hypothesis falsifies the second property. With .to_numpy(copy=True), the four warning tests fail.

In CI, on this branch merged with master, core's full suite passes, the 62 new tests included, on Ubuntu and Windows under Python 3.11 to 3.14 with microdf 1.5.12. ruff format --check . passes too.

policyengine-us

No formula on policyengine-us main reads with an option, so this change moves no policyengine-us result there. With it, a policyengine-us formula can again read with options=[ADD] or options=[DIVIDE] in a Microsimulation. No policyengine-us Microsimulation has run on this branch.

A formula can still get a MicroSeries from a plain read that Simulation._calculate sends to calculate_add, such as add(person, period, ["ssi"]). That MicroSeries labels values by position, so it pairs correctly with arrays and with unprojected reads of its entity. On main, wi_wheap_categorically_eligible projects a plain read of snap and ORs it with add(person, period, ["ssi"]) > 0, so the second gap below applies to it. By our reading of the code, a Microsimulation would raise microdf's ValueError there only when this formula reads both ssi and snap over the year before any other formula does.

Remaining gaps

  • Simulation.calculate_add and calculate_divide read each term with self.calculate, which in a Microsimulation decodes Enums and applies weights by default (simulation.py#L2093-L2096, #L2152-L2160). So an Enum read with DIVIDE over a year returns member names in a Microsimulation and an EnumArray in a Simulation. On master that read raised the TypeError. Now a formula that compares it with a member, as read == Tenure.OWNER does in the test, gets False for every person, where a Simulation gets [True, False, True]. test_enum_read_with_divide_over_a_year_gives_member_names_in_a_microsimulation records both results. For the same reason, an Enum read with ADD, or with DIVIDE over a month, raises a TypeError in a Microsimulation, and calculate_add(..., use_weights=False) returns a MicroSeries that still carries weights.
  • The first time a Microsimulation computes a plain read that Simulation._calculate sends to calculate_add or calculate_divide, the read still returns a weighted MicroSeries to the formula, as on master. A formula that projects one and combines it with another Series pairs values by label too. Once the Microsimulation has cached the value, after an earlier plain read or a read with ADD or DIVIDE, a later plain read returns the cached array instead. The holder stores it with numpy.asarray, which under pandas 3 gives a read-only view of the MicroSeries. So a formula that writes to that read raises ValueError: assignment destination is read-only, while a Simulation computes the same formula. On master, a formula that wrote to a second such read raised the same error. No formula on policyengine-us main writes in place to a value it takes straight from an entity read.
  • In a Simulation, a read with ADD or DIVIDE can hand the formula the array the simulation stores. DIVIDE over a year returns the stored yearly value, so a write to it changes every later read. ADD over a year of a monthly flow and DIVIDE over a month of a yearly flow return the value they cache. A write to that value changes later plain reads at that period, while a later read with the option computes the value again. In a Microsimulation, Population.__call__ copies these reads, so after such a write the two classes return different values. No formula on policyengine-us main reads with ADD or DIVIDE.
  • For a simulation class that doesn't subclass core's Microsimulation but has "micro" in its name, such as policyengine-uk's, Population.__call__ passes unweighted=True to the calculate methods. Simulation.calculate_add and calculate_divide don't accept that argument, so a read with ADD or DIVIDE in such a class raises a TypeError. On policyengine-uk main (PolicyEngine/policyengine-uk@d3db7ac07d), only two test reads use an option, and both expect policyengine-uk's StoredDrawError before the read reaches core.

impact: no output moves for any variable that computed before; formulas that read with options=[ADD] or options=[DIVIDE] now compute in a Microsimulation (policyengine-us main has none since PolicyEngine/policyengine-us#10730)

axiom: n/a: core infrastructure fix

🤖 Generated with Claude Code

MaxGhenis and others added 6 commits November 3, 2026 07:16
…vide

Population.__call__ passes decode_enums=False to calculate_add or
calculate_divide for every options=[ADD] or options=[DIVIDE] read in a
formula, as it has since 0abe43a. Simulation's methods take that
argument, but Microsimulation's overrides did not, so every such read
raised a TypeError in a Microsimulation.

Both overrides now take decode_enums, with Simulation's default of
False, and pass it to super(). Simulation.calculate_add and
calculate_divide accept the flag without using it, so every call that
worked before returns the same values.

Example tests run formulas that read with ADD and DIVIDE on a small
country-template system. Hypothesis properties check that a
Microsimulation's unweighted reads equal a Simulation's over generated
data and read sequences, and that formula results equal the same
arithmetic in NumPy. A strict xfail records that an Enum read with
DIVIDE returns labels in a Microsimulation and an EnumArray in a
Simulation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In a Microsimulation, calculate_add and calculate_divide return a
MicroSeries, and pandas pairs two Series' values by index label.
GroupPopulation.project labels each member's value with its group's
position. So a formula that projected such a read and combined it with
another Series raised a ValueError, or, when every group had one member
and the data listed them out of group order, paired members with another
group's value. policyengine-us's wi_wheap_categorically_eligible has
that shape (lines 38-44).

Population.__call__ now returns the NumPy array a Simulation returns for
these reads. They raised a TypeError in every Microsimulation on master,
so no value that computed before moves.

The fixture now takes each person's household, so data can list members
in any order, and adds fee_or_all_paid, a reader with the PE-US
formula's shape. The Enum DIVIDE test now records both classes' results
instead of failing as a strict xfail.

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

Population calls and calculate_add and calculate_divide requests now
pair each variable with periods its option accepts, and population calls
read only variables the population can read, so they return values
instead of mostly raising in both classes. The data lists people in any
order. hypothesis.event reports the mix of values, zeros and errors.

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

A new property checks that each population call a formula makes with
ADD or DIVIDE returns a NumPy array in both classes. The NumPy-arithmetic
property now runs for each formula, 25 examples each: with 100 examples
drawn across all ten, one seeded run never drew fee_or_all_paid or
members_grant_divided.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pandas 3 returns a read-only view from Series.to_numpy(), so in a
Microsimulation a formula that wrote to such a read would raise
"ValueError: assignment destination is read-only" where a Simulation
computes. Copy the values instead, and test that population calls return
writable arrays in both classes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The NumPy reference sums a household's grants and then divides by 12,
while a household's DIVIDE read divides each member's float32 grant by 12
and then sums. Whole grants that nearly cancel, such as 9,990 and -10,000
in one household, made members_grant_divided return -0.83331299 against
-0.83333333, past rtol=1e-6, so the property could fail by chance. With
yearly amounts in multiples of 12, every monthly share is whole, every sum
and share is exact in float32, and the property checks exact equality.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis MaxGhenis changed the title Accept decode_enums in Microsimulation.calculate_add and calculate_divide Fix formula reads with ADD or DIVIDE in a Microsimulation Nov 11, 2026
@MaxGhenis
MaxGhenis marked this pull request as ready for review November 11, 2026 05:41
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

I'm handing this PR to the US + core hub to land. The independent review approves, and CI passes at 99e8a02e0.

  • Head: 99e8a02e0. Five commits after d18c54c6b act on both review rounds. 753bef26d returns NumPy arrays from formula reads with ADD or DIVIDE, and 885be7a81 copies them so formulas can write to them. 141219eb6 and 99a808e6e extend the tests, and 99e8a02e0 draws the properties' yearly inputs in multiples of 12, so float32 rounding can't fail the NumPy comparison. I also updated the title and description.
  • Independent review: Opus 5.5 reviewed the PR in two rounds. Round 1, a Subfleet review job, requested changes at d18c54c6b (~/reviews/core-647-microsim-decode-enums/review-r1.md): a projected MicroSeries paired members with other groups' values. Round 2, an in-session agent, reviewed d18c54c6b..99e8a02e0, asked for changes to the description only, and approved the revised description at the same head (review-r2.md in the same folder).
  • Passed locally at 99e8a02e0: the 37 new tests, core's full suite (2,886 passed, 4 skipped, 3 xfailed), ruff format --check ., and the three properties at 1,000, 1,000 and 300 examples for each formula.
  • CI at the head: all 19 checks passed in run 42896284226. GitHub reports MERGEABLE, with merge state CLEAN.
  • Merge: squash.
  • Depends on: nothing. No hold or decision applies.
  • Follow-ups: the four gaps under "Remaining gaps" in the description. None of them changes what the 21 policyengine-us formulas return.

hua7450 added a commit to PolicyEngine/policyengine-us that referenced this pull request Nov 12, 2026
PolicyEngine/policyengine-core#647 fixes the cause in Core: it lets a
Microsimulation accept the keyword an entity call passes with
options=[ADD] or options=[DIVIDE]. Once that is released the option works
again, so a check that bans it here would be wrong. The 26 formula files
stay without the option, which was redundant and lets `liheap` run on
today's Core.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
hua7450 added a commit to PolicyEngine/policyengine-us that referenced this pull request Nov 12, 2026
…LIHEAP cooling registry notes (#10730)

* Add a national LIHEAP total and LIHEAP cooling registry notes

Sum each state's regular LIHEAP payment in a new liheap variable, add an
Illinois regular heating payment without crisis assistance, add a Michigan
Home Heating Credit registry entry, and record each state's cooling
treatment in its registry note.

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

* Guard the LIHEAP payment list and reword the Rhode Island cooling note

Add regular_payments.yaml to the aggregate-list integrity test, which checks
that each member exists, is state-gated and is not dropped between year
blocks. Say that Rhode Island plans no regular cooling payment, since its
FY2027 plan still allows a leftover-funds cooling grant.

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

* Make state LIHEAP formulas run in a microsimulation

The national `liheap` total evaluates every state's formula over a
mixed-state population, which no household test does. Two patterns on main
raised errors there:

- 26 formula files passed options=[ADD] to an entity call. Core's
  Microsimulation.calculate_add rejects the keyword an entity call passes.
  The option is redundant: a plain read already sums a monthly variable over
  the year through the same Core function.
- The DC and Illinois payment formulas look up a matrix by an income level
  that is 0 for households outside the state. Add the floor Maryland and
  Kansas already use.

Also:
- Reword the North Dakota registry note: the state reserves an optional
  direct cooling payment, so its cooling component is not only equipment.
- Add a `liheap` case with Texas, DC and Illinois households in one
  simulation.
- Add a code-health check against options=[ADD] and options=[DIVIDE].
- Check that every state payment in the LIHEAP registry entry is in the
  list `liheap` sums.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Drop the options=[ADD] code-health check

PolicyEngine/policyengine-core#647 fixes the cause in Core: it lets a
Microsimulation accept the keyword an entity call passes with
options=[ADD] or options=[DIVIDE]. Once that is released the option works
again, so a check that bans it here would be wrong. The 26 formula files
stay without the option, which was redundant and lets `liheap` run on
today's Core.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Pin the Michigan, California and Illinois list entries and add the program prefix

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits November 14, 2026 13:30
…ents

Population.__call__ sends a formula's read to calculate, calculate_add or
calculate_divide with one set of keyword arguments. For each calculate
method that Microsimulation overrides, the test binds every keyword call
that Simulation's method takes to the override. On master, the four calls
that pass decode_enums to calculate_add or calculate_divide fail.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
In a Microsimulation, Population.__call__ gets a MicroSeries from a read
with ADD or DIVIDE and copied it with result.to_numpy(copy=True). microdf
warns on every MicroSeries.to_numpy call that the result carries no
weights, and it reports the warning at the caller's line, so each such
read in a formula raised a UserWarning at population.py.

numpy.array copies the values into a new array that the formula can write
to, and it calls neither MicroSeries.to_numpy nor MicroSeries.values.

The new test reads a person variable and a household variable with each
option and checks that no warning mentions MicroSeries.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Master and 3.36.0 raise the TypeError on these reads before any to_numpy
call, so no release emitted the microdf warning that the second fragment
described. Delete that fragment, and state the first in one sentence, as
docs/engineering/skills/github-prs.md asks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hua7450 added a commit to PolicyEngine/policyengine-us that referenced this pull request Nov 15, 2026
* Add a national LIHEAP total and LIHEAP cooling registry notes

Sum each state's regular LIHEAP payment in a new liheap variable, add an
Illinois regular heating payment without crisis assistance, add a Michigan
Home Heating Credit registry entry, and record each state's cooling
treatment in its registry note.

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

* Add Arkansas LIHEAP summer regular cooling assistance

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

* Count Arkansas LIHEAP cooling in the national LIHEAP total

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

* Guard the LIHEAP payment list and reword the Rhode Island cooling note

Add regular_payments.yaml to the aggregate-list integrity test, which checks
that each member exists, is state-gated and is not dropped between year
blocks. Say that Rhode Island plans no regular cooling payment, since its
FY2027 plan still allows a leftover-funds cooling grant.

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

* Pay Arkansas LIHEAP cooling to unsubsidized renters with utilities in rent

Plan item 3.3 makes these renters eligible. Rewords the energy account
comments, adds an in-rent renter case, and checks the national liheap
total.

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

* Make state LIHEAP formulas run in a microsimulation

The national `liheap` total evaluates every state's formula over a
mixed-state population, which no household test does. Two patterns on main
raised errors there:

- 26 formula files passed options=[ADD] to an entity call. Core's
  Microsimulation.calculate_add rejects the keyword an entity call passes.
  The option is redundant: a plain read already sums a monthly variable over
  the year through the same Core function.
- The DC and Illinois payment formulas look up a matrix by an income level
  that is 0 for households outside the state. Add the floor Maryland and
  Kansas already use.

Also:
- Reword the North Dakota registry note: the state reserves an optional
  direct cooling payment, so its cooling component is not only equipment.
- Add a `liheap` case with Texas, DC and Illinois households in one
  simulation.
- Add a code-health check against options=[ADD] and options=[DIVIDE].
- Check that every state payment in the LIHEAP registry entry is in the
  list `liheap` sums.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Drop the options=[ADD] code-health check

PolicyEngine/policyengine-core#647 fixes the cause in Core: it lets a
Microsimulation accept the keyword an entity call passes with
options=[ADD] or options=[DIVIDE]. Once that is released the option works
again, so a check that bans it here would be wrong. The 26 formula files
stay without the option, which was redundant and lets `liheap` run on
today's Core.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Stop Arkansas LIHEAP income limits at the federal maximum

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Cite HHS's FY2025 median income table for the capped limits

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

* Pin the Michigan, California and Illinois list entries and add the program prefix

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

* Fix issues from review: cap case, fuel condition, rent-route tests

Addresses DTrim99's round-1 review; upstream main merged first.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
hua7450 added a commit to PolicyEngine/policyengine-us that referenced this pull request Nov 15, 2026
* Add a national LIHEAP total and LIHEAP cooling registry notes

Sum each state's regular LIHEAP payment in a new liheap variable, add an
Illinois regular heating payment without crisis assistance, add a Michigan
Home Heating Credit registry entry, and record each state's cooling
treatment in its registry note.

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

* Add Georgia LIHEAP regular cooling assistance

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

* Count Georgia LIHEAP cooling in the national LIHEAP total

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

* Guard the LIHEAP payment list and reword the Rhode Island cooling note

Add regular_payments.yaml to the aggregate-list integrity test, which checks
that each member exists, is state-gated and is not dropped between year
blocks. Say that Rhode Island plans no regular cooling payment, since its
FY2027 plan still allows a leftover-funds cooling grant.

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

* Apply Georgia LIHEAP cooling review fixes

Describes the lower income limit as the Level I limit, checks the
national liheap total, and records the cooling benefit amounts in the
source-conflict candidates.

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

* Make state LIHEAP formulas run in a microsimulation

The national `liheap` total evaluates every state's formula over a
mixed-state population, which no household test does. Two patterns on main
raised errors there:

- 26 formula files passed options=[ADD] to an entity call. Core's
  Microsimulation.calculate_add rejects the keyword an entity call passes.
  The option is redundant: a plain read already sums a monthly variable over
  the year through the same Core function.
- The DC and Illinois payment formulas look up a matrix by an income level
  that is 0 for households outside the state. Add the floor Maryland and
  Kansas already use.

Also:
- Reword the North Dakota registry note: the state reserves an optional
  direct cooling payment, so its cooling component is not only equipment.
- Add a `liheap` case with Texas, DC and Illinois households in one
  simulation.
- Add a code-health check against options=[ADD] and options=[DIVIDE].
- Check that every state payment in the LIHEAP registry entry is in the
  list `liheap` sums.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Drop the options=[ADD] code-health check

PolicyEngine/policyengine-core#647 fixes the cause in Core: it lets a
Microsimulation accept the keyword an entity call passes with
options=[ADD] or options=[DIVIDE]. Once that is released the option works
again, so a check that bans it here would be wrong. The 26 formula files
stay without the option, which was redundant and lets `liheap` run on
today's Core.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Point the Georgia floor candidate at the moved income test

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

* Pin the Michigan, California and Illinois list entries and add the program prefix

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

* Fix issues from review: in-rent route, fuel condition, ruling record

Addresses DTrim99's round-1 review; upstream main merged first.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
hua7450 added a commit to PolicyEngine/policyengine-us that referenced this pull request Nov 15, 2026
* Add a national LIHEAP total and LIHEAP cooling registry notes

Sum each state's regular LIHEAP payment in a new liheap variable, add an
Illinois regular heating payment without crisis assistance, add a Michigan
Home Heating Credit registry entry, and record each state's cooling
treatment in its registry note.

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

* Add Oklahoma LIHEAP regular summer cooling assistance

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

* Count Oklahoma LIHEAP cooling in the national LIHEAP total

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

* Guard the LIHEAP payment list and reword the Rhode Island cooling note

Add regular_payments.yaml to the aggregate-list integrity test, which checks
that each member exists, is state-gated and is not dropped between year
blocks. Say that Rhode Island plans no regular cooling payment, since its
FY2027 plan still allows a leftover-funds cooling grant.

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

* Apply Oklahoma LIHEAP cooling review fixes

Points the Register reference at its first cited page, describes the
guideline lag by benefit year, checks the national liheap total, and
records the cooling income boundary in the source-conflict candidates.

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

* Make state LIHEAP formulas run in a microsimulation

The national `liheap` total evaluates every state's formula over a
mixed-state population, which no household test does. Two patterns on main
raised errors there:

- 26 formula files passed options=[ADD] to an entity call. Core's
  Microsimulation.calculate_add rejects the keyword an entity call passes.
  The option is redundant: a plain read already sums a monthly variable over
  the year through the same Core function.
- The DC and Illinois payment formulas look up a matrix by an income level
  that is 0 for households outside the state. Add the floor Maryland and
  Kansas already use.

Also:
- Reword the North Dakota registry note: the state reserves an optional
  direct cooling payment, so its cooling component is not only equipment.
- Add a `liheap` case with Texas, DC and Illinois households in one
  simulation.
- Add a code-health check against options=[ADD] and options=[DIVIDE].
- Check that every state payment in the LIHEAP registry entry is in the
  list `liheap` sums.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Drop the options=[ADD] code-health check

PolicyEngine/policyengine-core#647 fixes the cause in Core: it lets a
Microsimulation accept the keyword an entity call passes with
options=[ADD] or options=[DIVIDE]. Once that is released the option works
again, so a check that bans it here would be wrong. The 26 formula files
stay without the option, which was redundant and lets `liheap` run on
today's Core.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Pin the Michigan, California and Illinois list entries and add the program prefix

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

* Fix issues from review: pinning cases, ruling record, fuel condition

Addresses DTrim99's round-1 review; upstream main merged first.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
hua7450 added a commit to PolicyEngine/policyengine-us that referenced this pull request Nov 15, 2026
* Add a national LIHEAP total and LIHEAP cooling registry notes

Sum each state's regular LIHEAP payment in a new liheap variable, add an
Illinois regular heating payment without crisis assistance, add a Michigan
Home Heating Credit registry entry, and record each state's cooling
treatment in its registry note.

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

* Add Louisiana LIHEAP regular cooling assistance

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

* Count Louisiana LIHEAP cooling in the national LIHEAP total

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

* Guard the LIHEAP payment list and reword the Rhode Island cooling note

Add regular_payments.yaml to the aggregate-list integrity test, which checks
that each member exists, is state-gated and is not dropped between year
blocks. Say that Rhode Island plans no regular cooling payment, since its
FY2027 plan still allows a leftover-funds cooling grant.

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

* Pay Louisiana LIHEAP cooling to renters with utilities in rent

Guide section 3.6 E.1 pays these renters; the subsidized energy-cost
gate still applies. Adds the allowance-equals-bill boundary, an in-rent
renter case, and the national liheap total check.

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

* Make state LIHEAP formulas run in a microsimulation

The national `liheap` total evaluates every state's formula over a
mixed-state population, which no household test does. Two patterns on main
raised errors there:

- 26 formula files passed options=[ADD] to an entity call. Core's
  Microsimulation.calculate_add rejects the keyword an entity call passes.
  The option is redundant: a plain read already sums a monthly variable over
  the year through the same Core function.
- The DC and Illinois payment formulas look up a matrix by an income level
  that is 0 for households outside the state. Add the floor Maryland and
  Kansas already use.

Also:
- Reword the North Dakota registry note: the state reserves an optional
  direct cooling payment, so its cooling component is not only equipment.
- Add a `liheap` case with Texas, DC and Illinois households in one
  simulation.
- Add a code-health check against options=[ADD] and options=[DIVIDE].
- Check that every state payment in the LIHEAP registry entry is in the
  list `liheap` sums.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Drop the options=[ADD] code-health check

PolicyEngine/policyengine-core#647 fixes the cause in Core: it lets a
Microsimulation accept the keyword an entity call passes with
options=[ADD] or options=[DIVIDE]. Once that is released the option works
again, so a check that bans it here would be wrong. The 26 formula files
stay without the option, which was redundant and lets `liheap` run on
today's Core.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Correct the EIA bill figure in Louisiana cooling test comments

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Note the accepted FY2027 Louisiana plan in the income rate comment

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

* Pin the Michigan, California and Illinois list entries and add the program prefix

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

* Fix issues from review: fuel condition and pinning cases

Addresses DTrim99's round-1 review; upstream main merged first.

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

* Apply review suggestions 5 and 6: cooling-season note, utility-allowance candidate covers cooling

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
hua7450 added a commit to PolicyEngine/policyengine-us that referenced this pull request Nov 15, 2026
* Add a national LIHEAP total and LIHEAP cooling registry notes

Sum each state's regular LIHEAP payment in a new liheap variable, add an
Illinois regular heating payment without crisis assistance, add a Michigan
Home Heating Credit registry entry, and record each state's cooling
treatment in its registry note.

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

* Add Kentucky LIHEAP regular cooling assistance

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

* Count Kentucky LIHEAP cooling in the national LIHEAP total

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

* Guard the LIHEAP payment list and reword the Rhode Island cooling note

Add regular_payments.yaml to the aggregate-list integrity test, which checks
that each member exists, is state-gated and is not dropped between year
blocks. Say that Rhode Island plans no regular cooling payment, since its
FY2027 plan still allows a leftover-funds cooling grant.

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

* Pay Kentucky LIHEAP cooling to renters with utilities in rent

921 KAR 4:116 Section 4(3)(a)2 pays the landlord for these renters. The
income-point bands now use heating's guideline year, which reproduces
the FY2026 workbook; the income limit keeps the 2026 guideline. Updates
shared parameter descriptions, test comments and margins, and checks
the national liheap total.

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

* Make state LIHEAP formulas run in a microsimulation

The national `liheap` total evaluates every state's formula over a
mixed-state population, which no household test does. Two patterns on main
raised errors there:

- 26 formula files passed options=[ADD] to an entity call. Core's
  Microsimulation.calculate_add rejects the keyword an entity call passes.
  The option is redundant: a plain read already sums a monthly variable over
  the year through the same Core function.
- The DC and Illinois payment formulas look up a matrix by an income level
  that is 0 for households outside the state. Add the floor Maryland and
  Kansas already use.

Also:
- Reword the North Dakota registry note: the state reserves an optional
  direct cooling payment, so its cooling component is not only equipment.
- Add a `liheap` case with Texas, DC and Illinois households in one
  simulation.
- Add a code-health check against options=[ADD] and options=[DIVIDE].
- Check that every state payment in the LIHEAP registry entry is in the
  list `liheap` sums.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Drop the options=[ADD] code-health check

PolicyEngine/policyengine-core#647 fixes the cause in Core: it lets a
Microsimulation accept the keyword an entity call passes with
options=[ADD] or options=[DIVIDE]. Once that is released the option works
again, so a check that bans it here would be wrong. The 26 formula files
stay without the option, which was redundant and lets `liheap` run on
today's Core.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Pin the Michigan, California and Illinois list entries and add the program prefix

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

* Fix issues from review: rent-route cases and fuel condition

Addresses DTrim99's round-1 review; upstream main merged first.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
hua7450 added a commit to PolicyEngine/policyengine-us that referenced this pull request Nov 15, 2026
* Add a national LIHEAP total and LIHEAP cooling registry notes

Sum each state's regular LIHEAP payment in a new liheap variable, add an
Illinois regular heating payment without crisis assistance, add a Michigan
Home Heating Credit registry entry, and record each state's cooling
treatment in its registry note.

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

* Add South Carolina LIHEAP regular cooling assistance

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

* Count South Carolina LIHEAP cooling in the national LIHEAP total

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

* Guard the LIHEAP payment list and reword the Rhode Island cooling note

Add regular_payments.yaml to the aggregate-list integrity test, which checks
that each member exists, is state-gated and is not dropped between year
blocks. Say that Rhode Island plans no regular cooling payment, since its
FY2027 plan still allows a leftover-funds cooling grant.

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

* Apply South Carolina LIHEAP cooling review fixes

Records the FY2026 and FY2027 plan service limits, describes the age
thresholds for heating and cooling, tightens test margins, and checks
the national liheap total.

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

* Make state LIHEAP formulas run in a microsimulation

The national `liheap` total evaluates every state's formula over a
mixed-state population, which no household test does. Two patterns on main
raised errors there:

- 26 formula files passed options=[ADD] to an entity call. Core's
  Microsimulation.calculate_add rejects the keyword an entity call passes.
  The option is redundant: a plain read already sums a monthly variable over
  the year through the same Core function.
- The DC and Illinois payment formulas look up a matrix by an income level
  that is 0 for households outside the state. Add the floor Maryland and
  Kansas already use.

Also:
- Reword the North Dakota registry note: the state reserves an optional
  direct cooling payment, so its cooling component is not only equipment.
- Add a `liheap` case with Texas, DC and Illinois households in one
  simulation.
- Add a code-health check against options=[ADD] and options=[DIVIDE].
- Check that every state payment in the LIHEAP registry entry is in the
  list `liheap` sums.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Drop the options=[ADD] code-health check

PolicyEngine/policyengine-core#647 fixes the cause in Core: it lets a
Microsimulation accept the keyword an entity call passes with
options=[ADD] or options=[DIVIDE]. Once that is released the option works
again, so a check that bans it here would be wrong. The 26 formula files
stay without the option, which was redundant and lets `liheap` run on
today's Core.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Pin the Michigan, California and Illinois list entries and add the program prefix

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

* Fix issues from review: FY2027 one-award limit and fuel condition

Addresses DTrim99's round-1 review; upstream main merged first.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
hua7450 added a commit to PolicyEngine/policyengine-us that referenced this pull request Nov 15, 2026
* Add a national LIHEAP total and LIHEAP cooling registry notes

Sum each state's regular LIHEAP payment in a new liheap variable, add an
Illinois regular heating payment without crisis assistance, add a Michigan
Home Heating Credit registry entry, and record each state's cooling
treatment in its registry note.

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

* Add Alabama LIHEAP regular cooling assistance

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

* Count Alabama LIHEAP cooling in the national LIHEAP total

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

* Guard the LIHEAP payment list and reword the Rhode Island cooling note

Add regular_payments.yaml to the aggregate-list integrity test, which checks
that each member exists, is state-gated and is not dropped between year
blocks. Say that Rhode Island plans no regular cooling payment, since its
FY2027 plan still allows a leftover-funds cooling grant.

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

* Verify Alabama LIHEAP cooling for FY2025 and pay in-rent renters

Adds the FY2025 plan, the February 2025 benefit chart and the FY2027
plan as references, a FY2025 case, and an in-rent renter case. Manual
section 6.1.3 pays unsubsidized renters with utilities in rent. Marks
2025-2026 verified and checks the national liheap total.

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

* Make state LIHEAP formulas run in a microsimulation

The national `liheap` total evaluates every state's formula over a
mixed-state population, which no household test does. Two patterns on main
raised errors there:

- 26 formula files passed options=[ADD] to an entity call. Core's
  Microsimulation.calculate_add rejects the keyword an entity call passes.
  The option is redundant: a plain read already sums a monthly variable over
  the year through the same Core function.
- The DC and Illinois payment formulas look up a matrix by an income level
  that is 0 for households outside the state. Add the floor Maryland and
  Kansas already use.

Also:
- Reword the North Dakota registry note: the state reserves an optional
  direct cooling payment, so its cooling component is not only equipment.
- Add a `liheap` case with Texas, DC and Illinois households in one
  simulation.
- Add a code-health check against options=[ADD] and options=[DIVIDE].
- Check that every state payment in the LIHEAP registry entry is in the
  list `liheap` sums.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Drop the options=[ADD] code-health check

PolicyEngine/policyengine-core#647 fixes the cause in Core: it lets a
Microsimulation accept the keyword an entity call passes with
options=[ADD] or options=[DIVIDE]. Once that is released the option works
again, so a check that bans it here would be wrong. The 26 formula files
stay without the option, which was redundant and lets `liheap` run on
today's Core.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Stop Alabama LIHEAP income limits at the federal maximum and drop a removed plan link

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Cite HHS's FY2025 median income table for the capped limits

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

* Pin the Michigan, California and Illinois list entries and add the program prefix

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

* Fix issues from review: 2025 heating cap sizes, fuel condition, pinning cases

Addresses DTrim99's round-1 review; upstream main merged first.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
hua7450 added a commit to PolicyEngine/policyengine-us that referenced this pull request Nov 15, 2026
* Add a national LIHEAP total and LIHEAP cooling registry notes

Sum each state's regular LIHEAP payment in a new liheap variable, add an
Illinois regular heating payment without crisis assistance, add a Michigan
Home Heating Credit registry entry, and record each state's cooling
treatment in its registry note.

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

* Add Nebraska LIHEAP regular cooling assistance

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

* Count Nebraska LIHEAP cooling in the national LIHEAP total

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

* Guard the LIHEAP payment list and reword the Rhode Island cooling note

Add regular_payments.yaml to the aggregate-list integrity test, which checks
that each member exists, is state-gated and is not dropped between year
blocks. Say that Rhode Island plans no regular cooling payment, since its
FY2027 plan still allows a leftover-funds cooling grant.

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

* Apply Nebraska LIHEAP cooling review fixes

Points the eligibility reference at its first cited page, adds the DHHS
energy assistance page, describes the guideline lag by federal fiscal
year, and checks the national liheap total.

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

* Make state LIHEAP formulas run in a microsimulation

The national `liheap` total evaluates every state's formula over a
mixed-state population, which no household test does. Two patterns on main
raised errors there:

- 26 formula files passed options=[ADD] to an entity call. Core's
  Microsimulation.calculate_add rejects the keyword an entity call passes.
  The option is redundant: a plain read already sums a monthly variable over
  the year through the same Core function.
- The DC and Illinois payment formulas look up a matrix by an income level
  that is 0 for households outside the state. Add the floor Maryland and
  Kansas already use.

Also:
- Reword the North Dakota registry note: the state reserves an optional
  direct cooling payment, so its cooling component is not only equipment.
- Add a `liheap` case with Texas, DC and Illinois households in one
  simulation.
- Add a code-health check against options=[ADD] and options=[DIVIDE].
- Check that every state payment in the LIHEAP registry entry is in the
  list `liheap` sums.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Drop the options=[ADD] code-health check

PolicyEngine/policyengine-core#647 fixes the cause in Core: it lets a
Microsimulation accept the keyword an entity call passes with
options=[ADD] or options=[DIVIDE]. Once that is released the option works
again, so a check that bans it here would be wrong. The 26 formula files
stay without the option, which was redundant and lets `liheap` run on
today's Core.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Cite Nebraska's FY2027 Guidance Document for the cooling tables

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

* Pin the Michigan, California and Illinois list entries and add the program prefix

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

* Fix issues from review: in-rent route, fuel condition, pinning cases

Addresses DTrim99's round-1 review; upstream main merged first.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
hua7450 added a commit to PolicyEngine/policyengine-us that referenced this pull request Nov 15, 2026
* Add a national LIHEAP total and LIHEAP cooling registry notes

Sum each state's regular LIHEAP payment in a new liheap variable, add an
Illinois regular heating payment without crisis assistance, add a Michigan
Home Heating Credit registry entry, and record each state's cooling
treatment in its registry note.

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

* Add Virginia EAP cooling assistance electric bill payment

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

* Count Virginia EAP cooling in the national LIHEAP total

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

* Guard the LIHEAP payment list and reword the Rhode Island cooling note

Add regular_payments.yaml to the aggregate-list integrity test, which checks
that each member exists, is state-gated and is not dropped between year
blocks. Say that Rhode Island plans no regular cooling payment, since its
FY2027 plan still allows a leftover-funds cooling grant.

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

* Apply Virginia LIHEAP cooling review fixes

Dates the cooling guideline lag from FY2025, fixes the electric bill
maximum comment, cites the EIA 2024 average bill in the tests, and adds
a retiree case that checks the national liheap total.

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

* Make state LIHEAP formulas run in a microsimulation

The national `liheap` total evaluates every state's formula over a
mixed-state population, which no household test does. Two patterns on main
raised errors there:

- 26 formula files passed options=[ADD] to an entity call. Core's
  Microsimulation.calculate_add rejects the keyword an entity call passes.
  The option is redundant: a plain read already sums a monthly variable over
  the year through the same Core function.
- The DC and Illinois payment formulas look up a matrix by an income level
  that is 0 for households outside the state. Add the floor Maryland and
  Kansas already use.

Also:
- Reword the North Dakota registry note: the state reserves an optional
  direct cooling payment, so its cooling component is not only equipment.
- Add a `liheap` case with Texas, DC and Illinois households in one
  simulation.
- Add a code-health check against options=[ADD] and options=[DIVIDE].
- Check that every state payment in the LIHEAP registry entry is in the
  list `liheap` sums.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Drop the options=[ADD] code-health check

PolicyEngine/policyengine-core#647 fixes the cause in Core: it lets a
Microsimulation accept the keyword an entity call passes with
options=[ADD] or options=[DIVIDE]. Once that is released the option works
again, so a check that bans it here would be wrong. The 26 formula files
stay without the option, which was redundant and lets `liheap` run on
today's Core.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Pin the Wayback capture of the 2023 Virginia cooling release

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* Label the WYDaily citation for Virginia's cooling guideline year as a press report

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

* Pin the Michigan, California and Illinois list entries and add the program prefix

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

* Fix issues from review: subsidized heat-in-rent ruling, fuel condition, size pin

Addresses DTrim99's round-1 review; upstream main merged first.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis merged commit a3cd0dd into master Nov 15, 2026
19 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

US + core hub merge audit, core#647 at 43b27e4da95bbcf2371b7a318aa4832f02568dba (squash): Fix formula reads with ADD or DIVIDE in a Microsimulation.

  • Reviews:
    • An Opus 5.5 reviewer requested changes at d18c54c6b7: a projected MicroSeries paired members with other groups' values. 753bef26d returns NumPy arrays from formula reads with ADD or DIVIDE, as a Simulation does, and 885be7a81 copies them so a formula can write to them.
    • A second Opus 5.5 review of d18c54c6b..99e8a02e0 found the code right and asked for description changes only. Its probes found that every Microsimulation read with ADD or DIVIDE returned a writable array that shared no memory with stored values.
    • Two commits followed. 9b538bb58 tests that each Microsimulation calculate method takes the keyword arguments Simulation's takes, and 9ddef016f copies with numpy.array, so microdf raises no warning about MicroSeries values.
    • A third Opus 5.5 review read the whole diff at 9ddef016f. It found the code right and asked for a description that matches the head and for one changelog fragment. 43b27e4da deletes the second fragment and states the first in one sentence, and the description now gives this head's counts, CI run and policyengine-us impact.
    • A fourth Opus 5.5 check at 43b27e4da found the description, the changelog fragment and CI at this head consistent with the code and tests.
  • Tests: the PR adds 62 tests: 21 signature tests, 29 example tests and 12 property tests. On master, 44 of the 62 fail. With .to_numpy() in place of the copy, 10 fail, and with .to_numpy(copy=True), 4 fail.
  • CI: run 43639955708 passed 19 of 19 jobs at this head, merged with master 003d09e15. Each of the four Ubuntu jobs passed 3,880 tests and each of the four Windows jobs passed 3,845, under Python 3.11 to 3.14 with microdf 1.5.12. All eight ran the 62 new tests.
  • Classification: a crash fix. On master, a formula read with options=[ADD] or options=[DIVIDE] raises TypeError in every core Microsimulation, so the change moves no value that computed before. No formula on policyengine-us main reads with an option since Add a national LIHEAP total, an Illinois regular heating payment and LIHEAP cooling registry notes policyengine-us#10730, so the change moves no policyengine-us result there. The description's "Remaining gaps" section lists four gaps that the PR leaves open.
  • PE-US core gate: with this head merged into master 003d09e15 and PE-US main 7c516c74bb, the 43 PE-US core test files that exercise subsampling, branches, inputs or holders passed all 1,203 tests.
  • Gates the lander checked at merge:
    • every check passes at the head;
    • MERGEABLE;
    • not a draft;
    • no CHANGES_REQUESTED review;
    • the merge pins the head (--match-head-commit).
  • Axiom: n/a; the PR changes no policy.

🤖 Generated with Claude Code

@MaxGhenis
MaxGhenis deleted the fix-microsim-add-divide-decode-enums branch November 15, 2026 04:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant