Skip to content

Produce ESI employer premiums scaled to BEA B4923C from restored ASEC policyholder fields (#454) - #1189

Open
MaxGhenis wants to merge 10 commits into
mainfrom
esi-employer-premiums-454
Open

MaxGhenis wants to merge 10 commits into
mainfrom
esi-employer-premiums-454

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Closes the code side of #454: the US base build now produces employer_sponsored_insurance_premiums, scaled to BEA NIPA Table 7.8 line 17 (B4923C) as decision d1317 ruled, with gates that go red when it is absent, zero-mass, misassigned or off its anchor. The data release is a separate call (d1318).

axiom: n/a: data pipeline

What changed since the first review

The first independent review (GPT-6.1 Sol) asked for changes on five points. All five held up when checked against sources, and the design changed. A second review found all five resolved and nothing blocking, and a delta review of its nits did too. A fourth review, of the BEA anchor and the feed re-pin, approved with nits; they are answered in the last commit.

Finding What this revision does
The anchor (BEA) was presented as active-worker only. BEA's own methodology says it covers "active and retired employees". The stage prices every current policyholder, scales that universe to the anchor, and writes the column for employed policyholders only. Non-employed policyholders' dollars (retirees, COBRA enrollees and others) are priced and left out, not loaded onto workers.
The payment rule subtracted MEPS-IC's average employee contribution, which includes enrollees who pay nothing, from "employer pays some" holders only. "Pays some" now subtracts the contribution of an employee who pays one: the average divided by one minus the published no-contribution share (MEPS-IC Tables II.C/D/E.4.a). A cohort with MEPS-IC's own mix reproduces the published mean of every cell.
Dropping one raw field switched off every assignment check. Both gates fail when a raw ASEC coverage column is missing.
An ACS local build would default-fill the input to zero on ACS rows and report coverage green. fill_reviewed_nulls refuses a frame whose input is null on some rows.
A pre-tax premium copied from PHIP_VAL was also deductible as a medical expense. The pre-tax premium is out of this PR. The US hub ruled on 2026-10-10 that the engine's premium inputs are disjoint (PolicyEngine/policyengine-us#10046), so a producer must move those dollars out of the reported premium on an engine that includes #10046. That is a separate, sequenced change; the code is kept on esi-pre-tax-premiums-454.

With the matched universe in place, the anchor was a methodology choice between two retiree-inclusive totals. Max ruled d1317 on 2026-10-11: BEA, not NHE Table 24.

Method

  • Source fields. NOW_OWNGRP, NOW_HIPAID, NOW_GRPFTYP, NOW_GRPFTYP2, PEMLR and NOEMP are restored from the sha-pinned Census person files for every pinned income year (2022 to 2025), by exact PERIDNUM join. The stage refuses a frame that breaks the Census universe relations.
  • Cells. MEPS-IC 2025 Series II by tier, State and firm size for private employers; 2024 Series III by census division for governments, aged by the private national 2025/2024 ratio. The federal cell and the government no-contribution share are stand-ins, and non-employed policyholders are priced as active employees, because MEPS-IC publishes none of the three.
  • Payment status. Employer pays all: the premium. Some: the premium minus the conditional contribution. None: zero.
  • Scale. One factor makes the weighted total over every policyholder equal BEA NIPA 7.8 line 17 for the build year ($977.034B for 2024, the 2026-09-30 vintage; 2023 and 2025 also pinned). The column is that factor times the employed policyholders' shares.

Full method, limits and sources: docs/us-esi-employer-premiums.md.

The feed re-pin

d1317 asked for the US Chronicle feed to carry the banked B4923C fact (PolicyEngine/chronicle#331). The pin moves from 51aa40f to cf9ece2: 51aa40f plus the digit-text guard match from Chronicle main's aa3e4de3 and the B4923C package as #331 merged it (branch feed/us-51aa40f-esi-b4923c). Built there:

  • all 40,178 rows of the previous feed are byte-identical, and exactly three B4923C facts are added ($923,195M, $977,034M, $1,027,929M);
  • compiled at 2024, with target aging on and again with it off, the registry is the same ordered list of 33,250 targets on both feeds; B4923C is a deferred reviewed exclusion, not a target (calibration is d1264);
  • a test ties the stage's anchor to the three facts the pinned feed added, so changing one without the other fails CI;
  • the release tool refuses a feed whose B4923C values differ from the stage's pinned anchor (us_esi_premiums_anchor_feed_mismatches). A diagnostic run under --allow-unpinned-feed or --allow-esi-premium-gaps continues and records the difference in esi_premiums.json.

Scripts and reports: experiments/us-esi-454/feed-repin/; procedure: docs/us-chronicle-feed-repin.md.

#1192 also re-pins the feed (from the older c5e5bf8 base) and conflicts with main. Whichever lands second re-pins onto a Chronicle base that holds both changes, carries the three B4923C scope pairs, and redoes the row comparison. Until then the feed check fails safe: a feed without B4923C is refused.

Measured on the pinned pools (target year 2024, before calibration)

Income years 2023 to 2025 (default) 2022 to 2024
Current ESI policyholders 92.4M 92.9M
Employed, with an employer 79.7M 79.8M
With a positive employer premium 74.0M 74.1M
Scale factor 0.971 0.965
Anchor-universe total $977.0B $977.0B
Employer premium column $863.9B $862.2B
Mean per policyholder with a positive premium $11,679 $11,642
Mean per employed policyholder $10,840 $10,805
Other policyholders' share, not in the column $113.2B $114.9B
Private-sector part of the column, against MEPS-IC's own private active-employee total ($668.6B, 2024) $665.6B (0.4% below) $663.7B (0.7% below)

Both gates pass on each pool, before and after the PUF-support clone. Receipts: experiments/us-esi-454/receipts/.

Readings not taken

Reading Employed column Mean per positive policyholder
BEA over every policyholder (as built, d1317) $863.9B $11,679
BEA over every policyholder, 65-and-over non-employed at half price $884.7B $11,961
NHE over every policyholder $925.7B $12,515
BEA loaded onto employed policyholders only (the first version of this PR) $977.0B $13,209
NHE loaded onto employed policyholders only $1,047.0B $14,155

Invariants

Each is a property test over generated populations (Hypothesis, 120 examples), in tests/engine_free/us/test_us_esi_premiums.py:

  • Conservation. The weighted share over every policyholder equals the anchor. The column equals the anchor times the employed share of weighted raw mass, and equals the anchor exactly when no one outside the column is priced.
  • Monotonicity. Pricing a policyholder outside the column never raises a worker's premium.
  • Bounds and structural zeros. Finite and nonnegative; zero outside employed policyholders with an employer and where the employer pays nothing.
  • Proportionality. Every premium is one common multiple of its MEPS-IC share.
  • Cell reconciliation. MEPS-IC's own pays-all / pays-some mix reproduces the published employer mean, exhaustively over every tier, State and employer cell (918 cells).
  • Determinism. No dependence on the seed or on row order; restaging a staged frame from its raw columns changes no one; support clones agree and conserve mass.
  • Weight homogeneity. Scaling every weight by c scales each value by 1/c and leaves the totals unchanged.
  • Differential. A scalar, per-person restatement of the rules agrees with the vectorized stage.

One more identity is held by example tests and a release check, not a property test: the stage's pinned B4923C values equal the feed's banked facts. A test checks them against the committed re-pin report, and the release tool checks the feed it compiles from.

Gates

  • us_esi_premiums_signal_gate: base build (before and after the clone) and release tool (base and calibrated export). Red on an absent, constant, negative or non-finite column, a premium outside the universe, a value that is not the common cell multiple, clone disagreement, or a missing raw column.
  • us_esi_premiums_anchor_gate: release tool, on the calibrated export, dense and sparse. Recomputes the anchor-universe total at release weights and requires it within 5% of BEA NIPA 7.8 line 17, with employed policyholders carrying 80% to 95% of it. Records the NHE Table 24 and MEPS-IC cross-checks (NHE is 7.2% above BEA for 2024, so a release scaled to NHE would fail).
  • The input is required in the release input-coverage manifest, with a reform-coverage probe (neutralizing it must lower cbo_household_market_income by at least $500B).
  • esi_premiums.json is required release evidence and carries the anchor verdict, both signal verdicts and the anchor-versus-feed verdict.
  • The standalone L0/refit export (tools/export_us_l0_refit_h5.py) refuses a frame whose column is absent or constant. It runs neither ESI gate.

Limits, stated in the doc: the raw-mean band catches unit errors in the cell table, not a level error inside the band; the 5% tolerance is a chosen criterion, since no release has calibrated this column yet.

What this does not do

  • Acceptance on real artifacts. Whether the total stays within 5% after calibration and after the sparse refit is measured only when a release builds (d1318). Nothing calibrates the column, so the anchor gate is what holds it.
  • The multispine pool. The stage runs in the single-spine base build. The pool wiring is Run the ESI premium stage in the stacked pool (#454) #1199, stacked on this PR.
  • ACS local releases. Refused, as above, until the input is assigned or transferred for every spine.
  • The pre-tax employee premium. As above.
  • A calibration target. The column is the employed part of the BEA fact, so the fact is not a sum target on it. That choice is d1264.

Test plan

  • engine-free and engine-us CI jobs green
  • tests/engine_free/us/test_us_esi_premiums.py and the feed, register, release-tool and spec-engine tests locally
  • tests/engine_contract/us/test_us_esi_premiums_engine.py in policyengine-us 2.2.1
  • tools/build_us_meps_ic_esi_cells.py --check against the pinned AHRQ PDFs
  • Feed rebuilt at cf9ece2 (twice, same digest), compared row by row and target by target with the previous feed
  • Stage, both gates and the PUF-support clone on both real pinned pools (receipts committed)

🤖 Generated with Claude Code

MaxGhenis and others added 3 commits October 10, 2026 12:04
AHRQ publishes the MEPS-IC State tables only as PDF. The tool pins four
PDFs by URL, byte length and SHA-256, extracts the cells the
meps_esi_premiums stage reads with pdftotext, and writes them with their
provenance to us_runtime/data/meps_ic_esi_premium_cells.json:

- 2025 Series II, private sector by State and firm size: average total
  premium and employee contribution, single / employee-plus-one / family;
- 2024 Series III, State and local government by census division (2025
  is not published), plus the 2024 private national rows that age it;
- 2025 Series I, pretax-contribution and health-insurance offer rates by
  firm size.

Values are copied as published; a suppressed cell stays null. The tool
refuses a reissued PDF, a label that matches no row or two, a table for
another survey year, and a United States row that differs from the AHRQ
national figure. --check fails if the committed file is stale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The declared meps_esi_premiums stage could not run: every pinned ASEC
input omits the Census current employment-based coverage fields, so
employer_sponsored_insurance_premiums was a recorded parity gap and
pre_tax_health_insurance_premiums had no producer at all.

Source fields. asec_census_person_columns restores NOW_OWNGRP,
NOW_HIPAID, NOW_GRPFTYP, NOW_GRPFTYP2, PEMLR and NOEMP from the pinned
Census person members by exact PERIDNUM join, for every pooled vintage
(none of the four pinned inputs carries them). Domains are the Census
API codebooks for survey years 2023-2026, checked against all four
pinned members.

Stage (us_runtime/esi_premiums.py), run in the base build's pre-clone
enrichment, both build paths:

- Employer premium: an employed current policyholder whose job has an
  employer takes the MEPS-IC cell of their tier and employer (private:
  State x firm size, 2025; State government and local/federal: census
  division, 2024 aged to 2025). Employer paid all -> premium; some ->
  premium minus employee contribution; none -> 0. Dependents,
  non-employed policyholders and the unincorporated self-employed carry
  0. One factor scales the weighted total to BEA NIPA 7.8 line 17 for
  the build year ($977.034B for 2024 in the 2026-09-30 vintage).
- Pre-tax premium: an employed policyholder with wages, a reported
  premium and an employee share pays PHIP_VAL through payroll with the
  MEPS-IC firm-size pretax-offer probability. The blake2b draw is a new
  registered site of the seed protocol.

The stage refuses missing, non-integer or out-of-codebook source values
and the Census universe relations the members satisfy; it never proxies
or defaults.

Anchor. NHE Table 24 ($1,047.0B) counts COBRA and retiree enrollees;
the same cells over every current policyholder reproduce it to 1.5%
($1,031.0B), 11.5% of that being non-employed policyholders. An
employed-only column therefore scales to BEA's compensation series and
holds NHE as an upper bound.

Gates. us_esi_premiums_signal_gate (structure, clone agreement, common
cell multiple, raw-share band) runs in the base build before and after
the clone and in the release tool on base and export.
us_esi_premiums_anchor_gate runs on the calibrated export for both the
dense and sparse defaults: red when either column is absent or
zero-mass, when the employer total leaves 5% of BEA or exceeds NHE.
Its verdict ships as the esi_premiums gate evidence;
--allow-esi-premium-gaps records without failing a diagnostic build.

Registers. The parity known-gap entry is removed; both inputs are
required release columns with two binding reform-coverage probes; the
cms_nhe ESI target families become deferred reviewed exclusions and
BEA's series is recorded source-absent; the source manifest, spec
bundle, seed-site inventory and spec identity pins are regenerated or
re-pinned at their observed values.

Measured on the real pinned pools (experiments/us-esi-454), before
calibration: 74.0M weighted policyholders with a positive employer
premium, scale factor 1.070, mean $13,209, pre-tax premiums $181.1B on
income years 2023-2025; all gates pass before and after the PUF clone.

Not wired: the multispine pool, whose operators run on CPS rows carrying
part of the stacked weight. Its releases fail the new gates until then.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/us-esi-employer-premiums.md: what the two inputs feed in
policyengine-us 2.2.1, the source fields, the cell method, the BEA
anchor and why NHE is only an upper bound, the pre-tax share
assumptions, the measurements on both pinned pools, the gates, lineage,
and what the change does not do. Adds the six restored columns to the
Census person-column review, the operator to the base-build ordering
note, and a changelog fragment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

The pre-tax premium column overlaps the other premium columns; the engine contract is disjoint

From the session that took the "engine's medical-expense base" follow-up in this PR's "Not in this PR" list. The fix for that double count belongs in this stage, not in the engine.

What the engine expects. PolicyEngine-US treats the premium inputs as disjoint: a premium is reported in pre_tax_health_insurance_premiums or in the other premium inputs (health_insurance_premiums, health_insurance_premiums_without_medicare_part_b, other_health_insurance_premiums), never both. Total employee-paid premiums are the sum. The US hub settled this on 2026-10-10 under the leaf-input rule. PolicyEngine/policyengine-us#10046 documents it and audits every consumer, and PolicyEngine/policyengine-us#10023 and PolicyEngine/policyengine-us#10055 are built on it.

What this stage writes. At 6f727eac, derive_pre_tax_health_insurance_premiums sets the pre-tax premium to PHIP_VAL for the selected workers. The same PHIP_VAL stays in health_insurance_premiums_without_medicare_part_b, and feeds other_health_insurance_premiums through the other_health_insurance_premiums stage. So about $181B sits in both sets.

Why the engine can't absorb it. Under the disjoint contract, federal Schedule A is already correct: it counts the non-pre-tax inputs in full and never reads the pre-tax input. If the engine subtracted the pre-tax input there, a caller who follows the contract ($2,000 pre-tax, $3,000 after tax) would get $1,000 on Schedule A instead of $3,000. PolicyEngine/policyengine-us#10067 adds tests that pin this, and makes no formula change.

What overlapping columns do, by engine version:

Engine Effect of the overlap
Current main, and the pinned 2.2.1 The $181B is excluded from wages and also counted in Schedule A medical expenses and in the state medical deductions that read the same helper.
After policyengine-us#10046 The above, plus the $181B is counted twice in SPM medical out-of-pocket expenses, SNAP, HUD and the other consumers to which #10046 adds the pre-tax input.

Ask. Before any release carries this column:

  1. Move the pre-tax dollars out of the non-pre-tax premium outputs for the selected workers, so that pre-tax plus non-pre-tax equals PHIP_VAL. That covers health_insurance_premiums_without_medicare_part_b and other_health_insurance_premiums, and health_insurance_premiums where attribute_self_employed_health_premiums writes it for the same person.
  2. Add the conservation invariant to the stage's property tests and to us_esi_premiums_signal_gate: for every person, pre-tax plus non-pre-tax premiums equal the reported premium, and at most one of the two is positive under the current all-or-nothing rule.
  3. Extend test_us_esi_premiums_engine.py: for a stage-produced worker, itemized_medical_expenses does not include the pre-tax premium.
  4. Sequence the release after an engine pin that includes policyengine-us#10046. Until then, netting the pre-tax dollars out of the other inputs would drop them from SPM medical out-of-pocket expenses, SNAP and HUD, which on main read only the non-pre-tax inputs.

I have not changed this branch.

@MaxGhenis

MaxGhenis commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor Author

Three corrections to my comment above, from the independent review of PolicyEngine/policyengine-us#10067. Asks 3 and 4 stand.

  1. The conservation identity is narrower than I wrote. "Pre-tax plus non-pre-tax equals PHIP_VAL" holds only for health_insurance_premiums_without_medicare_part_b on measured ASEC rows.
    • other_health_insurance_premiums is a residual: derive_us_other_health_insurance_from_asec sets it to the reported premium less modeled CHIP, Marketplace and Medicaid premiums, floored at zero. It needs its own identity after netting, not the PHIP_VAL one.
    • On PUF-support rows the premium outputs are QRF predictions or a share of PUF E17500, not PHIP_VAL. Person-level disjointness against a carried pre-tax premium cannot be asserted there. The follow-up should say what the authoritative total is on those rows.
  2. The $181B is an input total, not a measured engine effect. It is this PR's pre-calibration sum of the pre-tax column. I did not run the engine on a dataset that carries it, so the table's rows describe which consumers would count those dollars, not by how much any output moves.
  3. Input totals and wage exclusions differ. The engine caps the wage exclusion at wages (irs_employment_income = max(0, employment_income - pre_tax_contributions)), so a pre-tax premium above wages is not fully excluded. Capping the pre-tax amount at wages in the stage, as the author plans, removes the part of the gap that comes from premiums above gross wages. The engine subtracts all pre-tax payroll contributions together before flooring at zero, so retirement deferrals and payroll HSA contributions can still limit how much of a premium is excluded.

MaxGhenis and others added 3 commits October 10, 2026 19:58
The first independent review asked for changes on five points; each held up
against sources.

Anchor and universe. BEA NIPA 7.8 line 17 is not an active-worker series:
BEA's State Personal Income methodology (December 2025, paragraphs 3.18 and
3.19) says it covers active and retired employees, as CMS NHE Table 24 does.
The stage now prices every current policyholder, scales that universe to NHE
Table 24 (the anchor #454 chose), and writes the column for employed
policyholders only. Retiree and COBRA dollars are priced and left out, not
loaded onto workers. BEA and MEPS-IC's own private-sector enrollment total are
recorded beside the anchor verdict.

Payment status. "Employer pays some" subtracts the contribution of an employee
who pays one: MEPS-IC's average contribution divided by one minus the published
share of enrollees with no contribution (Tables II.C/D/E.4.a). A cohort with
MEPS-IC's own mix reproduces the published cell mean.

Gates. Both gates fail when a raw ASEC coverage column is missing. The anchor
gate recomputes the anchor-universe total at release weights and bands the
employed share. The evidence file carries the base and export signal verdicts,
so a waived failure stays on the record. An ACS local build refuses a frame
whose input is null on some rows instead of default-filling it. The output
family is registered at the raw-stage operator boundary.

Pre-tax premium. Removed from this change. PolicyEngine-US treats the premium
inputs as disjoint (PolicyEngine/policyengine-us#10046), so a producer must
move those dollars out of the reported premium on an engine that includes that
change. The earlier derivation is kept on branch esi-pre-tax-premiums-454. The
stage now has one output and no seeded draw; the seed-protocol registration is
reverted.

Measured on the default pinned pool, target year 2024, before calibration:
$925.7B over 74.0M employed policyholders with a positive premium, mean
$12,515. Receipts regenerated for both pools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reform-probe count drops to 42 with the pre-tax probe gone, the pool
tool's import graph reaches esi_premiums through the operator boundary (75
modules), and the ESI stage no longer reads PHIP_VAL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis MaxGhenis changed the title Produce ESI employer premiums and pre-tax employee premiums from restored ASEC policyholder fields (#454) Produce ESI employer premiums from restored ASEC policyholder fields (#454) Oct 10, 2026
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Round 2 is pushed (head 4110519). On the comment above about the pre-tax premium column:

The disjoint contract is right, and the fix is on the data side. This PR no longer produces pre_tax_health_insurance_premiums at all. Netting those dollars out of the reported premium moves about $180B out of health_insurance_premiums_without_medicare_part_b (and, through the other-health stage, other_health_insurance_premiums and the self-employed health_insurance_premiums attribution), which changes what the release tool's input-mass parity gate and the eCPS parity reference expect, and it is only correct on an engine that includes PolicyEngine/policyengine-us#10046. So it is a separate change, sequenced after that engine pin. The earlier derivation is kept on branch esi-pre-tax-premiums-454.

I closed PolicyEngine/policyengine-us#10064, which I had filed on the premise that the engine should subtract the pre-tax input.

The first independent review's five blocking findings and what changed for each are in the PR description. A second review is running against this head.

The second independent review approved with nits and no blocking findings.

- Target parity: the NHE private-employer family no longer borrows the
  all-employer text. Nothing holds the private part of the column to that
  fact; it is recorded against MEPS-IC's active-employee total.
- L0/refit export: the ESI column joins the required person source columns,
  so the standalone export refuses a frame where it is absent or constant.
  The doc says that path still runs neither ESI gate.
- Doc: NOEMP is the employer of last year's longest job while PEIO1COW is
  the current job; an unmeasured "most" is removed; the unincorporated
  self-employed are priced and left out; the NHE digest is of the zip
  archive.
- Cell table: --check compares published values, titles and pins and no
  longer depends on the installed poppler version.
- Tests: every tier x State x employer cell reconciles to its published
  mean under the published mix; the anchor gate is exercised without each
  raw column and without the household State.

The spec bundle is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Max ruled d1317: anchor on BEA NIPA 7.8 (B4923C), not NHE Table 24

Ruled 2026-10-11 (cos decide d1317), on the "methodology choice for Max" table in this PR's description. The row to build is "BEA over every policyholder": price every current policyholder, scale that universe to BEA NIPA Table 7.8 line 17 (B4923C, CY2024 $977.034B, BEA's 2026-09-30 vintage), and keep the column for employed policyholders only. The receipts give $863.9B for the column and a mean of $12,515 → $11,679 per policyholder with a positive premium.

Why: both national totals include retirees, so the only independent check on the active-worker piece is MEPS-IC's private active-employee total for 2024 ($668.6B). The private part of the column is $713.2B under NHE (+6.7%) and $665.6B under BEA (−0.4%).

What this needs on this PR:

  1. Scale source: B4923C in place of NHE Table 24, from the fact chronicle#331 banked (bea-nipa-private-group-health-insurance). The US Microcosm feed doesn't carry it yet, so this needs a feed re-pin. Per the Materialize Medicaid and CHIP take-up via anchored count-calibration (ACA stage pattern) #331 notes, the feed base also needs chronicle main's aa3e4de3 guard hunk for the integer year guards.
  2. us_esi_premiums_anchor_gate: within 5% of B4923C. Record NHE Table 24 and MEPS-IC as cross-checks.
  3. Docs (docs/us-esi-employer-premiums.md), the PR description and the receipts: BEA is the anchor, and NHE is shown as the alternative reading.
  4. The property tests are anchor-agnostic. Check that none hard-code $1,047.0B.

Unchanged: d1264 (whether and how to calibrate the column against a national total) and d1318 (the release) are still open. This ruling only picks the producer's scale anchor.

Context: CRFB's Medicare for All work prices the wage offset from this column, so the anchor sets its size (about 6.7% smaller under BEA).

MaxGhenis added a commit that referenced this pull request Oct 11, 2026
Max's ruling d1317 on #1189 moves the stage's scale anchor from CMS NHE
Table 24 to BEA NIPA 7.8 (B4923C). This branch reads the anchor from the
stage and does not restate it: the comments, help text and test fixtures
that named NHE now name the stage's anchor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1317)

Decision d1317 (2026-10-11) chose BEA NIPA Table 7.8 line 17 (B4923C) over
CMS NHE Table 24 as the scale anchor, with the US Chronicle feed re-pinned to
carry the banked fact.

Stage. EMPLOYER_PREMIUM_ANCHOR is now B4923C for 2023 to 2025 ($923,195M,
$977,034M, $1,027,929M, the 2026-09-30 vintage); NHE Table 24 becomes the
recorded cross-check beside MEPS-IC's active-employee total. The universe
design is unchanged: every current policyholder is priced and scaled, and
the employed part is written. On the default pool the column is $863.9B
(mean $11,679 per policyholder with a positive premium); its private part is
0.4% below MEPS-IC's own active-employee total.

Feed. The pin moves from Chronicle 51aa40f to cf9ece2 (tag
microcosm-us-feed-454-v1): 51aa40f plus the digit-text guard match from
aa3e4de3 and the B4923C package as chronicle#331 merged it. All 40,178
previous rows are byte-identical, exactly three facts are added, all 33,250
compiled targets are identical, and two rebuilds give the same digest. The
release tool refuses a feed whose B4923C values differ from the stage's
anchor (us_esi_premiums_anchor_feed_mismatches). The family is a deferred
reviewed exclusion, not a target (calibration is d1264).

Also answers the round-3 review's nits: the NHE private-employer family has
its own verdict basis, the NOEMP sentence is qualified, and the L0 export's
generic input-mass parity check is disclosed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis MaxGhenis changed the title Produce ESI employer premiums from restored ASEC policyholder fields (#454) Produce ESI employer premiums scaled to BEA B4923C from restored ASEC policyholder fields (#454) Oct 11, 2026
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

The d1317 change is at head 3cba55254. I've updated the PR description. A round-4 independent review is running.

  1. Scale source. The stage scales to B4923C: $923,195M, $977,034M and $1,027,929M for 2023 to 2025. The US feed is re-pinned to Chronicle cf9ece2 (tag microcosm-us-feed-454-v1), which is 51aa40f plus the aa3e4de3 guard hunk plus the Materialize Medicaid and CHIP take-up via anchored count-calibration (ACA stage pattern) #331 package. All 40,178 previous rows are byte-identical and three facts are added. All 33,250 compiled targets are unchanged, and two rebuilds give the same digest. The release tool refuses a feed whose B4923C values differ from the stage's pinned values.
  2. Anchor gate. It holds the anchor-universe total within 5% of B4923C. NHE Table 24 and MEPS-IC's active-employee total are recorded as cross-checks.
  3. Docs, receipts and description. All three now treat BEA as the anchor and show NHE as the alternative reading. On the default pool the column is $863.9B, with a mean of $11,679 per policyholder with a positive premium. Its private part is $665.6B, against MEPS-IC's $668.6B.
  4. Property tests. They are anchor-agnostic. They read the anchor from the module, and the test constant ANCHOR is now B4923C. NHE is kept as a named cross-check.

d1264 and d1318 are untouched.

The review approved with no blocking findings; this answers its nits.

- Four places still called NHE Table 24 the anchor: the US_DONORS note, the
  --allow-esi-premium-gaps help, a release-tool comment and a test name.
- A waived anchor-versus-feed mismatch left no record. The check is now
  _check_us_esi_anchor_against_feed: it stops a release build, lets a
  diagnostic run continue under --allow-unpinned-feed or
  --allow-esi-premium-gaps, and its result is written to esi_premiums.json
  (anchor_feed block, schema_version 3).
- Nothing in CI tied the anchor to the pinned feed. A test now compares
  EMPLOYER_PREMIUM_ANCHOR with the three facts the committed re-pin report
  says the pinned feed added, and with the pin's digest and row count. The
  release tool's check has direct tests for pass, refuse and each waiver.
- The two cms_nhe families shared an origin text that described the BEA
  series; they have their own. The manifest note uses BEA's wording for what
  the anchor counts.
- The registry comparison now compares ordered lists with target aging on
  and off (identical; 33,250 distinct names).
- Docs: status date, the two checks that hold the anchor to the feed, the
  waiver wording, and what a later feed re-pin must carry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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