# CMS 2027 listed formulary coverage snapshot

This static snapshot supports the county plan lookup's prescription comparison. It reports exact product listings, tiers, prior authorization (PA), step therapy (ST), and quantity limits (QL). It does not calculate pharmacy prices, estimate annual drug cost, recommend substitutions, or establish a person's eligibility or coverage for a particular indication.

## Sources and dates

- [CMS monthly 2027 public use file](https://data.cms.gov/sites/default/files/2026-10/73ce0d29-e440-4d9a-9608-93678a50e8c8/2027_20261008.zip).
- [CMS 2027 record layout](https://data.cms.gov/sites/default/files/2026-10/PUFRecordLayout-2027.pdf).
- [CMS 2027 methodology](https://data.cms.gov/sites/default/files/2026-10/Methodology-PUF-2027.pdf).
- [NLM October 5, 2026 Current Prescribable Content](https://download.nlm.nih.gov/rxnorm/RxNorm_full_prescribe_10052026.zip), verified against the checksum on the [official release page](https://www.nlm.nih.gov/research/umls/rxnorm/docs/rxnormfiles.html). This public subset requires no UMLS license, unlike the full RxNorm release. [Subset documentation](https://www.nlm.nih.gov/research/umls/rxnorm/docs/prescribe.html).

`sourceDate` and `checkedDate` mean the sources were checked on October 15, 2026. `archiveFilenameDate` is October 8, 2026; `memberFilenameDate` is October 31, 2026. These are filename labels and are not inferred service-effective dates. `nameSourceDate` is the NLM release date, October 5, 2026. Plan year is explicitly checked against every Basic Drugs source row.

CMS says the monthly PUF comes from the first new Medicare Plan Finder posting for a month and can differ from subsequent live Plan Finder updates. This snapshot must not be presented as guaranteed current carrier coverage.

## Data scope and joins

Four CMS members are included. Pharmacy networks, beneficiary costs, insulin cost sharing, and geographic pricing data are not fetched or published by this generator.

| Source member | Input rows | Use |
| --- | ---: | --- |
| Basic Drugs Formulary | 963,666 | 286 formularies, retaining every proxy-NDC row and its restrictions |
| Plan Information | 108,263 | 5,495 unique contract-plan-segment entries after checking repeated county rows agree |
| Excluded Drugs Formulary | 14,074 | Plan-specific supplemental coverage for drugs excluded from the basic Part D benefit |
| Indication Based Coverage Formulary | 112 | Plan-specific covered indications |

The Excluded Drugs file describes supplemental **covered** drugs for enhanced alternative plans. Its name does not mean that the listed drugs are excluded from that plan's supplemental coverage.

IDs match the existing landscape generator: `CONTRACT_ID_PLAN_ID_numericSegment`, for example `H0028_007_0`. Contract and three-digit plan IDs retain all source zeros. Segment `000` becomes `0` to match existing landscape IDs; the original is also retained as `sourceSegmentId`. Formulary IDs and proxy NDCs remain zero-preserving strings. Repeated county rows must agree on formulary, suppression, and segment. No plan-name or carrier-name fuzzy matching is used.

Supplemental and indication files identify contract and plan only. Their rows attach to each exact matching segment of that contract-plan. The generator rejects orphaned rows. There are three unique suppressed plans (18 source county rows). Suppression is a strict boolean; unknown flags fail generation. CMS methodology explains that suppression can concern pricing, pharmacy network, and/or formulary data. A suppressed entry must be shown as unavailable rather than inferred absent, even when another plan shares its formulary.

Basic absence alone is insufficient to determine the display result. Consumers must inspect plan-specific indications and supplemental coverage too. A product with an indication row is conditional on that indication; the tool does not determine whether the person meets it. A missing plan mapping, suppressed plan, failed asset load, or invalid data is unknown. Only after successful matching and inspection of all relevant published files may the result say that an exact product is not listed in this snapshot; this is not a coverage denial.

## Exact prescription names

The catalog contains all 21,514 active English RxNorm products with term types SCD, SBD, GPCK, and BPCK in the public NLM release. Canonical names retain ingredients, strength, dosage form, brand, and pack details as published. Same-RxCUI NLM `PSN` and `SY` strings are search aliases only. Aliases never change the RxCUI selected for coverage matching. There is no brand-to-generic, ingredient-only, approximate, or retired-code substitution.

All 5,703 distinct CMS RxCUIs in the three drug files resolve to exact active NLM product names. `unmapped-names.json` records this reconciliation, including a zero-length list. The generator writes the report and fails if a future input leaves any CMS product unmapped. Keeping the broader NLM product catalog permits users to select an exact product even if it appears in none of the CMS drug files.

CMS documents the Basic Drugs NDC as an 11-digit **proxy** NDC associated with the drug product. It is not proof of coverage for every dispensed package. Multiple rows for an RxCUI are retained rather than selecting a favorable row or silently merging differing restrictions.

## Published format

Location: `public/data/cms-formulary/2027-20261015/`.

- `README.md`: a published copy of this methodology. It is not included in the JSON asset hash manifest.
- `metadata.json`: schema version 1, year 2027, source URLs and dates, row and entity counts, input SHA-256 hashes, and each catalog/plan/formulary asset's byte count and SHA-256.
- `drugs.json`: array of `{rxcui,name,aliases}`. All identifiers are strings.
- `plans.json`: object keyed by the existing canonical plan ID. Values contain `{formularyId,suppressed,sourceSegmentId}`, plus optional `indications` and `supplemental` maps keyed by RxCUI. Indications are arrays of verbatim CMS disease strings.
- `formularies/{FORMULARY_ID}.json`: `{version:1,year:2027,id,sourceVersion,drugs}` with `drugs` keyed by RxCUI and containing arrays of rows.
- Basic rows: `{ndc,tier,pa,st,ql,qlAmount,qlDays}`. Tier is a number; flags are booleans; QL amount/days are source strings or null. Supplemental rows have the same restriction fields, no NDC, and `cappedBenefit:boolean`.
- `unmapped-names.json`: CMS name reconciliation and unmapped RxCUI list.

The Basic file encodes flags as Y/N; supplemental QL uses 1/0. The parser explicitly supports those source encodings and rejects anything else. QL amount and days retain decimal/source formatting. Missing amounts are null, never zero or an inferred limit.

The compact snapshot is approximately 108 MB uncompressed, divided into 286 formulary assets so a browser can fetch only formularies needed for the selected county. Source ZIPs are outside the public directory. No member is a large monolithic browser payload.

## Reproduction and proof

Run from the repository root with Python 3 (standard library only):

```sh
python3 scripts/generate-cms-formulary-data.py --cache-dir /absolute/path/to/nonpublic/cms-source
```

An optional `--output-dir` enables a separate verification build. The default writes the versioned public directory. The script checks cached bytes before use. Missing CMS sources are fetched through bounded byte ranges from the pinned outer archive: approximately 7.6 MB in total, avoiding the 2.3 GB archive and all pharmacy members. A server ignoring Range is rejected before reading its body. Missing NLM source is downloaded from the pinned 75 MB public release.

The generator pins SHA-256 hashes for every selected nested ZIP, every extracted CMS text file, and the NLM ZIP. It verifies every source row count, all 2027 Basic years, 286 formularies, 5,495 unique plans, flag encodings, identifier formatting, repeated county-row agreement, unsuppressed formulary references, and exact name reconciliation. All 963,666 Basic rows must survive into the output. Stale formulary files cause failure. Outputs and ordering are deterministic.

The current generation passed these checks with 21,514 catalog products, 5,703 resolved CMS products, zero unmapped names, 286 formularies, 5,495 plans, and three suppressed plans. A focused integration audit matched all 5,427 distinct Part D plan IDs in the existing 2027 state landscape assets, with zero unmatched IDs. All 288 catalog, plan, and formulary content hashes were independently rechecked; the largest formulary asset is 432,868 bytes. Invalid boolean encodings were confirmed to fail. Browser behavior and county integration require their own application tests; successful data generation is not evidence of a deployed or reviewed application.
