fix(pfs): correct CY2026 final-rule FR citation (90 FR 49266) + citation/robustness cleanups (refs #591 #593 #595 #597)
This commit is contained in:
@@ -20,7 +20,7 @@ def _(mo):
|
||||
(CMS-1832-P, 90 FR 32352, July 16, 2025) proposed the **first-ever
|
||||
split conversion factor** — separate dollar amounts for clinicians who
|
||||
qualify as Advanced APM Participants (QPs) versus everyone else — and
|
||||
the Final Rule (90 FR 101174) changed several of those numbers again
|
||||
the Final Rule (90 FR 49266) changed several of those numbers again
|
||||
before taking effect.
|
||||
|
||||
**Mechanism:** MACRA 2015 (section 1848(d)(20) of the Act) sets two
|
||||
@@ -142,7 +142,7 @@ def _(alt, cf_walk, mo):
|
||||
`pfs.rules` registry yet.*
|
||||
|
||||
**Sources:** CY2025 — 89 FR 98452. CY2026 NPRM — 90 FR 32352
|
||||
(CMS-1832-P). CY2026 Final Rule — 90 FR 101174.
|
||||
(CMS-1832-P). CY2026 Final Rule — 90 FR 49266.
|
||||
"""),
|
||||
]
|
||||
)
|
||||
@@ -261,14 +261,15 @@ def _(mo):
|
||||
mo.md("""
|
||||
## 2. RVU-Level Deltas — Which HCPCS Codes Move the Most
|
||||
|
||||
`pfs.rvu_proposed` (14,169 rows, CMS-1832-P) is compared against
|
||||
`pfs.rvu` for CY2025 (pre-rule baseline) and CY2026 (what actually got
|
||||
finalized). Both `pfs.rvu` and `pfs.rvu_proposed` are deduplicated to
|
||||
one row per HCPCS base code (no modifier), `status_code = 'A'`
|
||||
(actively priced), and — for the proposed table specifically — a
|
||||
non-null non-facility PE RVU, since ~29% 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).
|
||||
`pfs.rvu_proposed` (14,169 rows as of this ingest, CMS-1832-P) is
|
||||
compared against `pfs.rvu` for CY2025 (pre-rule baseline) and CY2026
|
||||
(what actually got finalized). Both `pfs.rvu` and `pfs.rvu_proposed`
|
||||
are deduplicated to one row per HCPCS base code (no modifier),
|
||||
`status_code = 'A'` (actively priced), and — for the proposed table
|
||||
specifically — a non-null non-facility PE RVU, since ~29% of
|
||||
`pfs.rvu_proposed` rows (as of this ingest) carry a null
|
||||
non-facility *or* facility PE RVU (CMS's Addendum B only populates
|
||||
the setting a code is actually priced in).
|
||||
""")
|
||||
return
|
||||
|
||||
@@ -737,8 +738,8 @@ def _(alt, mo, qp_differential):
|
||||
[
|
||||
differential_chart,
|
||||
mo.md("""
|
||||
**Sources:** RVUs — `pfs.rvu`, CY2026 (90 FR 101174). CFs —
|
||||
`pfs.rules.RULES[2026]` (final, 90 FR 101174) and
|
||||
**Sources:** RVUs — `pfs.rvu`, CY2026 (90 FR 49266). CFs —
|
||||
`pfs.rules.RULES[2026]` (final, 90 FR 49266) and
|
||||
`RULES[2026].proposed` (NPRM, 90 FR 32352). National unadjusted
|
||||
means GPCI = 1.0 — actual payment varies by locality.
|
||||
"""),
|
||||
|
||||
@@ -125,7 +125,7 @@ def _attachment_text(store: Store, item_key: str) -> str:
|
||||
return "\n\n".join(parts)
|
||||
|
||||
|
||||
_TAG_RE = re.compile(r"<[^>]+>")
|
||||
_TAG_RE = re.compile(r"</?[A-Za-z][^>]*>")
|
||||
|
||||
|
||||
def _strip_html_wrapper(text: str) -> str:
|
||||
@@ -138,12 +138,15 @@ def _strip_html_wrapper(text: str) -> str:
|
||||
|
||||
The FR text itself also carries inline HTML — e.g. Cloudflare's
|
||||
obfuscated ``<a href="/cdn-cgi/l/email-protection#...">`` mailto
|
||||
anchors and page-break anchors. Those are stripped with a plain
|
||||
``<[^>]+>`` regex (this is a controlled, known corpus — not
|
||||
attacker-supplied HTML — so no HTML-parsing dependency is needed),
|
||||
keeping the anchor's inner text. HTML entities (``&`` etc.) are
|
||||
then unescaped. Plain-text markers like ``[[Page NNNNN]]`` contain no
|
||||
angle brackets and pass through untouched.
|
||||
anchors and page-break anchors. Those are stripped with a
|
||||
``</?[A-Za-z][^>]*>`` regex (this is a controlled, known corpus —
|
||||
not attacker-supplied HTML — so no HTML-parsing dependency is
|
||||
needed), keeping the anchor's inner text. The tag must start with a
|
||||
letter (optionally preceded by ``/``) so free-text comparison
|
||||
fragments like ``p<0.05`` or ``x>2`` are never mistaken for markup.
|
||||
HTML entities (``&`` etc.) are then unescaped. Plain-text
|
||||
markers like ``[[Page NNNNN]]`` contain no angle brackets and pass
|
||||
through untouched.
|
||||
"""
|
||||
idx = text.find("<pre>")
|
||||
if idx == -1:
|
||||
@@ -173,7 +176,9 @@ def _rule_text(store: Store, item_key: str) -> str:
|
||||
for (storage_path,) in rows:
|
||||
path = Path(storage_path)
|
||||
if path.suffix.lower() == ".txt" and path.exists():
|
||||
return _strip_html_wrapper(path.read_text())
|
||||
return _strip_html_wrapper(
|
||||
path.read_text(encoding="utf-8", errors="replace")
|
||||
)
|
||||
return _attachment_text(store, item_key)
|
||||
|
||||
|
||||
|
||||
@@ -418,7 +418,13 @@ RULES: dict[int, RuleYear] = {
|
||||
budget_neutrality_adjustor=1.0049, # +0.49% BN adjustment
|
||||
clinical_labor_rate_update=True,
|
||||
telehealth_originating_site_fee=30.38,
|
||||
federal_register_citation="90 FR 101174",
|
||||
# Corrected from "90 FR 101174" (mistranscribed; no such page in
|
||||
# this rule). The Final Rule (RIN 0938-AV50, Vol. 90 No. 212,
|
||||
# Nov 5, 2025) runs 90 FR 49266-50481; per this registry's
|
||||
# convention (see other years above) the citation is the rule's
|
||||
# first page. The CF statement itself ($33.5675 QP / $33.4009
|
||||
# non-QP) is at the pincite 90 FR 49961.
|
||||
federal_register_citation="90 FR 49266",
|
||||
# NPRM values (pre-final) per :pincite:`2KVJ2HKX` — CMS-1832-P,
|
||||
# 90 FR 32352, transcribed from data/fr_downloads/2025-13271.txt.
|
||||
proposed=ProposedRule(
|
||||
|
||||
@@ -258,7 +258,10 @@ class QppYear(BaseModel):
|
||||
# the same NPRM) — it reads as stale boilerplate describing the
|
||||
# pre-2023 -> 2023 statutory step-up, not a change CMS-1832-P
|
||||
# itself proposes.
|
||||
# - Final Rule 90 FR 49928 (line 69796-69798) drops the "increase
|
||||
# - Final Rule 90 FR 49980 (line 69796-69798; corrected from a
|
||||
# mistranscribed "90 FR 49928" — that page marker is at line 64990,
|
||||
# these lines fall between the [[Page 49980]] marker at line 69792
|
||||
# and the [[Page 49981]] marker at line 69913) drops the "increase
|
||||
# from" framing entirely: "The thresholds to achieve QP status in
|
||||
# the 2026 QP Performance Period (2028 payment year) are set to
|
||||
# 75 percent for the payment amount, and 50 percent for the
|
||||
@@ -373,7 +376,12 @@ QPP: dict[int, QppYear] = {
|
||||
risk_standards=_risk_standards(),
|
||||
apm_incentive_pct=None,
|
||||
qp_cf_applies=True,
|
||||
citation="90 FR 49928",
|
||||
# Corrected from "90 FR 49928" alone: the QP threshold statement
|
||||
# itself is at 90 FR 49980 (see docstring pincite above); 49928
|
||||
# is retained alongside it since it's independently the source
|
||||
# for the Partial QP payment-amount confirmation (docstring
|
||||
# pincite, line 65021-65026).
|
||||
citation="90 FR 49928, 49980",
|
||||
# NPRM proposals per :pincite:`2KVJ2HKX` — CMS-1832-P, 90 FR
|
||||
# 32352, transcribed from data/fr_downloads/2025-13271.txt;
|
||||
# disposition from data/fr_downloads/2025-19787.txt.
|
||||
|
||||
@@ -152,6 +152,7 @@ class TestRuleDocs:
|
||||
"[[Page 12345]]\n"
|
||||
'Contact us at <a href="/cdn-cgi/l/email-protection#x">'
|
||||
"someone</a> regarding CMS & PFS.\n"
|
||||
"A finding was significant (p<0.05 and >2 cm).\n"
|
||||
"</pre></body></html>"
|
||||
)
|
||||
store.attach_file(rule_key, txt)
|
||||
@@ -165,6 +166,7 @@ class TestRuleDocs:
|
||||
assert "someone" in text
|
||||
assert "CMS & PFS" in text
|
||||
assert "[[Page 12345]]" in text
|
||||
assert "p<0.05 and >2 cm" in text
|
||||
|
||||
def test_pdf_only_falls_back_to_extract_attachment(self, store, rule_key, tmp_path):
|
||||
pdf = tmp_path / "rule.pdf"
|
||||
|
||||
@@ -149,7 +149,7 @@ class TestSpecificValues:
|
||||
assert cy2026.telehealth_originating_site_fee == 30.38
|
||||
|
||||
def test_cy2026_federal_register(self):
|
||||
assert cy2026.federal_register_citation == "90 FR 101174"
|
||||
assert cy2026.federal_register_citation == "90 FR 49266"
|
||||
|
||||
def test_cy2026_clinical_labor_update(self):
|
||||
assert cy2026.clinical_labor_rate_update is True
|
||||
|
||||
Reference in New Issue
Block a user