Skip to content

Show per-geography household-record coverage on the populace overview#97

Open
DTrim99 wants to merge 2 commits into
PolicyEngine:mainfrom
DTrim99:geography-coverage-card
Open

Show per-geography household-record coverage on the populace overview#97
DTrim99 wants to merge 2 commits into
PolicyEngine:mainfrom
DTrim99:geography-coverage-card

Conversation

@DTrim99

@DTrim99 DTrim99 commented Jul 8, 2026

Copy link
Copy Markdown

Companion to populace#358, which adds a geography_coverage section (unweighted household-record counts by state and congressional district) to each release's demographics.json.

What it shows

A "Geography coverage" card on the populace overview:

  • Congressional districts count, median records/district, districts under 50 records (with a district-ready / blocks district analysis tone badge — zero is the readiness bar), and min records/state.
  • When any district is under 50: a "thinnest districts" chip row (e.g. TX-08: 25), red below the floor.

Why

Record counts are the resolution floor no calibration can rescue. The current national-only release has 48 districts under 50 records — which just blocked congressional-district features on the child-poverty dashboard, discovered only by ad-hoc H5 inspection. This makes CD-readiness visible per release, including on staging candidates before publish.

Plumbing

  • loadRelease now also fetches demographics.json (.catch(() => ({})) — releases without it, i.e. all current ones until populace#358 ships in a build, render nothing).
  • geography_coverage threaded through buildCalibrationlatestPopulaceCalibrationSummary/api/populace (also listed in source_artifacts) → typed in use-populace.ts.

Typecheck and next build pass.

🤖 Generated with Claude Code

Renders the geography_coverage section populace releases now publish in
demographics.json (populace#358): unweighted household-record counts by
state and congressional district. KPI row (districts, median records,
districts under 50 with a district-ready / blocks-district-analysis
badge, min state records) plus the thinnest districts when any fall
under 50. Records are the sub-national resolution floor — the 2026-07
national-only release has 48 districts under 50 (median 114, min TX-08
at 25), which blocked congressional-district features with no dashboard
visibility. Hidden for releases without the section.

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

@MaxGhenis MaxGhenis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice, focused addition — the "record counts are the resolution floor no calibration can rescue" framing is exactly right, and the card makes CD-readiness visible per release instead of by ad-hoc H5 inspection. Reviewed the data contract, the plumbing, backward-compat, and conventions. One thing I'd fix before merge (the stray lockfile); the rest is optional.

Verified

Consumes populace#358's output, doesn't recompute. The card reads the precomputed geography_coverage from demographics.json end-to-end: loadReleaseUncached fetches it → buildCalibration passes it through → latestPopulaceCalibrationSummary threads it to the summary → /api/populace exposes it and lists demographics in source_artifacts → typed in use-populace.ts. The component only sorts counts to pick the 10 thinnest for display; every number shown (n_geographies, household_records_median, n_under_50, household_records_min) comes straight from the artifact.

The type contract matches populace#358 field-for-field. I checked GeographyCoverageBlock against geography_coverage_payload in populace#358's demographics.py: unit, states/congressional_districts blocks, and {n_geographies, household_records_min/median/max, n_under_50, n_under_100, counts} all line up, and the consumer handles congressional_districts: null (the no-district-column case populace#358 tests). So the card will populate correctly once a release is built with that payload — no silent "—" from a name mismatch.

Backward-compatible. .catch(() => ({})) on the demographics.json fetch plus the null fallthrough means releases published before the section exists render nothing rather than breaking — consistent with the PR description.

Findings

1. Remove frontend/package-lock.json before merge. This repo is bun (bun.lock is the tracked lockfile on main, and the repo tooling actively rejects npm). package.json is unchanged in this PR, so no dependency was actually added — the lockfile looks like an accidental npm install, and it's 5,633 of the 5,739 added lines. A package-lock.json sitting next to bun.lock is a real hazard: Vercel or any npm-based tooling can resolve dependencies from it and drift from the bun-resolved tree. Suggest deleting it and adding package-lock.json to .gitignore.

2. Minor: text-red-600 is the one off-system color. Everything else uses semantic tokens (text-muted-foreground, border-border, the KpiCard tone system) — text-red-600 on the sub-floor chips is the only raw Tailwind palette value in the diff, and there's no prior use of it in the repo. text-destructive would keep it on the token system and adapt to dark mode consistently with the negative tone you're already using on the KPI above it.

3. Repo-level (not a blocker for this PR): there's no CI. gh pr checks reports "no checks reported" — the repo has no .github/workflows, so the "typecheck and next build pass" claim can't be enforced automatically. The frontend already has make typecheck (tsc --noEmit) and next build; a minimal Actions workflow running both on PRs would make that guarantee real going forward. (For this PR I verified the populace#358 contract by hand in lieu of CI — worth noting tsc wouldn't have caught a field-name mismatch anyway, since the payload crosses the boundary as JsonObject.)

Functionally this is ready once the lockfile is gone. Thanks for wiring the readiness signal in before it bites another dashboard.

The package-lock.json was an accidental npm install artifact in a bun
repo (5.6k lines of the diff, and a real npm/bun resolution-drift
hazard) — removed and gitignored. The sub-floor district chips move
from raw text-red-600 to the text-destructive token, consistent with
the KpiCard tone system.

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

DTrim99 commented Jul 8, 2026

Copy link
Copy Markdown
Author

Both findings addressed: the stray package-lock.json is removed and gitignored (it was indeed an accidental npm install artifact — nothing in package.json changed), and the sub-floor chips now use text-destructive instead of raw text-red-600. Noted the no-CI point — happy to add a minimal typecheck+build workflow in a follow-up if wanted.

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