Give md_ccs_service_unit an explicit UNIT_1 select default - #9402
Open
MaxGhenis wants to merge 2 commits into
Open
Give md_ccs_service_unit an explicit UNIT_1 select default#9402MaxGhenis wants to merge 2 commits into
MaxGhenis wants to merge 2 commits into
Conversation
Negative or NaN childcare hours map to 0 units in the unit_hours bracket, which no select condition matched, so numpy filled those rows with the integer 0. A full simulation only avoided a crash because core's defined_for path passes a bare integer through as Enum index 0, which happens to be UNIT_1. Make UNIT_1 the explicit default per COMAR 13A.14.06.02B(59) (one unit is 3 hours or less per day), add YAML and pytest coverage, and cite the definitions section. Follow-up to PolicyEngine#9400. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9402 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 3 1 -2
Lines 33 19 -14
=========================================
- Hits 33 19 -14
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Address two review nits. The comment now describes the UNIT_1 default as a fallback for unmatched values (negative, NaN and infinite hours all map to 0 units in the bracket) rather than values below the one-unit ceiling. The NaN test seeds full-time hours and asserts the injected NaN so a failed cache injection cannot pass by coincidence. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to PolicyEngine/policyengine-us#9400. That PR's sweep of Enum-valued variables found
md_ccs_service_unitas the one remainingselectwhose conditions are not exhaustive. Itsunit_hoursbracket returns 0 units for negative or NaNchildcare_hours_per_day(verified:calc([-1, 0, 2, 3, 3.0001, 6, 10, nan])gives[0, 1, 1, 1, 2, 3, 3, 0]), and no condition matched 0, so numpy'sselectfilled those rows with the integer 0.What happens today
[UNIT_1, 0]. Encoding that directly raisesAttributeError: 'int' object has no attribute 'index'; with the unmatched row first it raisesValueError: Invalid value(s) ['0', 'MDCCSServiceUnit.UNIT_1'] for enum MDCCSServiceUnit.Simulationdoes not crash today. The variable hasdefined_for = StateCode.MD, and policyengine-core'sdefined_forpath (simulation.py:np.where(mask, array, default_value)followed byitem.index if isinstance(item, Enum) else item) passes the bare 0 through as Enum index 0, which happens to be UNIT_1. Settingdefined_for = Noneon the loaded variable reproduces the crash. So the current answer is right by coincidence of member order; this PR makes it right by rule.Fix
Drop the redundant
units == 1branch and give the selectdefault=MDCCSServiceUnit.UNIT_1, mirroring #9400. UNIT_1 is the semantically right default: COMAR 13A.14.06.02B(59)(a) defines one unit as 3 hours or less per day, so anything at or below that ceiling, including out-of-range hours, is one unit. It also matches how zero hours is already treated. No output changes for any hours at or above 0 (units 1, 2, 3 map exactly as before); negative hours keep producing UNIT_1. The variable'sreferencenow cites the definitions section (13A.14.06.02) alongside the payment section (.11).Consumers are consistent with the zero-hours treatment:
md_ccs_weekly_copaycharges $1/week for UNIT_1 (eligible children only; eligibility does not depend on hours) andmd_ccs_payment_ratescales the UNIT_3 base rate by 1/3.Considered and not done: clamping with
max_(hours, 0). It is redundant given the default for negative hours, andnumpy.maximumpropagates NaN, so it would not cover the NaN case anyway. NaN cannot be supplied as a direct input (core rejects it inset_input); the default guards the computed path regardless.Tests
md_ccs_service_unit.yaml: negative hours alone; an adult listed first with a negative-hours child second (the ordering that raisesAttributeErroron the raw path); a negative-hours child first with a full-time sibling (theValueErrorordering).edge_cases.yamlCase 38: negative hours give the $1/week copay, the same as Case 36 (zero hours).md_ccs_payment_rate.yaml: negative hours in a Region X licensed center give $127/week ($381 UNIT_3 base × 1/3).policyengine_us/tests/test_md_ccs_service_unit_enum_default.py(collected bymake test-other-python): encodes the raw formula output directly, which is the path a variable withoutdefined_fortakes, for both row orderings and for NaN. Two of its three tests fail on unfixedmain(04a961a) with the object array[UNIT_1, 0]; all three pass with the fix.mainbecause of thedefined_forpassthrough, so they pin the intended behaviour rather than catch the crash; the pytest is the regression guard.uvx ruff@0.9.0 formatandruff checkare clean on the changed Python files. Local environment: Python 3.14.4, policyengine-core 3.30.2.Related
ccdf_age_groupand left this variable for a follow-up.🤖 Generated with Claude Code