From bbf3f52f4d141a3d4e99244e7f18763a0052eadd Mon Sep 17 00:00:00 2001 From: kert Date: Thu, 12 Nov 2026 11:43:22 -0500 Subject: [PATCH] =?UTF-8?q?fix(pfs):=20cms-summaries=20=E2=80=94=20acknowl?= =?UTF-8?q?edged=20only=20for=20pure=20thanks,=20conflicting=20cues=20uncl?= =?UTF-8?q?ear,=20five=20more=20pair=20phrases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pfs/cms_summaries.py | 30 +++++++--- src/pfs/codetables.py | 2 +- src/pfs/curated.yaml | 2 +- tests/pfs/fixtures/fr_comment_pairs.json | 11 +++- tests/pfs/test_cms_summaries.py | 75 ++++++++++++++++-------- 5 files changed, 86 insertions(+), 34 deletions(-) diff --git a/src/pfs/cms_summaries.py b/src/pfs/cms_summaries.py index 105ce9a..ec651e1 100644 --- a/src/pfs/cms_summaries.py +++ b/src/pfs/cms_summaries.py @@ -46,7 +46,7 @@ _QUANTIFIERS = ( ("many", re.compile(r"\b(?:many|numerous) (?:of the )?commenters\b", re.I)), ("several", re.compile(r"\bseveral (?:of the )?commenters\b", re.I)), ("some", re.compile(r"\bsome (?:of the )?commenters\b", re.I)), - ("few", re.compile(r"\b(?:a )?few (?:of the )?commenters\b", re.I)), + ("few", re.compile(r"\b(?:a )?few (?:of the )?(?:commenters|comments)\b", re.I)), ("one", re.compile(r"\b(?:one|a|another) commenter\b", re.I)), ) @@ -72,14 +72,19 @@ _ACTIONS = ( r"\bnot (?:to )?finaliz|\bare not adopting\b|\bdecline" # R45, from the live responses: a plain refusal r"|\bdo not have (?:the )?(?:legislative )?authority\b" - r"|\bit would be inappropriate\b" - r"|\bdo not believe\b[^.]{0,100}\bare warranted\b", + r"|\bit would be inappropriate to (?:finalize|adopt|pay|establish|apply|implement)\b" + r"|\bdo not believe\b[^.]{0,100}\bare warranted\b" + # R46: "we do not believe that finalizing ..." refuses to finalize + r"|\bnot (?:believe|think)\b[^.]{0,60}\bfinaliz", re.I, ), ), - ("acknowledged", re.compile(r"\bthank|\bappreciat", re.I)), ) +_THANKS = re.compile(r"\bthank|\bappreciat", re.I) +_CONTRAST = re.compile(r"\b(?:but|however|although|while|though)\b", re.I) +_SENTENCES = re.compile(r"(?<=[.!?])\s+") + def quantifier_of(comment_text: str) -> str: """CMS's quantifier for the summarized commenters, else ``unspecified``.""" @@ -91,10 +96,19 @@ def quantifier_of(comment_text: str) -> str: def response_action_of(response_text: str) -> str: """What CMS did in response, else ``unclear``. ``acknowledged`` means - the response only thanks or appreciates.""" - for name, pattern in _ACTIONS: - if pattern.search(response_text): - return name + every sentence only thanks or appreciates. A response carrying both a + finalized-as-proposed and a not-finalized cue is ``unclear`` (R46).""" + hits = [name for name, pattern in _ACTIONS if pattern.search(response_text)] + if "finalize_as_proposed" in hits and "not_finalized" in hits: + return "unclear" + if hits: + return hits[0] + body = re.sub(r"^\s*Response:\s*", "", response_text, flags=re.I) + sentences = [x for x in _SENTENCES.split(body.strip()) if x] + if sentences and all( + _THANKS.search(x) and not _CONTRAST.search(x) for x in sentences + ): + return "acknowledged" return "unclear" diff --git a/src/pfs/codetables.py b/src/pfs/codetables.py index 87648cc..090a4db 100644 --- a/src/pfs/codetables.py +++ b/src/pfs/codetables.py @@ -336,7 +336,7 @@ class CmsSummaryRow: action are CMS's wording read by regex or a local model — never our own count of comments (``pfs.comment_stance``), never merged with it. Only labels and paragraph ids are stored, no comment text. ``codes`` - is empty for a pair matched by a family synonym alone.""" + is empty for a pair matched by a pair phrase alone.""" family: str item_key: str diff --git a/src/pfs/curated.yaml b/src/pfs/curated.yaml index 81ee897..03a90fe 100644 --- a/src/pfs/curated.yaml +++ b/src/pfs/curated.yaml @@ -5,7 +5,7 @@ HOME-VISITS: synonyms: [home visit, home visits, home or residence, residence visit, domiciliary, rest home visit, house call, house calls] # Phrases that qualify a Comment:/Response: pair besides a code (pfs.cms_summaries). # Visit-naming only; each was checked against every phrase-only row it admits. - pair_phrases: [home or residence visit, home or residence services, home or residence e/m, residence visit, rest home visit, home visit codes, home visit e/m, house call, domiciliary care visit] + pair_phrases: [home or residence visit, home or residence services, home or residence e/m, residence visit, rest home visit, home visit codes, home visit e/m, house call, domiciliary care visit, visit in the home, home residence e/m, domiciliary care setting, domiciliary care settings, home visit pes, home or residence setting] members: - {code: '99341', role: base, cite: {item_key: PETM225D, p_id: 1672}} - {code: '99342', role: base, cite: {item_key: PETM225D, p_id: 1672}} diff --git a/tests/pfs/fixtures/fr_comment_pairs.json b/tests/pfs/fixtures/fr_comment_pairs.json index f51d227..4c8553f 100644 --- a/tests/pfs/fixtures/fr_comment_pairs.json +++ b/tests/pfs/fixtures/fr_comment_pairs.json @@ -17,5 +17,14 @@ "XU44ZSI9:759": "Response: The statute defines an originating site facility as a physician's or practitioner's office, hospital, critical access hospital, rural health clinic, or FQHC. Additionally, the statute only permits telehealth services to be furnished at an originating site located in a rural health professional shortage area as defined in section 332(a)(1)(A) of the Public Health Service Act or within a county that is not included in a metropolitan statistical area. We do not have the legislative authority (except for SNFs as indicated previously) to expand the definition of an originating site facility or to allow telehealth services to be furnished in a hospital regardless of geographic location.", "YYAWUFQX:1736": "Response: It is unfortunate that these services were not included in the recent 5-Year Review and that the specialty did not propose them for review. We believe that it would be inappropriate to apply increases to these codes without a multi-specialty review of the work involved in these services. However, we would be willing to consider any RUC recommendations that might be forthcoming for revised work RVUs for the home visit and domiciliary care codes. Such a RUC review could be completed at the same time that the RUC is reviewing the nursing facility E/M services previously discussed in this section. As we indicated for the nursing facility E/M codes in the prior response, we would consider forthcoming RUC recommendations for the home care and domiciliary care E/M codes as part of the third 5-Year Review because of the similarity to the other E/M services considered in this review.", "DE2VH9PD:1347": "Response: Other than the scope of service element for EHR and other electronic technology, we do not believe additional changes to the scope of service elements for CCM are warranted at this time. We are requiring certified EHR technology for certain foundational or “core” elements, including structured recording of medications and medication allergies. As finalized in the scope of service in the CY 2014 PFS final rule with comment period we are also requiring medication reconciliation with review of adherence and potential interactions, and oversight of patient self-management of medications. We believe it would be overly burdensome, especially given the broad eligible beneficiary population and final RVU inputs, to include more specific requirements related to medication management, especially when greater specificity is likely not necessary to ensure adequate care. The CCM services are by definition non-face-to-face services; therefore we are not including a requirement for home or domiciliary visits or community based care (although there is a requirement related to coordinating home and community based care). Practitioners who engage in remote monitoring of patient physiological data of eligible beneficiaries may count the time they spend reviewing the reported data towards the monthly minimum time for billing the CCM code, but cannot include the entire time the beneficiary spends under monitoring or wearing a monitoring device. If we believe changes to the scope of service elements are warranted in the future, we will propose them through notice and comment rulemaking taking the comments we received to date into consideration.", - "JCWARTJD:1214": "Comment: Some commenters requested that CMS broaden MOD1 and MOD2 eligibility across several settings and code sets. Commenters stated that the beneficiaries most in need of longitudinal care are also in settings such as nursing facilities, Rural Health Clinics (RHCs), Federally Qualified Health Centers (FQHCs), and Critical Access Hospitals (CAHs). Others asked for the use of MOD1 and MOD2 in conjunction with codes beyond E/M codes, such as collaborative care management services, the shared medical appointment codes proposed earlier in this section, and medical nutrition therapy." + "JCWARTJD:1214": "Comment: Some commenters requested that CMS broaden MOD1 and MOD2 eligibility across several settings and code sets. Commenters stated that the beneficiaries most in need of longitudinal care are also in settings such as nursing facilities, Rural Health Clinics (RHCs), Federally Qualified Health Centers (FQHCs), and Critical Access Hospitals (CAHs). Others asked for the use of MOD1 and MOD2 in conjunction with codes beyond E/M codes, such as collaborative care management services, the shared medical appointment codes proposed earlier in this section, and medical nutrition therapy.", + "3U9CYPEJ:1670": "Response: We appreciate the commenters' support of the proposed denominator codes. Based on comments indicating that some eligible professionals exclusively make domiciliary care visits, we are adding the following codes to the electronic prescribing measure's denominator for 2010: 99324 through 99328; 99334 through 99337; and 99346.", + "JCWARTJD:1231": "Response: We appreciate commenters' feedback on the interaction between modifiers B1 and B2 and modifier -25. Currently, as finalized in the CY 2025 PFS final rule (89 FR 97856 through 97858) and the CY 2026 PFS final rule (90 FR 49462 through 49464), HCPCS code G2211 is not payable when the O/O E/M visit or home or residence E/M is reported with modifier -25, except when the same-day service is an annual wellness visit, vaccine administration, or another Medicare Part B preventive service, including the Initial Preventive Physical Examination, furnished in the office or outpatient or home or residence setting. We are not revising this policy in this final rule.", + "JCWARTJD:1165": "Response: We thank commenters for requesting clarification regarding the applicability of the modifier when the service is furnished via telehealth. Since both HCPCS code G2211 and the O/O and home or residence E/M visits are currently on the Medicare telehealth services list, we are clarifying that Modifiers B1 and B2 can be used when an O/O or home/residence E/M visit is furnished via telehealth, provided all other requirements are met.", + "JCWARTJD:5899": "Response: We thank commenters for their recommendations; however, we do not agree with commenters regarding a delay in modifying the definition of CEHRT. The proposed modifications are consistent with the timelines outlined in the HTI-5 proposed rule and HTI-5 final rule (90 FR 60970 through 61034; and 91 FR 67916 through 67998). In the CY 2027 PFS proposed rule (91 FR 44170), we stated that we do not believe that finalizing, as proposed, the modifications to the definition of CEHRT are contingent upon the final actions from ONC regarding its proposals to remove and revise health IT certification criteria as proposed in the HTI-5 proposed rule. As identified in Table C-G 1 of this final rule, ONC finalized, as proposed, its proposals in the HTI-5 final rule with respect to health IT certification criteria in 45 CFR 170.315(a)(12), (e)(3), (g)(1), (g)(2), (c)(3), and (c)(4) (91 FR 67928, 91 FR 67950, 91 FR 67957, 91 FR 67958, 91 FR 67944, and 91 FR 67945, respectively). Regarding concerns that MIPS eligible clinicians would not have sufficient time for implementation, we reiterate ( printed page 71557) that the proposed modifications to the definition of CEHRT have the effect of removing, rather than adding, requirements with respect to the definition of CEHRT. Therefore, we do not expect any new implementation requirements to arise from these proposed removals that would require additional time for health IT developers and their customers to achieve compliance.", + "XU44ZSI9:1581": "Response: The availability of coverage under the screening benefit does not depend on whether or not a disease condition is discovered during the annual screening examination. Medicare covers the screening examination regardless of the findings at the time of the screening examination, but if the provider decides to perform and bill Medicare for the more comprehensive eye exam, the cost of the screening examination is considered bundled into the Medicare payment for the more expensive comprehensive eye examination. For example, if a disease, cataract, or a macular degeneration condition is discovered at the time of the glaucoma screening, the provider may decide to perform a medically necessary comprehensive eye examination and bill Medicare Part B for that more expensive covered service. In this example, it would be inappropriate for the provider to bill Medicare for the less expensive glaucoma screening service as well as the more comprehensive and expensive service because it would be duplicative for Medicare to pay for both services. In this situation, the only eye service that may be billed Medicare is the comprehensive eye examination and it would be presumed that the glaucoma screening service is bundled into the Medicare payment for the comprehensive eye service.", + "YYAWUFQX:1476": "Response: We agree that the second criterion could be clarified, to state that it would be inappropriate to have collected the specimen other than during the procedure, and have done so in this final rule. Our intent in establishing this criterion was to ensure that tests that meet the definitions in this provision are not directly related to the care provided in the hospital, and we believe the modification we are finalizing meets that intent more clearly than our original proposal.", + "X-PETM225D:3620": "Comment: A few comments provided general support of our proposal to make conforming changes to omit the reference to “for claims identified by place of service modifier 12” from the description for CPT codes 99341 through 99350. One commenter indicated strong support because these coding changes will help bring transparency to the services provided in ( printed page 69825) those settings. These commenters believe that under current coding and billing conventions, “home” services are all lumped together and thus difficult to untangle and analyze productively.", + "X-PETM225D:406": "Response: We appreciate the perspective of interested parties, but we would need code-level PE details to evaluate potential code valuation issues.", + "X-YBM4IZUS:429": "Response: We thank the commenters for their support and feedback." } \ No newline at end of file diff --git a/tests/pfs/test_cms_summaries.py b/tests/pfs/test_cms_summaries.py index b214139..433c016 100644 --- a/tests/pfs/test_cms_summaries.py +++ b/tests/pfs/test_cms_summaries.py @@ -39,8 +39,9 @@ CY2008 = {int(k.split(":")[1]): v for k, v in _RAW.items() if k.startswith("54GT OTHER = { int(k.split(":")[1]): v for k, v in _RAW.items() - if not k.startswith(("PETM", "54GT")) + if not k.startswith(("PETM", "54GT", "X-")) } +TEXT_X = {k[2:]: v for k, v in _RAW.items() if k.startswith("X-")} TEXT = {**CY2023, **CY2008, **OTHER} FAM = SimpleNamespace( @@ -96,6 +97,46 @@ def test_response_action_plain_refusals_in_live_text(p): assert response_action_of(TEXT[p]) == "not_finalized" +def test_a_few_comments_is_few_not_one(): + # live CY2023 ¶3620: "A few comments ... One commenter indicated" + assert quantifier_of(TEXT_X["PETM225D:3620"]) == "few" + + +@pytest.mark.parametrize("p", [1231, 1165, 1670]) +def test_a_response_that_does_more_than_thank_is_not_acknowledged(p): + # live: "We are not revising this policy" / "we are clarifying" / "we are adding" + assert response_action_of(TEXT[p]) != "acknowledged" + + +def test_thanks_with_a_but_clause_is_not_acknowledged(): + # live PETM225D ¶406: "We appreciate ..., but we would need code-level PE details" + assert response_action_of(TEXT_X["PETM225D:406"]) == "unclear" + assert response_action_of(TEXT_X["YBM4IZUS:429"]) == "acknowledged" + + +def test_pure_thanks_is_acknowledged(): + assert response_action_of(TEXT[1680]) == "acknowledged" + assert ( + response_action_of("Response: We thank commenters. We appreciate it.") + == "acknowledged" + ) + + +def test_conflicting_cues_are_unclear(): + # live CY2027 ¶5899: "we do not believe that finalizing, as proposed, ..." + assert response_action_of(TEXT[5899]) == "unclear" + + +def test_inappropriate_needs_a_refusal_object(): + # live YYAWUFQX ¶1476: "it would be inappropriate to have collected" is not a refusal + assert response_action_of(TEXT[1476]) != "not_finalized" + assert response_action_of(TEXT[1581]) != "not_finalized" + assert ( + response_action_of("Response: It would be inappropriate to finalize this.") + == "not_finalized" + ) + + def test_a_few_is_not_one(): assert quantifier_of("A few commenters said") == "few" @@ -286,36 +327,24 @@ def _hash_of(log, unit): return (e.input_hash,) -def test_summaries_skip_corrections_and_non_pfs_duplicates(): - s, keys = _store() +def test_summaries_skip_correction_items(): + s, _ = _store() try: - s.create( - Rule( - title="Medicare Program; CY 2023 PFS Correction", - date_published="2023-01-05", - ) + corr = s.create( + Rule(title="CY 2023 PFS Correction notice", date_published="2023-01-06") ) - # a correction item carrying the same text must add no rows - corr = [ - k - for k in [ - s.create( - Rule( - title="CY 2023 PFS Correction notice", - date_published="2023-01-06", - ) - ) - ] - ][0] - s._con().execute( # noqa: SLF001 + con = s._con() # noqa: SLF001 + con.execute( "INSERT INTO fr_anchors (item_key, p_id, page, ordinal, text) VALUES (?,?,?,?,?)", (corr, 1, 1, 1, "Comment: A commenter on 99341."), ) - s._con().execute( # noqa: SLF001 + con.execute( "INSERT INTO fr_anchors (item_key, p_id, page, ordinal, text) VALUES (?,?,?,?,?)", (corr, 2, 1, 2, "Response: We thank the commenter."), ) - s._con().commit() # noqa: SLF001 + con.commit() + # not vacuous: the pair is found, but the correction item is skipped + assert corr in {x.item_key for x in fr_pair_spans(s, FAM.codes)} assert corr not in {r.item_key for r in summaries(s, FAM)} finally: s.close()