Skip to content

Compare the Wisconsin Works CMC infant age limit with the child's age in weeks - #10259

Merged
PavelMakarchuk merged 5 commits into
mainfrom
fix-9815-wi-works-cmc-age
Oct 27, 2026
Merged

PavelMakarchuk merged 5 commits into
mainfrom
fix-9815-wi-works-cmc-age

Conversation

@PavelMakarchuk

@PavelMakarchuk PavelMakarchuk commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

wi_works_placement gave the Custodial Parent of an Infant (CMC) placement when a member's monthly_age was at or below cmc_infant_age_limit (2, unit: month). monthly_age is the age in years evaluated for a month, so any child aged 0, 1 or 2 years gave the unit the CMC placement and the $673 grant instead of the $653 Community Service Job (CSJ) grant.

Wis. Stat. § 49.148(1m)(a)1 gives the CMC grant to "a custodial parent of a child 8 weeks old or less", and W-2 Manual 7.4.3 ends the placement 8 weeks (56 days) after the birth. Before January 1, 2012 the limit was 12 weeks: 2011 Wis. Act 32, SECTION 1361s, struck "12" and inserted "8", and SECTION 9408 (1d) (file page 530) made that change take effect on January 1, 2012. The 1997-98 and 2009-10 statutes both read "12 weeks old or less".

Changes:

  • Parameter. cmc_infant_age_limit is now in weeks: 12 from 1997-09-01 (the W-2 start date that amount.yaml uses) and 8 from 2012-01-01. It was a 2-month approximation from 2011-10-01, with no value before that date. The W-2 Manual reference title now says 7.4.3 (it said 7.4.5), and the 1997-98 statutes and Act 32 are cited.
  • Formula. It compares the limit with age * WEEKS_IN_YEAR. With 52 weeks a year, a child of 56 days is 7.97 weeks (CMC) and a child of 57 days is 8.12 weeks (CSJ), the same split as Manual 7.4.3.4. With an age in whole years, age 0 still gets CMC (the model cannot tell an infant of 8 weeks or less from an older infant); the code comment now says so.

Reproduction from the issue (January 2025): a child aged 1 or 2 now gives CSJ and $653 (was CMC and $673); age 0 stays CMC and $673; age 3 stays CSJ and $653.

Tests

  • wi_works_placement.yaml: the cases set age in years instead of monthly_age. New cases: 56 days (age: 0.1533, CMC) and 57 days (age: 0.1561, CSJ); 13 weeks, 1 year and 2 years (CSJ); and for the 12-week rule, a 10-week-old in January 2011 (CMC) and January 2012 (CSJ), and 84 days (CMC) and 85 days (CSJ) in January 2011. The boundary ages sit at least 0.028 weeks from the limit, so float32 rounding cannot flip them.
  • integration.yaml: new Case 12, a 1-year-old gives CSJ and $653; the age comments in Cases 2, 9 and 12 use weeks.
  • On main, 6 of these cases fail (placement Cases 5, 6, 7, 8 and 10, integration Case 12). With the earlier 2-month version of this PR, the 57-day case fails. policyengine-core test on states/wi/dcf/works: 67 passed.

The pre-2012 cases use January periods on purpose. In local runs with monthly test periods outside January (2011-11, 2012-02, 2012-12 and 2013-06), the YAML runner did not apply unkeyed inputs of yearly variables such as age and state_code (they fell back to 40 and CA), so a CSJ expectation would pass by default. January periods (2011-01, 2012-01, 2013-01, 2014-01, 2025-01) applied them.

Out of scope: the statute also denies CMC when another adult in the W-2 group works or is W-2 eligible, and limits ARP to an unmarried woman who is not a custodial parent. #10493 tracks both.

axiom: TheAxiomFoundation/rulespec-us#1948 queued

Fixes #9815

🤖 Generated with Claude Code

wi_works_placement compared the 2-month CMC limit (8 weeks, Wis. Stat.
49.148(1m)(a)1) with monthly_age, which is the age in years. Children aged
1 and 2 got the CMC placement and the $673 grant instead of the $653 CSJ
grant. Use age x 12, as the state child care age-group variables do, and
set age in the placement tests instead of monthly_age.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PavelMakarchuk
PavelMakarchuk marked this pull request as ready for review October 23, 2026 17:03
@codecov

codecov Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codecov Report

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

Additional details and impacted files
@@            Coverage Diff            @@
##              main    #10259   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            4         1    -3     
  Lines           76        27   -49     
  Branches         2         0    -2     
=========================================
- Hits            76        27   -49     
Flag Coverage Δ
unittests 100.00% <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.

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

DTrim99 commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Compare the Wisconsin Works CMC infant age limit with the child's age in months

Summary

The PR fixes #9815. Before it, wi_works_placement gave the Custodial Parent of an Infant (CMC) placement when a member's monthly_age was at or below cmc_infant_age_limit (2, unit: month). But monthly_age holds the age in years:

  • age is a float with no unit, so core treats it as a flow.
  • Reading it for a month divides the annual value by 12, and monthly_age multiplies by 12 again.

So every child aged 0, 1 or 2 put the unit in CMC, with $673 instead of the $653 CSJ grant. The PR makes two changes:

  • Formula. It compares the limit with person("age", period.this_year) * MONTHS_IN_YEAR, the same pattern as the AK, KS, KY, AL and DC child care age-group variables.
  • Tests. The placement cases now set age instead of monthly_age.

The fix is right under current law:

  • The law. Wis. Stat. § 49.148(1m)(a)1 gives the $673 grant to "a custodial parent of a child 8 weeks old or less". W-2 Manual 7.4.3.4 ends the placement on the 56th day. The 8-week limit has applied since January 1, 2012 (2011 Wis. Act 32). Before that it was 12 weeks.
  • The comparison. <= matches "8 weeks old or less". A child of 56 days is 1.84 months old, which gives CMC.

Other checks:

  • Tests. I hand-checked 12 expectations, and all are correct. The formula is vectorized, and it adds no state-gated key variable.
  • CI. 29/29 checks pass.
  • Branch. It merges cleanly with main.

One thing needs fixing: the PR body has no axiom: line.

The suggestions are optional:

  • add test cases close to 8 weeks on each side;
  • encode the limit in weeks, with the 12-week limit before 2012, and fix the manual section in the reference title;
  • say in the code that age 0 always gives CMC when ages are whole years;
  • open a follow-up issue for the "other adult" exclusion in § 49.148(1m)(a)1 (pre-existing).

Critical (must fix)

None.

Should address

1. Add the axiom: line to the PR body.

This is a policy fix, because it changes the placement and the grant for children aged 1 and 2. The body has no line starting with axiom in any format, and no axiom text anywhere. Pavel is staff, so the line is required, and axiom: needed isn't available. See "Axiom line assessment" for the recommended queued line.

Suggestions

1. Test close to the 8-week boundary on each side.

The new cases test 0 months, 1.5 months (age: 0.125) and 3 months (age: 0.25). The old "exactly 2 months" case is gone, so the nearest cases are 0.5 and 1 month away from the limit.

An age of exactly 2 months is fragile, because age is stored as float32:

  • age: 0.16666667 × 12 gives 2.0, which is CMC.
  • age: 0.1666667 × 12 gives 2.0000005, which is CSJ.

So use ages a little way from the limit, for example:

- name: Case 4, CMC with an infant about 7.8 weeks old.
  period: 2025-01
  input:
    people:
      person1:
        age: 25
      person2:
        age: 0.15
    households:
      household1:
        members: [person1, person2]
        state_code: WI
  output:
    # 0.15 years x 12 = 1.8 months <= 2 (about 55 days, within 8 weeks)
    wi_works_placement: CMC

- name: Case 5, CSJ with an infant about 8.9 weeks old.
  period: 2025-01
  input:
    people:
      person1:
        age: 25
      person2:
        age: 0.17
    households:
      household1:
        members: [person1, person2]
        state_code: WI
  output:
    # 0.17 years x 12 = 2.04 months > 2 (about 62 days, past 8 weeks)
    wi_works_placement: CSJ

2. Encode the limit in weeks, with its history, and fix the reference title.

Before this PR the parameter never bound, because years were compared with 2. Now it decides the placement, so three pre-existing details in placement/cmc_infant_age_limit.yaml start to matter:

  • Effective date. The file has a single value, 2011-10-01: 2, which also carries back to earlier years.
    • 2011 Wis. Act 32, SECTION 1361s (file page 226), amended (1m)(a)1 to read "A custodial parent of a child 12 8 weeks old or less".
    • SECTION 9408 (1d) (file page 530), "REDUCTION IN CHILD'S AGE FOR CARETAKER OF NEWBORN GRANT", says: "The treatment of section 49.148 (1m) (a) 1. of the statutes takes effect on January 1, 2012."
    • The cut of the CSJ grant from $673 to $653 took effect earlier, on October 1, 2011 (SECTION 9408 (3i), same page).
    • So from October to December 2011, an infant with a fractional age of 61-84 days (about 9 to 12 weeks) now gets CSJ at $653, where the law gives CMC at $673. Before October 2011 both grants were $673, so the placement didn't change the amount.
  • Approximation. 2 months is about 60.9 days, but the placement ends on day 56 (Manual 7.4.3.4: "eight calendar weeks (7 days x 8 weeks = 56 days) ... CMC cannot extend beyond the date the child turns 8 weeks of age"). So ages of 57-60 days still give CMC. For example, age: 0.16 (about 58 days) gives 1.92 months, which is CMC.
  • Title. The second reference's title says "W-2 Manual 7.4.5", but its href and the page itself are 7.4.3.

Suggested parameter:

description: Wisconsin limits the Custodial Parent of an Infant placement to custodial parents of children at or below this age in weeks under the Wisconsin Works program.

values:
  1997-09-01: 12
  2012-01-01: 8

metadata:
  unit: week
  period: month
  label: Wisconsin Works CMC infant age limit
  reference:
    - title: Wisconsin Statutes 49.148(1m)(a)1
      href: https://docs.legis.wisconsin.gov/statutes/statutes/49/iii/148
    - title: "2011 Wisconsin Act 32, sections 1361s and 9408(1d) #page 226,530"
      href: https://docs.legis.wisconsin.gov/2011/related/acts/32.pdf#page=226
    - title: W-2 Manual 7.4.3 - Custodial Parent of an Infant (CMC)
      href: https://dcf.wisconsin.gov/manuals/w-2-manual/Production/07/7.4.3_Custodial_Parent_of_an_Infant_CMC_.htm

The 12 is the text that Act 32 struck. The 1997-09-01 start matches amount.yaml.

In the formula, use age_in_weeks = person("age", period.this_year) * WEEKS_IN_YEAR. WEEKS_IN_YEAR is 52 and is already exported by model_api. Some checks:

  • 56 days → 7.97 weeks, which gives CMC.
  • 57 days → 8.12 weeks, which gives CSJ.
  • 84 days → 11.96 weeks, which gives CMC under the 12-week rule.

Why this is only a suggestion:

  • Whole-year ages, which is all microdata and most household inputs, give the same result either way.
  • programs.yaml doesn't claim 2011 (TANF is verified from 2021).
  • The parameter file and the PR body both document the 2-month approximation.

3. Say in the code that age 0 always gives CMC.

The PR body says that with ages in whole years, age 0 still gets CMC. In the microdata every age-0 child is 0-11 months old, so every Wisconsin Works unit with one gets $673. Only about 8 of every 52 weeks of that year qualify. The code comment doesn't say this. Something like this would help:

# With age in whole years (as in the microdata), every child aged 0
# counts as 8 weeks old or less, so age 0 always gives CMC.

4. Open a follow-up issue for the "other adult" exclusion (pre-existing, outside the diff).

§ 49.148(1m)(a)1 continues: "unless another adult member of the custodial parent's Wisconsin Works group is participating in, or is eligible to participate in, a Wisconsin Works employment position or is employed in unsubsidized employment". Manual 7.4.3.1 adds: "If another adult in the W-2 Group is working in unsubsidized employment or is eligible for W-2 participation, regardless of the W-2 Group's income, the family is not eligible for CMC."

has_infant doesn't check for another adult. Take a two-parent unit like integration Case 8 (one parent earning $1,500), but with a newborn:

  • The head. It gives CMC and $673.
  • The manual. It treats the family as a two-parent family instead (the CSJ grant is $653).

The ARP branch is similar. Para. (a)2 requires "an unmarried woman ... not a custodial parent of a dependent child", but the formula checks only the pregnancy month. Neither gap is changed by this PR.

Verified correct

  • Root cause.
    • How core reads age for a month. age is value_type = float with no unit, so its quantity_type defaults to FLOW. Core's YEAR-to-MONTH branch (calculate_divide) returns the annual value / 12, and monthly_age multiplies by 12, so it returns years.
    • Corroboration. The issue's reproduction shows monthly_age = 1 and 2 for ages 1 and 2 in releases 1.811.2 and 2.25.2. nv_ccdp_age_group.py notes the same: "monthly_age ... returns age in YEARS".
    • Effect. The old formula compared years with a limit in months.
  • The fix. person("age", period.this_year) * MONTHS_IN_YEAR is the pattern in ak_ccap_age_group, ks_ccap_*_age_group, ky_ccap_child_age_category, al_ccsp_age_category and dc_ccsp_age_category, as the body says, and in many other state child care age-group variables.
  • The law and amounts.
    • Statute. § 49.148(1m)(a): "Any of the following may receive a monthly grant of $673: 1. A custodial parent of a child 8 weeks old or less ...". § 49.148(1)(b)1 gives the CSJ grant as "a monthly grant of $653".
    • Manual. W-2 Manual 7.4.3: "A custodial parent of an infant 8 weeks old or younger ... may receive a monthly CMC payment of $673."
    • LFB. LFB Informational Paper 47 (January 2023), file page 17: "a custodial parent of a child who is eight weeks old or younger is eligible to receive a monthly grant of $673". Its table on file page 12 lists "Community Service Job $653" and "Caretaker of Infant 673".
    • The model. amount.yaml has CSJ 653 (from 2011-10-01) and CMC 673.
  • Claimed years. The 8-week limit applies to every year from 2012, including all of TANF's verified years (2021 on). Wis. Stat. 49.148's history note lists only 2011 Acts 32 and 257 and 2013 Acts 20 and 165 since 2009, and the current text still says 8 weeks.
  • Boundary. <= matches "8 weeks old or less" and the manual's "cannot extend beyond the date the child turns 8 weeks of age". In float32, 56 days is 0.15332 years, which × 12 = 1.84 months, so it gives CMC. 61 days gives 2.004 months, which is CSJ (see Suggestion 2 for days 57-60).
  • Vectorization and code.
    • The age product is an array operation, followed by spm_unit.any and select, with no Python branching on values.
    • defined_for = StateCode.WI is unchanged.
    • No new int or enum key variable is added, and the placement default (CSJ) is a key in amount.yaml, so the NH Add New Hampshire state supplementation for 2026 #10156 pattern doesn't apply.
  • Callers.
    • Only wi_works reads wi_works_placement, through p.amount[placement]. monthly_age itself is unchanged.
    • Other monthly_age readers with month-based limits already convert: ME CCAP and WA WCCC multiply by 12, and NV CCDP uses this PR's pattern.
  • Hand-checked expectations (12):
    • wi_works_placement.yaml:
      • Case 1: child 5 → 60 months, not pregnant → CSJ.
      • Case 2: age 0 → 0 ≤ 2 → CMC.
      • Case 3: 0.125 × 12 = 1.5 ≤ 2 → CMC.
      • Case 4: 0.25 × 12 = 3 > 2 → CSJ.
      • Case 5: 1 × 12 = 12 → CSJ.
      • Case 6: 2 × 12 = 24 → CSJ.
      • Case 7: pregnancy month 7 ≥ 7 → ARP.
      • Case 8: month 6 < 7 → CSJ.
      • Case 9: infant aged 0 plus a pregnancy in month 8 → CMC first in select → CMC.
      • Without spm_units, core puts all members in one default SPM unit, so spm_unit.any sees the child.
    • integration.yaml:
      • Case 2 (age 0, $1,000 earnings, $500 cash): eligible, CMC, 673.
      • Case 9 (age 3): 36 months → CSJ, 653.
      • Case 12 (age 1, no income, $500 cash, below the $2,500 limit): eligible, 12 months → CSJ, 653 (CSJ from 2011-10-01).
    • "4 new cases fail on main". On main, monthly_age would be 0.25, 1, 2 and 1, all ≤ 2, giving CMC. So placement Cases 4, 5 and 6 and integration Case 12 fail on main. Cases 3 (0.125) and 2/9 (0) pass on both.
  • PR body.
    • The quotes from § 49.148(1m)(a)1 and the Manual 7.4.3 (56 days) are accurate.
    • "54 passed" was the folder's count at the first commit. The merge of main added 7 wi_works_eligible cases, so there are 61 at the head, and all pass in CI.
  • Changelog. changelog.d/fix-9815-wi-works-cmc-age.fixed.md is a top-level fragment with the fixed type, and its text matches the change.
  • Source-conflict ratchet (Tie source-conflict markers to the register shared with Axiom #10287).
    • New comment. The one new comment in variables/ ("The limit is in months; monthly_age is in years, so convert age.") matches neither CONFLICT_COMMENT_RE nor ATTEMPT_RE.
    • Not scanned. Test YAML isn't scanned.
    • Register. No WI Works file is in KNOWN_UNMARKED, the register or candidates.yaml.
    • Main's changes. Main's only change to the test since the merge base edits unrelated rows.
  • Partner tests. No files under tests/policy/baseline/partners/** are touched, and Household API Partners passes.

Axiom line assessment

The body has no axiom: line (Should 1). encoded-correct isn't available:

Recommended line. File a dispatch-ready pe-parity issue (modeled on rulespec-us#1920 or #1467) and use axiom: TheAxiomFoundation/rulespec-us#<n> queued. The issue should have:

  • Module. Provisionally us-wi/statutes/49/148.yaml, following us-ia/statutes/422/12.yaml, for § 49.148(1m)(a)1 (and (1)(b)1 for the CSJ grant if it is in scope).
  • Corpus citation and blocker. Ingest Wis. Stat. 49.148, 2011 Wis. Act 32 (SECTION 1361s and SECTION 9408 (1d), file pages 226 and 530) and W-2 Manual 7.4.3. Then get approval for the billed encoder run.
  • Law, verbatim. § 49.148(1m)(a)1 (including the "unless another adult member ..." clause), the Act 32 strike-through ("12 8 weeks") and its January 1, 2012 effective date, and Manual 7.4.3.4 ("7 days x 8 weeks = 56 days").
  • Required outputs:
    • wi_w2_cmc_eligible: custodial parent of a child at most 8 weeks old (12 weeks before January 1, 2012), with no other W-2 group adult working or W-2 eligible;
    • the monthly grant: $673 for CMC, against $653 for CSJ (statute (1)(b)1; LFB table).
  • Companion tests (from the statute, the manual and the LFB paper, not PolicyEngine output):
    • Manual 7.4.3.1 Example 4: a 5-week-old baby, and the other adult is on SSDI and not W-2 eligible → CMC, $673.
    • A child 56 days old → CMC. A child 57 days old → not CMC (Manual 7.4.3.4).
    • Manual 7.4.3.1 Example 2: a 3-week-old baby, and the spouse works in unsubsidized employment → not CMC.
    • A 10-week-old in November 2011 → CMC under the 12-week rule. A 10-week-old in February 2012 → not CMC.
    • The CSJ grant is $653 and the CMC grant is $673 (LFB Paper 47, file page 12).

CI status

All 29 checks pass at 460fb5859e (gh pr checks 10259). The workflow run on that SHA was created 2026-10-23 18:44 UTC, after #10287 and #10334. That includes:

  • the Baseline states shards 1-4;
  • the Contrib shards;
  • Household API Partners;
  • PR validation;
  • Microsimulation;
  • the Python Rest shards;
  • Quick Feedback;
  • codecov.

None is pending or failing.

Branch status

The branch is 2 commits ahead of main (the fix on 2026-10-16 and a merge of main on 2026-10-23) and 48 behind. git merge-tree --write-tree PolicyEngine/main 460fb5859e merges cleanly. Since the merge base, main hasn't changed any of these:

  • Wisconsin Works variables, parameters or tests;
  • age or monthly_age.

No other open PR touches wi/dcf/works or monthly_age.

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.

The fix is right: monthly_age is in years, so 1- and 2-year-olds wrongly got CMC $673, and comparing age x 12 against the 2-month limit matches 49.148(1m)(a)1 ('8 weeks old or less') from 2012. All 12 expectations hand-check. One should-address item: there's no axiom line, so please file a pe-parity issue for us-wi/statutes/49/148 and mark it queued. Four suggestions, including near-boundary tests (exactly 1/6 of a year is fragile in float32) and encoding the limit in weeks. Full report in the comment above.

…tory

Wis. Stat. 49.148(1m)(a)1 limits the Custodial Parent of an Infant
placement to a child "8 weeks old or less". The limit was 12 weeks from
the start of W-2 until 2011 Wis. Act 32 (SECTION 1361s) cut it to 8
weeks from January 1, 2012 (SECTION 9408 (1d)). The parameter was a
2-month approximation from 2011-10-01, so ages of 57-60 days still
gave CMC, and the 12-week rule before 2012 was missing.

- Set the limit to 12 weeks from 1997-09-01 and 8 weeks from
  2012-01-01, with unit week. Fix the W-2 Manual section in the
  reference title (7.4.3, not 7.4.5) and cite the 1997-98 statutes and
  Act 32.
- Compare the limit with age x WEEKS_IN_YEAR. Note that age 0 always
  gives CMC when ages are whole years.
- Add boundary cases at 56 and 57 days (8-week rule) and at 84 and 85
  days (12-week rule, 2011), and a 10-week-old in January 2011 (CMC)
  and January 2012 (CSJ). The ages sit away from the limit, so float32
  rounding cannot flip them.

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

Copy link
Copy Markdown
Collaborator Author

Thanks @DTrim99. All items are addressed in b03f836. Disposition of each:

Should address

1. axiom: line — fixed. No rulespec-us issue covered W-2 (searched "Wisconsin Works", "49.148", "W-2 Wisconsin", "custodial parent", "9815", "10259"), so I filed TheAxiomFoundation/rulespec-us#1948 (pe-parity). It has the module (us-wi/statutes/49.148.yaml, the form that rulespec-us#1726 uses for 71.52), the verbatim law ((1m)(a)1 with the "unless another adult member ..." clause, (1)(b)1, the Act 32 strike-through and SECTION 9408 (1d), Manual 7.4.3.1 and 7.4.3.4), the required outputs and companion tests from the Manual examples and the Act, not from PolicyEngine output. Blocker recorded there: chapter 49 and the W-2 Manual are locked on axiom-corpus main but not in the pinned release us-rulespec-2026-10-25-mt-separate-filing; Act 32 and the pre-2012 statutes are not in any lock; and the billed encoder run needs approval. The body now has axiom: TheAxiomFoundation/rulespec-us#1948 queued.

Suggestions

1. Near-boundary tests — done. I used ages from the Manual's day-56/57 split rather than 0.15/0.17, because with the limit in weeks those are the cases that tell the rules apart:

  • Case 4: age: 0.1533 (56 days) × 52 = 7.97 weeks → CMC.
  • Case 5: age: 0.1561 (57 days) × 52 = 8.12 weeks → CSJ. With the 2-month limit this was CMC (1.87 months), and it fails on the previous head.
  • Cases 11 and 12 (January 2011): age: 0.23 (84 days) = 11.96 weeks → CMC; age: 0.233 (85 days) = 12.12 weeks → CSJ.

Every boundary age is at least 0.028 weeks from the limit, so float32 rounding cannot flip it.

2. Limit in weeks with its history, and the reference title — done. I checked the sources before the change:

  • 2011 Wis. Act 32, SECTION 1361s (file page 226) reads "a child 12 8 weeks old or less", and SECTION 9408 (1d) (file page 530) sets January 1, 2012.
  • The 1997-98 statutes (49.pdf, file page 19, including the pre-2-1-99 note) and the 2009-10 statutes (file page 18) both read "12 weeks old or less", so 12 weeks applies from the W-2 start.

cmc_infant_age_limit.yaml is now 1997-09-01: 12, 2012-01-01: 8, unit: week, with your suggested description and references plus the 1997-98 statutes. The title now says W-2 Manual 7.4.3. The formula uses age * WEEKS_IN_YEAR. New cases 9 and 10: a 10-week-old (age: 0.19, 9.88 weeks) gets CMC in January 2011 and CSJ in January 2012. One more effect: the old parameter had no value before 2011-10-01 (p(...) returned None), and the new 1997 value fills that gap.

Test gotcha: the pre-2012 cases use January periods on purpose. In local runs with periods outside January (2011-11, 2012-02, 2012-12, 2013-06), the YAML runner did not apply unkeyed inputs of yearly variables such as age and state_code (they fell back to 40 and CA), so a November 2011 CSJ case would pass by default. I verified with -v that every 2011 and 2012 case reads WI and the infant's age.

3. Comment that age 0 always gives CMC — done. The formula comment now says: "With age in whole years (as in the microdata), every child aged 0 counts as at or below the limit, so age 0 always gives CMC."

4. Follow-up issue for the "other adult" exclusion — done. Filed #10493. It covers the CMC other-adult exclusion and the ARP "unmarried woman ... not a custodial parent of a dependent child" condition, with your Case 8 example and Manual 7.4.3.1 Examples 1-4. rulespec-us#1948 links it.

Checks

  • policyengine-core test on states/wi/dcf/works: 67 passed (61 before plus 6 new). On main, 6 of the cases fail (placement Cases 5, 6, 7, 8 and 10, integration Case 12).
  • Code-health: test_source_conflicts.py, test_parameter_yaml_keys.py, the reference URL tests and parameters.py pass (258 passed). The new formula comment matches neither CONFLICT_COMMENT_RE nor ATTEMPT_RE.
  • make format changed no other files. The changelog fragment now describes the weeks limit and its 12-to-8 history. No partner contract test files are touched.
  • The PR title now says "in weeks".

@DTrim99

DTrim99 commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

PR Review (round 2): Compare the Wisconsin Works CMC infant age limit with the child's age in weeks

Summary

Since round 1 (460fb5859e), Pavel has pushed one commit, b03f836775. It addresses every prior item:

  • Axiom line. The body now has axiom: TheAxiomFoundation/rulespec-us#1948 queued. The issue is OPEN, has the pe-parity label, is dispatch-ready and matches the head.
  • Limit in weeks. cmc_infant_age_limit is now 1997-09-01: 12, 2012-01-01: 8, unit: week. The formula compares it with age * WEEKS_IN_YEAR (52).
  • References. The W-2 Manual title now says 7.4.3, and the parameter cites the 1997-98 statutes and 2011 Wis. Act 32.
  • Boundary tests. New cases cover 56 and 57 days (8-week rule), 84 and 85 days (12-week rule), and a 10-week-old in January 2011 and in January 2012.
  • Age-0 comment. The formula comment now says that age 0 always gives CMC when ages are whole years.
  • Follow-up issue. Wisconsin Works: CMC ignores the other-adult exclusion and ARP ignores the unmarried, no-dependent-child condition (Wis. Stat. 49.148(1m)(a)) #10493 tracks the other-adult and ARP gaps.

I checked the head against the law:

  • Current rule. Wis. Stat. § 49.148(1m)(a)1 gives the grant to "a custodial parent of a child 8 weeks old or less". Wis. Admin. Code DCF 101.18(2)(a) says the same: "a child who is 8 weeks old or less". W-2 Manual 7.4.3.4 ends CMC on day 56.
  • Earlier rule. The 1997-98 and 2009-10 statutes read "12 weeks old or less". Act 32 moved the limit to 8 weeks on January 1, 2012.
  • The boundary. With 52 weeks a year, the cut falls at 56.2 days, so day 56 gives CMC and day 57 gives CSJ. The 12-week cut falls at 84.3 days, between days 84 and 85.
  • Hand checks. I hand-checked all 17 new or changed expectations, and every one is correct.

Other checks:

I have one optional suggestion: correct the PR body's count of cases that fail on main.

Prior findings

Finding Status Evidence
Should 1: add the axiom: line fixed The body ends with axiom: TheAxiomFoundation/rulespec-us#1948 queued. The issue is OPEN, labelled pe-parity, and dispatch-ready (see "Axiom line assessment").
Suggestion 1: test close to the 8-week boundary on each side fixed Placement Case 4 uses age: 0.1533 (56 days), which gives 7.97 weeks and CMC. Case 5 uses 0.1561 (57 days), which gives 8.12 weeks and CSJ. For the 12-week rule, Cases 11 and 12 use 0.23 (84 days, 11.96 weeks, CMC) and 0.233 (85 days, 12.12 weeks, CSJ). These ages are better than the 0.15 and 0.17 I suggested, because they separate the 8-week rule from the old 2-month one. Every boundary age is at least 0.028 weeks from the limit (float32 checked below).
Suggestion 2: limit in weeks, 12 weeks before 2012, reference title fixed The parameter is now 1997-09-01: 12, 2012-01-01: 8, unit: week, with the description I suggested. Its references are § 49.148(1m)(a)1, the 1997-98 statutes (file page 19, checked: "a child who is 12 weeks old or less", both before and after the 2-1-99 amendment), Act 32 ("... #page 226,530", quoted, with the href on 226, as CLAUDE.md requires) and "W-2 Manual 7.4.3". The formula uses person("age", period.this_year) * WEEKS_IN_YEAR. WEEKS_IN_YEAR is 52, from policyengine_core.commons.formulas, which model_api exports. The variable's reference tuple adds the 7.4.3 URL.
Suggestion 3: say in the code that age 0 always gives CMC fixed The comment now reads: "With age in whole years (as in the microdata), every child aged 0 counts as at or below the limit, so age 0 always gives CMC."
Suggestion 4: follow-up issue for the "other adult" exclusion fixed #10493 is OPEN. It covers both the CMC other-adult exclusion and the ARP condition ("unmarried woman ... not a custodial parent of a dependent child"). The PR body and rulespec-us#1948 both link it.

New findings

Critical (must fix)

None.

Should address

None.

Suggestions

1. Correct the PR body's count of cases that fail on main (optional).

The Tests section says: "On main, 6 of these cases fail (placement Cases 5, 6, 7, 8 and 10, integration Case 12)." It also says the old parameter's p(...) "returned None" before 2011-10-01.

On main, the three January 2011 placement cases (9, 11 and 12) fail as well, with an error instead of a wrong placement:

  • No value for 2011. Main's cmc_infant_age_limit has a single value, 2011-10-01: 2.
  • Backdating doesn't help. backdate_parameters copies a parameter's earliest value back only to 2015-01-01 (FIRST_MODELED_YEAR), and only when that value starts after that date. So the parameter has no value for January 2011.
  • The lookup fails. Core's ParameterNodeAtInstant skips a child whose value is None, so p.cmc_infant_age_limit raises ParameterNotFoundError.

So 9 cases fail on main: 6 with the wrong placement and 3 with the error. The same three 2011 cases also error at the round-1 head, where the parameter also started on 2011-10-01, so not only the 57-day case fails there. I worked this out by reading the code and didn't run main.

This doesn't affect the model. If you update the body, say "9 cases fail on main (6 with the wrong placement; Cases 9, 11 and 12 error because the parameter had no value before 2011-10-01)".

Verified correct

  • The law, current and historical.
    • Statute. § 49.148(1m)(a)1: "A custodial parent of a child 8 weeks old or less ... may receive a monthly grant of $673".
    • Administrative code. DCF 101.18(2)(a) (Register May 2026): "A custodial parent of a child who is 8 weeks old or less ... may receive a monthly grant in the amount provided under s. 49.148 (1m), Stats."
    • Manual. W-2 Manual 7.4.3.4: "The CMC placement must end eight calendar weeks (7 days x 8 weeks = 56 days) after the child is born."
    • Before 2012. The 1997-98 statutes (file page 19) read "a custodial parent of a child who is 12 weeks old or less", and the 2009-10 statutes (file page 18) read "A custodial parent of a child 12 weeks old or less". Act 32 SECTION 1361s struck "12" and inserted "8", and SECTION 9408 (1d) made that change take effect on January 1, 2012 (checked in round 1).
    • Start date. The 1997-09-01 start matches amount.yaml.
  • No source conflict. The statute, the code and the manual agree: "8 weeks old or less" read as a continuous age, with day 56 as the last CMC day. No choice between sources is made, so no register entry or # Source conflict marker is needed.
  • How age in weeks is derived.
    • Formula. age is a yearly float input in years. The formula reads it for period.this_year (so it avoids the monthly_age flow-division bug) and multiplies by 52.
    • 8-week cut. The cut falls at age 8/52 = 0.15385 years, which is 56.2 days in a 365.25-day year (56.15 in a 365-day year). So day 56 gives CMC and day 57 gives CSJ under either convention, which matches Manual 7.4.3.4.
    • 12-week cut. It falls at 12/52 = 0.23077 years (84.3 days), so day 84 gives CMC and day 85 gives CSJ.
    • Float32 at the limit. float32(8/52) × 52 = 8.0, which gives CMC, and float32(12/52) × 52 = 12.0, which also gives CMC (<=).
    • A week either side. One week before (7/52) gives 7.0000005, which is CMC. One week after (9/52) gives 9.0, which is CSJ.
    • Day inputs. For ages entered as days/365.25: 56 days gives 7.9726 (CMC), 57 gives 8.1150 (CSJ), 84 gives 11.9589 (CMC) and 85 gives 12.1013 (CSJ).
  • Hand-checked expectations (all 17 new or changed; float32 products computed):
    • wi_works_placement.yaml:
      • Case 2 (age: 0, was monthly_age: 1): 0 weeks ≤ 8 → CMC.
      • Case 3 (0.125, was monthly_age: 2): 6.5 ≤ 8 → CMC.
      • Case 4 (0.1533, 55.99 days): 7.9716 ≤ 8 → CMC.
      • Case 5 (0.1561, 57.01 days): 8.1172 > 8 → CSJ.
      • Case 6 (0.25): 13 > 8 → CSJ.
      • Case 7 (1): 52 → CSJ.
      • Case 8 (2): 104 → CSJ.
      • Case 9 (2011-01, 0.19, about 69 days): 9.88 ≤ 12 → CMC.
      • Case 10 (2012-01, 0.19): 9.88 > 8 → CSJ.
      • Case 11 (2011-01, 0.23, 84.0 days): 11.96 ≤ 12 → CMC.
      • Case 12 (2011-01, 0.233, 85.1 days): 12.116 > 12 → CSJ.
      • Case 13 (renamed only): pregnancy month 7 ≥ 7 → ARP.
      • Case 14 (renamed only): month 6 < 7 → CSJ.
      • Case 15 (age: 0, was monthly_age: 1, with a pregnancy in month 8): has_infant comes first in select → CMC.
    • integration.yaml:
      • Case 2 (comment only, age 0): eligible, CMC, 673.
      • Case 9 (comment only, age 3): 156 weeks → CSJ, 653.
      • Case 12 (new, age 1, no income, $500 cash, under the $2,500 limit): eligible, 52 weeks → CSJ, 653 (CSJ is $653 from 2011-10-01).
  • The January test periods work.
    • CMC cases prove it. The 2011-01 CMC Cases 9 and 11 can pass only if state_code: WI and the infant's age are applied: the default state isn't WI, which gives CSJ, and the default age of 40 also gives CSJ. They pass in CI, so the 2011-01 and 2012-01 CSJ cases (10 and 12), built the same way, also read their inputs. They don't pass by default.
    • Outside January. The PR body's note about non-January periods matches a known YAML-runner limit for yearly inputs with a monthly period.
  • Code.
    • The formula is vectorized: an array product, spm_unit.any and select, with no branching on values. defined_for = StateCode.WI is unchanged.
    • unit: week has 18 precedents in the parameter tree.
    • The parameter description follows the house template.
  • NH Add New Hampshire state supplementation for 2026 #10156 pattern. No new state-gated int or enum key variable is added. wi_works_placement, the amount.yaml breakdown key, is unchanged, and its default (CSJ) is a key, so non-WI units in multi-state simulations look up a key that exists.
  • Callers. Only wi_works reads wi_works_placement, and nothing else reads cmc_infant_age_limit.
  • Years.
    • Pre-2012 values. Every year from 2012 uses 8 weeks, which covers all claimed years (TANF is verified from 2021, and the model computes from 2015). The 1997-2011 value of 12 weeks is history, and the 2011 tests exercise it.
    • The 2011 gap. The new 1997 value also fills the gap before 2011-10-01, where the old parameter had no value.
  • Changelog. changelog.d/fix-9815-wi-works-cmc-age.fixed.md is top-level with the fixed type, and it now describes the weeks limit and the change from 12 to 8 weeks.
  • PR body claims.
    • The body's quotes and file pages (1997-98 page 19, 2009-10 page 18, Act 32 pages 226 and 530) are accurate.
    • "At least 0.028 weeks from the limit" holds: the smallest gap is Case 4, 8 − 7.9716 = 0.0284.
    • The 67-case total matches CI: 12 + 6 + 5 + 6 + 13 + 5 + 15 + 5 across the eight WI Works files.
    • The PR title now says "in weeks".
  • Source-conflict ratchet (Tie source-conflict markers to the register shared with Axiom #10287).
    • No growth. The new formula comment matches neither CONFLICT_COMMENT_RE nor ATTEMPT_RE on main's version of the test.
    • Removed comment. The parameter comment removed by the PR ("Wisconsin statute specifies 8 weeks; 2 months is an approximation.") didn't match either.
    • Register and backlog. No WI Works file is in KNOWN_UNMARKED, the register or candidates.yaml on main.
    • CI. CI ran test_conflict_sounding_comments_do_not_grow on the merged tree, and it PASSED (Rest shard 1).
  • Main-side guards.
  • Partner tests. No files under tests/policy/baseline/partners/** are touched, and Household API Partners passes.

Axiom line assessment

Valid. The line is axiom: TheAxiomFoundation/rulespec-us#1948 queued. rulespec-us#1948 meets every requirement:

  • Status. It is OPEN, labelled pe-parity, and was updated 2026-10-27.
  • Module and corpus citation. us-wi/statutes/49.148.yaml for us-wi/statute/49.148, in the 71.52 form from Add state supplement to household benefits #1726. It names the chapter 49 lock and the W-2 Manual 7.4.3 lock.
  • Verbatim law. § 49.148(1m)(a)1, including the "unless another adult member ..." clause, and (1)(b)1 ($653 CSJ). Also Act 32 SECTIONS 1361s and 9408 (1d) and (3i), the 1997-98 and 2009-10 "12 weeks" texts, and Manual 7.4.3.1 and 7.4.3.4.
  • Required outputs. The age limit (56 days from 2012-01-01, 84 days before) and wi_w2_cmc_eligible, which includes the other-adult exclusion. Also wi_w2_cmc_grant ($673) and wi_w2_csj_grant ($653 from 2011-10-01, $673 before).
  • Companion tests from external sources. Manual 7.4.3.1 Examples 1, 2 and 4, Manual 7.4.3.4 for 56 and 57 days, and Act 32 for 10 weeks in November 2011 and February 2012 and for 84 and 85 days. None comes from PolicyEngine output.
  • Blocker. queued is justified. Chapter 49 and Manual 7.4.3 aren't in the pinned corpus release, Act 32 and the pre-2012 statutes aren't in any lock, and the billed signed encoder run needs approval. No RuleSpec was hand-written.
  • Matches the head. It describes the age compared in weeks, 12 weeks before January 1, 2012 and 8 weeks from then, and the same 56/57-day and 84/85-day boundaries as the YAML tests.

CI status

gh pr checks 10259, re-checked at the end: 33 pass, 2 fail, 0 pending (head b03f836775).

Branch status

The branch is 3 commits ahead of main (the fix, a merge of main, and b03f836775) and 70 behind.

  • Merge. git merge-tree --write-tree PolicyEngine/main b03f836775 merges cleanly.
  • Main's changes. Since the merge base 237da945eb, main hasn't changed any of these:
    • Wisconsin DCF (W-2) variables, parameters or tests;
    • age or monthly_age;
    • model_api.
  • Ratchet files. Main's changes to test_source_conflicts.py and the source-conflict register don't involve WI Works files.
  • Sibling PRs. No other open PR touches wi/dcf/works.

Verdict: APPROVE

@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.

Approving round 2: 0 critical, 0 should. Full report in the comment above. CI note: the Rest shard 3 failure (test_mt_state_agi_invariants) comes from main and is fixed by #10496; re-run after it lands.

@hua7450 hua7450 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.

Reviewed the infant-age comparison and historical threshold change in #9815. The eight-week rule and January 1, 2012 transition are supported by the governing sources. Approving.

@PavelMakarchuk
PavelMakarchuk merged commit 3bd4349 into main Oct 27, 2026
35 checks passed
@PavelMakarchuk
PavelMakarchuk deleted the fix-9815-wi-works-cmc-age branch October 27, 2026 23:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants