Skip to content

Explain silent no-op society-wide reports with an informational callout#1117

Merged
MaxGhenis merged 1 commit into
mainfrom
no-op-report-callout
Jul 17, 2026
Merged

Explain silent no-op society-wide reports with an informational callout#1117
MaxGhenis merged 1 commit into
mainfrom
no-op-report-callout

Conversation

@MaxGhenis

Copy link
Copy Markdown
Contributor

What

The society-wide report output now shows an informational callout above the result cards when a reform is a complete no-op — every output is identical to current law. Previously the page rendered "No change" everywhere (budget, deciles, winners/losers at 100% no change, poverty, inequality) with no explanation, so users could not tell a genuine zero-impact reform from one that never touched anything in effect that year.

This is the "exact-zero callout" from #1116 (suggestion 1). Motivating repro: an Oregon Kids' Credit expansion modeled for 2029, after the credit sunsets (HB 3235) — editing its amount for 2029+ changes nothing, and the null result read as user error.

Why

Silent no-ops are indistinguishable from real zero-impact reforms and from plain start-date / simulated-year mismatches. The callout names the likely cause and the year to check.

Detection rule

isSocietyWideReportNoOp(output) returns true only for an exact whole-report no-op — all four must hold:

  • budget.budgetary_impact exactly 0
  • budget.benefit_spending_impact exactly 0
  • budget.tax_revenue_impact exactly 0
  • intra_decile.all['No change'] exactly 1 (100% of the population unchanged)

Missing or malformed fields are treated as NOT a no-op, and the function never throws. Both US and UK report shapes are handled (the checked fields are identical across them). Because all four conditions are required, a single zero panel — e.g. a revenue-neutral reform that still moves money between households — does not trigger the callout.

Callout

Reuses the repo's Alert idiom (@/components/ui, informational teal-bordered styling with IconInfoCircle, matching FloatingAlert's info type). Copy is sentence case, with <year> from the report context via useReportYear():

  • Title: "This reform doesn't change any policy in <year>"
  • Body: "Every output is identical to current law. Check that your parameters' start dates cover <year>, and that the programs you edited are in effect that year — some credits phase in later or expire."

Files

  • app/src/utils/isSocietyWideReportNoOp.ts — pure detection util
  • app/src/pages/report-output/NoOpReportCallout.tsx — the callout component
  • app/src/pages/report-output/SocietyWideOverview.tsx — renders the callout above the card grid when the util returns true (the large line count is prettier re-indentation from the new Stack wrapper; the logical change is ~7 lines — see the git diff -w)
  • app/src/pages/report-output/SocietyWideOverview.story.tsx — adds a CompleteNoOp story (plus the CountryProvider / ReportYearProvider decorators it needs to render)
  • changelog_entry.yaml — user-facing fixed line

Test coverage

  • Util unit tests (app/src/tests/unit/utils/isSocietyWideReportNoOp.test.ts): all-zero US → true; all-zero UK → true; tiny nonzero budgetary / benefit-spending / tax-revenue → false; 99.9% no change → false; revenue-neutral with real winners and losers → false; null / undefined / missing budget / missing intra_decile / missing "No change" / non-numeric → false.
  • Component tests (SocietyWideOverview.test.tsx): callout renders with the interpolated year on an all-zero fixture; falls back to a generic year phrase when there is no report-year context; absent on the default (nonzero) fixture; absent on a revenue-neutral fixture.

Verification

  • vitest (util + component suites): 36 passed
  • tsc --noEmit clean; eslint . --cache 0 warnings; prettier --check clean
  • Visual: rendered the CompleteNoOp Storybook story and confirmed the callout appears above the cards with the informational styling and the 2029 year interpolated.

Fixes #1116

🤖 Generated with Claude Code

When a reform only edits programs not in effect in the simulated year
(for example a credit that has sunset), every society-wide output reads
"No change" with no explanation, leaving users unable to tell a genuine
no-op from a start-date or sunset mismatch.

Add isSocietyWideReportNoOp, a pure detection util that returns true only
for an exact whole-report no-op: budgetary impact, benefit-spending and
tax-revenue components all exactly 0, and the winners/losers intra-decile
breakdown 100% "No change". Missing or partial fields are treated as NOT
a no-op and it never throws; both US and UK shapes are handled.
SocietyWideOverview renders an informational (not error) callout with the
simulated year above the cards when it returns true, so a single zero
panel never triggers it.

Fixes #1116

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
policyengine-app-v2 Ready Ready Preview, Comment Jul 17, 2026 9:51pm
policyengine-calculator Ready Ready Preview, Comment Jul 17, 2026 9:51pm
policyengine-calculator-next Ready Ready Preview, Comment Jul 17, 2026 9:51pm
policyengine-website Ready Ready Preview, Comment Jul 17, 2026 9:51pm

Request Review

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.

Silent 'No change' when a reform only edits a program not in effect in the simulated year

1 participant