# Changelog

User-visible changes to the Verzi Healthcare Data API and dashboard. Format follows [Keep a Changelog](https://keepachangelog.com/); dates are ISO-8601 UTC.

The full commit history is at [github.com/verzihealth/cms-star-ratings](https://github.com/verzihealth/cms-star-ratings). Anything not user-visible (refactors, test-only changes, internal tooling) is deliberately omitted from this log.

---

## Unreleased

### Added
- **Source archive (`/archive/*`).** The raw, immutable layer under every parsed table: every byte a publisher served, stored exactly as served and hashed (sha256) before any parsing, with zip members as first-class files, versioned per document, and resolvable to the version in force on a date of service. Sources so far: HHS OIG LEIE (full file and monthly supplements), NCCI PTP and MUE tables (with the DME supplier MUE table and the MUE archive back to 2020), the DMEPOS fee schedule (every quarter CMS posts, 1998 to 2026), and HCPCS Level II quarterly files (2023 to 2026). Endpoints: `GET /archive/sources`, `GET /archive/{source_id}/files`, `GET /archive/{source_id}/docs`, `GET /archive/docs/{doc_id}/versions`, `GET /archive/docs/{doc_id}/as-of?date=`, `GET /archive/files/{sha256}` (metadata and a one-hour link to the original bytes), `GET /archive/files/{sha256}/text` (the stored text rendering with its renderer version), `GET /archive/runs`. The as-of answer names the rule that decided it (the publisher's stated window, or when the archive first saw the version) and never guesses. `/sources` gains `warrants`, `archive_file_count`, `last_fetch_at`, `last_change_at`, and `latest_publisher_version`. Text from files that carry CPT descriptors (AMA license) is not served. Spec: `docs/ARCHIVE_SPEC.md`.

### Internal (not a public surface)
- **Cost Plus Wellness provider contracts (schema `wellness`, internal only).** A crawler captures every contract from costpluswellness.com (page labels, the PDF, its sha256, and the fetch date), keeps every version so a changed contract is a new version, and an extractor pulls the fixed contract terms (parties, plan sponsors, effective date, term and renewal, rate basis, fee schedule named, locality, facility or non-facility, percentage of Medicare, carve-outs, patient cost sharing, claim submission window, payment timing, the TPA or same-day system, and termination), each with the page and section it came from. Every field goes through human review before it counts as a fact. The site Terms of Use limit use to internal business use and forbid republishing, so this data is not on any public surface: the `/wellness/*` endpoints are gated behind the admin key and hidden from the OpenAPI schema, the source is kept off the public `/sources` catalog, and the index files are internal. The `/wellness/rates` endpoint turns a verified percentage into dollars per CPT and locality using the Medicare fee schedules below.

### Added
- **DailyMed labels, the official SPL record.** `GET /rx/labels` searches human prescription drug labels from DailyMed (the official Structured Product Labeling record, not openFDA's copy) by drug name, labeler, or a required section, returning set_id, version, effective date, title, labeler, and which of the four key sections are present. `GET /rx/labels/{set_id}` returns one label's boxed warning, indications, dosage and administration, and contraindications section text (parsed by LOINC code, nested subsections included), its RxNorm concepts, and its pharmacologic classes. Every `dailymed_url` is pinned to the version we hold (`lookup.cfm?setid=...&version=N`), so a citation never shows newer text than we stored. `GET /rx/drug-classes` serves the pharmacologic classes from DailyMed's official class mapping (1,539 classes) with the number of labels in each, filterable by name and FDA class type (EPC, MoA, PE, CS). Chosen over openFDA's label copy because DailyMed updates daily, keeps version history, and publishes the official SPL-to-RxNorm and pharmacologic class mapping files, while openFDA's copy is weekly, flattens tables, and attaches drug codes only on an exact match. New source `rx_dailymed` at `/sources`. Migration 097.
- **Drugs@FDA: the approved-drug application record.** `GET /rx/drugsfda` searches every FDA-approved drug application (29,336 NDA, ANDA, and BLA applications) by sponsor, drug name, active ingredient, application type, and first-approval-date window, returning the sponsor, products, ingredients, first and latest approval dates, and submission and product counts, ordered by first approval date. `GET /rx/drugsfda/{appl_no}` returns one application's products (form, strength, ingredient, therapeutic equivalence code, marketing status) and its complete submission history, the original plus every supplement with approval dates. The data comes from the FDA's own Drugs@FDA data files, not openFDA's copy. `first_approval_date` is the earliest approved submission; a submission status of TA is a tentative approval (approved on the merits but blocked by another product's patent or exclusivity). New source `rx_drugsfda` at `/sources`, refreshed monthly. Migration 096.
- **FDA drug recalls, matched to the NDC spine.** `GET /rx/recalls` serves the openFDA drug enforcement dataset (about 18,000 recall enforcement reports) filterable by NDC, ingredient, recalling firm, hazard class (Class I, II, III), status (Ongoing, Completed, Terminated), and report-date window. Each recall carries its classification, status, firm, reason, distribution pattern, and report, initiation, and termination dates. NDCs are extracted from three places, because the openFDA drug-code block is present on only about a fifth of records: the openFDA block, the free-text product description, and the code-info lot list. Each extracted NDC is normalized to 11 digits and matched against the current NDC directory; matched NDCs also resolve to their RxNorm ingredient. The match is fuzzy, so the response reports it honestly: `match_stats` and the `methodology_note` state that about 51% of recalls carry a parseable package NDC and about 19% match at least one NDC to the current directory (an older recall cites a discontinued NDC no longer listed). New source `rx_drug_recalls` at `/sources`, refreshed weekly. Migration 095.
- **FDA drug establishments are now queryable.** The FDA Drug Establishments Current Registration Site (DECRS) has been loaded since migration 089 and no endpoint could read it. `GET /rx/establishments` searches all 9,878 registered drug plants worldwide by operation, ISO-3 country, and firm or registrant name, returning FEI number, firm, country, and the registered operations. `operation=API MANUFACTURE` isolates the 2,400 plants that make an active ingredient, the supply-side view behind a drug shortage. `GET /rx/establishments/operations` lists the 15 operation types with counts. The `operations` array is now split into one element per operation (the source file separates them with semicolons, not commas), so a plant that registers "ANALYSIS; API MANUFACTURE; PACK" now matches an API-maker filter; the previous load stored the whole string as one element and missed those plants. The public file does not link a plant to the NDCs it makes; the `methodology_note` says so.
- **CMS Medicare fee schedules (public domain), loaded for internal rate math.** The Medicare Physician Fee Schedule (`medicare.mpfs_rvu`, `medicare.mpfs_gpci`, and the conversion factor) and the Ambulatory Surgical Center payment rates (`medicare.asc_rates`). With these, "a percentage of Medicare" becomes dollars per CPT code and locality. The non-facility allowed amount is `CF x [(work_rvu x work_gpci) + (pe_rvu x pe_gpci) + (mp_rvu x mp_gpci)]` for the named locality. Loaded for the recent years CMS publishes, refreshed quarterly. Today the only reader is the admin-only `/wellness/rates`, so these two sources are held off the public `/sources` catalog until a public fee-schedule endpoint ships (they will return to the catalog in the same change). Note: a CMS 2-digit locality code is not unique nationally, so resolve a locality by name where the code is shared.
- **FDA Orange Book and the generic launch radar.** The Orange Book has been loaded since migration 089 and no endpoint could read it. `GET /rx/orange-book` searches all 48,664 approved products by trade name, ingredient, applicant, application type (N brand, A generic), product type, and therapeutic equivalence code. `GET /rx/orange-book/launch-radar` serves `rx.v_generic_launch_radar`: the 2,457 brand prescription products with no approved generic of the same ingredient, dosage form, and strength, ordered by the date the last listed barrier falls, filterable by ingredient, applicant, and date window, with `unprotected=true` for the 866 products carrying no patent and no exclusivity at all. `loss_of_exclusivity` is an outer bound rather than a forecast: it is the latest listed patent or exclusivity date, so a late device or formulation patent can sit years past the compound patent, and most generics arrive earlier through challenges and settlements that this data does not carry. The `methodology_note` says so on every response.
- **Drug economics spine (schema `rx`).** The platform now speaks NDC. Seven new sources at `/sources`: the FDA NDC Directory (116K products, 218K packages, normalized to 11-digit NDCs), RxNorm prescribable content (NDC-to-RXCUI links and ingredient relationships, no UMLS license needed), NADAC weekly acquisition cost stored as change-only price history per NDC from 2013 forward, CMS Spending by Drug for Part D, Part B, and Medicaid (unpivoted to one row per drug, manufacturer, and year) plus the quarterly Part D file and Part D by geography and drug, Medicaid State Drug Utilization Data from 2019 forward, openFDA drug shortages, and the Mark Cuban Cost Plus Drug Company public price list captured as dated, change-only snapshots. Refreshed on the existing weekly, monthly, and quarterly pipeline cadences. API endpoints follow in a later release.
- **AHRQ MEPS Prescribed Medicines, 2018 to 2024.** Fill-level prescription events with 11-digit NDC, quantity, days supply, payer splits, and person weights, joined to the Full-Year Consolidated file for age and insurance coverage (`rx.meps_rx_fills`, about 200K fills per year). `rx.mv_meps_ingredient_mix` gives the weighted drug mix by year, payer segment, and active ingredient; `person_segment` `private_under_65` is the employer-covered population, which is the honest basis for commercial drug-mix questions that Part D cannot answer. `GET /rx/meps/mix` serves it with median generic NADAC beside the per-unit amount payers actually paid.
- **`/rx/*` Drug Economics API.** `GET /rx/summary` (counts and freshness), `GET /rx/ingredients` and `/rx/ingredients/{rxcui}` (RxNorm ingredients with NDC counts, median generic NADAC, CMS spending, Cost Plus matches, shortages), `GET /rx/ndcs/{ndc11}` and `/rx/ndcs/{ndc11}/nadac` (package profile and change-only price series), `GET /rx/nadac/movers` (largest price moves in a window), `GET /rx/spending` and `/rx/spending/top` (CMS Spending by Drug for Part D, Part B, Medicaid), `GET /rx/shortages`, `GET /rx/cost-plus` and `/rx/cost-plus/{slug}` (Cost Plus public price beside median NADAC, with the implied acquisition cost from the published formula and dated history), `GET /rx/employers` and `/rx/employers/{ein}` (DOL 5500 health plans with funding class, PBM, disclosed PBM fees, and drug-coverage carriers; default sort is active participants, largest first), and `GET /rx/pbms` (PBM rollup by plans, covered participants, and disclosed compensation). Every response carries a `methodology_note`.
- **DOL Form 5500 now covers every industry, with Schedule C and Schedule A.** `dol_5500_filings` previously held healthcare-NAICS sponsors only. It now loads all filings and records the welfare-benefit and funding flags, with a derived `health_funding_class` (self-funded only, self-funded with stop-loss, insured only) for health plans. New `dol_5500_service_providers` (Schedule C Part 1 Item 2: every service provider a plan paid, with direct and indirect compensation, DOL service codes, and a PBM flag with a normalized PBM family) and `dol_5500_insurance_contracts` (Schedule A: carrier, persons covered, health, drug, and stop-loss benefit flags, premiums earned, claims paid). The healthcare-only `financial_filings` aggregation is unchanged.
- **Which self-funded employers buy pharmacy and medical from one conglomerate.** Every DOL 5500 health plan now carries an `integration` class: `same_parent` when the PBM on Schedule C and the health carrier or medical administrator belong to one company (Caremark with Aetna, Express Scripts with Cigna, Optum Rx with UnitedHealthcare or UMR, CarelonRx with Anthem), `blues_affiliated` for Prime Therapeutics with a Blue Cross plan, `different`, or unclassified when a plan names no PBM or no carrier. Self-funded plans file no Schedule A for their medical administrator, so the administrator is read from Schedule C as well. For 2023 that is 295 same-parent self-funded plans covering 3.29M employees. `GET /rx/employers?integration=same_parent` and `GET /rx/employers/integration` (counts, covered employees, and disclosed PBM fees by class and by parent, plus the largest plans).
- **Medicare Part B ASP payment limits and the NDC-to-HCPCS crosswalk, 27 quarters since 2020.** What Medicare pays per billing unit for clinician-administered drugs (`rx.asp_pricing`) and the CMS crosswalk from every NDC to its HCPCS code with billing units per package (`rx.asp_ndc_hcpcs`), loaded quarterly with preliminary files flagged and replaced by the final. `GET /rx/asp` searches by description, HCPCS, or NDC. `GET /rx/biosimilars` puts each FDA Purple Book biosimilar beside its reference product's payment limit (Stelara J3358 against its seven biosimilars, Avastin, Remicade, Prolia and Xgeva) and the cheapest Cost Plus product of the same molecule.
- **Healthcare construction signals — new product surface.** Early construction and expansion signals from public building-permit portals, state Certificate of Need / Determination of Need programs, and statewide project registries across 19 states, refreshed daily by a new pipeline. Permits are keyword-screened and LLM-verified for healthcare relevance; CON/DoN filings are healthcare by definition. Each signal is conservatively resolved (state-scoped exact and fuzzy matching) to a facility, health system, and an organizational contact from public NPPES data where possible. Three endpoints: `GET /signals` (search by state, city, category, system, minimum value, recency), `GET /signals/summary` (counts and dollar volume by state and category), and `GET /signals/systems` (per-system rollup ranked by dollar volume). ~15.6K signals at launch spanning a 400-day window, including hospital towers, ASC build-outs, imaging centers, and senior-care projects with reported values from thousands of dollars to nearly $1B.
- **MSSP financial and quality results, PY2013 to PY2024.** We held 5,361 Shared Savings Program ACOs and 182K participants but no financial results for any of them. The gap is closed. New `mssp_financial_results` holds **5,476 ACO-years** with benchmarks, expenditures, shared savings and losses, quality scores, provider counts by type, beneficiary demographics, risk scores, and utilization. New `mssp_quality_measures` holds **127,374 per-measure results** across 74 distinct measure codes. Three endpoints: `GET /vbc/financials` (search and rank, including `sort=losses`), `GET /vbc/financials/summary` (program rollup by year and track), and `GET /vbc/acos/{aco_id}/financials` (one ACO's full history plus cumulative earned savings). The PY2024 load reconciles exactly with the CMS fact sheet on four independent figures: 476 ACOs, 10,326,340 assigned beneficiaries, $4.12B in performance payments, and 75% of ACOs earning them.
- **Rates are normalized to percent across all 13 MSSP performance years.** CMS switches these columns between a 0-1 fraction and a 0-100 percentage from file to file, and the `%` suffix does not track the switch: PY2024 dropped the suffix but kept the 0-100 scale. Left as published, a PY2018 savings rate of 0.0144 and a PY2024 savings rate of 4.215 describe comparable performance but sort 300 places apart. Savings rate, minimum savings and loss percentages, share and loss rates, quality score, and the dual and long-term-institutionalized percentages are all resolved to percent. Every row also carries `source_row`, the CMS row exactly as published, so the raw values remain available.
- **`qual_score_note` records why a quality score is absent.** PY2014 publishes "P4R" for 119 first-year ACOs, meaning they were scored on reporting the measures rather than on performance. That token now has its own column, so it is distinguishable from CMS small-cell suppression.
- **ACO REACH refreshed to PY2024, with PY2021 to PY2024 history and the full published column set.** REACH was frozen at one performance year (PY2023) since launch. It loaded a fraction of what CMS publishes. The layer now holds **421,523 participant records across four performance years** (was 128,548 for one year), **399 financial and quality records** (was 132), and **578 entity-years** covering PY2021 to PY2026 (was 74 for one year). Participant records gain participation dates, the APO election, and all 14 benefit-enhancement waiver flags. The flags cover telehealth, the SNF 3-day stay, post-discharge home visits, concurrent hospice care, and the rest. Financial results grow from 39 columns to 115. The new columns give provider counts by type (PCP, specialist, NP, PA, CNS, FQHC, RHC, CAH), every quality subscore including CAHPS and the 11 stewardship measures, beneficiary demographics, the inpatient expenditure split, and utilization rates. `GET /vbc/reach` and `GET /vbc/reach/{entity_id}` accept a `year` parameter. The participant list is now paged with `provider_limit`, `provider_offset`, and `provider_total`. One entity reached 18,506 participants in a single year.
- **`GET /accuracy/discipline-coverage`** — we now publish how much of the nation's practitioner discipline we actually hold, per state, benchmarked against the NPDB Public Use File (the only national count of state licensure actions and exclusions). Two classes are reported separately because they are different populations: exclusions (federal LEIE + state Medicaid lists) currently cover **52.1%** of NPDB-reported excluded practitioners for 2023–2025, while state board licensure actions cover **0.3%** — Colorado is the only board feed loaded so far. The gaps are published alongside the wins. Unauthenticated, like the rest of `/accuracy`. The benchmark file carries no names or NPIs; only aggregate counts are published, and state counts below 11 are withheld.
- **Primary-source state license data: Washington, Connecticut, Colorado** — the first state-board licenses loaded from the states' own files rather than NPPES self-attestation. All three come from official state open-data portals with daily refresh, no accounts, fully scriptable. New sources `wa_doh` (2,444,206 credentials), `ct_dph` (785,467 health-profession credentials), and `co_dora` (1,524,923 individual licenses) at `/sources`, with NPI linkage backfilled from NPPES license-number matching (digits + last name, confirmed against first name in WA; digit + name match in CT/CO).
- **Colorado disciplinary actions** — the CO DORA file carries structured discipline (case number, action, effective/complete dates). Of 51,848 distinct disciplinary records, the 3,266 that resolve to an NPI load into `license_disciplinary_actions`; the rest carry no NPI and are not matched by name. First state-board disciplinary source beyond exclusion lists.
- **Washington NPI coverage tripled** — WA licenses now resolve to 136,556 providers, up from 43,373, lifting NPI-linked active clinicians across the three states from 22.9% to 28.7% (40% within WA). The previous match required NPPES and WA DOH to agree on the license prefix, but they use different codes for the same credential, so most licenses could never link. Precision improved at the same time: 47 NPIs that had been attached to two different people are gone, and every linked row now agrees with NPPES on first name. Affects `/licenses/{npi}`, `/licenses/{npi}/disciplinary`, and the `/provider/{npi}` dossier.
- **WA/CT/CO refresh daily** — the three state license sources now run on a new daily pipeline cadence (11:00 UTC), each downloading its own file. `/sources/{id}/history` shows the daily trail.
- **IRS 990 coverage extended to tax years 2020–2023** — the bulk XML parser previously reached only 2024-index filings because the IRS index CSVs for earlier years omit the batch-file column needed to locate each filing's XML. That mapping is now recovered by reading each archive's directory listing directly, without downloading the multi-gigabyte archives. Result: **21,439 Form 990 filings across 5,270 healthcare EINs** (was 3,541 / 3,375) and **144,944 executive-compensation records** (was 24,027), feeding `/systems/{id}/financials` and `/systems/{id}/financials/executives`.
- **Executive-to-provider links now populate** — `executive_compensation.matched_npi` and the `irs_990_schj` attribution signals had been empty since launch (two bugs: candidates were filtered by a state that was never recorded for the filing organization, and names were parsed in the wrong order). Fixed: **44,763 executives now resolve to an NPI** (14,070 high-confidence, 30,693 medium) producing **44,025 new employment-attribution signals**. Matching stays conservative — an executive whose name fits more than one provider is left unmatched rather than guessed, so common names like "Robert Grossman" (17 NPIs nationally) produce no link.
- License Intelligence: primary-source verification links. Each license on `GET /licenses/{npi}` and each alert on `GET /licenses/alerts/recent` now carries a `verify_at` block with the state board lookup and disciplinary-page URLs. `GET /licenses/state/{state}` gains a `board_directory` block. Directory: 222 board portals across all 50 states + DC, link-checked; dead links are excluded.
- **Oregon joins the exclusion layer (34 states)** — OR publishes names + per-person exclusion-letter PDFs only (no dates, no NPI), served from a SharePoint list the loader reads through the public SOAP endpoint. Thin, but official — and it completes coverage of every state with an obtainable list. Letter URLs are kept in each record's raw payload.
- **`GET /ma-events/recent` now returns data** — a new SEC 8-K parser scans every logged healthcare 8-K for M&A item codes (Entry into Material Agreement, Completion of Acquisition/Disposition) and loads typed events into `ma_events`: acquisition announced, acquisition completed, and divestiture, each linked to its SEC filing. Target names and deal values are extracted only when confidently matched — otherwise the event carries the filing link and item facts without guessing. Initial load: 24 events (2023–2026) across Tenet, CHS, UHS, Acadia, Select Medical, Surgery Partners, Addus, Astrana, Pennant, Encompass, and Enhabit, including Astrana's $708M Prospect Health acquisition.
- **Physician employer-move detection is live** — `physician_employer_moves` now carries 207,725 arrival/departure events, derived from three PECOS reassignment snapshots (2026-06-10, 2026-08-13, 2026-08-17). The view had been empty since launch because only one snapshot existed; this week's DAC/PECOS refreshes supplied the comparison points.
- **Exclusion Screening page on the dashboard** (`/exclusions` at app.verzi.health) — screen a provider by NPI against the federal LEIE + all 33 state lists with a single verdict banner, or search state lists by name (most state records carry no NPI). Shows live coverage stats (states loaded, record counts, share of state-excluded NPIs absent from the federal list). Backed by new unauthenticated `/demo/exclusions/*` endpoints (check, search capped at 25 rows, coverage).
- **Exclusion Screening published at `/resources/exclusion-screening`** — product overview for the combined federal + state exclusion layer: per-state coverage table, methodology (primary sources, conservative NPI validation, `oig_reciprocal` handling), and the API surface. Listed in the `/resources` catalog.
- **State exclusion endpoints under `/exclusions`** — the state Medicaid exclusion layer is now queryable directly: `GET /exclusions/comprehensive/{npi}` (one-call federal LEIE + all 33 state lists; the top-level `excluded` flag covers both), `GET /exclusions/state/{npi}` (state-list records for an NPI), `GET /exclusions/state/search` (name/state/entity-type search — most state records carry no NPI, so name search is the primary screening path), and `GET /exclusions/state/coverage` (which states are loaded, per-state counts, LEIE overlap, last-load timestamps). `POST /exclusions/screen` now also returns `state_excluded` hits alongside the federal ones, and `GET /exclusions/crossref/{npi}` carries `state_exclusion_records` plus a risk flag when an NPI is state-excluded but absent from the federal LEIE.
- **State Medicaid exclusion lists — batch 2: 12 more states, 8,598 records (33 states, 85,697 total)** — DE, NE, GA, NV, IA, AK, HI, AZ, KS, NC, WV, WY join the `state_exclusions` layer from their primary state sources. Seven of these publish PDF tables; the pipeline runtime now bundles pdfplumber, so all 33 states — including NJ from batch 1 — sync monthly. Minnesota is the one newly-confirmed dead end (Radware captcha on the DHS page); Oregon stays deferred (names-only SharePoint list). Only remaining unbuilt states are the five blocked ones (MA, NH, MN, ME, FL) and thin OR.
- **State Medicaid exclusion lists — 21 states, 77,099 records** — new `state_exclusions` table loaded from each state's primary public source (CA 22.5K, TX 13.4K, NY 9.0K, PA 6.7K, LA 5.9K, MI, NJ, AR, OH, AL, MD, SC, KY, CO, MO, WA, MS, ND, MT, IN, TN). These lists are additive to the federal OIG LEIE: 69.8% of the 11,286 distinct state-excluded NPIs do not appear in LEIE at all. Records with a valid NPI also materialize into `license_disciplinary_actions` (visible in `/licenses/{npi}/disciplinary` and `/licenses/alerts/recent`); rows a state marks as federal-OIG mirrors are flagged `oig_reciprocal` and excluded from that materialization to avoid double-counting. Each state is a registered source (`{st}_medicaid_exclusions`) with independent load history at `/sources`; 20 states sync monthly in the pipeline (NJ's PDF source loads via script until the Lambda bundles a PDF parser). NPI handling is conservative: state-published NPIs only, validated by check digit — no name matching.
- **`facility_affiliations` on DAC provider records and the `/provider/{npi}` dossier** — all facility types a clinician is affiliated with (hospital, home health, hospice, nursing home, dialysis, IRF, LTCH), with CCN and facility type. The existing `hospital_affiliations` field keeps its shape and now carries hospital names joined from our facilities data.
- **Authorized Official employment signals** — every NPPES Type 2 organization names an Authorized Official; when that name uniquely matches exactly one individual NPI nationally, an attribution signal now links the two (687,084 signals, source `nppes_authorized_official`, confidence 0.60). Signals carry a new `employer_npi` identifier field — the first signal source whose employer is identified by NPI rather than EIN or PAC ID — visible in `/provider/{npi}/employment.attribution_signals[]`.
- **License-state corroboration signals** — 1.24M signals (source `nppes_address_match`, type `state_license_address`, confidence 0.70) marking providers whose active license state matches their PECOS employer's primary state. This signal class existed in code but had never successfully emitted — its signal type was never whitelisted and its upsert broke when the license natural key changed; both fixed.


### Changed
- **DAC, PECOS reassignment, and NPPES refreshed** after 66 days stale — DAC to the 2026-07-31 file (2.01M rows), reassignment re-derived (1.77M), NPPES to the August file (9.73M providers, now including Authorized Official name/title/credential on 1.96M organizations). `/sources` freshness reflects the reloads.
- **Source freshness now distinguishes "no refresh commitment" from "late"** — sources with no expected rhythm (hand-curated mappings, static reference crosswalks, as-needed enrichment, declared-but-not-yet-live loaders) previously fell back to monthly staleness thresholds and showed permanently stale badges at `/sources`. They now report an explicit `exempt` freshness with the reason, and never degrade `/status`. A genuinely late scheduled source still turns `/status` amber or red exactly as before.
- **`ca_medical_board` source entry declares its real cadence** — the entry is NPPES-derived (no state loader exists yet) but declared weekly cadence, so it drifted to "critical" by construction and kept `/status` red for two months. Now monthly, matching the NPPES reload cycle that actually feeds it; the notes field says so explicitly.
- **Hospital Financial & Regulatory Deep-Dive** — a coordinated set of additions closing several hospital financial/regulatory gaps in one pass. See `docs/HOSPITAL_FINANCIALS.md` (published at `/resources/hospital-financials`) for the full writeup.
  - **HCRIS deep worksheet parse: balance sheet, charity care, cost centers, DSH** — the HCRIS loader previously extracted 8 numeric coordinates from the same NMRC file we already download quarterly; it now extracts across four more worksheet domains. New tables: `hcris_balance_sheet` (Worksheet G — full balance sheet with derived current/quick/debt-to-equity ratios), `hcris_uncompensated_care` (Worksheet S-10 — charity care charges/costs, bad debt, total uncompensated care), `hcris_cost_centers` (Worksheet A + C Part I — charges, total costs, and salary/non-salary split for ~20 named cost centers plus all other reported lines). New `hcris_reports` fields: `operating_income`, `other_income`, `total_salaries`, `dsh_patient_pct`, `dsh_payment_amount`, `medicare_days`/`medicaid_days`/`medicare_discharges`/`medicaid_discharges` (populating the previously-unused `medicare_days_pct`/`medicaid_days_pct` columns). New endpoints: `GET /cost-reports/{ccn}/balance-sheet`, `GET /cost-reports/{ccn}/charity-care`, `GET /cost-reports/{ccn}/cost-centers`. Every coordinate was empirically validated by reconciling internal worksheet subtotals and cross-checking against a real hospital's published financials — every headline figure matched exactly or within trivial rounding.
  - **Hospital Readmissions Reduction Program (HRRP) penalties** — new `hrrp_penalties` and `hrrp_measures` tables loaded from the CMS IPPS Final Rule HRRP Supplemental Data Files (FY2021–FY2026). Three new endpoints under `/vbc/`: `GET /vbc/hrrp/{ccn}` (payment adjustment factor history), `GET /vbc/hrrp/{ccn}/measures` (per-condition excess readmission ratios for AMI, COPD, HF, pneumonia, CABG, THA/TKA), and `GET /vbc/hrrp` (search/rank hospitals by penalty severity). `payment_reduction_pct` is always derived from the payment adjustment factor rather than trusted from CMS's own column, whose unit is inconsistent across fiscal years in the source files.
  - **Case Mix Index (CMI) and wage index** — new `ipps_impact` table loaded from the CMS IPPS Final Rule Impact File (FY2023–FY2026). New endpoint `GET /hospitals/{ccn}/case-mix-index` returns the full fiscal-year history (CMI, transfer-adjusted CMI, wage index, DSH patient percentage, resident-to-ADC ratio); `GET /hospitals/{ccn}` now includes a compact `case_mix_index` block with the latest year. CMI is always resolved to the current MS-DRG grouper version for that fiscal year, so a schema change isn't needed when CMS increments the grouper annually.
  - **Hospital service lines, accreditation, and teaching status** — CMS Provider of Services (POS) file now feeds a new `facility_services` table (~50 curated clinical service-line flags — cardiac cath lab, open heart surgery, NICU, transplant services, imaging, behavioral health, and more) plus new `facilities` columns for accreditation agency/dates and medical school affiliation. New endpoint `GET /hospitals/{ccn}/services`; `GET /hospitals/{ccn}` now includes `accreditation_agency`, `accreditation_expiration_date`, and `medical_school_affiliation`. Accreditation-organization and teaching-status code labels were empirically validated (not pulled from an official crosswalk, which wasn't accessible) against known accreditor market share before shipping — unverified codes are stored raw rather than guessed. `scripts/enrichment/enrich_from_pos.py` gains two new always-refresh enrichment passes (service availability and accreditation legitimately change over time, unlike the existing backfill-only legal-name/bed-count passes).
  - **CBSA (metro/micro area) population estimates** — new `cbsa_population` table loaded from Census Bureau annual estimates (2020-2025). `GET /hospitals/{ccn}` now includes a `cbsa_population` block (latest year + year-over-year growth); new endpoint `GET /market-intelligence/cbsa/{cbsa_code}/population` for the full history.
  - **ClinicalTrials.gov integration** — new `clinical_trials`, `clinical_trial_sites`, and `clinical_trial_investigators` tables loaded from the free ClinicalTrials.gov API v2 (193,782 US-located studies, 2.37M site records, 188K investigators). Site-to-facility and investigator-to-NPI matching uses a conservative normalized name + location match (109,684 sites and 82,639 investigators matched with zero ambiguous guesses — ties are left unmatched rather than resolved arbitrarily). New endpoints: `GET /clinical-trials` (search), `GET /clinical-trials/{nct_id}` (detail with matched sites/investigators), `GET /hospitals/{ccn}/clinical-trials` (facility rollup by status/phase), `GET /provider/{npi}/clinical-trials` (investigator's trials). `GET /provider/{npi}` now includes a compact `clinical_trials` dossier block.
  - **Facility square footage, depreciation, and EBITDA** — extends the same HCRIS Worksheet A/B-1/C parse: `hcris_cost_centers` now carries `square_footage` per cost center (Worksheet B-1 reuses the identical cost-center line numbers already validated), and `hcris_reports` gains `total_facility_sqft`, `depreciation_expense` (Worksheet A-7 Part III), and a derived `ebitda` (operating income + depreciation). Closes the EBITDA gap explicitly left open in the initial HCRIS deep-parse work, which needed a depreciation figure not yet located at the time. All figures validated exactly or within trivial rounding against a real hospital's published numbers.
- **Enterprise contact-sales tier** on the dashboard pricing section — the $1,499/mo tier is renamed "Scale" and a new custom-priced Enterprise tier covers the employment + financial intelligence lines, custom data cuts, security review, and MSA. Procurement note added: 100% public and licensed data, no PHI, no BAA required.

### Fixed
- **`/sources` understated our own freshness on 20 datasets we hold and serve.** The page reported "never loaded" for Part D Prescriber (102.9M rows), Physician Utilization (29.1M), Open Payments (27.7M), HCRIS, MA Star Ratings, HSAF, CDC PLACES, ClinicalTrials.gov, HRRP, IPPS Impact, the VBC/ACO layer, the CBSA crosswalk and population files, Dartmouth, AHRQ SDOH, Medicare Geographic Variation, CMS POS service lines, Medicaid SDUD, the SEC 8-K feed, and the employer typology crosswalk. Only the pipeline ever wrote `last_loaded_at`, and it writes it only for runs it wrapped. A loader run by hand never reached it, and neither did a yearly source that had already loaded its last published year: the Checker finds nothing new, so the loader never runs, so the page keeps saying never. Every registered loader now stamps the catalog itself when it succeeds, and a test fails the build when a new one does not.
- **`/sources` published per-run numbers as record counts.** The page claimed we hold 73 hospitals, 17 nursing homes, 6 hospices, 3 long-term care hospitals and 8 clinicians. Those were the counts of CSV files inside the last quarterly CMS ZIP. NPPES showed 73,243, the size of one weekly delta against a 9.7M-row registry. `last_record_count` now means what it says: the rows a source currently holds. The per-run number stays in the load history at `/sources/{source_id}/history`.
- **DAC stopped reporting its own refreshes.** The pipeline calls the source `dac`; the published catalog row is `dac_providers`. Every audit and freshness write went to a source_id no row has, and an `UPDATE` that matches nothing is not an error. So `/sources` froze DAC at its last manual load while the monthly pipeline kept reloading it, and `/sources/dac_providers/history` showed no runs at all. The two ids are mapped explicitly now, and a mismatch is a test failure rather than a silent no-op.
- **`GET /rx/demand` county and metro rows now cover rural counties and Connecticut.** A prescriber's county was taken from the ZIP-to-CBSA crosswalk, so prescribers in about 1,200 counties outside any metro or micro area had no county row, and Connecticut had no county or metro rows at all. The county is now the one that holds most of the practice ZIP's population, and the metro is that county's CBSA.
- **Paging through a list endpoint no longer repeats or skips rows.** Many list endpoints sorted on columns that tie, such as participant counts, dollar amounts, distance, or last name. Postgres returns tied rows in no fixed order, so `limit` and `offset` pages could return the same row twice and never return another. Every paged query now ends its sort on a unique key. This affects `/rx/employers`, `/rx/employers/metros`, `/rx/asp`, `/rx/biosimilars` and the other `/rx` lists, facility searches by radius, Part D and physician utilization, Open Payments, OIG exclusions, HPSA, DAC, PECOS, clinicians, HCRIS, community health, market opportunity, signals, and the VBC lists.
- **`/status` and `/sources` no longer flag healthy CMS sources as critical.** Eight CMS Archive sources declared a `weekly` update cadence. That is how often the pipeline checks them, not how often CMS publishes, and the freshness thresholds read that field. Every one of them turned critical between releases while working correctly, leaving `/status` red with 10 critical sources. Measured from the CMS archive API over the last 13 releases, the median gap is 91 days for hospitals, long-term care, inpatient rehab and hospice, 84 days for home health and dialysis, and about 30 days for nursing homes and doctors/clinicians. Cadences now match the publisher, so the two monthly sources stay on monthly thresholds rather than being lumped in as quarterly. `/status` is green with zero critical sources. How often the pipeline checks is unchanged.
- **The homepage no longer shows invented numbers.** It carried a hardcoded fallback row count, source count, facility count and clinician count, plus sample market-share and community-health panels that named real health systems beside fabricated figures. The fallbacks rendered whenever `/platform/stats` was unreachable, and the sample panels rendered always, so a visitor could not tell a real figure from a placeholder. The page now renders live figures, and omits a count entirely when the API does not answer.
- **Column lookups now fall back to a case-insensitive match.** CMS re-cases column names between files: the MSSP savings rate is `Sav_rate` in twelve files and `Sav_Rate` in PY2021, and the ambulance expenditure is `CapAnn_AmbPay` except in PY2014, where it is `CapAnn_ambpay`. The loader's fixed set of name variants missed both and produced silent NULLs for a whole year. Found by checking every field against every file header rather than by spot-checking.
- **ACO REACH participant records were dropped on load.** The table held a unique key on ACO, performance year, and participant name. CMS publishes one row per participation spell instead. A clinician whose capitation arrangement changes mid-year is several rows with several start dates. The key discarded 16% to 33% of each year's published rows. The key is removed. The loader now collapses only rows that are identical in every column. A REACH participant list is a set of dated spells, not a set of names.
- **Hospital affiliations were empty on DAC and dossier endpoints** — CMS removed the `hosp_afl_*` columns from the DAC national file in 2026; the affected endpoints read those columns and silently returned no affiliations. They now read the Facility Affiliation dataset (2.25M rows). `GET /dac/hospital/{ccn}/affiliates` matches providers through the same dataset.
- **Weekly CMS quality data resumed after a months-long silent stall** — CMS replaced its provider-data archive API in 2026. The old endpoint froze its index around Feb–April and later returned 503, so the weekly sync saw "no new data" and hospital, nursing home, hospice, and other CMS quality sources stopped updating (hospitals and hospice last refreshed 2026-02). All downloaders now use the new archive API, and the stalled quarters are backfilled.
- **`/status` and `/sources` no longer hide staleness on sources without load-tracking history** — a source with no recorded load timestamp used to classify as "never loaded" and was excluded from the health rollup, which is how the stall above went unflagged. A source that has never recorded a load is now judged by its age since registration on the same cadence thresholds, so it turns stale/critical like any other source.
- **`GET /hospitals/{ccn}` returned 500 for every hospital from ~2026-07-23 to 2026-08-13** — the hospital deep-dive code deployed on 07-23, but its database migrations were not applied to production, so every detail request failed on a missing table. Search, history, and other endpoints were unaffected. Fixed by applying migrations 045–059 and loading the six new data sources (HRRP, CMI/wage index, service lines, CBSA population, HCRIS deep worksheets, ClinicalTrials.gov) to production. Two guards now prevent recurrence: migrations run automatically before each deploy and abort it on failure, and a synthetic probe checks the public endpoints every 5 minutes and alarms on consecutive failures.
- **Hospice facility count corrected: 8,391, not 14,067** — the 2020-08 and 2020-11 CMS hospice files quote CCNs and ZIPs in Excel's text-forcing format (`="011500"`). The loader stored the wrapper literally, which created 5,676 duplicate facility rows under phantom IDs and ~823K quality/patient-experience rows that never joined to the real facility. The duplicate rows are removed, the historical measure rows are re-keyed to the correct CCNs (no history lost), and the loader now strips the wrapper so reloads cannot reintroduce it. If you had paged through `/hospice`, roughly 40% of rows were duplicates of other rows; per-CCN lookups for the affected facilities previously required the wrapped literal and now work with the plain CCN.
- **HCRIS `net_income` was silently wrong for every hospital** — the worksheet coordinate pointed at line 02500 ("Other Income"), not the actual Net Income line (02900). For a hospital with material non-operating income, this understated net income by orders of magnitude. Also fixed: `total_discharges` was reading a Medicare-days count instead of an actual discharge count, which meant `cost_per_discharge` was calculated against the wrong denominator for every loaded report. Both are corrected in the loader; existing rows are corrected on the next reload.
- **HCRIS "latest report" queries could nondeterministically return a stale, superseded report** — `/cost-reports/{ccn}` and related endpoints picked the most recent report by fiscal year end with no tiebreaker, so a hospital with two report submissions for the same fiscal year (e.g. an amended resubmission) could have either one served depending on unspecified row order. Now breaks ties by preferring the higher `rpt_rec_num` (more recently processed). Affects roughly 13% of loaded report-years.
- **HCRIS balance sheet quick ratio now uses the standard definition** (current assets minus inventory and prepaid expenses) rather than summing individual line items, which understated it.
- **HCRIS square footage bounded against known data-entry anomalies** — a small number of source submissions carry physically-impossible square footage values (one report showed 22 billion sq ft; a worksheet subtotal line was being read as if it were a single cost center, showing up to 200M sq ft). Both are now excluded/nulled rather than surfaced.

### Changed
- **Dashboard redesigned on the Verzi corporate brand** (verzihealth.com): deep green-black theme (`#00110F`), turquoise accent, pill CTAs, JetBrains Mono as the data voice. The homepage hero now shows a **live pipeline ledger** — real per-source refresh dates and freshness straight from the public `/sources` registry — and a full **data atlas** table of all 45 sources with license, cadence, and last-load date. Every headline stat carries a provenance chip linking to the registry. PDF/print export keeps the light palette.
- **Dashboard homepage hero** rewritten verify-first: "Provider data you can actually verify," with direct links to `/sources` and `/accuracy` and a "Check Our Accuracy" call-to-action. Site metadata updated to match.

### Removed
- **Named-system case study retired from `/resources`** — the catalog now carries methodology + product docs only. The accuracy benchmark at `/accuracy` continues to publish per-system variance; systems under private evaluation are excluded from public surfaces.

---

## [2026-07-08]

### Added
- **Per-source freshness monitor** — `/status` and `/sources` now show whether each registered source is refreshing within its declared cadence. New shared module `api/providers/_shared/freshness.py` classifies each `source_registry` row as `fresh` / `stale` / `critical` / `never` against per-cadence thresholds (daily = 2/8d, weekly = 10/21d, monthly = 45/100d, quarterly = 100/200d, annual = 400/550d). Customer-facing consequence: if we say a source is monthly and it's actually 4 months stale, `/sources` shows a red "stale · 121d" badge with an explanatory note instead of hiding the drift. The `/status` pipeline check switches from "look at the single most recent successful run" (which was silently masking slow-cadence outages) to a per-source rollup that fails when any source is critical. Three CloudWatch alarms in `infra/template.yaml` (`PipelineNoRunAlarm` 25h, `PipelineWeeklyAlarm` 8d, `PipelineFailureAlarm` any) implement the same principle server-side. Alarm on the absence of success, not just the presence of failure.
- **NPPES Authorized Official schema** — migration 052 adds 8 AO columns (last/first/middle name, title, credential, name prefix/suffix, phone) to `nppes_providers` plus a partial index on the AO name pair for Type 2 orgs. Loader updated to capture them from the NPPES bulk file. Fields will populate on the next NPPES full-refresh run (monthly cadence). Unblocks the future `authorized_official` attribution signal migration.
- **Tenet Healthcare decomposition** — 6 new `system_hierarchy` rows: TENET parent + TENET_USPI, TENET_HEALTHSYSTEM, TENET_CALIFORNIA, TENET_FLORIDA, TENET_HOSPITALS. Sourced from CMS Hospital All Owners disclosures (60 hospitals under TENET HEALTHCARE CORPORATION + 24 USPI + 18 regional). EIN 952557091 linked. `/systems/TENET` now returns a real rollup.
- **UHS decomposition** — 7 new rows: UHS parent + UHS_ACUTE + UHS_BEHAVIORAL + UHS_DELAWARE + UHS_HOLDING + UHS_GEORGIA + UHS_PENNSYLVANIA. Only public behavioral-health rollup at scale (Provo Canyon, Cambridge, Cygnet). 145 hospitals under UNIVERSAL HEALTH SERVICES INC. EIN 232077891 linked.
- **Surgery Partners decomposition** — 3 new rows: SGRY parent + SGRY_ASC + SGRY_STONEBRIDGE. ASC roll-up; 17 direct facilities. EIN 473620923 linked. Unique in the market — no other public API exposes ASC ownership at NPI level.
- **Amazon One Medical decomposition** — 1 new row: ONE_MEDICAL as standalone primary-care group. Identified via 4 PECOS org_pac_id matches (ONE MEDICAL GROUP INC, ONE MEDICAL OF NY PC, ONE MEDICAL SENIORS GROUP INC, ONE MEDICAL P.C.). Amazon acquired for $3.9B Feb 2023.
- **IRS 990 XML index for 2020 + 2021** — 989K more rows loaded after fixing the `sub_date VARCHAR(4)` schema constraint (migration 049 widened to TEXT to accept the legacy `1/2/2020 3:03:27 PM` format). Combined with 2022-2024, healthcare-EIN discovery graph now spans 5 tax years (~2.7M index rows).

### Fixed
- **6 remaining sec_10k EINs** — Acadia, American Well, LifeStance, Privia Health, Select Medical, Pennant Group all previously had CIK-in-EIN or SEC placeholder `000000000`. Backfilled real 9-digit EINs by parsing each company's most recent 10-K cover page for the `XX-XXXXXXX` pattern. All 30 `org_eins.sec_10k` rows now have valid EINs.

### Removed
- **QPP MIPS TIN loader** was killed as a plan item after investigation confirmed CMS redacts TIN across all public files (Doctors and Clinicians, PECOS Individual Enrollment, QPP annual). No public path exists; moved to DATA_GAPS Tier 2.1 with external-constraint status.

---

## [2026-07-07]

### Added
- **DOL Form 5500 healthcare backfill** — loaded 107K healthcare-NAICS filings across tax years 2020-2024 (peak years 2021-2024 each cover 24-27K filings representing ~26M W-2 employees per year). Data lives in `dol_5500_filings` (raw) and `financial_filings` (aggregated per EIN × tax period). Powers the `/systems/{id}/financials/workforce` endpoint for any org whose EIN is known. Confirmed correct at the top of the distribution — Trinity Health at 158K W-2 employees (2023), Kaiser 149K, matching each system's publicly-stated head-counts.
- **DOL 5500 employment attribution signals** — 222,138 new signals in `employment_attribution_signals` with `source='dol_5500'`, `signal_type='participant'`, `confidence=0.55`. Cross-cites the DOL W-2 employer floor for NPIs whose PECOS reassignment org matches a linked EIN. Top employers surfaced: NYU (5,724 NPIs), Mayo Clinic (4,899), Montefiore (2,797), Henry Ford (2,538). Ready to grow substantially once the org_eins fuzzy-match review lands.
- **IRS 990 XML index for 2022-2023** — 1.36M new `irs_990_index` rows spanning both years. Combined with the pre-existing 2024 index, the healthcare EIN discovery graph now covers three tax years. 2020-2021 blocked on a `tax_period VARCHAR(4)` constraint issue (older years use different format); tracked in DATA_GAPS.

### Fixed
- **`org_eins` CIK-in-EIN data-model bug** — 30 rows sourced from SEC 10-K filings were storing the SEC EDGAR CIK in the `ein` column (variable-width, not a real EIN). Migration 047 added a proper `cik` column and moved the misplaced values. `scripts/providers/sec_filings/backfill_eins.py` then backfilled 25 real 9-digit EINs via SEC EDGAR submissions API (e.g. Tenet now correctly resolves to EIN 952557091 instead of CIK 70318). Remaining 5 issuers (American Well, LifeStance, Privia Health, Select Medical, Pennant Group) return SEC's placeholder `000000000` and are documented as a follow-up gap.
- **`org_eins.org_pac_id` first population** — 51 rows auto-linked to PECOS `org_pac_id` via trigram fuzzy match (similarity ≥ 0.85) using `scripts/providers/sec_filings/link_org_eins_to_pac.py`. 198 ambiguous candidates written to `data/ein_pac_id_review.csv` for human triage.

---

## [2026-07-02]

### Added
- **`/my-usage` HTML dashboard** — the authenticated usage endpoint now serves a styled dashboard to browsers with a tier badge, color-coded usage progress bar (green under 50%, amber under 90%, red at 90%+), tile grid, and a contextual upgrade CTA. JSON shape unchanged for API clients; a new `billing_tier` key is additive.
- **JSON-LD structured data** on the six main marketing pages (`/`, `/pricing`, `/quickstart`, `/sources`, `/accuracy`, `/legal/data-disclosure`). Enables Google Rich Results eligibility — pricing tiles as native price cards, quickstart as a HowTo card, `/sources` as a Dataset result.
- **`/changelog`** — public user-visible change log (this file). Content-negotiated: browsers get styled HTML, `curl` gets raw markdown.
- **`/status`** — public traffic-light health page. Shows real-time state of the four dependencies (PostgreSQL, S3, SES, pipeline freshness).
- **`/health/deep`** — deep health check endpoint. Runs live dependency probes. `/health` remains shallow + fast for CI polling.
- **`/examples`** and **`/examples/{lang}`** — copy-paste ready code samples in Python, Node.js, curl, Go, and PHP. Browsers get a styled page; `curl` gets the raw source file with the correct MIME type.
- **OpenAPI/Swagger enrichment** — 12 endpoints across `hospitals`, `nursing-homes`, `exclusions`, `provider` (npi_profile), `licenses`, and `mips` routers now carry `summary`, `description`, path/query examples, and response payload examples. The Swagger UI at `/docs` "Try it" form now auto-fills with real CCNs, NPIs, and state codes.
- **Sitemap now includes `/resources/{slug}` and `/examples/{lang}` URLs** with `lastmod` pulled from each file's mtime — helps search engines prioritize recently-updated content.

### Changed
- `/pricing` "Start with Free" CTAs pre-wired to `/billing/checkout?tier=starter` and `?tier=pro`. Currently return `503` until Stripe env vars are set; will flip on live automatically the moment the keys land.
- `/quickstart` "What's next" section now leads with `/examples` as the primary CTA (was "Full API Reference").
- `/health` split into shallow (`/health`, unchanged, fast) vs deep (`/health/deep`, runs real dependency probes).

---

## [2026-06-28]

### Added
- **`/quickstart`** — 5-minute developer integration guide with tabbed curl / Python / JavaScript samples, endpoint gallery, rate-limit callouts.
- **Open Graph social preview cards** — 1200×630 branded PNGs on every marketing page. LinkedIn / Twitter / Slack previews now render with a proper card instead of a bare URL.
- **`/sources/{source_id}`** now returns a styled HTML detail page in browsers (was JSON-only). Completes the `/sources/*` HTML family (list, detail, history).
- **`/resources` catalog + `/resources/{slug}`** rendered documents — the methodology docs (API Style Guide, Accuracy Benchmark, License Intelligence, Financial Intelligence, Employment Reconciliation) are now browsable as styled HTML with the site's brand chrome. `curl` still gets raw markdown.
- **`robots.txt`** and **`sitemap.xml`** for search-engine discovery. Allows public marketing surfaces; blocks admin / billing internals.

### Fixed
- `/billing/plans` legacy URL 301s to the canonical `/pricing`. Old page said "17 data sources" and referenced the wrong tier limits.

---

## [2026-06-27]

### Added
- **`/sources/{source_id}/history`** HTML — per-source refresh audit timeline with status-colored markers, four stat tiles (session count, success rate, last run, last status), and a smart empty state.
- **`/accuracy`** HTML — the marquee proof page. Four summary tiles (median variance, mean variance, systems within ±10%, within ±25%), methodology callout, sorted results table with color-coded variance (green/amber/red), and a ground-truth submission invitation.
- **`/platform/stats`** HTML — live dashboard with pulse-animated "refreshed Xs ago" badge, four stat tiles (data rows, sources, facilities, clinicians), last-pipeline-run card with status pill.
- SEO + Open Graph meta tags across seven public pages (description, `og:title`, `og:description`, `og:type`, `og:site_name`, `og:url`, `twitter:card`).
- **`/pricing`** page with four tiers (Free / $49 Starter / $199 Pro / Enterprise), FAQ, and cross-sell to the executive dashboard.
- **Shared content-negotiation helper** (`api/providers/_shared/content_negotiation.py:prefers_html`) — same URL serves HTML to browsers and JSON to API clients based on `Accept` header + explicit `?format=` override.

### Changed
- **`BILLING_TIERS`** aligned with the new 4-tier pricing (Free 10K/day, Starter 50K/day, Pro 500K/day, Enterprise 10M/day). Was 3 tiers with stale limits.
- **`/billing/checkout`** accepts a `?tier=` query param (starter or pro). Tier flows through Stripe checkout metadata so the webhook upgrades to the right tier.

### Fixed
- `test_admin::test_get_organization` was failing because it referenced a `starter` tier that didn't exist in `BILLING_TIERS`. Now passes.

---

## [2026-06-26]

### Added
- **Live source catalog at `/sources`** — every dataset we ingest with license, refresh cadence, accuracy class, and last-loaded state. 38 sources across CMS (13), other federal (5 — OIG, HRSA, CDC, AHRQ, Census/OMB), academic (1 — Dartmouth), state open-data (1 — CA Medical Board), commercial (2 — Google Maps, FMP), and Verzi-original layers (13). Same URL serves JSON to API clients.
- **Per-source refresh history at `/sources/{source_id}/history`** — every load attempt, success or failure, with timing and row-count deltas.
- **`/platform/stats`** endpoint — live row counts, source counts, facility / clinician counts, last pipeline run status. Backs the marketing numbers without hardcoding them.
- **Data Use & Disclosure Statement** at `/legal/data-disclosure` — canonical, version-controlled statement of which datasets we ingest, under what license, and our HIPAA classification. Maintained outside the auto-generated Terms/Privacy so Termly regenerations can't wipe substantive disclosure.
- **LoadSession instrumentation** — every pipeline loader run now records a session row (`source_id`, `started_at`, `completed_at`, `status`, `rows_loaded`, `error_message`) to the `load_sessions` table.

### Changed
- Homepage stats now fetch from `/platform/stats` via Next.js ISR (1-hour revalidation) instead of hardcoded strings. Row counts stop drifting.
- Data source attribution rewritten across README, `PROJECT_OVERVIEW`, `terms.html`, `privacy.html`, and the homepage to reflect the full 30+ source catalog (CMS, HHS OIG, HRSA, CDC, AHRQ, IRS, DOL, SEC EDGAR, Census, Dartmouth, plus commercial APIs) instead of claiming CMS-only. Every `312M+ rows` / `27 sources` reference updated to live counts or durable phrasing.
- Signup form bot-prevention hardening: honeypot field, disposable email denylist, per-IP rate limit, Cloudflare Turnstile scaffolded and env-flag-gated.

### Fixed
- Migration tooling: `run_migrations.py --stamp` bootstrap flag, cleaner prod auth flow via AWS Secrets Manager (`cms-dev/postgres-credentials`), fixed URL-encoding issue with prod password.

---

## [2026-06-17]

### Added
- **Financial Intelligence** product layer — IRS Form 990 deep XML pipeline (Schedule R + Schedule J), DOL Form 5500 loader, SEC EDGAR 10-K via FMP for 30 curated public healthcare tickers.
- **`financial_filings`** table — cross-source EIN × filing_type × tax_period_end unified store. 15K+ rows.
- **`executive_compensation`** table — Schedule J + 10-K NEO records, 267 entries with matched NPIs for clinician-executives.
- **Financial Intelligence API endpoints**: `/systems/{id}/financials`, `/systems/{id}/financials/executives`, `/systems/{id}/financials/workforce`, `/companies/{ticker}`, `/companies/search`, `/ma-events/recent`, `/provider/{npi}/financial-affiliation`.

---

## [2026-06-16]

### Changed
- **API consolidation**: `/quality` nested under `/provider`, `/quality` router renamed to `/mips`, fixed `/systems` route collision.

---

## [2026-06-11]

### Added
- **License + Quality Intelligence v1**
  - `provider_licenses` (5M+ NPI-keyed licenses derived from NPPES taxonomy across all 50 states)
  - `license_disciplinary_actions` (8.5K OIG LEIE joined to NPI)
  - License Intelligence API: `/licenses/state/{state}`, `/licenses/{npi}`, `/licenses/{npi}/disciplinary`, `/licenses/alerts/recent`
  - Quality Intelligence API: `/quality/{npi}`, `/quality/leaderboards/{specialty}`, `/quality/systems/{system_id}`
  - Extended `/provider/{npi}` dossier with new `license_intelligence`, `quality_intelligence`, `employment_intelligence` blocks

---

## [2026-06-10]

### Added
- **Physician Employment Intelligence v2** — multi-source attribution + system decomposition
  - `org_eins`, `employment_attribution_signals`, `system_hierarchy` tables
  - Large national systems decomposed into operating divisions
  - Accuracy benchmark at `/accuracy` — per-system variance vs publicly-stated counts
- **Physician Employment Intelligence v1** — initial employer typology product
  - `physician_employer_systems`, `employer_facts` materialized view
  - 12 API endpoints at `/provider/*`, `/employers/*`, `/systems/*`

---

## Older

Prior to 2026-06-10, changes were tracked via git log only. The last major additions before that date were VBC/ACO analytics, HCAHPS benchmarking materialized views, market intelligence (HSAF), community health (CDC PLACES, Medicare Geographic Variation, AHRQ SDOH), and the initial 9 CMS provider-type ingestion.

---

## How to read this

- **Added** — new endpoints, tables, or user-visible features
- **Changed** — modifications to existing behavior (usually backward-compatible)
- **Fixed** — bugs or inconsistencies resolved
- **Deprecated** — features scheduled for removal (none yet)
- **Removed** — features removed (none yet)
- **Security** — security-relevant changes (none breaking; hardening only)

For the machine-readable API changelog, hit `curl https://api.verzi.health/changelog` — returns raw markdown.
