All checks were successful
CI / lint (push) Successful in 29s
CI / notebooks-smoke (push) Successful in 1m25s
Deploy / notebooks (push) Has been skipped
Deploy / zotero (push) Has been skipped
Deploy / docs (push) Has been skipped
Deploy / api (push) Has been skipped
Deploy / llm (push) Has been skipped
Deploy / mc (push) Has been skipped
Infra CI / notebooks (push) Successful in 1m9s
Infra CI / zotero (push) Successful in 24s
Infra CI / docs (push) Successful in 16s
Infra CI / api (push) Successful in 2m13s
Infra CI / llm (push) Successful in 1m12s
Infra CI / mc (push) Successful in 19s
Deploy / report (push) Successful in 12s
CI / test (push) Successful in 13m29s
The CY2027 notebook defined the fr_md/cfr_md jump-link wrappers (P40 #638, P41 #641) but only used them in four spots — every other FR citation rendered as plain text. All simple 'NN FR NNNNN' cites (intro, CF-walk sources, BN-decomposition narrative, RVU-delta and GPCI sources, cross-NPRM cell, QPP proposal, QP-differential sources, comment-period callout) and interpolated registry citations now go through fr_md(), and the bare 42 CFR 414.1430(a) cite through cfr_md(). Compound qpp citations ('91 FR 44260, 44285-44286') stay plain — the resolver grammar takes one locator. All six page cites verified against the bib anchor maps via stack bib fr-jump; headless re-run renders 18 federalregister.gov + 4 ecfr.gov links.
1596 lines
60 KiB
Python
1596 lines
60 KiB
Python
import marimo
|
||
|
||
__generated_with = "0.23.1"
|
||
app = marimo.App(width="medium")
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _():
|
||
import marimo as mo
|
||
|
||
return (mo,)
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(fr_md, mo):
|
||
mo.md(f"""
|
||
# CY2027 PFS Proposed Rule — Financial Changes & Advanced APM
|
||
|
||
CMS's CY2027 Physician Fee Schedule NPRM (CMS-1848-P, {fr_md("91 FR 43842")},
|
||
published July 16, 2026; docket CMS-2026-2377) is a **proposed rule
|
||
only** — there is no CY2027 Final Rule (CMS-1848-F) yet, and every
|
||
"CY2027" figure below is a proposal, not a finalized payment
|
||
parameter. The comment period, thresholds, and Advanced-APM
|
||
schedule captured here can all still change before the Final Rule
|
||
ships.
|
||
|
||
**What this means:** every number below is read live from
|
||
`pfs.rules.RULES` / `pfs.rules.PROPOSED` (via `proposed_for(2027)`)
|
||
and `qpp.QPP` (the repo's rule registries) and the `pfs`/`cms`
|
||
DuckLake schemas — nothing here is a hard-coded figure. Columns are
|
||
explicitly labeled **"final rule pending"** rather than fabricating
|
||
a CY2027-final column that does not exist yet. Dollar figures in
|
||
Sections 1-2 and 4 are **national, GPCI-unadjusted (GPCI = 1.0)**
|
||
unless a figure says otherwise; **Section 3** carries the locality
|
||
story — the CY2027 NPRM's proposed GPCIs (`pfs.gpci_proposed`,
|
||
Addendum E) against the final-rule history in `pfs.gpci`, with
|
||
locality-adjusted payment examples.
|
||
""")
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _():
|
||
import altair as alt
|
||
import polars as pl
|
||
|
||
from conf import connect
|
||
from conf.display import plain_years
|
||
from pfs.rules import RULES, proposed_for
|
||
from qpp import QPP, for_payment_year
|
||
|
||
connect.theme()
|
||
|
||
# connect.theme() puts assets/ on sys.path — reuse the HTI-5 design
|
||
# tokens directly instead of re-typing hex literals: AMBER/TEAL are
|
||
# the gain/loss diverging pair (Sections 2-4), INDIGO_MED the
|
||
# default series, PLUM/GRAY the fixed categorical follow-ons for
|
||
# the GPCI locality lines (Section 3).
|
||
from fhirworx import AMBER, GRAY, INDIGO_MED, PLUM, TEAL
|
||
|
||
from pfs.calcs.payment import payment as pfs_payment
|
||
|
||
# PFS reference data lives in the DuckLake lakehouse (M5, #514) —
|
||
# notebooks connect read-only.
|
||
con = connect.ducklake()
|
||
|
||
def q(sql):
|
||
return con.execute(sql).pl()
|
||
|
||
def cfr_md(ref, text=""):
|
||
"""eCFR jump link as markdown (P41, #641) — deterministic
|
||
cite→URL transform, no store needed; degrades to plain text on
|
||
a malformed cite so the notebook never breaks."""
|
||
try:
|
||
from bib import cfrlink
|
||
|
||
return cfrlink.md_link(ref, text=text)
|
||
except Exception:
|
||
return text or ref
|
||
|
||
def fr_md(ref, text=""):
|
||
"""FR web jump link as markdown (P40, #638) — page, ¶-ordinal,
|
||
or quote refs resolve against the bib anchor map; degrades to
|
||
plain text when bib.sqlite or the map is unavailable so the
|
||
notebook never breaks offline."""
|
||
try:
|
||
from bib import frlink
|
||
|
||
return frlink.md_link(ref, store=connect.bib(), text=text or ref)
|
||
except Exception:
|
||
return text or ref
|
||
|
||
return (
|
||
AMBER,
|
||
GRAY,
|
||
INDIGO_MED,
|
||
PLUM,
|
||
QPP,
|
||
RULES,
|
||
TEAL,
|
||
alt,
|
||
con,
|
||
cfr_md,
|
||
for_payment_year,
|
||
fr_md,
|
||
pfs_payment,
|
||
pl,
|
||
plain_years,
|
||
proposed_for,
|
||
q,
|
||
)
|
||
|
||
|
||
# ── 1. The conversion-factor walk ─────────────────────────────────────
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(mo):
|
||
mo.md("""
|
||
## 1. The Conversion Factor Walk — CY2026 Final → CY2027 Proposed
|
||
|
||
CY2026 was the first year the PFS published a split conversion
|
||
factor — a qualifying-APM (QP) track and a nonqualifying-APM
|
||
(non-QP) track, each with a standard and an anesthesia CF. The
|
||
CY2027 NPRM proposes new values for all four; the chart below
|
||
places the CY2026 **Final Rule** values (the only finalized
|
||
baseline that exists) alongside the CY2027 **NPRM proposal**
|
||
(final rule pending).
|
||
""")
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(RULES, pl, proposed_for):
|
||
_cy26 = RULES[2026]
|
||
_prop = proposed_for(2027)
|
||
|
||
_rows = [
|
||
{"vintage": "CY2026 Final", "family": "Standard", "track": "Non-QP", "cf": _cy26.conversion_factor},
|
||
{"vintage": "CY2027 Proposed (final rule pending)", "family": "Standard", "track": "Non-QP", "cf": _prop.conversion_factor},
|
||
{"vintage": "CY2026 Final", "family": "Standard", "track": "QP", "cf": _cy26.cf_qp},
|
||
{"vintage": "CY2027 Proposed (final rule pending)", "family": "Standard", "track": "QP", "cf": _prop.cf_qp},
|
||
{"vintage": "CY2026 Final", "family": "Anesthesia", "track": "Non-QP", "cf": _cy26.anesthesia_cf},
|
||
{"vintage": "CY2027 Proposed (final rule pending)", "family": "Anesthesia", "track": "Non-QP", "cf": _prop.anesthesia_cf},
|
||
{"vintage": "CY2027 Proposed (final rule pending)", "family": "Anesthesia", "track": "QP", "cf": _prop.anesthesia_cf_qp},
|
||
# NOTE: RULES[2026].anesthesia_cf models the non-QP anesthesia CF
|
||
# only — the CY2026 Final Rule anesthesia QP CF is not captured
|
||
# in the registry (see pfs.rules module docstring), so that bar
|
||
# is deliberately omitted rather than guessed. No CY2027-final
|
||
# bars exist anywhere in this table — there is no Final Rule yet.
|
||
]
|
||
cf_walk = pl.DataFrame(_rows)
|
||
cf_walk
|
||
return (cf_walk,)
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(alt, cf_walk, fr_md, mo):
|
||
_vintage_order = ["CY2026 Final", "CY2027 Proposed (final rule pending)"]
|
||
_track_domain = ["Non-QP", "QP"]
|
||
|
||
cf_chart = (
|
||
alt.Chart(cf_walk.to_pandas())
|
||
.mark_bar()
|
||
.encode(
|
||
x=alt.X("vintage:N", title=None, sort=_vintage_order),
|
||
xOffset=alt.XOffset("track:N", sort=_track_domain),
|
||
y=alt.Y("cf:Q", title="Conversion factor ($/RVU)", scale=alt.Scale(zero=False)),
|
||
color=alt.Color("track:N", title="Track", scale=alt.Scale(domain=_track_domain)),
|
||
tooltip=["vintage", "family", "track", alt.Tooltip("cf:Q", format="$.4f")],
|
||
)
|
||
.properties(width=300, height=280)
|
||
.facet(column=alt.Column("family:N", title=None))
|
||
.resolve_scale(y="independent")
|
||
.properties(title="CY2026 Final vs. CY2027 Proposed PFS Conversion Factors")
|
||
)
|
||
|
||
mo.vstack(
|
||
[
|
||
cf_chart,
|
||
mo.md(f"""
|
||
*CY2026 Final anesthesia QP is not shown — that CF is not
|
||
captured in the `pfs.rules` registry (see module docstring).
|
||
No CY2027-final bars appear anywhere in this notebook — the
|
||
Final Rule (CMS-1848-F) has not been published.*
|
||
|
||
**Sources:** CY2026 Final Rule — {fr_md("90 FR 49266")}.
|
||
CY2027 NPRM — {fr_md("91 FR 43842")} (CMS-1848-P).
|
||
"""),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(fr_md, mo):
|
||
mo.md(f"""
|
||
### Budget-neutrality adjustor decomposition
|
||
|
||
CMS's own NPRM narrative ({fr_md("91 FR 44242")}) describes the CY2027 CF
|
||
derivation explicitly: start from the **CY2026 conversion factors
|
||
with the one-time 2.50% statutory increase backed out**, multiply
|
||
by the 0.53% budget-neutrality adjustment, then multiply by the
|
||
section 1848(d)(20) qualifying/nonqualifying-APM annual update
|
||
(+0.75% / +0.25%). Reproducing that arithmetic against the
|
||
registry's actual CY2026-final and CY2027-proposed CFs is a useful
|
||
cross-check that the registry's transcription is internally
|
||
consistent with CMS's narrative — for the **standard** CF only;
|
||
the anesthesia CFs reflect "the same overall PFS adjustments with
|
||
the addition of anesthesia-specific PE and MP adjustments"
|
||
({fr_md("91 FR 44242")}) that are not modeled as separate registry fields, so they
|
||
are excluded from this reconstruction rather than approximated.
|
||
""")
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(RULES, fr_md, mo, pl, proposed_for):
|
||
_cy26 = RULES[2026]
|
||
_prop = proposed_for(2027)
|
||
|
||
# CMS's own stated CF-derivation components for CY2027 (91 FR 44242,
|
||
# file lines ~39088-39106): back the CY2026 CFs out of the one-time
|
||
# 2.50% statutory increase, then reapply BN + the annual update.
|
||
# NONE of these bare percentages is a field anywhere in `pfs.rules`
|
||
# or `qpp` — there is nothing to attribute-access for them. They are
|
||
# kept as literals ONLY because CMS's narrative states them as bare
|
||
# numbers, not derived from any other registry value; this is a
|
||
# cited transcription of that narrative, not a second source of
|
||
# truth for `budget_neutrality_adjustor` or the CFs themselves
|
||
# (both of which — `bn_adjustor` / `registry_cf` below — ARE read
|
||
# live from the registry).
|
||
_ONE_TIME_BACKOUT = 1.0250 # CY2026's one-time +2.50% statutory increase, backed out per 91 FR 44242
|
||
_ANNUAL_UPDATE_MULT = {
|
||
"Non-QP": 1.0025, # +0.25%/yr nonqualifying-APM update, sec. 1848(d)(20), 91 FR 44242
|
||
"QP": 1.0075, # +0.75%/yr qualifying-APM update, sec. 1848(d)(20), 91 FR 44242
|
||
}
|
||
|
||
def _reconstruct(cy2026_final_cf, track, bn_adjustor):
|
||
return (cy2026_final_cf / _ONE_TIME_BACKOUT) * bn_adjustor * _ANNUAL_UPDATE_MULT[track]
|
||
|
||
_bn_rows = [
|
||
{
|
||
"track": "Non-QP",
|
||
"cy2026_final_cf": _cy26.conversion_factor,
|
||
"one_time_backout": _ONE_TIME_BACKOUT,
|
||
"bn_adjustor": _prop.budget_neutrality_adjustor,
|
||
"annual_update_mult": _ANNUAL_UPDATE_MULT["Non-QP"],
|
||
"reconstructed_cy2027_proposed_cf": round(
|
||
_reconstruct(_cy26.conversion_factor, "Non-QP", _prop.budget_neutrality_adjustor), 4
|
||
),
|
||
"registry_cy2027_proposed_cf": _prop.conversion_factor,
|
||
},
|
||
{
|
||
"track": "QP",
|
||
"cy2026_final_cf": _cy26.cf_qp,
|
||
"one_time_backout": _ONE_TIME_BACKOUT,
|
||
"bn_adjustor": _prop.budget_neutrality_adjustor,
|
||
"annual_update_mult": _ANNUAL_UPDATE_MULT["QP"],
|
||
"reconstructed_cy2027_proposed_cf": round(
|
||
_reconstruct(_cy26.cf_qp, "QP", _prop.budget_neutrality_adjustor), 4
|
||
),
|
||
"registry_cy2027_proposed_cf": _prop.cf_qp,
|
||
},
|
||
]
|
||
bn_decomp = pl.DataFrame(_bn_rows).with_columns(
|
||
(pl.col("reconstructed_cy2027_proposed_cf") - pl.col("registry_cy2027_proposed_cf"))
|
||
.abs()
|
||
.round(4)
|
||
.alias("abs_diff")
|
||
)
|
||
|
||
mo.vstack(
|
||
[
|
||
bn_decomp,
|
||
mo.md(f"""
|
||
`reconstructed_cf = (CY2026_final_cf / one_time_backout) ×
|
||
bn_adjustor × annual_update_mult` — `bn_adjustor` and
|
||
`registry_cy2027_proposed_cf` are read live from
|
||
`RULES[2026]` / `proposed_for(2027)`. `one_time_backout`
|
||
({_ONE_TIME_BACKOUT}) and `annual_update_mult`
|
||
({_ANNUAL_UPDATE_MULT["Non-QP"]} non-QP /
|
||
{_ANNUAL_UPDATE_MULT["QP"]} QP) are **cited transcriptions
|
||
of CMS's NPRM narrative ({fr_md("91 FR 44242")},
|
||
`data/fr_downloads/2026-14327.txt:39088-39106`)**, not
|
||
registry fields — no field in `pfs.rules` or `qpp` models
|
||
the 2.50%/0.53%/0.25%/0.75% components individually, only
|
||
their combined effect on `conversion_factor` / `cf_qp`.
|
||
"""),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(mo, proposed_for):
|
||
_prop = proposed_for(2027)
|
||
mo.vstack(
|
||
[
|
||
mo.md("### Caveat — a drafting error in the NPRM's own summary section"),
|
||
mo.callout(
|
||
mo.md(f"""
|
||
`pfs.rules.PROPOSED[2027].notes` documents a transcription
|
||
caveat that is worth surfacing here rather than only in
|
||
source comments — quoted live below, not retyped:
|
||
|
||
> {_prop.notes}
|
||
"""),
|
||
kind="warn",
|
||
),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
# ── 2. RVU-level deltas ────────────────────────────────────────────────
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(mo):
|
||
mo.md("""
|
||
## 2. RVU-Level Deltas — Which HCPCS Codes Move the Most
|
||
|
||
`pfs.rvu_proposed` filtered to `cms_rule_id = 'CMS-1848-P'` (14,518
|
||
rows as of this ingest) is compared against `pfs.rvu` for CY2026
|
||
(the current final baseline — there is no CY2027 final table to
|
||
compare against). Both tables are deduplicated to one row per
|
||
HCPCS base code (no modifier) via `QUALIFY row_number() ... = 1`
|
||
per the P36 convention, `status_code = 'A'` (actively priced), and
|
||
— for the proposed table specifically — a non-null non-facility PE
|
||
RVU, since a meaningful share of `pfs.rvu_proposed` rows carry a
|
||
null non-facility *or* facility PE RVU (CMS's Addendum B only
|
||
populates the setting a code is actually priced in).
|
||
""")
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(q):
|
||
rvu_delta_raw = q("""
|
||
WITH proposed AS (
|
||
SELECT hcpcs, description, work_rvu, non_fac_pe_rvu, mp_rvu,
|
||
work_rvu + non_fac_pe_rvu + mp_rvu AS total_nf_rvu
|
||
FROM pfs.rvu_proposed
|
||
WHERE cms_rule_id = 'CMS-1848-P'
|
||
AND (mod IS NULL OR mod = '')
|
||
AND status_code = 'A'
|
||
AND non_fac_pe_rvu IS NOT NULL
|
||
QUALIFY row_number() OVER (PARTITION BY hcpcs ORDER BY hcpcs) = 1
|
||
),
|
||
final_2026 AS (
|
||
SELECT hcpcs, work_rvu + non_fac_pe_rvu + mp_rvu AS total_nf_rvu
|
||
FROM pfs.rvu
|
||
WHERE year = 2026 AND (mod IS NULL OR mod = '') AND status_code = 'A'
|
||
QUALIFY row_number() OVER (PARTITION BY hcpcs ORDER BY hcpcs) = 1
|
||
)
|
||
SELECT
|
||
p.hcpcs,
|
||
p.description,
|
||
p.total_nf_rvu AS total_nf_rvu_proposed,
|
||
f26.total_nf_rvu AS total_nf_rvu_2026final
|
||
FROM proposed p
|
||
JOIN final_2026 f26 USING (hcpcs)
|
||
""")
|
||
rvu_delta_raw
|
||
return (rvu_delta_raw,)
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(RULES, pl, proposed_for, rvu_delta_raw):
|
||
_cf_2026_final = RULES[2026].conversion_factor
|
||
_cf_2027_proposed = proposed_for(2027).conversion_factor
|
||
|
||
rvu_delta = rvu_delta_raw.with_columns(
|
||
(pl.col("total_nf_rvu_proposed") * _cf_2027_proposed).round(2).alias("dollar_2027proposed"),
|
||
(pl.col("total_nf_rvu_2026final") * _cf_2026_final).round(2).alias("dollar_2026final"),
|
||
).with_columns(
|
||
(pl.col("dollar_2027proposed") - pl.col("dollar_2026final")).round(2).alias("delta_vs_2026final"),
|
||
)
|
||
rvu_delta
|
||
return (rvu_delta,)
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(AMBER, RULES, TEAL, alt, fr_md, mo, proposed_for, rvu_delta):
|
||
_n = 20
|
||
|
||
_winners = rvu_delta.sort("delta_vs_2026final", descending=True).head(_n)
|
||
_losers = rvu_delta.sort("delta_vs_2026final", descending=False).head(_n)
|
||
_top40 = _winners.vstack(_losers)
|
||
|
||
_plot_df = _top40.to_pandas()
|
||
_plot_df["label"] = _plot_df["hcpcs"] + " — " + _plot_df["description"].str.slice(0, 40)
|
||
_plot_df["sign"] = _plot_df["delta_vs_2026final"].apply(lambda v: "Gain" if v >= 0 else "Loss")
|
||
|
||
winners_losers_chart = (
|
||
alt.Chart(_plot_df)
|
||
.mark_bar()
|
||
.encode(
|
||
x=alt.X("delta_vs_2026final:Q", title="$ change, non-QP CF (national unadjusted, GPCI=1.0)"),
|
||
y=alt.Y("label:N", title=None, sort=alt.SortField(field="delta_vs_2026final", order="descending")),
|
||
color=alt.Color(
|
||
"sign:N",
|
||
title=None,
|
||
scale=alt.Scale(domain=["Gain", "Loss"], range=[TEAL, AMBER]),
|
||
legend=alt.Legend(orient="top"),
|
||
),
|
||
tooltip=["hcpcs", "description", alt.Tooltip("delta_vs_2026final:Q", format="$.2f")],
|
||
)
|
||
.properties(
|
||
title="Top 20 Winners / Top 20 Losers — CY2027 Proposed vs. CY2026 Final",
|
||
width=700,
|
||
height=600,
|
||
)
|
||
)
|
||
|
||
mo.vstack(
|
||
[
|
||
winners_losers_chart,
|
||
mo.md(f"""
|
||
**Sources:** `pfs.rvu_proposed` (`CMS-1848-P`) — CY2027 NPRM,
|
||
{fr_md(proposed_for(2027).federal_register_citation)}. Baseline —
|
||
`pfs.rvu` CY2026 Final ({fr_md(RULES[2026].federal_register_citation)}).
|
||
Both sides priced with each vintage's own non-QP standard CF
|
||
— this delta blends RVU-table changes with the proposed CF
|
||
change, it is not an RVU-only comparison.
|
||
"""),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(mo, proposed_for, rvu_delta):
|
||
mo.vstack(
|
||
[
|
||
mo.md(f"""
|
||
**Full detail — {rvu_delta.height:,} codes** matched across
|
||
the `CMS-1848-P` proposed RVU table and CY2026-final RVU
|
||
table (search the table below by HCPCS or description).
|
||
"""),
|
||
mo.ui.table(
|
||
rvu_delta.sort("delta_vs_2026final").to_pandas(),
|
||
page_size=25,
|
||
label="RVU / Payment Deltas by HCPCS",
|
||
),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(con, fr_md, mo, proposed_for):
|
||
_new = con.execute("""
|
||
SELECT count(*) FROM pfs.rvu_proposed p
|
||
WHERE p.cms_rule_id = 'CMS-1848-P' AND (p.mod IS NULL OR p.mod='') AND p.status_code='A' AND p.non_fac_pe_rvu IS NOT NULL
|
||
AND NOT EXISTS (SELECT 1 FROM pfs.rvu f WHERE f.year=2026 AND f.hcpcs=p.hcpcs AND (f.mod IS NULL OR f.mod=''))
|
||
""").fetchone()[0]
|
||
_dropped = con.execute("""
|
||
SELECT count(*) FROM pfs.rvu f
|
||
WHERE f.year=2026 AND (f.mod IS NULL OR f.mod='') AND f.status_code='A'
|
||
AND NOT EXISTS (SELECT 1 FROM pfs.rvu_proposed p WHERE p.cms_rule_id = 'CMS-1848-P' AND p.hcpcs=f.hcpcs AND (p.mod IS NULL OR p.mod=''))
|
||
""").fetchone()[0]
|
||
mo.md(f"""
|
||
> **Coverage caveat:** {_new} HCPCS codes appear in the `CMS-1848-P`
|
||
> proposed RVU table with no CY2026-final counterpart (new/renumbered
|
||
> codes), and {_dropped} CY2026-final codes have no match in the
|
||
> proposed table (dropped, bundled, or excluded from the Addendum B
|
||
> extract used to build the lake table). Both groups are excluded
|
||
> from the delta analysis above rather than shown with a fabricated
|
||
> baseline.
|
||
|
||
**Source:** `pfs.rvu_proposed` — CMS-1848-P Addendum B,
|
||
{fr_md(proposed_for(2027).federal_register_citation)}. `pfs.rvu` — CY2026
|
||
PFS Final Rule Addendum B.
|
||
""")
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(mo):
|
||
mo.md("""
|
||
### Cross-NPRM check — CY2026 NPRM vs. CY2027 NPRM (optional)
|
||
|
||
`pfs.rvu_proposed` holds **two** NPRM partitions in the same table
|
||
— `CMS-1832-P` (CY2026 NPRM, 14,169 rows) and `CMS-1848-P` (CY2027
|
||
NPRM, 14,518 rows). For codes proposed in both rulemakings, this
|
||
compares total non-facility RVUs NPRM-to-NPRM — a coarse signal of
|
||
which codes CMS has been proposing to move for two consecutive
|
||
rulemaking cycles, independent of which CF applies.
|
||
""")
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(q):
|
||
cross_nprm = q("""
|
||
WITH cy2026_nprm AS (
|
||
SELECT hcpcs, description, work_rvu + non_fac_pe_rvu + mp_rvu AS total_nf_rvu
|
||
FROM pfs.rvu_proposed
|
||
WHERE cms_rule_id = 'CMS-1832-P' AND (mod IS NULL OR mod = '')
|
||
AND status_code = 'A' AND non_fac_pe_rvu IS NOT NULL
|
||
QUALIFY row_number() OVER (PARTITION BY hcpcs ORDER BY hcpcs) = 1
|
||
),
|
||
cy2027_nprm AS (
|
||
SELECT hcpcs, work_rvu + non_fac_pe_rvu + mp_rvu AS total_nf_rvu
|
||
FROM pfs.rvu_proposed
|
||
WHERE cms_rule_id = 'CMS-1848-P' AND (mod IS NULL OR mod = '')
|
||
AND status_code = 'A' AND non_fac_pe_rvu IS NOT NULL
|
||
QUALIFY row_number() OVER (PARTITION BY hcpcs ORDER BY hcpcs) = 1
|
||
)
|
||
SELECT
|
||
a.hcpcs, a.description,
|
||
a.total_nf_rvu AS total_nf_rvu_cy2026_nprm,
|
||
b.total_nf_rvu AS total_nf_rvu_cy2027_nprm,
|
||
round(b.total_nf_rvu - a.total_nf_rvu, 4) AS rvu_delta_nprm_to_nprm
|
||
FROM cy2026_nprm a
|
||
JOIN cy2027_nprm b USING (hcpcs)
|
||
ORDER BY abs(b.total_nf_rvu - a.total_nf_rvu) DESC
|
||
LIMIT 10
|
||
""")
|
||
return (cross_nprm,)
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(cross_nprm, fr_md, mo):
|
||
mo.vstack(
|
||
[
|
||
cross_nprm,
|
||
mo.md(f"""
|
||
**Sources:** `pfs.rvu_proposed` — `CMS-1832-P` ({fr_md("90 FR 32352")})
|
||
and `CMS-1848-P` ({fr_md("91 FR 43842")}) partitions of the same table.
|
||
Total non-facility RVUs only — no dollar conversion, since
|
||
the two NPRMs' CFs are not directly comparable across a
|
||
one-year gap without also re-basing for the RVU-neutral
|
||
budget-neutrality adjustment each rulemaking applies.
|
||
"""),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
# ── 3. GPCI & Locality ──────────────────────────────────────────────────
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(mo):
|
||
mo.md("""
|
||
## 3. GPCI & Locality — Proposed CY2027 Geographic Indices
|
||
|
||
Geographic Practice Cost Indices scale each RVU component to local
|
||
input costs: `payment = (work_rvu x work_gpci + pe_rvu x pe_gpci +
|
||
mp_rvu x mp_gpci) x CF`. Every dollar figure in Sections 1-2 is
|
||
national/unadjusted (all GPCIs = 1.0); this section is where
|
||
locality enters, from `pfs.gpci_proposed` — the NPRM **Addendum E**
|
||
workbooks, ingested per rule partition exactly like
|
||
`pfs.rvu_proposed`.
|
||
|
||
GPCIs update on a **biennial** cycle, so the table holds three
|
||
partition-years: the CY2026 NPRM (`CMS-1832-P`) proposed CY2026
|
||
values *and* CY2027 phase-in values, and the CY2027 NPRM
|
||
(`CMS-1848-P`) re-proposes CY2027. That makes "did CMS change its
|
||
mind about 2027?" a plain self-join, done below.
|
||
|
||
> **Work-floor caveat:** the proposed work GPCIs are published
|
||
> **without** the statutory 1.0 work floor — that is the Addendum E
|
||
> column heading's own wording, while the CY2025 baseline column in
|
||
> the CY2026-NPRM workbook was published *with* the floor. Values
|
||
> here are shown exactly as published; whether a floor applies to
|
||
> actual CY2027 payment depends on legislation not adjudicated in
|
||
> this notebook.
|
||
""")
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(q):
|
||
gpci_2027p = q("""
|
||
WITH prop AS (
|
||
SELECT mac, state, locality, locality_name,
|
||
work_gpci, pe_gpci, mp_gpci
|
||
FROM pfs.gpci_proposed
|
||
WHERE cms_rule_id = 'CMS-1848-P' AND gpci_year = 2027
|
||
),
|
||
fin AS (
|
||
SELECT mac, locality,
|
||
work_gpci AS work_gpci_2026f,
|
||
pe_gpci AS pe_gpci_2026f,
|
||
mp_gpci AS mp_gpci_2026f
|
||
FROM pfs.gpci WHERE year = 2026
|
||
)
|
||
SELECT p.*,
|
||
f.work_gpci_2026f, f.pe_gpci_2026f, f.mp_gpci_2026f,
|
||
round(p.work_gpci - f.work_gpci_2026f, 4) AS d_work,
|
||
round(p.pe_gpci - f.pe_gpci_2026f, 4) AS d_pe,
|
||
round(p.mp_gpci - f.mp_gpci_2026f, 4) AS d_mp,
|
||
round((p.work_gpci + p.pe_gpci + p.mp_gpci) / 3
|
||
- (f.work_gpci_2026f + f.pe_gpci_2026f + f.mp_gpci_2026f) / 3,
|
||
4) AS d_mean
|
||
FROM prop p
|
||
JOIN fin f USING (mac, locality)
|
||
""")
|
||
gpci_2027p
|
||
return (gpci_2027p,)
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(AMBER, TEAL, alt, fr_md, gpci_2027p, mo, pl, proposed_for):
|
||
_n = 15
|
||
_up = (gpci_2027p["d_mean"] > 0).sum()
|
||
_down = (gpci_2027p["d_mean"] < 0).sum()
|
||
_flat = (gpci_2027p["d_mean"] == 0).sum()
|
||
|
||
_movers = (
|
||
gpci_2027p.sort("d_mean", descending=True)
|
||
.head(_n)
|
||
.vstack(gpci_2027p.sort("d_mean").head(_n).sort("d_mean", descending=True))
|
||
.with_columns(
|
||
(pl.col("locality_name") + " (" + pl.col("state") + ")").alias("label"),
|
||
pl.when(pl.col("d_mean") >= 0)
|
||
.then(pl.lit("Gain"))
|
||
.otherwise(pl.lit("Loss"))
|
||
.alias("sign"),
|
||
)
|
||
)
|
||
|
||
gpci_movers_chart = (
|
||
alt.Chart(_movers.to_pandas())
|
||
.mark_bar()
|
||
.encode(
|
||
x=alt.X("d_mean:Q", title="Mean GPCI change (unweighted mean of work/PE/MP)"),
|
||
y=alt.Y("label:N", title=None, sort=alt.SortField(field="d_mean", order="descending")),
|
||
color=alt.Color(
|
||
"sign:N",
|
||
title=None,
|
||
scale=alt.Scale(domain=["Gain", "Loss"], range=[TEAL, AMBER]),
|
||
legend=alt.Legend(orient="top"),
|
||
),
|
||
tooltip=[
|
||
"locality_name", "state", "mac", "locality",
|
||
alt.Tooltip("d_work:Q", format="+.4f"),
|
||
alt.Tooltip("d_pe:Q", format="+.4f"),
|
||
alt.Tooltip("d_mp:Q", format="+.4f"),
|
||
],
|
||
)
|
||
.properties(
|
||
title="GPCI Movers — CY2027 Proposed vs. CY2026 Final, Top ±15 Localities",
|
||
width=640,
|
||
height=520,
|
||
)
|
||
)
|
||
|
||
mo.vstack(
|
||
[
|
||
gpci_movers_chart,
|
||
mo.md(f"""
|
||
Across the {gpci_2027p.height} payment localities matched to
|
||
the CY2026-final GPCI table: **{_up} rise, {_down} fall,
|
||
{_flat} unchanged** on the unweighted mean of the three
|
||
components (a GAF proxy — CMS's official GAFs in Addendum D
|
||
are utilization-weighted and not loaded here).
|
||
|
||
**Sources:** `pfs.gpci_proposed` (`CMS-1848-P`, gpci_year
|
||
2027) — CY2027 NPRM Addendum E,
|
||
{fr_md(proposed_for(2027).federal_register_citation)}. Baseline —
|
||
`pfs.gpci` year 2026 (CY2026 Final).
|
||
"""),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(mo, q):
|
||
_drift = q("""
|
||
WITH projected AS (
|
||
SELECT mac, locality, locality_name, state,
|
||
work_gpci AS w_1832, pe_gpci AS p_1832, mp_gpci AS m_1832
|
||
FROM pfs.gpci_proposed
|
||
WHERE cms_rule_id = 'CMS-1832-P' AND gpci_year = 2027
|
||
),
|
||
reproposed AS (
|
||
SELECT mac, locality,
|
||
work_gpci AS w_1848, pe_gpci AS p_1848, mp_gpci AS m_1848
|
||
FROM pfs.gpci_proposed
|
||
WHERE cms_rule_id = 'CMS-1848-P' AND gpci_year = 2027
|
||
)
|
||
SELECT a.locality_name, a.state,
|
||
a.w_1832, b.w_1848, a.p_1832, b.p_1848, a.m_1832, b.m_1848,
|
||
round(abs(b.w_1848 - a.w_1832) + abs(b.p_1848 - a.p_1832)
|
||
+ abs(b.m_1848 - a.m_1832), 4) AS total_abs_drift
|
||
FROM projected a
|
||
JOIN reproposed b USING (mac, locality)
|
||
ORDER BY total_abs_drift DESC
|
||
""")
|
||
_changed = _drift.filter(_drift["total_abs_drift"] > 0)
|
||
if _changed.height == 0:
|
||
_msg = (
|
||
f"**All {_drift.height} localities carry identical CY2027 GPCIs in "
|
||
"both rulemakings** — the CY2027 NPRM re-proposes the CY2026 "
|
||
"NPRM's phase-in values unchanged."
|
||
)
|
||
else:
|
||
_msg = (
|
||
f"**{_changed.height} of {_drift.height} localities moved** between "
|
||
"the CY2026 NPRM's projected 2027 values and the CY2027 NPRM's "
|
||
"re-proposed values (largest movers below)."
|
||
)
|
||
_parts = [
|
||
mo.md(f"""
|
||
### Did CMS change its 2027 mind? — CMS-1832-P projection vs. CMS-1848-P proposal
|
||
|
||
{_msg}
|
||
""")
|
||
]
|
||
if _changed.height > 0:
|
||
_parts.append(
|
||
mo.ui.table(
|
||
_changed.head(15).to_pandas(),
|
||
label="Largest 1832-P → 1848-P CY2027 GPCI revisions",
|
||
)
|
||
)
|
||
mo.vstack(_parts)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(GRAY, INDIGO_MED, PLUM, alt, mo, pl, q):
|
||
# Long-term: highest- and lowest-cost localities (by CY2026-final mean
|
||
# GPCI) against the all-locality median, 2016-2026 final + 2027
|
||
# proposed (flagged).
|
||
_extremes = q("""
|
||
SELECT mac, locality, locality_name, state,
|
||
(work_gpci + pe_gpci + mp_gpci) / 3 AS mean_gpci
|
||
FROM pfs.gpci WHERE year = 2026
|
||
ORDER BY mean_gpci DESC
|
||
""")
|
||
_hi = _extremes.row(0, named=True)
|
||
_lo = _extremes.row(-1, named=True)
|
||
|
||
_hist = q(f"""
|
||
WITH sel AS (
|
||
SELECT year, locality_name, work_gpci, pe_gpci, mp_gpci
|
||
FROM pfs.gpci
|
||
WHERE (mac = '{_hi["mac"]}' AND locality = '{_hi["locality"]}')
|
||
OR (mac = '{_lo["mac"]}' AND locality = '{_lo["locality"]}')
|
||
),
|
||
med AS (
|
||
SELECT year, 'All-locality median' AS locality_name,
|
||
median(work_gpci) AS work_gpci,
|
||
median(pe_gpci) AS pe_gpci,
|
||
median(mp_gpci) AS mp_gpci
|
||
FROM pfs.gpci GROUP BY year
|
||
),
|
||
prop AS (
|
||
SELECT gpci_year AS year, locality_name, work_gpci, pe_gpci, mp_gpci
|
||
FROM pfs.gpci_proposed
|
||
WHERE cms_rule_id = 'CMS-1848-P' AND gpci_year = 2027
|
||
AND ((mac = '{_hi["mac"]}' AND locality = '{_hi["locality"]}')
|
||
OR (mac = '{_lo["mac"]}' AND locality = '{_lo["locality"]}'))
|
||
),
|
||
propmed AS (
|
||
SELECT gpci_year AS year, 'All-locality median' AS locality_name,
|
||
median(work_gpci) AS work_gpci,
|
||
median(pe_gpci) AS pe_gpci,
|
||
median(mp_gpci) AS mp_gpci
|
||
FROM pfs.gpci_proposed
|
||
WHERE cms_rule_id = 'CMS-1848-P' AND gpci_year = 2027
|
||
GROUP BY gpci_year
|
||
)
|
||
SELECT *, false AS proposed FROM sel
|
||
UNION ALL SELECT *, false FROM med
|
||
UNION ALL SELECT *, true FROM prop
|
||
UNION ALL SELECT *, true FROM propmed
|
||
ORDER BY year, locality_name
|
||
""")
|
||
|
||
_long = _hist.unpivot(
|
||
index=["year", "locality_name", "proposed"],
|
||
on=["work_gpci", "pe_gpci", "mp_gpci"],
|
||
variable_name="component",
|
||
value_name="gpci",
|
||
).with_columns(
|
||
pl.col("component").replace(
|
||
{"work_gpci": "Work", "pe_gpci": "PE", "mp_gpci": "MP"}
|
||
)
|
||
)
|
||
|
||
_domain = [_hi["locality_name"], _lo["locality_name"], "All-locality median"]
|
||
_range = [INDIGO_MED, PLUM, GRAY]
|
||
|
||
_charts = []
|
||
for _comp in ("Work", "PE", "MP"):
|
||
_cdf = _long.filter(pl.col("component") == _comp).to_pandas()
|
||
_line = (
|
||
alt.Chart(_cdf[~_cdf["proposed"]])
|
||
.mark_line(point=True)
|
||
.encode(
|
||
x=alt.X("year:O", title="Year"),
|
||
y=alt.Y("gpci:Q", title="GPCI", scale=alt.Scale(zero=False)),
|
||
color=alt.Color(
|
||
"locality_name:N",
|
||
title=None,
|
||
scale=alt.Scale(domain=_domain, range=_range),
|
||
legend=alt.Legend(orient="bottom") if _comp == "PE" else None,
|
||
),
|
||
tooltip=["locality_name", "year:O", alt.Tooltip("gpci:Q", format=".4f")],
|
||
)
|
||
)
|
||
_prop_pts = (
|
||
alt.Chart(_cdf[_cdf["proposed"]])
|
||
.mark_point(shape="diamond", size=90, filled=True)
|
||
.encode(
|
||
x="year:O",
|
||
y="gpci:Q",
|
||
color=alt.Color(
|
||
"locality_name:N",
|
||
scale=alt.Scale(domain=_domain, range=_range),
|
||
legend=None,
|
||
),
|
||
tooltip=["locality_name", "year:O", alt.Tooltip("gpci:Q", format=".4f")],
|
||
)
|
||
)
|
||
_charts.append(
|
||
(_line + _prop_pts).properties(title=f"{_comp} GPCI", width=240, height=220)
|
||
)
|
||
gpci_longterm_chart = alt.hconcat(*_charts)
|
||
|
||
mo.vstack(
|
||
[
|
||
gpci_longterm_chart,
|
||
mo.md(f"""
|
||
Final-rule GPCIs 2016-2026 (`pfs.gpci`) for the
|
||
highest-cost locality (**{_hi["locality_name"]}**,
|
||
{_hi["state"]}) and lowest-cost locality
|
||
(**{_lo["locality_name"]}**, {_lo["state"]}) by CY2026 mean
|
||
GPCI, against the all-locality median. Diamond markers at
|
||
2027 are the **CY2027 NPRM proposal — final rule pending**,
|
||
with the work-floor caveat above applying to the Work
|
||
panel.
|
||
"""),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(gpci_2027p, mo, pfs_payment, pl, proposed_for, q):
|
||
# Locality-adjusted payment for the Section-1 example trio at the
|
||
# best/worst CY2027-proposed localities vs the national (GPCI=1.0)
|
||
# rate, all priced at the CY2027 proposed non-QP CF.
|
||
_best = gpci_2027p.sort("d_mean", descending=True).row(0, named=True)
|
||
_worst = gpci_2027p.sort("d_mean").row(0, named=True)
|
||
|
||
_rvu = q("""
|
||
SELECT hcpcs, description, work_rvu, non_fac_pe_rvu, mp_rvu
|
||
FROM pfs.rvu
|
||
WHERE year = 2026 AND (mod IS NULL OR mod = '')
|
||
AND hcpcs IN ('99213', '27447', '70553')
|
||
QUALIFY row_number() OVER (PARTITION BY hcpcs ORDER BY hcpcs) = 1
|
||
""")
|
||
|
||
_localities = pl.DataFrame(
|
||
[
|
||
{
|
||
"mac": r["mac"],
|
||
"locality": r["locality"],
|
||
"where": f"{r['locality_name']} ({r['state']})",
|
||
"work_gpci": r["work_gpci"],
|
||
"pe_gpci": r["pe_gpci"],
|
||
"mp_gpci": r["mp_gpci"],
|
||
}
|
||
for r in (_best, _worst)
|
||
]
|
||
+ [
|
||
{
|
||
"mac": "NATL",
|
||
"locality": "00",
|
||
"where": "National (GPCI = 1.0)",
|
||
"work_gpci": 1.0,
|
||
"pe_gpci": 1.0,
|
||
"mp_gpci": 1.0,
|
||
}
|
||
]
|
||
)
|
||
|
||
_rvu_x_loc = _rvu.join(_localities.select("mac", "locality"), how="cross")
|
||
_priced = pfs_payment(
|
||
_rvu_x_loc,
|
||
_localities.select("mac", "locality", "work_gpci", "pe_gpci", "mp_gpci"),
|
||
cf=proposed_for(2027).conversion_factor,
|
||
)
|
||
gpci_payment_table = (
|
||
_priced.join(_localities.select("mac", "locality", "where"), on=["mac", "locality"])
|
||
.with_columns(pl.col("payment_amount").round(2))
|
||
.pivot(on="where", index=["hcpcs", "description"], values="payment_amount")
|
||
)
|
||
|
||
mo.vstack(
|
||
[
|
||
mo.ui.table(gpci_payment_table.to_pandas(), label="Locality-adjusted payment — CY2027 proposed non-QP CF"),
|
||
mo.md(f"""
|
||
`pfs.calcs.payment.payment` (the module's locality formula)
|
||
applied to CY2026-final RVUs at the CY2027 **proposed**
|
||
non-QP CF ({proposed_for(2027).conversion_factor}) — final
|
||
rule pending. Localities shown are the CY2027 proposal's
|
||
biggest mean-GPCI gainer (**{_best["locality_name"]}**) and
|
||
decliner (**{_worst["locality_name"]}**), with the
|
||
national GPCI = 1.0 rate for reference.
|
||
"""),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
# ── 4. Primary Care Services (ACO attribution set) ──────────────────────
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(cfr_md, mo, q):
|
||
pcs_codes = q("""
|
||
SELECT hcpcs_code AS hcpcs, trim(description) AS pcs_description
|
||
FROM cms.primary_care_service_code
|
||
ORDER BY hcpcs
|
||
""")
|
||
assert pcs_codes.height == 109, (
|
||
f"expected 109 designated primary care service codes, got {pcs_codes.height}"
|
||
)
|
||
mo.md(f"""
|
||
## 4. Primary Care Services — the ACO Attribution Set
|
||
|
||
The **{pcs_codes.height} designated primary care service codes**
|
||
used for Medicare Shared Savings Program beneficiary assignment
|
||
({cfr_md("42 CFR 425.400(c)")}), read live from
|
||
`cms.primary_care_service_code` — the attribution reference list
|
||
published to the lake from the aco module. These are the services
|
||
whose billing patterns decide which ACO a beneficiary is attributed
|
||
to, so their PFS valuation moves both clinician revenue **and** the
|
||
attribution denominators every MSSP financial calculation stands
|
||
on.
|
||
|
||
Two disclosures, once: (1) this is the **current-law list applied
|
||
retrospectively** — the designation set itself changes across
|
||
rulemakings and is not versioned by year in this repo; (2) all
|
||
aggregate statements are **unweighted by utilization** (the lake
|
||
carries no claims-volume reference), so each code counts equally.
|
||
""")
|
||
return (pcs_codes,)
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(RULES, pl, proposed_for, q):
|
||
_pqm_raw = q("""
|
||
WITH pcs AS (
|
||
SELECT hcpcs_code AS hcpcs, trim(description) AS pcs_description
|
||
FROM cms.primary_care_service_code
|
||
),
|
||
prop AS (
|
||
SELECT hcpcs, status_code AS status_2027p,
|
||
work_rvu AS work_2027p,
|
||
work_rvu + coalesce(non_fac_pe_rvu, fac_pe_rvu) + mp_rvu
|
||
AS total_2027p
|
||
FROM pfs.rvu_proposed
|
||
WHERE cms_rule_id = 'CMS-1848-P' AND (mod IS NULL OR mod = '')
|
||
QUALIFY row_number() OVER (PARTITION BY hcpcs ORDER BY hcpcs) = 1
|
||
),
|
||
fin AS (
|
||
SELECT hcpcs, status_code AS status_2026f,
|
||
work_rvu AS work_2026f,
|
||
work_rvu + coalesce(non_fac_pe_rvu, fac_pe_rvu) + mp_rvu
|
||
AS total_2026f
|
||
FROM pfs.rvu
|
||
WHERE year = 2026 AND (mod IS NULL OR mod = '')
|
||
QUALIFY row_number() OVER (PARTITION BY hcpcs ORDER BY hcpcs) = 1
|
||
)
|
||
SELECT pcs.hcpcs, pcs.pcs_description,
|
||
fin.status_2026f, fin.work_2026f, fin.total_2026f,
|
||
prop.status_2027p, prop.work_2027p, prop.total_2027p
|
||
FROM pcs
|
||
LEFT JOIN fin USING (hcpcs)
|
||
LEFT JOIN prop USING (hcpcs)
|
||
ORDER BY pcs.hcpcs
|
||
""")
|
||
|
||
_cf26 = RULES[2026].conversion_factor
|
||
_cf27p = proposed_for(2027).conversion_factor
|
||
pqm_near = _pqm_raw.with_columns(
|
||
(pl.col("total_2026f") * _cf26).round(2).alias("dollar_2026final"),
|
||
(pl.col("total_2027p") * _cf27p).round(2).alias("dollar_2027proposed"),
|
||
).with_columns(
|
||
(pl.col("dollar_2027proposed") - pl.col("dollar_2026final"))
|
||
.round(2)
|
||
.alias("dollar_delta"),
|
||
pl.when(pl.col("total_2026f").is_not_null() & pl.col("total_2027p").is_not_null())
|
||
.then(
|
||
(100 * (pl.col("dollar_2027proposed") - pl.col("dollar_2026final"))
|
||
/ pl.col("dollar_2026final")).round(2)
|
||
)
|
||
.alias("pct_change"),
|
||
pl.when(pl.col("total_2026f").is_not_null() & pl.col("total_2027p").is_not_null())
|
||
.then(pl.lit("both"))
|
||
.when(pl.col("total_2027p").is_not_null())
|
||
.then(pl.lit("new_in_2027"))
|
||
.when(pl.col("total_2026f").is_not_null())
|
||
.then(pl.lit("missing_from_2027"))
|
||
.otherwise(pl.lit("absent_both"))
|
||
.alias("coverage"),
|
||
)
|
||
assert pqm_near.height == 109, pqm_near.height
|
||
return (pqm_near,)
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(RULES, mo, pqm_near, proposed_for):
|
||
mo.vstack(
|
||
[
|
||
mo.md(f"""
|
||
### Near-term — every designated code, CY2026 final vs. CY2027 proposed
|
||
|
||
One row per designated code: CY2026-final and
|
||
CY2027-proposed work/total RVUs (non-facility PE where
|
||
priced, facility otherwise), dollars at each vintage's own
|
||
standard CF (CY2026 final {RULES[2026].conversion_factor};
|
||
CY2027 proposed non-QP
|
||
{proposed_for(2027).conversion_factor} — **final rule
|
||
pending**), national unadjusted; Section 3 carries the
|
||
locality adjustment.
|
||
"""),
|
||
mo.ui.table(
|
||
pqm_near.sort("pct_change", nulls_last=True).to_pandas(),
|
||
page_size=25,
|
||
label="All 109 designated primary care services — near-term comparison",
|
||
),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(AMBER, TEAL, alt, mo, pl, pqm_near):
|
||
_both = pqm_near.filter(pl.col("coverage") == "both").with_columns(
|
||
pl.when(pl.col("pct_change") >= 0)
|
||
.then(pl.lit("Gain"))
|
||
.otherwise(pl.lit("Loss"))
|
||
.alias("sign")
|
||
)
|
||
_one_sided = pqm_near.filter(pl.col("coverage") != "both")
|
||
|
||
pqm_near_chart = (
|
||
alt.Chart(_both.to_pandas())
|
||
.mark_bar()
|
||
.encode(
|
||
x=alt.X("pct_change:Q", title="% change in national payment, CY2026 final → CY2027 proposed"),
|
||
y=alt.Y("hcpcs:N", title=None, sort=alt.SortField(field="pct_change", order="descending")),
|
||
color=alt.Color(
|
||
"sign:N",
|
||
title=None,
|
||
scale=alt.Scale(domain=["Gain", "Loss"], range=[TEAL, AMBER]),
|
||
legend=alt.Legend(orient="top"),
|
||
),
|
||
tooltip=[
|
||
"hcpcs", "pcs_description",
|
||
alt.Tooltip("dollar_2026final:Q", format="$.2f"),
|
||
alt.Tooltip("dollar_2027proposed:Q", format="$.2f"),
|
||
alt.Tooltip("pct_change:Q", format="+.2f"),
|
||
],
|
||
)
|
||
.properties(
|
||
title="Designated Primary Care Services — % Payment Change (every matched code)",
|
||
width=640,
|
||
height=max(300, 11 * _both.height),
|
||
)
|
||
)
|
||
|
||
_sided_lines = "\n".join(
|
||
f" - `{r['hcpcs']}` — {r['pcs_description']} (*{r['coverage'].replace('_', ' ')}*)"
|
||
for r in _one_sided.iter_rows(named=True)
|
||
)
|
||
mo.vstack(
|
||
[
|
||
pqm_near_chart,
|
||
mo.md(f"""
|
||
**{_both.height} of 109** designated codes price in both
|
||
tables and appear above. The remaining
|
||
**{_one_sided.height}** are one-sided or unpriced and are
|
||
listed explicitly rather than plotted with a fabricated
|
||
baseline:
|
||
|
||
{_sided_lines}
|
||
|
||
The one-sided groups are computed live, so their
|
||
composition tracks the data: as of this ingest every
|
||
designated code that prices anywhere prices in **both**
|
||
tables (the APCM G-codes already carry CY2026-final RVUs),
|
||
and the unmatched remainder is entirely `absent_both` —
|
||
deleted or replaced legacy codes (telephone E/M 99441-3,
|
||
prolonged 99354-5, consult-era visits, pre-APCM
|
||
check-in/CCM G-codes) that stay on the attribution
|
||
designation list without a current PFS price.
|
||
"""),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(RULES, pl, proposed_for, q):
|
||
_hist = q("""
|
||
WITH pcs AS (
|
||
SELECT hcpcs_code AS hcpcs, trim(description) AS pcs_description
|
||
FROM cms.primary_care_service_code
|
||
),
|
||
fin AS (
|
||
SELECT year, hcpcs,
|
||
work_rvu + coalesce(non_fac_pe_rvu, fac_pe_rvu) + mp_rvu
|
||
AS total_rvu
|
||
FROM pfs.rvu
|
||
WHERE (mod IS NULL OR mod = '')
|
||
QUALIFY row_number() OVER (PARTITION BY hcpcs, year ORDER BY hcpcs) = 1
|
||
),
|
||
prop AS (
|
||
SELECT 2027 AS year, hcpcs,
|
||
work_rvu + coalesce(non_fac_pe_rvu, fac_pe_rvu) + mp_rvu
|
||
AS total_rvu
|
||
FROM pfs.rvu_proposed
|
||
WHERE cms_rule_id = 'CMS-1848-P' AND (mod IS NULL OR mod = '')
|
||
QUALIFY row_number() OVER (PARTITION BY hcpcs ORDER BY hcpcs) = 1
|
||
)
|
||
SELECT pcs.hcpcs, pcs.pcs_description, u.year, u.total_rvu,
|
||
u.year = 2027 AS proposed
|
||
FROM pcs
|
||
JOIN (SELECT * FROM fin UNION ALL SELECT * FROM prop) u USING (hcpcs)
|
||
WHERE u.total_rvu IS NOT NULL
|
||
ORDER BY pcs.hcpcs, u.year
|
||
""")
|
||
|
||
_cf = pl.DataFrame(
|
||
{
|
||
"year": [y for y in sorted(RULES) if y >= 2015] + [2027],
|
||
"cf": [RULES[y].conversion_factor for y in sorted(RULES) if y >= 2015]
|
||
+ [proposed_for(2027).conversion_factor],
|
||
}
|
||
)
|
||
pqm_long = (
|
||
_hist.join(_cf, on="year")
|
||
.with_columns((pl.col("total_rvu") * pl.col("cf")).round(2).alias("dollar"))
|
||
.with_columns(
|
||
(
|
||
100
|
||
* (pl.col("total_rvu") / pl.col("total_rvu").shift(1).over("hcpcs") - 1)
|
||
)
|
||
.round(2)
|
||
.alias("rvu_yoy_pct")
|
||
)
|
||
)
|
||
return (pqm_long,)
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(AMBER, TEAL, alt, mo, pqm_long):
|
||
_hm = pqm_long.to_pandas()
|
||
pqm_heatmap = (
|
||
alt.Chart(_hm)
|
||
.mark_rect(stroke="#F7F5F0", strokeWidth=1)
|
||
.encode(
|
||
x=alt.X("year:O", title="Year (2027 = proposed)"),
|
||
y=alt.Y("hcpcs:N", title=None),
|
||
color=alt.Color(
|
||
"rvu_yoy_pct:Q",
|
||
title="RVU YoY %",
|
||
scale=alt.Scale(domainMid=0, range=[AMBER, "#EDEBE6", TEAL]),
|
||
),
|
||
tooltip=[
|
||
"hcpcs", "pcs_description", "year:O",
|
||
alt.Tooltip("total_rvu:Q", format=".2f"),
|
||
alt.Tooltip("dollar:Q", format="$.2f"),
|
||
alt.Tooltip("rvu_yoy_pct:Q", format="+.2f"),
|
||
],
|
||
)
|
||
.properties(
|
||
title="Total-RVU Year-over-Year % Change — Every Designated Code, 2015-2027p",
|
||
width=560,
|
||
height=11 * _hm["hcpcs"].nunique(),
|
||
)
|
||
)
|
||
mo.vstack(
|
||
[
|
||
pqm_heatmap,
|
||
mo.md("""
|
||
Long-term valuation of **each individual designated code**:
|
||
cells are year-over-year % change in total RVUs (first
|
||
available year blank). Sustained white rows are codes CMS
|
||
has left untouched for a decade; the visible vertical band
|
||
at 2021 is the office/outpatient E/M revaluation. Hover any
|
||
cell for that code-year's RVUs and dollars.
|
||
"""),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(INDIGO_MED, alt, mo, pl, pqm_long):
|
||
_span = (
|
||
pqm_long.sort("hcpcs", "year")
|
||
.group_by("hcpcs", "pcs_description")
|
||
.agg(
|
||
pl.col("dollar").first().alias("dollar_first"),
|
||
pl.col("dollar").last().alias("dollar_last"),
|
||
pl.len().alias("n_years"),
|
||
)
|
||
.filter(pl.col("n_years") >= 5)
|
||
.with_columns((pl.col("dollar_last") - pl.col("dollar_first")).abs().alias("abs_move"))
|
||
.sort("abs_move", descending=True)
|
||
.head(12)
|
||
)
|
||
_sel = pqm_long.filter(pl.col("hcpcs").is_in(_span["hcpcs"]))
|
||
|
||
_rows = []
|
||
_codes = _span["hcpcs"].to_list()
|
||
for _i in range(0, len(_codes), 4):
|
||
_row_charts = []
|
||
for _code in _codes[_i : _i + 4]:
|
||
_cdf = _sel.filter(pl.col("hcpcs") == _code).to_pandas()
|
||
_solid = (
|
||
alt.Chart(_cdf[~_cdf["proposed"]])
|
||
.mark_line(point=True, color=INDIGO_MED)
|
||
.encode(
|
||
x=alt.X("year:O", title=None, axis=alt.Axis(values=[2015, 2020, 2026])),
|
||
y=alt.Y("dollar:Q", title=None, scale=alt.Scale(zero=False)),
|
||
tooltip=["hcpcs", "year:O", alt.Tooltip("dollar:Q", format="$.2f")],
|
||
)
|
||
)
|
||
_prop = (
|
||
alt.Chart(_cdf[_cdf["year"] >= 2026])
|
||
.mark_line(point=alt.OverlayMarkDef(shape="diamond", size=70), color=INDIGO_MED, strokeDash=[4, 3])
|
||
.encode(
|
||
x="year:O",
|
||
y="dollar:Q",
|
||
tooltip=["hcpcs", "year:O", alt.Tooltip("dollar:Q", format="$.2f")],
|
||
)
|
||
)
|
||
_row_charts.append(
|
||
(_solid + _prop).properties(title=str(_code), width=150, height=120)
|
||
)
|
||
_rows.append(alt.hconcat(*_row_charts))
|
||
pqm_multiples = alt.vconcat(*_rows)
|
||
|
||
mo.vstack(
|
||
[
|
||
pqm_multiples,
|
||
mo.md("""
|
||
The 12 designated codes with the largest absolute national
|
||
dollar move across their observed span (min. 5 priced
|
||
years). Solid line = final rules at each year's own final
|
||
CF; dashed segment to the diamond = **CY2027 proposed, at
|
||
the proposed non-QP CF — final rule pending**. Dollar
|
||
trajectories mix RVU changes with CF changes deliberately —
|
||
this is realized-price history, not resource-weight
|
||
history (the heatmap above isolates RVUs).
|
||
"""),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(gpci_2027p, mo, pl, pqm_near, proposed_for):
|
||
# Cross-cut: what the GPCI proposal does to one bread-and-butter
|
||
# attribution code (99213) at the Section-3 extreme localities.
|
||
_r = pqm_near.filter(pl.col("hcpcs") == "99213").row(0, named=True)
|
||
_best = gpci_2027p.sort("d_mean", descending=True).row(0, named=True)
|
||
_worst = gpci_2027p.sort("d_mean").row(0, named=True)
|
||
_natl = _r["dollar_2027proposed"]
|
||
|
||
_rows = pl.DataFrame(
|
||
[
|
||
{
|
||
"where": f"{loc['locality_name']} ({loc['state']})",
|
||
"work_gpci": loc["work_gpci"],
|
||
"pe_gpci": loc["pe_gpci"],
|
||
"mp_gpci": loc["mp_gpci"],
|
||
"mean_gpci_delta_vs_2026f": loc["d_mean"],
|
||
}
|
||
for loc in (_best, _worst)
|
||
]
|
||
)
|
||
mo.vstack(
|
||
[
|
||
mo.md(f"""
|
||
### GPCI × attribution cross-cut — 99213 under the CY2027 proposal
|
||
|
||
`99213` (established-patient office visit — the single most
|
||
attribution-relevant E/M code) prices at
|
||
**${_natl:.2f}** nationally under the CY2027 proposed
|
||
non-QP CF. At the proposal's extreme localities that
|
||
becomes a spread of locality-adjusted rates (Section 3's
|
||
payment table carries the exact figures for the example
|
||
trio); the GPCI movement below is what drives it.
|
||
"""),
|
||
mo.ui.table(_rows.to_pandas(), label="Extreme localities — proposed CY2027 GPCIs (vs CY2026-final mean)"),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
# ── 5. Advanced APM ─────────────────────────────────────────────────────
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(mo):
|
||
mo.md("""
|
||
## 5. Advanced APM (QP) Requirements
|
||
|
||
Under MACRA 2015, clinicians who participate heavily enough in an
|
||
Advanced Alternative Payment Model (Advanced APM) during a "QP
|
||
Performance Period" become Qualifying APM Participants (QPs) for
|
||
the payment year two years later (`payment_year = performance_year
|
||
+ 2`). The CY2027 NPRM's own regulatory-impact-analysis recital
|
||
covers **QP Performance Period 2027 / payment year 2029** —
|
||
`qpp.QPP[2027]`, reachable as `qpp.for_payment_year(2029)`.
|
||
|
||
`qpp.QPP`'s payment-year keying was corrected under task #620
|
||
shortly before this notebook was written — thresholds are keyed to
|
||
**payment year**, not performance year, and every table below
|
||
labels both explicitly so the correction is visible rather than
|
||
silently assumed.
|
||
""")
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(for_payment_year, mo):
|
||
_governs_2029 = for_payment_year(2029)
|
||
mo.md(f"""
|
||
`qpp.for_payment_year(2029)` resolves to QP Performance Period
|
||
**{_governs_2029.performance_year}** ({_governs_2029.citation}) —
|
||
the performance period whose determinations govern the payment
|
||
year that the CY2027 NPRM's proposed conversion factors apply to.
|
||
`qp_cf_applies` is **{_governs_2029.qp_cf_applies}** for this
|
||
performance period.
|
||
""")
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(QPP, pl, plain_years):
|
||
_rows = []
|
||
for _perf_year, _qy in sorted(QPP.items()):
|
||
_prop = _qy.proposed
|
||
_rows.append(
|
||
{
|
||
"qp_performance_period": _perf_year,
|
||
"payment_year": _qy.payment_year,
|
||
"qp_payment_pct": _qy.qp_thresholds.payment_amount_pct,
|
||
"qp_patient_pct": _qy.qp_thresholds.patient_count_pct,
|
||
"partial_qp_payment_pct": _qy.partial_qp_thresholds.payment_amount_pct,
|
||
"partial_qp_patient_pct": _qy.partial_qp_thresholds.patient_count_pct,
|
||
"revenue_nominal_pct": _qy.risk_standards.revenue_nominal_pct,
|
||
"benchmark_nominal_pct": _qy.risk_standards.benchmark_nominal_pct,
|
||
"apm_incentive_pct": _qy.apm_incentive_pct,
|
||
"qp_cf_applies": _qy.qp_cf_applies,
|
||
"cehrt_required": _qy.cehrt_required,
|
||
"has_nprm_proposal": _prop is not None,
|
||
"citation": _qy.citation,
|
||
}
|
||
)
|
||
apm_thresholds = pl.DataFrame(_rows)
|
||
plain_years(apm_thresholds)
|
||
return (apm_thresholds,)
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(QPP, cfr_md, mo):
|
||
mo.md(f"""
|
||
Columns are labeled `qp_performance_period` (the year Advanced-APM
|
||
participation is measured) and `payment_year` (the year the
|
||
resulting QP status is applied) explicitly and separately — every
|
||
threshold in this table governs by **`payment_year`**, per
|
||
{cfr_md("42 CFR 414.1430(a)")} and section 1833(z)(2) of the Act. `QPP[2027]`
|
||
(payment year {QPP[2027].payment_year}) is the entry this
|
||
notebook's CF walk (Section 1) corresponds to.
|
||
""")
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(alt, apm_thresholds, cfr_md, fr_md, mo):
|
||
_df = apm_thresholds.to_pandas()
|
||
_df["apm_incentive_pct"] = _df["apm_incentive_pct"].fillna(0.0)
|
||
_df["status"] = _df["apm_incentive_pct"].apply(lambda v: "Paid" if v > 0 else "None under current law")
|
||
|
||
incentive_chart = (
|
||
alt.Chart(_df)
|
||
.mark_bar()
|
||
.encode(
|
||
x=alt.X("payment_year:O", title="Payment year"),
|
||
y=alt.Y("apm_incentive_pct:Q", title="APM incentive payment (% of base-year Part B paid claims)"),
|
||
color=alt.Color(
|
||
"status:N",
|
||
title=None,
|
||
scale=alt.Scale(domain=["Paid", "None under current law"]),
|
||
),
|
||
tooltip=["qp_performance_period", "payment_year", "apm_incentive_pct", "status"],
|
||
)
|
||
.properties(title="APM Incentive Payment — Not a Clean Sunset", width=600, height=300)
|
||
)
|
||
|
||
_citations = ", ".join(sorted(set(_df["citation"])))
|
||
|
||
mo.vstack(
|
||
[
|
||
incentive_chart,
|
||
mo.md(f"""
|
||
The lump-sum APM Incentive Payment does **not** sunset
|
||
cleanly after payment year 2026. Payment year 2027 (this
|
||
notebook's prior year) has no incentive under current law.
|
||
The Consolidated Appropriations Act, 2026 (CAA 2026, Pub.
|
||
L. 119-75) then **revives a 3.1% incentive for payment
|
||
year 2028 only** — CMS-1848-P proposes to codify that
|
||
revival into {cfr_md("42 CFR 414.1450(b)(1)")}
|
||
({fr_md("91 FR 44218")}-44219, {fr_md("91 FR 44286")}).
|
||
Payment year 2029 (`QPP[2027]`, the year this
|
||
notebook's own CF walk applies to) reverts to **no**
|
||
incentive — the amendatory text lists no applicable
|
||
percentage for payment year 2029 at all. So the pattern
|
||
across 2025-2029 is paid / paid / gap / revived / gap, not
|
||
a single cutoff.
|
||
|
||
**Sources:** `qpp.QPP` performance years 2023-2027
|
||
({_citations}).
|
||
"""),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(QPP, fr_md, mo):
|
||
_prop = QPP[2027].proposed
|
||
mo.vstack(
|
||
[
|
||
mo.md(f"""
|
||
### CMS-1848-P proposed QPP changes — proposals only, no Final Rule disposition yet
|
||
|
||
`qpp.QPP[2027].proposed.changes` ({fr_md(_prop.federal_register_citation)},
|
||
{_prop.cms_rule_id}) is the single source of truth for the
|
||
narrative below — quoted live, not retyped. Unlike the
|
||
CY2026 notebook's disposition table, there is **no**
|
||
finalized/not-finalized column here: CMS-1848-F has not
|
||
been published.
|
||
"""),
|
||
mo.callout(mo.md(_prop.changes), kind="info"),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(mo):
|
||
mo.md("""
|
||
### What QP status is worth in dollars (CY2027 proposed) — 3 example HCPCS
|
||
|
||
National, unadjusted (GPCI = 1.0) payment for a single unit of
|
||
service, computed as `(work_rvu + non_fac_pe_rvu + mp_rvu) × CF`,
|
||
using **CY2026-final RVUs** (the current baseline; CY2027 has no
|
||
finalized RVU table yet) priced at the **CY2027 NPRM's proposed**
|
||
QP and non-QP conversion factors:
|
||
|
||
- **99213** — established-patient office visit, low complexity (E/M)
|
||
- **27447** — total knee arthroplasty (major procedure)
|
||
- **70553** — MRI brain, without and with contrast (imaging)
|
||
""")
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(pl, proposed_for, q):
|
||
_example_rvu = q("""
|
||
SELECT hcpcs, description, work_rvu, non_fac_pe_rvu, mp_rvu,
|
||
work_rvu + non_fac_pe_rvu + mp_rvu AS total_rvu
|
||
FROM pfs.rvu
|
||
WHERE year = 2026 AND (mod IS NULL OR mod = '')
|
||
AND hcpcs IN ('99213', '27447', '70553')
|
||
""")
|
||
|
||
_prop = proposed_for(2027)
|
||
_rows = []
|
||
for _r in _example_rvu.iter_rows(named=True):
|
||
_pay_nonqp = round(_r["total_rvu"] * _prop.conversion_factor, 2)
|
||
_pay_qp = round(_r["total_rvu"] * _prop.cf_qp, 2)
|
||
_rows.append(
|
||
{
|
||
"hcpcs": _r["hcpcs"],
|
||
"description": _r["description"],
|
||
"total_rvu_cy2026final": _r["total_rvu"],
|
||
"non_qp_payment_cy2027proposed": _pay_nonqp,
|
||
"qp_payment_cy2027proposed": _pay_qp,
|
||
"qp_differential": round(_pay_qp - _pay_nonqp, 2),
|
||
}
|
||
)
|
||
|
||
qp_differential = pl.DataFrame(_rows)
|
||
qp_differential
|
||
return (qp_differential,)
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(alt, fr_md, mo, proposed_for, qp_differential):
|
||
differential_chart = (
|
||
alt.Chart(qp_differential.to_pandas())
|
||
.mark_bar()
|
||
.encode(
|
||
x=alt.X("hcpcs:N", title="HCPCS"),
|
||
y=alt.Y("qp_differential:Q", title="QP minus non-QP payment ($, national unadjusted)"),
|
||
tooltip=["hcpcs", "description", alt.Tooltip("qp_differential:Q", format="$.2f")],
|
||
)
|
||
.properties(title="Dollar Value of QP Status — CY2027 Proposed (final rule pending)", width=500, height=320)
|
||
)
|
||
|
||
mo.vstack(
|
||
[
|
||
differential_chart,
|
||
mo.md(f"""
|
||
**Sources:** RVUs — `pfs.rvu`, CY2026 Final ({fr_md("90 FR 49266")}).
|
||
CFs — `proposed_for(2027)`, CY2027 NPRM
|
||
({fr_md(proposed_for(2027).federal_register_citation)},
|
||
{proposed_for(2027).cms_rule_id}) — final rule pending.
|
||
National unadjusted means GPCI = 1.0 — actual payment
|
||
varies by locality.
|
||
"""),
|
||
]
|
||
)
|
||
return
|
||
|
||
|
||
# ── 6. Provenance ───────────────────────────────────────────────────────
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(cfr_md, mo):
|
||
mo.md(f"""
|
||
## 6. Provenance & Comment-Period Status
|
||
|
||
Ingest-log entries (`cms.ingest_log`) for every FR-ingested table
|
||
this notebook reads — `pfs.rvu`, `pfs.rvu_proposed`, `pfs.gpci`,
|
||
and `pfs.gpci_proposed` — so every figure above can be traced back
|
||
to a specific ingest run, source file, and Federal Register
|
||
citation. `cms.primary_care_service_code` (Section 4's 109-code
|
||
attribution list) is a reference table published from the aco
|
||
module's CCLF/attribution data, not an FR ingest, so it carries no
|
||
ingest-log row; its provenance is the MSSP assignment
|
||
specification ({cfr_md("42 CFR 425.400(c)")}).
|
||
""")
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(fr_md, mo, proposed_for):
|
||
from datetime import date
|
||
|
||
_prop = proposed_for(2027)
|
||
_close = _prop.comment_close
|
||
_today = date.today()
|
||
_open = _today <= _close
|
||
_status = "OPEN" if _open else "CLOSED"
|
||
|
||
mo.callout(
|
||
mo.md(f"""
|
||
**Comment period: {_status}** — {_prop.cms_rule_id} ({fr_md(_prop.federal_register_citation)})
|
||
published {_prop.published.isoformat()}, comment period closes
|
||
**{_close.isoformat()}** (`proposed_for(2027).comment_close`,
|
||
read live from the registry; today is {_today.isoformat()}).
|
||
"""),
|
||
kind="success" if _open else "neutral",
|
||
)
|
||
return
|
||
|
||
|
||
@app.cell(hide_code=True)
|
||
def _(mo, q):
|
||
provenance = q("""
|
||
SELECT run_id, ingested_at, module, table_name, rule_id,
|
||
source_file, sha256, rows, fr_citation, pincite_key
|
||
FROM cms.ingest_log
|
||
WHERE table_name IN ('pfs.rvu', 'pfs.rvu_proposed',
|
||
'pfs.gpci', 'pfs.gpci_proposed')
|
||
ORDER BY ingested_at DESC
|
||
""")
|
||
mo.ui.table(provenance.to_pandas(), label="Ingest Log — Tables Used in This Notebook")
|
||
return
|
||
|
||
|
||
if __name__ == "__main__":
|
||
app.run()
|