Methodology Changelog
A public record of every material change to how DocTransparency presents its data. We update this page whenever editorial methodology, source attribution, scoring, or display logic changes.
The current methodology is documented at /methodology.
2026-06-20 — Editorial framing: symmetric payment caveat, disclaimer consolidation, single metric name
Source: Second pass of the same external editorial review that prompted the 2026-06-19 data-integrity fix. This pass addresses framing rather than data: how the existing numbers are contextualised on the profile. The changes are template-level and apply to the full corpus on the next site build.
Changes that affect what visitors see:
- Added a neutralising caveat beneath a high industry-payment percentile ("Top X% … for {specialty}"), mirroring the caveat already shown for low Medicare volume. Previously only the low-volume figure was softened, which biased the read toward alarm for academic subspecialists who combine low Medicare fee-for-service volume with high disclosed industry engagement. The caveat reads that a higher payment rank reflects disclosed industry relationships common among subspecialists — not wrongdoing.
- Consolidated repeated disclaimers. The "not a quality rating / not medical advice" language now appears at three strategic anchors — the hero banner, the industry-payments section, and the legal footer — instead of being repeated in the narrative, Medicare summary, "Data Sources" block, and auto-generated summary. The legal cover is unchanged; the mid-page repeats were redundant.
- Standardised the name of the data-completeness indicator to Data Coverage everywhere it is shown to readers. Some surfaces (the legal footer and parts of /methodology, /about, /trust, /press) still called it "Transparency Score" while the profile hero and FAQ already said "Data Coverage." One concept, one name.
Why this matters:
- These profiles are Your-Money-or-Your-Life (YMYL) content about identifiable physicians. Softening one statistic but not its mirror image is itself a form of editorialising; symmetric context keeps the page informative rather than alarming.
- Two names for the same indicator confused readers and undercut the page's authority. "Data Coverage" is the descriptive term — it names what the indicator measures (how much public federal data exists) and does not read as a quality grade.
No changes to:
- The Data Coverage formula, the four data sources (NPPES, Open Payments, Medicare Provider Utilization, PECOS), or any displayed number.
- The data vintage or coverage scope.
2026-06-19 — Payments-by-company labelling fix and removal of per-100-services equivalence
Source: External editorial review on a single profile flagged a data-integrity bug and a derived metric we agreed was editorially misleading. Both apply to every profile that has industry-payment data, so the fix is applied to the full corpus on the next site build.
Changes that affect what visitors see:
- Fixed the "Payments by company (YEAR)" block on every provider profile. The block displayed a multi-year cumulative total under a single-year label — it summed every year of available data while the heading named only the most recent year. The block now shows only the most recent payment year the source data covers for that provider; the lifetime view is preserved behind an "All-time payments by company (YEAR–YEAR)" toggle, clearly labelled.
- Updated the "Top 3 companies account for X% of total payments" line to use the single-year denominator in the default block, and exposes the lifetime denominator only in the all-time toggle. The two ratios are not interchangeable; pinning the lifetime ratio under a single-year label inflated apparent concentration.
- Removed the "Equivalent to $X per 100 Medicare services performed" line. The numerator was a multi-year Open Payments total and the denominator was a single-year Medicare service count, so the ratio was scale-mismatched. Beyond the math, the two quantities have no causal relationship and the framing was loaded — incompatible with our editorial stance that profiles inform, not alarm.
Why this matters:
- These profiles are Your-Money-or-Your-Life (YMYL) content about identifiable physicians. A label naming a single year must show that year's data; a derived metric that asks the reader to read causation into it must not appear without a strong, inline caveat. We chose to drop the derived metric rather than re-engineer its framing.
- The bug was introduced when the company aggregate was first added; it had been present in every profile that has multi-year industry payments. The full corpus is re-rendered on the next site build, at which point every affected profile carries the corrected block.
No changes to:
- The Data Coverage formula or the four data sources (NPPES, Open Payments, Medicare Provider Utilization, PECOS).
- The "Payment trend by year" block, which was already correctly year-keyed.
- Any other derived metric on the profile.
2026-04-17 — Pre-launch hardening
Changes that affect what visitors see:
- Every provider profile now displays a "verify on NPPES" link next to the NPI, plus per-section "verify on CMS" links for Medicare Provider Utilization and Open Payments. Each link points directly to the official CMS source record for that provider's NPI.
- Every provider profile now includes a persistent disclaimer banner at the top: "Informational, not a quality rating," with a direct link to the Provider Portal for corrections.
- Every provider profile now includes a persistent legal footer summarizing data sources, statutory basis (Sunshine Act §6002 ACA, FOIA), and the non-clinical-advice disclaimer.
- Two new pages: Provider Portal (with defined SLAs and CMS dispute pathways) and Press (fact sheet and editorial position).
- About page strengthened with explicit "no industry funding / no advertising / no data sale" up-front statement and "What we don't claim" section.
Changes to legal documents:
- Methodology now includes the statutory basis for publication (Sunshine Act §6002, FOIA, Bartnicki v. Vopper), per-source data vintage dates, the Open Payments self-reported caveat, a corrections workflow with 5-day SLA, and named editorial responsibility.
- Terms of Use hardened with: operator identity (unincorporated project pending US legal entity), explicit no-scraping / no-AI-training acceptable use, DMCA designated agent, ALL-CAPS warranty disclaimer with $100 liability cap and healthcare-decisions carve-out, indemnification, Delaware governing law, AAA arbitration with jury and class-action waiver, and 30-day arbitration opt-out.
- Privacy Policy expanded to: full nginx server-log enumeration with 14-day retention and GDPR Art. 6(1)(f) lawful basis, GDPR controller designation (Yoel Castaño, Spain — Art. 3(1) applies for all visitors regardless of location), CCPA/CPRA notices with 12-month lookback and 45-day SLA, third-party processor list (Hetzner DE, Cloudflare US with SCCs), no-cookies-at-launch commitment, COPPA <13 statement, provider takedown process, and breach notification commitment.
Operational changes (not visible on public pages):
- 809 orphan profile directories (legacy NPPES taxonomy-code slugs from earlier generation) removed from
output/. All current profiles use human-readable specialty slugs. /404page now includes<meta name="robots" content="noindex, follow">and is excluded from the sitemap.- Open Graph image (
static/og-default.png, 1200×630) added for Facebook / Twitter card previews.
No changes to:
- The Data Coverage formula
- The four data sources (NPPES, Open Payments, Medicare Provider Utilization, PECOS)
- The data vintage (NPPES weekly snapshot 2026-04, Open Payments PY2023, Medicare CY2022, PECOS 2026-04)
- Coverage scope (Texas + Florida)
2026-04-16 — Full audit remediation
Source: Internal 6-team adversarial audit. 87 findings identified, 84 resolved across 5 sprints.
Major changes that affect what visitors see:
- Hub provider count fix: corrected JOIN multiplication in
scripts/11_generate_hubs.pythat previously inflated provider totals on specialty/state and specialty/state/city hubs. - Company hub display fix: company directory pages now correctly show top doctors with payment amounts (previously a context mismatch caused empty rows).
- Doctor profile URL format fix in analysis pages: links from
/analysis/...to provider profiles now resolve correctly. - Per-procedure median values:
scripts/10_generate_profiles.pynow computes medians from sorted arrays instead of the previous "AVG-as-median" approximation. - Coverage display: replaced hardcoded coverage defaults with safer
N/Aand dynamic ranges where available.
Open Payments category reclassification:
- "Current or prospective ownership or investment interest" → reclassified as financial (was: scientific)
- "Royalty or License" → reclassified as financial (was: scientific)
- Narrative copy and on-page chips updated accordingly.
E-E-A-T improvements:
- Founder name (Yoel Castaño), LinkedIn link, and Organization schema added to
/aboutand footer. - Editorial responsibility named on
/methodology.
Technical infrastructure:
- All SQL queries refactored to parameterized form (was: string concatenation in
_state_filter()— potential injection). - Jinja2 autoescape enabled (was: false — CMS data was rendered un-escaped).
- HSTS preload header, Permissions-Policy, CSP pinned to
unpkg.com/[email protected]/, autoindex disabled in nginx. - Deploy runs as non-root
deployuser.
Data refreshes:
- All 497K pages regenerated against the same data vintage as the next entry.
2026-04-13 — Initial code-data integrity fixes (commit bfa12c9)
Pre-audit changes that established the current data invariants. Documented in MEMORY.md under "What Was Fixed in Code (bfa12c9)":
- Prevented JOIN multiplication in hub provider totals (
scripts/11_generate_hubs.py). - Fixed company hub context mismatch (
top_doctors+amount) in hubs/template. - Fixed doctor profile URL format in analysis links (
scripts/13_generate_analysis.py). - Replaced fake "median via AVG" with real median from sorted arrays.
- Replaced hardcoded coverage defaults with safer
N/Aand dynamic ranges. - Reclassified Open Payments categories (Royalty / Investment → financial).
How we record changes
This changelog records:
- Any change to what data we display on profile pages (new fields, removed fields, changed labels)
- Any change to how data is sourced or computed (e.g., new data vintage, score formula change)
- Any change to legal documents (Methodology, Terms, Privacy, About)
- Any change to provider-facing process (correction SLA, takedown workflow)
- Major infrastructure changes that affect how the site is served
This changelog does NOT record:
- Bug fixes that don't change visible output (purely internal corrections)
- Cosmetic style changes (CSS-only, layout tweaks)
- Adding or removing individual provider profiles (that happens automatically with each data refresh)
For the complete commit-level history, see the GitHub repository (public at launch).
For specific provider data corrections, see the Provider Portal.
For questions about any change documented here: [email protected].