Skip to content

Fix multi-entity trace truncation in PE derivation narratives#37

Merged
MaxGhenis merged 1 commit into
mainfrom
multi-entity-fix
May 17, 2026
Merged

Fix multi-entity trace truncation in PE derivation narratives#37
MaxGhenis merged 1 commit into
mainfrom
multi-entity-fix

Conversation

@MaxGhenis
Copy link
Copy Markdown
Contributor

Summary

Reported by Max on scenario_000: the PolicyEngine derivation said "the household's only income is $45,000 of self-employment income" but the AGI it cited ($81,821) only reconciles if you include the spouse's $40,000 in wages. The narrative had silently dropped half the household.

Root cause

_value_repr and _is_zero in policybench.case_reference_explanations were calling value[0] on every OpenFisca trace value. Per-person variables like irs_gross_income come through as length-N arrays ([45000, 40000] for [head, spouse]); we were taking the head's row and discarding the spouse's. This affected every multi-person scenario.

Fix

Render multi-entity numeric arrays as sum (per entity: a, b, ...) and require every entity to be zero before pruning a subtree:

irs_gross_income = 85000 (per entity: 45000, 40000)
  irs_employment_income = 40000 (per entity: 0, 40000)
  self_employment_income = 45000 (per entity: 45000, 0)

Booleans surface as [True, False]. Length-1 arrays still collapse to a scalar so most renderings stay terse.

Regenerated outputs

  • All 2,188 US case narratives re-run with the fixed renderer (0 errors).
  • Cents rounded as in #36.
  • app/src/data.json + the paper-snapshot data.json copies refreshed; manifest SHA256 updated.

scenario_000 now reads:

The household's gross income of $85,000 consists of $45,000 in self-employment income and $40,000 in employment income. An above-the-line deduction of $3,179 for one-half of self-employment tax reduces gross income to an adjusted gross income of $81,821 ...

Upstream

The same fix lives in policyengine.derivations (policyengine.py#365). policybench currently ships its own copy of the renderer; once PE.py 4.5.2 publishes we'll switch to the upstream module.

Test plan

  • uv run pytest -m "not slow" (251 passed)
  • ruff check
  • Modal renders the corrected narrative on scenario_000

🤖 Generated with Claude Code

The previous trace renderer took the first element of every OpenFisca
trace value, which silently dropped every other entity's contribution.
For joint households, the narrative would describe the head's row as
the entire household: scenario_000's federal_income_tax_before_refundable_credits
narrative reported "the household's only income is \$45,000 of
self-employment income" — silently dropping the spouse's \$40,000 in
wages. AGI \$81,821 magically appeared without an explanation that
reconciled.

Switch _value_repr and _is_zero to handle per-entity arrays as a unit:
- length-1 collapses to a scalar (the common tax-unit/household case),
- length>1 renders as ``sum (per entity: a, b, ...)`` for numeric values
  so the summariser sees both the total and the per-person decomposition,
- booleans surface as ``[True, False]``,
- ``_is_zero`` requires every entry to be zero before pruning a subtree.

Regenerate all 2,188 US narratives (0 errors) and the dashboard +
paper-snapshot data.json copies. Apply the cents-round pass that #36
introduced.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
policybench-site Ready Ready Preview, Comment May 17, 2026 9:11pm

Request Review

@MaxGhenis MaxGhenis merged commit e9a93ad into main May 17, 2026
4 checks passed
@MaxGhenis MaxGhenis deleted the multi-entity-fix branch May 17, 2026 21:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant