Skip to content

Model the Minnesota Paid Leave employer premium - #10294

Merged
MaxGhenis merged 19 commits into
mainfrom
mn-employer-paid-leave-premium
Nov 9, 2026
Merged

MaxGhenis merged 19 commits into
mainfrom
mn-employer-paid-leave-premium

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

What this changes

Minnesota Paid Leave had only an employee-side variable, so employer_additional_state_payroll_tax, employer_payroll_tax and the employer-level employer_total_additional_state_payroll_tax left out Minnesota's employer premium. The part of the permitted employee share an employer doesn't deduct also had nowhere to land. This PR models the premium under Minn. Stat. 268B.14:

  1. The employer owes the premium (subd. 1), at the annual rate: 0.88% for 2026 and 2027. Subd. 6 sets 0.7%, and the commissioner adjusts it under subd. 7. Minnesota's Department of Employment and Economic Development (DEED) keeps the rate at 0.88% for 2027.
  2. The employer pays at least half (subd. 3). Employees pay "the remaining portion, if any", by wage deduction.
  3. A small employer eligible under subd. 5a pays 75% of the rate, and must itself pay at least 25% of the full rate (subd. 5a(b)).
  4. The employer pays the premium less the employee deduction. That deduction equals state_payroll_premium_deduction_share times the permitted employee portion.
2026 and 2027 Large employer Small employer (subd. 5a)
Premium rate 0.88% 75% × 0.88% = 0.66%
Employer minimum 50% × 0.88% = 0.44% 25% × 0.88% = 0.22%
Largest employee deduction 0.88% − 0.44% = 0.44% 0.66% − 0.22% = 0.44%
Employer pays, at deduction share s 0.88% − s × 0.44% 0.66% − s × 0.44%

Each rate applies to taxable wages: wages up to the Social Security base rounded to the nearest $1,000, which gives $185,000 in 2026 (268B.01, subd. 43). 268B.14, subd. 4 caps wages at the same base without the rounding, $184,500 for 2026. The model, as on main, follows subd. 43's rounded figure, the one DEED publishes. Using $184,500 instead would lower the 2026 maximum premium per worker from $1,628 to $1,623.60. In its small employer designation notice, DEED says "Like large employers, small employers can deduct up to 0.44% of wages", and defines the 2026 basis period and the small employer wage limit.

Parameters (gov.states.mn.tax.payroll.paid_leave)

  • rate: 0.88% from 2026, with 2027 entered explicitly.
  • employer_minimum_share: 50%.
  • small_employer.rate_share: 75%.
  • small_employer.employer_minimum_share: 25% of the full rate.
  • small_employer.max_headcount: 30.
  • small_employer.max_average_wage_ratio: 150% (subd. 5a(a)(2)).
  • small_employer.state_average_annual_wage: $73,989 for 2026 premiums (DEED's statewide average annual wage for 2025).

Each parameter starts with a 0000-01-01 sentinel. PolicyEngine US backdates first values only to 2015, so without one, every Minnesota calculation before 2015 fails with ParameterNotFoundError; a 2014 YAML case guards this. rate's sentinel of 0 keeps the program off before 2026. The others carry their 2026 values, so a reform that sets an earlier rate gets the statutory split and a nonzero employer minimum.

This PR removes employee_rate (0.44%). The model now derives the permitted employee rate as the premium rate less the employer's minimum. It applies a reform of the rate or either size's shares to both the employee and employer amounts. Users whose reforms set gov.states.mn.tax.payroll.paid_leave.employee_rate must now set rate, employer_minimum_share or small_employer.employer_minimum_share.

Variables

  • mn_paid_leave_small_employer: the subd. 5a(a) test (see the proxies below).
  • mn_paid_leave_premium_rate: the rate, or 75% of it for a small employer.
  • mn_paid_leave_permitted_employee_rate: the premium rate less the employer's minimum, from 0 ("the remaining portion, if any") up to the premium rate.
  • mn_paid_leave_premium: the premium rate times taxable wages.
  • mn_employee_paid_leave_contribution: the permitted employee rate times taxable wages times the deduction share. Its output doesn't change at any current parameter value.
  • mn_employer_paid_leave_contribution: the premium less the employee contribution.
  • mn_employer_additional_state_payroll_tax: added to employer_additional_state_payroll_tax.
  • employer_total_mn_paid_leave_taxable_wages: the employer-level wage base.
  • employer_total_additional_state_payroll_tax gains a Minnesota branch. It computes (premium rate − share × permitted employee rate) × employer-level wages, reading the same two rate variables as the per-worker path.

Proxies for subds. 5a–5c, and what the model leaves out

  • Employee count (subd. 5b). employer_headcount (default 100) stands in for the employer's highest quarterly count of wage records in the basis period, the four quarters ending September 30 of the prior year. For an employer not covered for the whole period, it stands in for the employer's own estimate (5b(c)).
  • Average wage (subd. 5c). The new input, mn_paid_leave_employer_average_quarterly_wage, takes the covered wages of the employer's highest-paid quarter divided by its highest employee count. For a new employer, enter its subd. 5c(b) estimate, which covers a tax year, divided by 4. The default of 0 treats the wage test as met, so employer_headcount alone decides the small employer rate unless a user sets the input. We rejected two alternatives:
    • Proxying with the worker's own wages would turn a firm-level test into a jump in the employer's premium at $110,983.52 a year of the worker's own pay, which the law doesn't have.
    • Deriving the average from employer-level totals would make the per-worker and employer-level paths disagree.
  • Wage limit. In its designation notice, DEED applies a limit of $27,745.88 a quarter to 2026 premiums, whose basis period runs from October 2024 to September 2025. As of 2026-11-09, we found no DEED limit for 2027 premiums (basis period October 2025 to September 2026), so the model carries the 2026 value forward. The model computes the limit as 150% × $73,989 / 4 = $27,745.875 and rounds it half up to DEED's $27,745.88. It compares the input with that limit at float32 precision, without rounding the input to the cent, so averages up to about $27,745.8818 meet it:
    • an input of $27,745.88 meets it;
    • $27,745.885 doesn't, as it wouldn't against DEED's published figure.
  • Employer-level wages. employer_total_mn_paid_leave_taxable_wages defaults to employer_total_taxable_earnings_for_social_security, the employer's total of each worker's wages up to the Social Security base: $184,500 in 2026 and $192,300 in 2027. Minnesota rounds those bases to $185,000 and $192,000. For a worker who earns more than the lower base, the default understates Minnesota's 2026 wages by up to $500 and overstates its 2027 wages by up to $300. Users can set the input directly.
  • The model leaves out:
    • approved private plans (subds. 1 and 6(2)–(3), section 268B.10), since the model assumes the employer participates in both state programs;
    • recalculation after a new employer's low estimate (subds. 5b(d), 5c(c));
    • subd. 3's minimum-wage floor on deductions;
    • DEED's administrative third condition for the small employer rate, that the employer filed every wage detail report in the basis period.

Source conflicts

This PR records two items in source_conflicts/candidates.yaml and captures the three texts they cite in source_conflicts/sources/us-mn/:

  • us-mn:paid-leave-small-employer-average-wage. Under subd. 5c(a), an employer's average wage equals its highest quarterly covered wages divided by its highest quarterly count of wage records, and DEED computes it the same way in its designation notice. On its small employer page, Unemployment Insurance Minnesota divides that quotient by four again. The model follows the statute. For example, an employer with 10 employees and $400,000 of covered wages in its highest-paid quarter averages $40,000 and pays the full 0.88%. Under the page's steps, it would average $10,000 and pay 0.66%.
  • us-mn:paid-leave-small-employer-wage-limit-cents. A quarter of 150% of $73,989 comes to $27,745.875, and DEED states the 2026 limit as $27,745.88. Subd. 5a(a)(2) doesn't state the limit per quarter or set its rounding, so the model follows DEED's figure, and an average of $27,745.88 qualifies.

Default outputs that change

From 2026, every Minnesota worker has an employer premium. At the defaults (100 employees, full deduction) it equals 0.44% of taxable wages, and it flows into:

  • employer_additional_state_payroll_tax;
  • employer_state_payroll_tax;
  • employer_payroll_tax;
  • employer_cost_of_employment.

For Minnesota employer-level inputs, the employer-level path rises the same way: employer_total_additional_state_payroll_tax, employer_total_state_payroll_tax, employer_total_payroll_tax and employer_total_cost_of_employment. Employee-side amounts don't change, because the permitted employee rate stays 0.44% for both sizes of employer. Household outputs don't change either: no household or tax-unit variable reads these employer-side variables. The CRFB tax_employer_payroll_tax reform adds only employer Social Security and Medicare tax to gross income, so it doesn't pick up the premium.

Invariants

policyengine_us/tests/core/test_state_payroll_premium_deduction_share_invariants.py checks these with Hypothesis in 2026 and 2027, over 8 derandomized examples that each mix 30 fixed workers with 4 to 30 drawn ones:

  • Properties 3–5 now cover Minnesota's employer side.
    • Conservation (3). Employee plus employer premium doesn't depend on the share. Only New York's employer cost stays unmodeled, and its employer amount stays 0 at any share.
    • Monotonicity (4). A larger share never lowers the employee amount or raises the employer amount.
    • Differential (5). When an employer's Minnesota wage total equals the worker's wages capped at the rounded base, the employer-level employer_total_additional_state_payroll_tax equals the per-worker employer_additional_state_payroll_tax.
  • New property 7 compares Minnesota with the statute. The test computes the premium from the statute and DEED figures without reading Minnesota's parameter files. It uses 0.88%, or 0.66% for 30 or fewer employees at an average quarterly wage of at most $27,745.88, and checks three things:
    • mn_paid_leave_premium equals that premium;
    • employee plus employer equals it at every share;
    • the employer pays its minimum (half the premium, or 0.22% of wages for a small employer) plus (1 − share) times the rest.
  • Every example includes Minnesota employers with 9 and 100 employees, one with 30 employees at a $27,745.88 average, one with 31 employees and one with 10 employees at $27,745.89. Hypothesis draws other averages in whole cents.

Tests

  • New policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml (25 cases). Comments derive each new value from the statute and DEED figures. The cases cover:

    • large and small employers at shares 1, 0.5 and 0;
    • the 30/31-employee boundary, and the wage limit at $27,745.88, $27,745.885 and $27,745.89;
    • the $185,000 cap, fractional cents and negative wages;
    • the flow into employer_payroll_tax ($8,132 on $100,000, with the unemployment insurance rate set to 0 so the case doesn't depend on Minnesota's unemployment insurance wage base);
    • 2014 (before parameter backdating), 2025 (the premium and the small employer test) and 2027, and a worker outside Minnesota;
    • the employer-level total, including a Minnesota-base override.
  • state_payroll_premium_deduction_share.yaml's Minnesota case now also checks the employer's $660. The 3 cases in mn_employee_paid_leave_contribution.yaml pass unchanged.

  • policyengine_us/tests/code_health/test_source_conflicts.py passes with the two new items (103 tests).

  • policyengine_us/tests/core/test_payroll_contributions.py adds Minnesota to the employer-side and employer-level cases. test_minnesota_paid_leave_shares_follow_the_parameters applies reforms of the rate and both employer minimums. It checks the per-worker and employer-level amounts, and the statutory split in 2025, when the reform sets only the rate. It also checks the derived employee portion against two bounds:

    • its floor: a small employer minimum of 80% leaves employees $0;
    • its cap: a small employer minimum of −10% leaves employees the whole $750 premium.

    test_minnesota_small_employer_wage_limit_rounds_half_up pins the limit's rounding with two reformed statewide averages. A 2026 average of $73,991 gives 150% × $73,991 / 4 = $27,746.625, which rounds half up to $27,746.63, so $27,746.63 qualifies and $27,746.64 doesn't. A 2027 average of $73,989.02 gives $27,745.8825, which rounds to $27,745.88, so $27,745.88 qualifies and $27,745.89 doesn't.

  • The two reform tests run 3 cases, and each case builds a reformed system. Locally on macOS (Apple M5 Max), test_payroll_contributions.py took 84.9 s of wall time at a 2.28 GiB peak with those 3 cases, and 46.7 s at 1.95 GiB without them. On Linux CI at 780a23ecca, the file ran in the core group's core-3 chunk on Rest Python shard 3 (run 42660976073). Its 107 cases took 112.4 s of test time, 63.5 s of it in the 3 reform cases. On main at ba9bfdea75, its 100 cases took 45.0 s in the core-2 chunk on shard 1 (run 42624467915). CI reports memory for each chunk, not each file, and the two chunks hold different files. In this PR's run, core-3 took 22 min 31 s and peaked at 8,555,256 KiB (8.16 GiB). On main, core-2 took 27 min 28 s and peaked at 9,549,272 KiB (9.11 GiB).

Microsimulation impact

We estimate that Minnesota's employer premium adds $860.2m to employer_additional_state_payroll_tax in 2026 on the default dataset. We compared main at ba9bfdea75 with this branch at 2c662e9e50. The commits after 2c662e9e50 change only tests, the changelog, the source-conflict backlog and source captures (source_conflicts/).

The premium reaches 1,731 sampled Minnesota workers (3.11m weighted), at the default 0.44% of taxable wages and at most $814 a worker, 0.44% of the $185,000 base. employer_state_payroll_tax and employer_payroll_tax rise by the same $860.2m. Income taxes, household net income and household benefits don't change.

Output Main total Change Records changed (weighted)
employer_additional_state_payroll_tax $4.31bn +$860.2m 1,731 (3,112,607)
employer_state_payroll_tax $56.27bn +$860.2m 1,731 (3,112,607)
employer_payroll_tax $854.06bn +$860.2m 1,731 (3,112,607)
state_income_tax $577.3bn $0 0 (0)
income_tax $2,595.3bn $0 0 (0)
household_net_income $17,198.7bn $0 0 (0)
household_benefits $2,060.5bn $0 0 (0)

impact: employer payroll tax +$860.2m in 2026 (1,731 Minnesota workers, 3,112,607 weighted), from Minnesota's employer paid leave premium; household outputs don't change
axiom: Minn. Stat. 268B.14 → TheAxiomFoundation/rulespec-us#1819 queued (extended with the employer outputs, the subd. 5a leaf inputs and companion rows matching these YAML cases; blocked on corpus ingestion, TheAxiomFoundation/axiom-corpus#893)

🤖 Generated with Claude Code

MaxGhenis and others added 7 commits October 19, 2026 14:33
Add employer_state_payroll_premium_deduction_share (default 1) for the
programs whose statute puts the premium on the employer and lets it deduct
up to a share from wages: CO FAMLI, DE Paid Leave, ME and MA PFML, MN Paid
Leave, NY PFL and DBL, the VT Child Care Contribution and WA PFML. The
employee amount is the permitted share times the deduction share; the
employer-side variables carry the part it does not deduct.

Encode Acts 2026 c. 101 ss. 25-26 for Massachusetts from 2027-01-01, with
DFML's 2027 family and medical rates, and cite CGS 31-49g(b)(1) for the
Connecticut paid leave rate.

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Describe the deduction-share programs by the employer's choice, not by
who owes the premium (NY and WA put the contribution on the worker), note
that MN and NY employer costs are not modeled, state why WA PFML is in and
Paid Leave Oregon is out, and note that Rev. Rul. 2025-4's treatment of an
employer-paid employee share as wages is not modeled. Cap the one-worker
employer's taxable total in the invariants so the employer-level path is
checked for every modeled state and size, pin the 2027 Massachusetts
parameter values, and add file-page anchors to the PDF references.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Minn. Stat. 268B.14 makes the premium payable by the employer, which pays at
least half (subd. 3); a small employer under subd. 5a pays 75% of the rate
and at least a quarter of the full rate. The employer pays the premium less
the employee deduction, so the part of the permitted employee share an
employer does not deduct (PR #10285) now lands on the employer side.

- New parameters: rate (0.88% for 2026 and 2027), employer_minimum_share,
  and small_employer/{rate_share, employer_minimum_share, max_headcount,
  max_average_quarterly_wage}. They replace employee_rate (0.44%), which is
  now derived.
- New variables: mn_paid_leave_small_employer (employer_headcount and a new
  average quarterly wage input stand in for subds. 5b and 5c),
  mn_paid_leave_premium_rate, mn_paid_leave_permitted_employee_rate,
  mn_paid_leave_premium, mn_employer_paid_leave_contribution,
  mn_employer_additional_state_payroll_tax and
  employer_total_mn_paid_leave_taxable_wages.
- employer_additional_state_payroll_tax and the employer-level
  employer_total_additional_state_payroll_tax include Minnesota.
- YAML cases computed from the statute and DEED rates, MN rows in the payroll
  contribution tests, and MN in the deduction-share invariants, with a new
  statute-literal property for Minnesota.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…re' into mn-employer-paid-leave-premium

# Conflicts:
#	policyengine_us/variables/input/employer_payroll.py
A reform of the rate or the employer minimums reaches both sides, and a small
employer minimum above the small employer premium leaves employees nothing
(subd. 5a(b)(2), "the remaining portion, if any").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits October 19, 2026 17:44
Brings in main through 1661d71, including #10279's update to North Carolina SA Case 37, which was the only CI failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 5 commits October 19, 2026 18:55
- The small employer wage limit is now the statutory 150% (subd. 5a(a)(2))
  times DEED's statewide average annual wage ($73,989 for 2026 premiums,
  from DEED's small employer designation notice) per quarter, compared in
  whole cents as DEED states it ($27,745.88). This replaces the
  max_average_quarterly_wage parameter, which had no period and cited a page
  that gives no year.
- The permitted employee rate is capped at the premium as well as floored at
  0 ("the remaining portion, if any"); the reform test covers both.
- The structural shares and limits no longer carry zero placeholders before
  2026; the rate alone gates the program.
- The property test draws the average wage in whole cents, so its float64
  reference can't disagree with the model's float32 input below a cent.
- Cite DEED's July 31, 2026 bulletin for the 2027 rate, and move the
  employee_rate removal into a removed changelog fragment.

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

- Restore the 0000-01-01 sentinels on the Minnesota Paid Leave shares and
  limits. pe-us backdates parameters only to 2015, so without them every
  Minnesota employer-side calculation before 2015 raised
  ParameterNotFoundError (caught by the delta review; a 2014 YAML case now
  guards it).
- Compare the unrounded average wage with DEED's cent-rounded limit at the
  input's float32 precision, instead of rounding the input to cents: an
  average a fraction of a cent above $27,745.88 now fails the test, as it
  does against DEED's published limit, while an input of exactly $27,745.88
  still meets it. A YAML case covers $27,745.885.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The Minnesota Paid Leave shares and limits now carry their 2026 values
  from 0000-01-01, so a reform that sets an earlier rate gets the statutory
  split instead of a zero employer minimum; the zero rate still keeps the
  program off before 2026, and pre-2015 years still compute.
- Round the small employer wage limit to the cent half up, as the repo does
  for Minnesota's taxable wage base, instead of numpy's half-to-even.
- Say that DEED calculates the statewide average annual wage each year, and
  that 150% of $73,989 / 4 is $27,745.875, which DEED gives as $27,745.88.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The sentinel comment now says what happens: earlier years read the 2026
  value every year, and the zero rate keeps every amount at 0 before 2026.
- A reform setting the statewide average to $73,991 puts the limit at
  $27,746.625, which half-up rounding makes $27,746.63; the new test fails
  under numpy's half-to-even rounding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review October 20, 2026 02:51
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Hand-off to the US + core hub.

🤖 Generated with Claude Code

@codecov

codecov Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (aae7f98) to head (e3966ea).
⚠️ Report is 93 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##              main    #10294    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files            1         1            
  Lines           37       193   +156     
==========================================
+ Hits            37       193   +156     
Flag Coverage Δ
unittests 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@DTrim99

DTrim99 commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Model the Minnesota Paid Leave employer premium

PR #10294 (author: MaxGhenis). Head a19c04c83c. The branch is 14 ahead of main and 19 behind. First review.

Summary

The PR adds the employer side of Minnesota Paid Leave under Minn. Stat. 268B.14:

  • Premium. The rate is 0.88% for 2026 and 2027, or 75% of it for a subd. 5a small employer.
  • Employer minimum. The employer pays at least 50% of the premium. A small employer pays at least 25% of the full rate.
  • Employee amount. The permitted employee rate is the premium rate less the employer minimum, times Let employers deduct less than the permitted state payroll premium, and apply Massachusetts' PFML cap swap from 2027 #10285's deduction share. The employer pays the rest of the premium.
  • Aggregates. The premium now flows into employer_additional_state_payroll_tax and its parents. employer_total_additional_state_payroll_tax gets a Minnesota branch.
  • Parameters. employee_rate is removed and the employee rate is now derived from rate and the employer minimum shares.

Scope. This review covers only #10294's own six commits (git diff 3698893465..a19c04c83c, 27 files). The #10285 findings are in our #10285 review and aren't repeated here.

Law and sources. I re-read 268B.14 subds. 1, 3, 4, 5a–5c, 6 and 7 and 268B.01 subd. 43 on the Revisor's site. I also re-read DEED's small employer designation notice and DEED's July 31, 2026 bulletin for 2027. Every rate, share, limit and wage base matches the law and DEED's figures:

  • 0.88%, 0.66%, 0.44% and 0.22%;
  • 30 employees;
  • 150% of $73,989, which gives $27,745.88 a quarter;
  • $185,000 in 2026 and $192,000 in 2027.

Code.

  • The employer amount is the premium less the employee amount, so nothing is counted twice.
  • The employer-level formula matches the per-worker formula exactly.
  • Non-Minnesota results are unchanged.
  • Minnesota employee amounts are unchanged at every current parameter value.
  • No household variable reads the employer-side outputs.

Tests, axiom and branch.

Open items. One should-fix item remains, the same one we raised on #10285: seven new parameter descriptions don't follow the house template. There are five optional suggestions.

Critical (must fix)

None.

Should address

1. Seven new parameter descriptions don't follow the house template.

  • Where: line 1 of each file under parameters/gov/states/mn/tax/payroll/paid_leave/:
    • rate.yaml: "Minnesota charges this Paid Leave premium rate on taxable wages for an employer participating in both the family and medical benefit programs."
    • employer_minimum_share.yaml: "Minnesota requires employers to pay at least this share of the Paid Leave premium; employees pay the remaining portion, if any, through a wage deduction."
    • small_employer/employer_minimum_share.yaml: "Minnesota requires small employers to pay at least this share of the full Paid Leave premium rate; employees pay the remaining portion of the small employer premium, if any, through a wage deduction."
    • small_employer/rate_share.yaml: "Minnesota charges small employers this share of the full Paid Leave premium rate."
    • small_employer/max_headcount.yaml: "Minnesota limits the Paid Leave small employer premium rate to employers with at most this many employees."
    • small_employer/max_average_wage_ratio.yaml: "Minnesota limits the Paid Leave small employer premium rate to employers whose average wage is at most this multiple of the state's average wage in covered employment for the basis period."
    • small_employer/state_average_annual_wage.yaml: "Minnesota compares a small employer's average wage with this statewide average annual wage in covered employment, which the Department of Employment and Economic Development calculates each year and applies to the Paid Leave basis period."
  • The template: "one sentence, spell the program out fully (no acronyms), [State] [verb] ... under the [Full Program Name] program."
    • Each description opens correctly with "Minnesota [verb]" and uses no acronyms.
    • None ends with "under the Paid Leave program"; each puts "Paid Leave" inside a noun phrase instead.
    • Five use verbs outside the house list (limits, provides, sets, excludes, deducts, uses): "charges" and "requires" twice each, and "compares" once.
    • Two add a second clause after a semicolon about the employee side. The variable docs already say that.
    • This is the same item we raised on Let employers deduct less than the permitted state payroll premium, and apply Massachusetts' PFML cap swap from 2027 #10285's Massachusetts and Delaware descriptions.
  • Fix, for example:
    • rate: "Minnesota sets this premium rate on taxable wages for an employer in both the family and medical benefit programs under the Paid Leave program."
    • employer_minimum_share: "Minnesota sets this minimum share of the premium that employers pay under the Paid Leave program."
    • small_employer.employer_minimum_share: "Minnesota sets this minimum share of the full premium rate that small employers pay under the Paid Leave program."
    • small_employer.rate_share: "Minnesota sets the small employer premium rate at this share of the full premium rate under the Paid Leave program."
    • small_employer.max_headcount: "Minnesota limits the small employer premium rate to employers with at most this many employees under the Paid Leave program."
    • small_employer.max_average_wage_ratio: "Minnesota limits the small employer premium rate to employers whose average wage is at most this multiple of the state's average wage in covered employment under the Paid Leave program."
    • small_employer.state_average_annual_wage: "Minnesota uses this statewide average annual wage in covered employment for the small employer wage test under the Paid Leave program."

Suggestions

1. Report the cost of the two new reform tests (CLAUDE.md, "Measure simulation-heavy additions").

  • What they build. test_minnesota_paid_leave_shares_follow_the_parameters (two parametrized cases) and test_minnesota_small_employer_wage_limit_rounds_half_up each build a full CountryTaxBenefitSystem(reform=(reform,)). That is three reformed systems in the core Python shard.
  • What's reported. The PR body gives time and peak memory for the invariants test but only a pass count (107) for test_payroll_contributions.py.
  • What CLAUDE.md asks. For new tests that build models, it asks for before/after elapsed time (including setup) and peak memory, and for a check of the affected CI group.
  • Why the tests are worth keeping. Their purpose is distinct: the reforms reach both sides through the derived rate, and they pin the floor, the cap and half-up rounding.
  • Fix. Add the numbers to the PR body.

2. Keep the 2027 carry-forward note in small_employer/state_average_annual_wage.yaml.

  • The gap. The basis period for 2027 premiums runs from October 2025 to September 2026. DEED has published no limit for it yet: pl.mn.gov still says "currently $27,745.88 per quarter". So $73,989 carries into 2027.
  • Where it's documented. The PR body says this, but the parameter file doesn't.
  • Fix. A one-line comment, such as "No DEED figure for 2027 premiums as of 2026-10-20; update when DEED issues 2027 small employer designations", keeps the follow-up with the value.

3. Separate the new flow case from Minnesota unemployment insurance values that are out of date on main.

  • The case. mn_employer_paid_leave_contribution.yaml, "The employer premium flows into employer state and total payroll taxes", asserts employer_state_unemployment_tax: 378. Its comment reads "0.9% default rate * the 42,000 wage base".
  • The problem. gov.states.mn.tax.payroll.unemployment.taxable_wage_base on main is a flat 0000-01-01: 42_000. Minnesota's 2026 base is $44,000. UIMN tax rates: "The Taxable wage base for 2026 is $44,000". Its history table gives 2024 $42,000, 2025 $43,000 and 2026 $44,000.
  • Why it matters here. The stale value comes from main, not this PR. But the new case pins it, so a later fix to the UI base would break a paid leave test.
  • Fix.
    • Set employer_state_unemployment_tax_rate_override: 0 in this case. Then it asserts employer_state_payroll_tax: 440 and employer_payroll_tax: 6,200 + 1,450 + 42 + 440 = 8,132.
    • Or keep only the paid leave assertions.
    • Either way, file a follow-up issue for the Minnesota UI wage base.

4. Update one sentence in rulespec-us#1819 to match the head's wage-limit logic.

  • The sentence. The Model the Minnesota Paid Leave employer premium #10294 extension says the limit is "compared in whole cents". It was last edited at 02:19 UTC on 2026-10-20, before f5b8ec27a2 and b0596abccf.
  • The head's logic.
    • The limit is 150% × the state average / 4, rounded half up to the cent.
    • The employer average is compared unrounded, at float32 precision.
    • So $27,745.885 fails, which the YAML pins.
  • Effect. The 14 companion rows all use whole cents and still match.
  • Fix. Reword the sentence and, optionally, add the $27,745.885 row so the encoder sees the boundary.

5. Small text fixes.

Verified correct

  • The statute (Minn. Stat. 268B.14):
    • subd. 1(a): "premiums accrue and become payable by each employer ... on the taxable wages that the employer paid to employees in covered employment". This is mn_paid_leave_premium on the employer.
    • subd. 3: "employers must pay a minimum of 50 percent of the annual premiums ... Employees, through a deduction in their wages ... must pay the remaining portion, if any". This gives employer_minimum_share = 0.5 and the floor at 0.
    • subd. 5a(a): eligible if "(1) has 30 or fewer employees pursuant to subdivision 5b; and (2) the average wage for that employer as calculated in subdivision 5c is less than or equal to 150 percent of the state's average wage in covered employment for the basis period". This gives max_headcount 30, max_average_wage_ratio 1.5 and the <= test.
    • subd. 5a(b): "75 percent of the annual premium rate ... (1) employers must pay a minimum of 25 percent of the rate calculated in subdivisions 6 and 7". This gives rate_share 0.75 and the small employer minimum of 0.25 × the full rate. The employee maximum is 0.66% − 0.22% = 0.44%, the same as for a large employer.
    • subds. 5b and 5c: the basis period is "the four-quarter period ending September 30 of the prior year". The employer's average is the maximum single-quarter covered wages divided by the maximum quarterly wage-record count. The docstrings of employer_headcount and mn_paid_leave_employer_average_quarterly_wage describe the proxies correctly.
    • subds. 6 and 7: the statutory rate is 0.7% (6(1)), and the commissioner adjusts it each year by July 31. The approved private plan rates (6(2)–(3)) are documented as not modeled.
  • DEED figures:
    • 2026 rate: DEED's press release "the premium rate will be 0.88 percent".
    • 2027 rate: DEED bulletin, July 31, 2026: "will remain steady at 0.88% of covered wages in Minnesota for 2027 ... employers can continue to collect up to 0.44% from employees in 2027 ... Small employers pay a smaller share – as little as 0.22%".
    • The small employer designation notice:
      • Page 1: "The effective rate for small employers is 0.66% ... Like large employers, small employers can deduct up to 0.44%", "30 or fewer employees", "150% of the statewide average wage", and "If this number was higher than $27,745.88, the employer was not designated".
      • Page 2: "The Statewide Average Annual Wage for 2025 was $73,989", and the table gives $18,497.25 and $27,745.88 a quarter. The page's prose says "$18,497.35"; that's a typo, and the table and arithmetic give $18,497.25.
      • The #page=1 and #page=2 anchors each show the cited figure.
    • Premiums page. pl.mn.gov returns 403 to automated requests. A search capture confirms "For 2026 and 2027, the Paid Leave premium rate is 0.88% ... The Paid Leave small employer rate is 0.66%".
  • Wage base:
    • 268B.01 subd. 43: "the maximum earnings in that year subject to the FICA Old-Age, Survivors, and Disability Insurance tax rounded to the nearest $1,000".
    • DEED's notice gives "rounded to the nearest thousand dollars (currently $185,000)".
    • 2026: $184,500 → $185,000. 2027: $192,300 → $192,000.
    • Subd. 4's unrounded wording conflicts with subd. 43. That conflict predates this PR (mn_paid_leave_taxable_wages is unchanged), and the PR body documents it.
  • Wages counted (OK Add Oklahoma state supplementation for 2026 #10165 lesson):
    • The base is gross employment_income, unchanged from main.
    • Minnesota "wages" include salary reductions under 401(k) and cafeteria plans: 268.035 subd. 29(b), "Nothing in this subdivision excludes from the term 'wages' any payment made under any type of salary reduction agreement, including ... cafeteria plan".
    • So deducting pre-tax amounts would be wrong, and the code doesn't.
  • Formulas:
    • mn_paid_leave_premium_rate = rate × where(small, 0.75, 1).
    • mn_paid_leave_permitted_employee_rate = rate × clip(premium share − employer minimum share, 0, premium share).
    • mn_employee_paid_leave_contribution = permitted rate × taxable wages × the clipped share.
    • mn_employer_paid_leave_contribution = premium − employee amount. It lies between the employer minimum and the full premium.
    • No double counting. The employee part feeds employee_state_payroll_tax; only the remainder feeds employer_additional_state_payroll_tax → employer_state_payroll_tax → employer_payroll_tax → employer_cost_of_employment.
    • Employer level. (premium rate − share × permitted rate) × employer_total_mn_paid_leave_taxable_wages is algebraically the per-worker formula. It reads the same small employer test, and property 5 checks the two agree.
  • The float32 boundary:
    • The limit. 100 × 1.5 × 18,497.25 = 2,774,587.5, +0.5, floor, /100 = $27,745.88.
    • Precision. In float32 the spacing near $27,745 is 2⁻⁹ ≈ $0.00195:
      • $27,745.88 → 27,745.880859, equal to np.float32(limit), so it meets the test;
      • $27,745.885 → 27,745.884766, above the limit, so it fails;
      • $27,745.89 → 27,745.890625, so it fails.
    • Rounding mode. DEED's 27,745.875 rounds to .88 under both half up and half to even, because the 7 is odd. So DEED's figure doesn't decide the mode. Half up is the usual convention, and the reform test pins it ($73,991 → $27,746.625 → $27,746.63).
  • Pre-2015 years and the 2026 start:
  • Defaults and other states:
    • Every new Minnesota variable has defined_for = StateCode.MN. The aggregate selects the Minnesota branch only for Minnesota.
    • The WI case and the multi-state property test (the optional states plus CA, CT, NJ, OR, PA, RI and TX) confirm other states are unchanged.
    • Minnesota employee amounts are unchanged: 0.0088 × 0.5 equals 0.0044 exactly in binary, for both large and small employers.
    • The new employer amounts at the defaults (100 employees, share 1) are intended.
    • Callers. employer_payroll_tax is read only by employer_cost_of_employment; the CRFB tax_employer_payroll_tax reform adds only employer Social Security and Medicare. So household outputs don't move.
  • Batch lessons:
  • Removing employee_rate:
    • No reference remains at the head or on main.
    • A top-level changelog.d/mn-employer-paid-leave-premium.removed.md documents it, alongside .added.md. Both types fit.
    • No partner contract test is touched.
  • Hand checks (2026 and $100,000 unless noted):
    • Large employer, share 1: 0.88% → premium 880; permitted 0.44% → employee 440; employer 440. The aggregates are 440 ✓.
    • Share 0.5: 220 / 660. Share 0: 0 / 880 ✓.
    • 30 employees (small): rate 0.66%; permitted (0.75 − 0.25) × 0.88% = 0.44%; premium 660; employee 440; employer 220 ✓.
    • Small, share 0.5: 220 / 440. Small, share 0: 0 / 660 ✓.
    • 31 employees: not small; 880 / employer 440 ✓.
    • Average wage, 10 employees: $27,745.88 → small, 660 / 220. $27,745.89 → 880 / 440. $27,745.885 → not small, 880 ✓.
    • $250,000 of wages: taxable 185,000; 0.88% → 1,628; 0.44% → 814 / 814 ✓.
    • $250,000, 9 employees: 0.66% × 185,000 = 1,221; employee 814; employer 407 = 0.22% × 185,000 ✓.
    • $8,000: 70.40 / 35.20 / 35.20 ✓. Negative wages: 0 ✓.
    • Flow: 378 + 440 = 818. Then 6,200 + 1,450 + 42 + 818 = 8,510 ✓. The UI part is arithmetically right but rests on the stale base in suggestion 3.
    • 2027: 880 / 440 / 440. 2025 and 2014: 0 ✓. WI: 0 ✓.
    • Employer level:
      • (0.88% − 0.44%) × 1,000,000 = 4,400 ✓.
      • 20 employees at share 0.25: (0.66% − 0.25 × 0.44%) × 1,000,000 = 5,500 ✓.
      • Minnesota base override: 0.44% × 370,000 = 1,628 ✓.
    • Deduction-share case: 0.88% × 100,000 − 220 = 660 ✓.
    • Python tests:
      • Employer side 440 / 440.
      • Minnesota wage base 7,000: employee 30.8, employer 30.8.
      • Employer total 0.44% × 100,000 = 440; the state-specific base gives 30.8 ✓.
    • Reform test (rate 1%, employer minimum 0.6):
      • Large: 1,000 / 400 / 600.
      • Small minimum 0.8: clip(−0.05) = 0, giving 750 / 0 / 750.
      • Small minimum −0.1: clip(0.85, 0, 0.75) = 0.75, giving 750 / 750 / 0 ✓.
    • Rounding test: 1.5 × 73,991 / 4 = 27,746.625 → 27,746.63. In float32, 27,746.63 → 27,746.630859 meets the test; 27,746.64 → 27,746.640625 fails ✓.
  • Test design:
    • Property 7 uses constants written from the statute and DEED (0.88%, 30 employees, $27,745.88), independent of the parameter files. It checks the premium, conservation and the employer's minimum plus the undeducted share.
    • The draws pin the 30/31-employee and $27,745.88/$27,745.89 boundaries, and the assertion mn_small[mn].any() and (~mn_small[mn]).any() guarantees both kinds of Minnesota employer appear.
    • The YAML file is named after mn_employer_paid_leave_contribution.
    • Periods are plain (no 2_026).

Axiom line assessment

Valid. The line reads axiom: Minn. Stat. 268B.14 → TheAxiomFoundation/rulespec-us#1819 queued (...; blocked on corpus ingestion, TheAxiomFoundation/axiom-corpus#893).

  • Status. rulespec-us#1819 is OPEN and labelled pe-parity. The blocker, axiom-corpus#893 ("Ingest state paid-leave premium sources ... for rulespec-us pe-parity"), is OPEN and was updated today.
  • Dispatch-ready. The body has:
    • the module target us-mn/statutes/268B/14.yaml with the proposed corpus citation us-mn/statute/268B.14;
    • the verbatim law for subds. 1(a), 3, 4, 5a(a)–(b), 5b(a)–(c), 5c(a)–(b) and 6, and for 268B.01 subd. 43;
    • DEED's figures for 2026 and 2027;
    • the required outputs, including mn_paid_leave_small_employer and the employer-paid premium;
    • the leaf inputs: employee count, employer average quarterly wage, deduction share and taxable wages;
    • companion tests hand-computed from the statute and DEED, not from PolicyEngine output;
    • an updated pasteable review_finding.
  • Match with the head.
    • All 14 rows of the "Extension for PE-US Model the Minnesota Paid Leave employer premium #10294" table match this head's YAML: 880/440/440, 880/220/660, 880/0/880, 660/440/220, 660/220/440, 660/0/660, 31 employees at 880/440/440, $27,745.88 → small, $27,745.89 → not small, 1,628/814/814, 1,221/814/407, 70.40/35.20/35.20, 2027 880/440/440, and 2025 at 0.
    • The 2027 comment's rows (220/220 and 1,689.60/844.80/844.80) also follow from 0.88% with the $192,000 base.
    • One sentence ("compared in whole cents") predates the head's wage-limit logic; suggestion 4 covers it.
  • Queued is the right status. rulespec-us main (5796861e58) has only us-mn/statutes/290; there is no 268B module.

CI status

29 checks pass and 3 are skipping at head a19c04c83c (gh pr checks 10294, re-checked at the end of this review). Nothing is pending.

Branch status

Verdict: REQUEST_CHANGES

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minnesota's rate, the employer/employee split, the small-employer reduction, the wage limit and the float32 boundary all match 268B.14 and DEED, and all 22 new cases hand-check. One should-address item: seven new MN parameter descriptions don't follow the house template (rewrites are in the report). Five suggestions. Merge #10285 first; the merge with it and with main is clean. Full report in the comment above.

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

Rewrite the seven Minnesota parameter descriptions to the house template
and point each statute href at its subdivision; note that no 2027 state
average wage has been published; pin the flow case's unemployment rate
to 0 so it no longer depends on main's out-of-date Minnesota UI wage
base; say the average wage input is a quarterly amount, because the
small employer test compares it with a quarterly limit; and reuse the
'caps what the employer may deduct' wording in the invariants docstring.

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

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: bfab0c9abe9e3483a58d77113cc5877e8c8f8f83

Picked up at a19c04c83c (unchanged since 2026-10-20). One commit on top, bfab0c9abe.

main is not merged, because #10285 hasn't merged.

What the item asked for

Should address: the seven descriptions (house template, DTrim99's wording), line 1 of each under parameters/gov/states/mn/tax/payroll/paid_leave/:

  • rate.yaml: "Minnesota sets this premium rate on taxable wages for an employer in both the family and medical benefit programs under the Paid Leave program."
  • employer_minimum_share.yaml: "Minnesota sets this minimum share of the premium that employers pay under the Paid Leave program."
  • small_employer/employer_minimum_share.yaml: "Minnesota sets this minimum share of the full premium rate that small employers pay under the Paid Leave program."
  • small_employer/rate_share.yaml: "Minnesota sets the small employer premium rate at this share of the full premium rate under the Paid Leave program."
  • small_employer/max_headcount.yaml: "Minnesota limits the small employer premium rate to employers with at most this many employees under the Paid Leave program."
  • small_employer/max_average_wage_ratio.yaml: "Minnesota limits the small employer premium rate to employers whose average wage is at most this multiple of the state's average wage in covered employment under the Paid Leave program."
  • small_employer/state_average_annual_wage.yaml: "Minnesota uses this statewide average annual wage in covered employment for the small employer wage test under the Paid Leave program."

Suggestion 1: cost of the two reform tests, now in the PR body.

  • Setup. Local Linux VM, 4 vCPU, one process; Linux CI is not separately measured, because no CI has run at this head.
  • With them, test_payroll_contributions.py: 107 passed, 170.0 s of test time, 226.9 s wall, peak RSS 1.61 GB.
  • Without them (-k 'not (test_minnesota_paid_leave_shares_follow_the_parameters or test_minnesota_small_employer_wage_limit_rounds_half_up)'): 104 passed, 70.0 s, 124.0 s wall, 1.51 GB.
  • The difference. The three reformed systems add about 100 s of test time (about 33 s each) and 0.10 GB of peak memory, in the Python core group.

Suggestion 2: the 2027 note. small_employer/state_average_annual_wage.yaml:9-11. "No Department of Employment and Economic Development figure for 2027 premiums as of 2026-10-20, so the 2026 value carries forward; update it when the department issues 2027 small employer designations."

  • I couldn't reach mn.gov or pl.mn.gov from this session, so I couldn't re-check whether DEED has published since. The date is DTrim99's check, not mine.

Suggestion 3 and hub P3 (a): the stale UI wage base. mn_employer_paid_leave_contribution.yaml, "The employer premium flows into employer state and total payroll taxes", now sets employer_state_unemployment_tax_rate_override: 0.

Suggestion 4: rulespec-us#1819. Draft sentence for the hub to post, replacing "compared in whole cents":

The small employer wage limit is 150% of the statewide average annual wage divided by 4, rounded half up to the cent ($27,745.875 → $27,745.88 for 2026); the employer's average quarterly wage is compared with it unrounded, so $27,745.88 qualifies and $27,745.885 does not.

Optional companion row: mn_paid_leave_employer_average_quarterly_wage: 27,745.885, 10 employees, $100,000 → not small: premium 880, employee 440, employer 440.

Suggestion 5: text fixes.

Hub P3 (b): units of the subd. 5c input. mn_paid_leave_employer_average_quarterly_wage.py:16-20 now says: "Enter a quarterly amount, wages per employee for one quarter, whichever subdivision applies: mn_paid_leave_small_employer compares it with a quarterly limit, 150 percent of the state's average annual wage divided by 4."

  • What this rests on. The model, which I read: the small-employer test divides the annual average by 4 before comparing. It doesn't rest on the statute, because revisor.mn.gov is unreachable from this session.
  • What I need. The verbatim text of Minn. Stat. 268B.14 subd. 5c(b), to confirm that an estimate for a newly covered employer is also a per-quarter figure. Until then the statutory unit is unverified. The label already says "average quarterly wage".

Hub P3 (c): invariants docstring. test_state_payroll_premium_deduction_share_invariants.py:3-9 now uses employee_state_payroll_tax's "caps what the employer may deduct ... and leaves it to decide how much of that to deduct" wording. It is the same text as #10285's fix, so the later merge sees identical lines.

Tests run (one file per process)

  • mn_employer_paid_leave_contribution.yaml: 22 passed.
  • mn_employee_paid_leave_contribution.yaml: 3 passed.
  • state_payroll_premium_deduction_share.yaml: 16 passed.
  • test_payroll_contributions.py: 107 passed.
  • test_state_payroll_premium_deduction_share_invariants.py: 2 passed.
  • test_input_variable_definitions.py: 4 passed.
  • test_source_conflicts.py: not run. It doesn't exist on this branch, which predates it on main; it will run once main is merged.

Hand computation: the one changed expectation is above (440 / 8,132). No other value changed.

Mutants: none. The only non-text change is a test input.

CI

None at this head. The pull_request workflow didn't start, because the PR conflicts with main (GitHub mergeable_state: dirty). The last green run was at a19c04c83c, before #10380.

Judgment calls

  • main not merged, per the item's order rule, even though that leaves the PR without CI. Merging Let employers deduct less than the permitted state payroll premium, and apply Massachusetts' PFML cap swap from 2027 #10285's head would have brought main in by the back door.
  • Anchors. Each href takes the first subdivision its title cites. A title citing "subdivisions 5a(a)(1) and 5b" opens 5a. One href per document, per CLAUDE.md. I couldn't open the Revisor page to confirm the anchor ids; they follow DTrim99's list.
  • The quarterly-units sentence describes the model's comparison, which I read, not the statute, which I couldn't.
  • PR body. I also removed local review-file paths from the body.

Reply to DTrim99

Thanks for the review. Taking each item in turn:

  1. Should address, seven descriptions. All rewritten to your wording, line 1 of each mn/tax/payroll/paid_leave/ parameter.
  2. Suggestion 1, reform-test cost.
    • Measured locally and added to the body: +100 s of test time and +0.10 GB peak for the three reformed systems (170.0 s / 1.61 GB with them, 70.0 s / 1.51 GB without).
    • Linux CI numbers will follow once CI can run (see the merge note).
  3. Suggestion 2, 2027 note. Added at small_employer/state_average_annual_wage.yaml:9-11.
  4. Suggestion 3, MN UI values. The flow case now zeroes the UI rate and asserts 440 and 8,132, so it no longer pins main's $42,000 base. Correct Minnesota UI taxable wage bases for 2014-2026 #10319 covers the base itself.
  5. Suggestion 4, rulespec-us#1819. The replacement sentence is drafted above for the hub to post, with the optional $27,745.885 row.
  6. Suggestion 5, text fixes. Subdivision anchors added to the seven parameter hrefs, and the stale "current head" line fixed in the body.
  7. Branch. The merge of main waits for Let employers deduct less than the permitted state payroll premium, and apply Massachusetts' PFML cap swap from 2027 #10285, as you suggested. Since your review, Fix Social Security secondary earnings threshold reform #10380 landed on main and conflicts with this branch's copy of Let employers deduct less than the permitted state payroll premium, and apply Massachusetts' PFML cap swap from 2027 #10285's Delaware files.

Generated by Claude Code

Minn. Stat. 268B.14 subd. 5c(b) divides estimated wages for a tax year by
estimated employees, so that quotient is annual, and the input takes a
quarterly amount.

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

MaxGhenis commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor Author

Hub review in progress

Superseded by the final review: #10294 (comment)


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: APPROVE
eef658a
METHODOLOGY_CHOICE: yes

Delta review of a19c04c83c..eef658a1b9 (bfab0c9abe and eef658a1b9). The head hasn't moved past eef658a1b9. The delta changes 10 files: seven parameter descriptions and hrefs, one variable docstring, one test docstring and one YAML case. No formula or parameter value changes, so no output moves relative to a19c04c83c. I made no edits, commits or pushes.

Bottom line: every should-address item in DTrim99's review is resolved, and each of his five suggestions is taken or properly handed to the hub (details below). The hub can dismiss the CHANGES_REQUESTED review with a note. All findings are P3: PR-body text, the drafted rulespec sentence, and a follow-up that already exists on main.

Findings

P1: none. P2: none.

P3-1. The PR body has four stale or contradictory statements. A reader could rely on each of them.

P3-2. The body has no impact: line. It has an "## Impact" section with a queue line, and the axiom: line is present. The build assigned impact to the hub, so this is the hub's to add once the run is done.

P3-3. The sentence drafted for rulespec-us#1819 leaves out float32 precision. The hand-off's draft says the employer average "is compared with it unrounded". The code compares average_wage <= np.float32(max_average_wage) on a float32 input. So an average about $0.001 above the limit still qualifies: r1 measured $27,745.881 as small and $27,745.882 as not small. An encoder working in exact decimals would split those two cases differently. Every companion row uses whole cents or $27,745.885, so none of them differ. Suggested wording:

The small employer wage limit is 150% of the statewide average annual wage divided by 4, rounded half up to the cent ($27,745.875 → $27,745.88 for 2026). The employer's average quarterly wage is compared with it without rounding to the cent (PolicyEngine compares at float32 precision, about $0.002 near this limit), so $27,745.88 qualifies and $27,745.885 does not.

The optional row ($27,745.885, 10 employees, $100,000 → not small; premium 880, employee 440, employer 440) follows from the code. The YAML case at mn_employer_paid_leave_contribution.yaml:129-138 asserts only small_employer: false and the premium of 880. I don't post anything to rulespec-us; the hub does.

P3-4. Follow-up, same on main: the subd. 4 vs 268B.01 subd. 43 wage base isn't in the source-conflict register.

  • The conflict. The body describes it ("The two statutes differ here"), and the model follows subd. 43's rounded $185,000. The deciding line, mn_paid_leave_taxable_wages.py:19 (1_000 * np.floor(social_security_cap / 1_000 + 0.5)), dates from Add Minnesota Paid Leave employee payroll tax #8408 and is unchanged here.
  • Witness. grep -n 268B source_conflicts/candidates.yaml source_conflicts/known-source-conflicts.yaml on main finds nothing.
  • Why it matters. CONTRIBUTING.md#source-conflicts says a conflict found "already in the code" goes into the register. For code on main that the PR doesn't change, step 1 can be skipped and the entry pinned to a main commit.
  • Not this PR's defect. test_source_conflicts.py passes on the simulated post-merge tree (below). Suggested follow-up: a register entry us-mn:paid-leave-taxable-wage-base-rounding. Readings: subd. 4, unrounded, $184,500 for 2026; 268B.01 subd. 43 and DEED, rounded, $185,000. The definition canon favours subd. 43.

DTrim99's items

Item Status at eef658a1b9
Should address: 7 descriptions Resolved. Line 1 of each of the seven files matches his suggested rewrites word for word. Each is one sentence, starts "Minnesota sets/limits/uses", uses no acronyms and ends "under the Paid Leave program". test_parameter_yaml_keys.py passes (34).
S1: reform-test cost Resolved. It's in the body: 107 vs 104 tests, +100 s and +0.10 GB, measured locally. My run of the whole file at the head gave 107 passed, 92.5 s of test time, 128.9 s wall and 1.63 GB peak RSS. That's in line with the body's 1.61 GB, though slower than its time. I didn't run the -k not split. Linux CI hasn't run.
S2: 2027 note Resolved. state_average_annual_wage.yaml:9-11. The date 2026-10-20 is DTrim99's check; neither the author nor I could reach DEED to re-check it.
S3: UI wage base Resolved, and needed. See "The UI wage base" below.
S4: rulespec-us#1819 Drafted for the hub. It needs the float32 qualifier (P3-3).
S5: anchors Resolved. Each href takes the first subdivision its title cites (#stat.268B.14.1, .3, .5a), in the same #stat.<section>.<subd> form main already uses (taxable_wage_base.yaml: 268.035#stat.268.035.24). I couldn't open the Revisor to confirm the ids resolve. test_reference_urls.py passes (32).
S5: body "current head" line Partly resolved. It was updated to 7cb554e630 but is stale again (P3-1).
Hub P3 (a): stale UI case Resolved, as S3.
Hub P3 (b): units of the subd. 5c input Resolved, and eef658a1b9 is right. See below.
Hub P3 (c): invariants docstring Resolved. Lines 1-12 of the docstring are byte-identical to #10285's head 62a9f63e69 (diff prints nothing). They reuse employee_state_payroll_tax.py's "the law caps what the employer may deduct from wages and leaves it to decide how much of that to deduct" wording.

What the build asked for that the PR doesn't do:

The hand-off's reply to DTrim99 matches what changed.

Every should-address item is resolved, so the hub can dismiss DTrim99's CHANGES_REQUESTED (5591809822) with a note pointing to bfab0c9abe.

The UI wage base (mn_employer_paid_leave_contribution.yaml:187-202)

The override isolates the case and hides no defect:

  • 0 is a real rate, not "unset". employer_state_unemployment_tax_rate_override defaults to −1, and employer_state_unemployment_tax_rate.py:18 applies the override when override >= 0.
  • Mutant M5 confirms it. Changing >= to > (so 0 means "unset") fails exactly this case.
  • main now carries Correct Minnesota UI taxable wage bases for 2014-2026 #10319 (009a9b59b2), which sets the 2026 MN UI base to $44,000. On the simulated post-merge tree, a default MN worker on $100,000 in 2026 gets employer_state_unemployment_tax 396, employer_state_payroll_tax 836 and employer_payroll_tax 8,528. So the old expectations of 378, 818 and 8,510 would have failed after the merge.
  • The UI tax is covered elsewhere, in gov/states/mn/tax/payroll/unemployment/. Zeroing it here loses nothing this file should test.

Hand values (2026, $100,000): Social Security 6.2% × 100,000 = 6,200 (the 2026 cap is above $100,000); Medicare 1.45% = 1,450; FUTA 0.6% × 7,000 = 42; UI 0; MN employer premium 0.88% × 100,000 − 0.44% × 100,000 = 440. Total 6,200 + 1,450 + 42 + 0 + 440 = 8,132, and the employer state payroll tax is 440. Both match the YAML case and a direct run.

Subd. 5c(b) units (mn_paid_leave_employer_average_quarterly_wage.py:15-24)

  • What the docstring now says. A 5c(b) employer's estimate is "estimated covered wages for the following tax year" divided by "estimated employee count", an annual figure, to be divided by 4 for this input.
  • What I could check. I couldn't fetch the Revisor. A web-search summary of the 2024 statute describes 5c(b) the same way, but that isn't a verbatim source. The statutory wording stays unverified by me (see "What I couldn't check").
  • The rule is sound either way. Subd. 5a(a)(2) compares the employer's average with "150 percent of the state's average wage in covered employment" and gives no unit. Comparing an annual estimate with 1.5 × the annual state average is the same test as comparing estimate/4 with 1.5 × annual/4, which is the model's $27,745.88 limit. So "divide by 4" is exact, not a conversion choice.
  • Worked example. A new 10-employee firm expecting $500,000 in wages has an annual average of $50,000, or $12,500 a quarter.
    • Entered as $12,500: small, employer pays 220 on a $100,000 worker.
    • Entered as $50,000 (the misuse the old wording invited): large, employer pays 440.
    • Both are confirmed by a direct run on the post-merge tree. The new wording prevents the misuse.

The defect, at main

On origin/main (6145ad4513), a 2026 MN worker on $100,000 has employer_additional_state_payroll_tax = 0 (mn_employer_paid_leave_contribution doesn't exist there; it raises ValueError) and mn_employee_paid_leave_contribution = 440. So the employer half of the 0.88% premium is missing; the defect reproduces. On the simulated post-merge tree it is 440, and the employee side stays 440.

Merge with main (hand-off claim checked)

Mutants

Each mutant was run in its own process on a scratch worktree, then restored; git status --porcelain is empty afterwards. The hand-off lists no mutants for this round ("The only non-text change is a test input"). Its earlier harness lives on the hub's machine, so I re-ran the named half-to-even mutant and wrote four more.

Mutant Killed by
M1: half-to-even (np.round) limit test_minnesota_small_employer_wage_limit_rounds_half_up (1 failed)
M2: drop + 0.5 (truncate to $27,745.87) YAML "An average quarterly wage equal to the limit meets the small employer wage test."
M3: headcount < max_headcount YAML: 3 small-employer cases at 30 employees
M4: drop mn_employer_additional_state_payroll_tax from employer_additional_state_payroll_tax YAML: "…pays half the premium", "A small employer pays 75%…", "The employer premium flows into…"
M5 (mine): UI override >= 0 → > 0 YAML "The employer premium flows into employer state and total payroll taxes."

Readers

  • Parameters. Readers of the seven parameters, which changed text only:
    • mn_paid_leave_premium_rate and mn_paid_leave_permitted_employee_rate (rate, the shares);
    • mn_paid_leave_small_employer (max_headcount, max_average_wage_ratio, state_average_annual_wage);
    • test_payroll_contributions.py's reforms.
  • mn_paid_leave_employer_average_quarterly_wage. Read by mn_paid_leave_small_employer, the invariants test, the YAML and test_payroll_contributions.py.
  • No reader is worse. The delta changes no formula, so no reader becomes worse. The whole PR changes only MN employer-side outputs at baseline from 2026, as the body says.
  • Population check. The hub's impact run should confirm that mn_employer_paid_leave_contribution ≈ 0.44% of capped MN wages at the defaults, and that household_net_income doesn't move.

Checked and sound

  • Scope.

  • Tests, one file per process with thread caps at 4; every changed test file passes in both runs.

    File Head Post-merge
    mn_employer_paid_leave_contribution.yaml 22 22
    mn_employee_paid_leave_contribution.yaml 3 3
    state_payroll_premium_deduction_share.yaml 16 16
    test_state_payroll_premium_deduction_share_invariants.py 2 (0.83 GB) 2 (1.43 GB)
    test_input_variable_definitions.py 4 4
    test_payroll_contributions.py 107 (1.63 GB) 107 (2.08 GB)
    test_source_conflicts.py n/a 103
    test_reference_urls.py n/a 32
    test_parameter_yaml_keys.py n/a 34

    The YAML files peaked at 2.0–2.4 GB. My first head run of the invariants file failed at collection because hypothesis wasn't installed in this VM's env; it passed after uv sync --extra dev.

  • CI. None has run at bfab0c9abe or eef658a1b9: the actions runs API returns total_count 0 for eef658a1b9, because the PR conflicts with main.

  • Axiom line. It is present and unchanged from the round DTrim99 found valid.

What I couldn't check

  • Sources. revisor.mn.gov, mn.gov, pl.mn.gov, uimn.org and content.govdelivery.com were all refused by this VM's proxy and DNS (curl CONNECT 403, WebFetch ENOTFOUND). So I couldn't fetch:

    • Minn. Stat. 268B.14 subds. 5a–5c verbatim;
    • DEED's 2026 and 2027 wage figures;
    • whether DEED has issued a 2027 small-employer limit since 2026-10-20;
    • whether the #stat.268B.14.* anchor ids resolve.

    These hosts must be allowed in the session's network settings for a later check. The source facts in this review rest on the earlier reviews' fetches and on the code.

  • Linux CI timing and memory for the reform tests, and CI as a whole: none can run until Let employers deduct less than the permitted state payroll premium, and apply Massachusetts' PFML cap swap from 2027 #10285 lands.

  • The impact run, which is the hub's.


Generated by Claude Code

MaxGhenis and others added 3 commits November 9, 2026 08:24
Resolve the two conflicts with the squashed payroll deduction-share change.
The input's documentation now names New York alone as the state whose
employer cost goes unmodeled, because this branch models Minnesota's. The
property test keeps main's workers on each side of the employer-size
thresholds, each with an average quarterly wage of 0, beside this branch's
Minnesota workers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the review's survivors

- source_conflicts/candidates.yaml records two ruled items.
  us-mn:paid-leave-small-employer-average-wage: the model takes subd. 5c(a)'s quotient, as DEED's designation notice does, where the UIMN page divides it by four again.
  us-mn:paid-leave-small-employer-wage-limit-cents: the model rounds the 2026 limit half up to DEED's $27,745.88.
- test_payroll_contributions.py checks the employer-level total under the shares reform, the statutory split in 2025 when a reform sets only the rate, and a 2027 limit that rounding up would raise to $27,745.89.
- mn_employer_paid_leave_contribution.yaml adds three 2025 cases for the small employer test, which reads the 2026 limits.
- The removed.md changelog names every parameter that replaces employee_rate.

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

Add captures of the three texts the two us-mn:paid-leave-small-employer items
cite: pages 1 and 2 of DEED's small employer designation notice (pdftotext),
the text of Unemployment Insurance Minnesota's small employer page (pandoc),
and Minn. Stat. 268B.14 as fetched. Keep only the UIMN page's text, because
mn.gov adds a bot-check token to the page it serves. In the two items, DEED
and UIMN act in their own documents, UIMN gets its full name once, and the
examples name the employer's covered wages. The changelog names employee_rate
in place of an unclear "it".

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

Copy link
Copy Markdown
Contributor Author

David, thanks for the review. I've addressed each point, so I dismissed the review to merge this PR. Here's where each one stands at e3966ea7a7:

An independent AI agent reviewed eef658a1b9 and confirmed each fix except two I finished later: the rulespec-us#1819 sentence and the stale #10285 head line. I merged main after #10285 landed (2c662e9e50). Since then, I've changed only tests, the changelog, source-conflict backlog items and source captures (780a23ecca, e3966ea7a7), so each fix above still stands.

🤖 Generated with Claude Code

@MaxGhenis
MaxGhenis merged commit f2bd9dc into main Nov 9, 2026
37 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

US + core hub merge audit, us#10294 at e3966ea7a7bf1c02890e849253b780b2465410fa (squash): Model the Minnesota Paid Leave employer premium.

🤖 Generated with Claude Code

@MaxGhenis
MaxGhenis deleted the mn-employer-paid-leave-premium branch November 9, 2026 21:24
MaxGhenis added a commit that referenced this pull request Nov 9, 2026
Main's Minnesota Paid Leave premium (#10294) and its premium deduction
share (#10285) touched the same employer payroll aggregates as this
branch. The employer aggregates now list Minnesota's and New Jersey's
components side by side, and employee_state_payroll_tax's documentation
names Alaska UI among the contributions the statute puts on the worker
and makes the employer withhold (AS 23.20.165(c)).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

3 participants