Skip to content

Warn and drop deprecated inputs instead of crashing#1494

Merged
anth-volk merged 3 commits into
mainfrom
warn-drop-deprecated-inputs
May 6, 2026
Merged

Warn and drop deprecated inputs instead of crashing#1494
anth-volk merged 3 commits into
mainfrom
warn-drop-deprecated-inputs

Conversation

@hua7450
Copy link
Copy Markdown
Collaborator

@hua7450 hua7450 commented May 6, 2026

Summary

When a partner posts a household payload containing a removed model variable, the simulation crashes with VariableNotFoundError → HTTP 500 and the partner sees no output at all — not just for the affected variable, but for everything they asked for. PR #1493 surfaced this with medical_out_of_pocket_expenses (deleted in policyengine-us 1.673.0); other deprecations will likely break the same way.

This PR adds a deprecation registry and a drop_deprecated_inputs helper that strips removed variables from the inbound payload before validation, surfacing a structured warning to the response next to the existing period warnings. The partner gets a 200 with full output for every non-dependent variable, plus an actionable migration hint.

It also handles deprecated variables used as scan axes. If axes[].name references a removed variable, that axis is dropped with a location-specific warning before the simulation builder expands axes, avoiding the same 500 path.

Behavior

Scenario Before After
Partner posts medical_out_of_pocket_expenses HTTP 500, no output HTTP 200, full output, warning in response body
Partner scans over axes[].name = medical_out_of_pocket_expenses HTTP 500 during axis expansion HTTP 200, deprecated axis dropped, warning in response body
Outputs that ignore that input (CTC, EITC, base SNAP eligibility, premium calculations, etc.) Lost (whole request 500s) Computed correctly
Outputs that depended on it (itemized medical deduction, SNAP excess medical deduction, HUD medical-expense deduction, Medicaid medically-needy spend-down, PA CCW deduction) Lost (whole request 500s) Fall back to default (often 0)

Tradeoff

This is a warn-and-drop, not a value-preserving shim. The partner's value is silently discarded; outputs that need that input fall back to defaults. For partners who passed 0 (e.g. MyFriendBen pre-#1493), this is a no-op semantically. For partners who passed a non-zero value with premium spend folded in, they'll need to migrate the premium portion to health_insurance_premiums to recover those outputs — the warning hint says exactly that.

Files

  • policyengine_household_api/utils/deprecated_inputs.py — new registry + helper for entity inputs and scan axes
  • policyengine_household_api/endpoints/household.py — wire the helper into /calculate before validation; merge warnings into the existing response field
  • tests/unit/utils/test_deprecated_inputs.py — unit coverage for drop+warn, no-deprecated passthrough, multi-person, axes safety, deprecated axis dropping, members-list safety, and message format
  • tests/unit/endpoints/test_household.py — end-to-end coverage confirming requests with medical_out_of_pocket_expenses as an input or axis return 200 + warning + correct unaffected outputs
  • changelog.d/warn-and-drop-deprecated-inputs.added.md — changelog fragment

Future deprecations

Each new entry in DEPRECATED_VARIABLES ships with a one-line migration hint. The registry is the single place to add future removals (e.g. when other umbrella inputs are decomposed) so we don't have to recur through this design.

Test plan

  • Unit tests cover drop + warning shape, multi-person, list-valued slots, and deprecated scan axes
  • Endpoint tests confirm HTTP 200 + warnings + non-medical outputs unaffected
  • uv run pytest tests/unit/utils/test_deprecated_inputs.py tests/unit/endpoints/test_household.py
  • uv run ruff check policyengine_household_api/utils/deprecated_inputs.py tests/unit/utils/test_deprecated_inputs.py tests/unit/endpoints/test_household.py
  • CI passes

🤖 Generated with Claude Code

hua7450 and others added 3 commits May 6, 2026 11:06
When a partner posts a household payload containing a removed model
variable (e.g. `medical_out_of_pocket_expenses`, deleted in
policyengine-us 1.673.0), the simulation crashes with
VariableNotFoundError and the partner sees HTTP 500 — the entire
calculation is lost, not just the medical-expense piece.

Add a deprecation registry and a `drop_deprecated_inputs` helper that
strips removed variables before validation, surfacing a structured
`DeprecatedVariableWarning` next to existing period warnings. Partners
get a 200 with full output for everything that doesn't depend on the
removed input, plus an actionable migration hint in the response body.

Outputs that did depend on the dropped input fall back to defaults
(e.g. itemized medical deduction, SNAP excess medical deduction, HUD
medical expense deduction, Medicaid medically-needy spend-down). This
is a soft landing, not a transparent shim — premium-heavy callers will
need to migrate their value to `health_insurance_premiums` to retain
those outputs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ormat

`test__given_invalid_household_shape__returns_400` posts a string for
`household` and expects a 400. Without a type guard, the warn-and-drop
helper hits `.items()` on a string before validation can reject it.
Add an `isinstance(household, dict)` short-circuit so non-dict shapes
flow straight to the existing Pydantic validator.

Also reformat the two new test files with `ruff format` to satisfy the
Lint job.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@anth-volk anth-volk self-requested a review May 6, 2026 17:16
Copy link
Copy Markdown
Collaborator

@anth-volk anth-volk left a comment

Choose a reason for hiding this comment

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

This is a critical fix, so let's merge, but I want to flag: drop_deprecated_inputs directly mutates the household instead of deep-copying, opening us up to a class of unexpected/unclear bugs down the road. Recommend making this non-mutative via deep-copy in a future PR.

@anth-volk anth-volk merged commit 3ee0ba3 into main May 6, 2026
3 checks passed
@anth-volk anth-volk deleted the warn-drop-deprecated-inputs branch May 6, 2026 17:18
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.

2 participants