Files
stack/tests/pfs/test_rounds.py
kert 07e134f570
Some checks failed
CI / lint (push) Successful in 1m5s
CI / notebooks-smoke (push) Successful in 2m5s
Deploy / changes (push) Successful in 17s
CI / test (push) Failing after 4m32s
Infra CI / notebooks (push) Successful in 1m4s
Infra CI / zotero (push) Successful in 18s
Infra CI / api (push) Successful in 1m27s
Infra CI / docs (push) Successful in 1m40s
Infra CI / llm (push) Successful in 1m5s
Infra CI / mc (push) Successful in 18s
Deploy / notebooks (push) Has been skipped
Deploy / zotero (push) Has been skipped
Deploy / docs (push) Has been skipped
Infra CI / bcda (push) Successful in 1m56s
Deploy / api (push) Successful in 1m32s
Deploy / bcda (push) Successful in 1m47s
Deploy / mc (push) Has been skipped
Deploy / llm (push) Successful in 1m8s
Deploy / report (push) Successful in 17s
fix(pfs): rounds — per-field citations, model fields only from plain sentences covering the code, maintain/recap/hypothetical fixes
R27: RoundRow.field_cites (JSON {field: p_id | "page:N"}, list for a joined
PE note); stamped per source, carried by merge with the winning value, set by
the paragraph rules; a derived decision cites the acted value's source.
R28: model numbers survive only in a non-recap, non-hypothetical sentence
covering the code; comparators/societies/quote only from such sentences; a
model label needs the RUC value and the acted value.
R29: maintain only as CMS's own 'are maintaining'/'will maintain', never after
'recommend(ed) (that)'; Response:/Comment: lead-ins and 'CY 20XX final rule'
recaps; 'would be the/consistent/...' no longer hypothetical; stated
interim/reject labels are not overridden by the numbers.
2026-11-12 01:37:59 -05:00

1752 lines
66 KiB
Python

"""pfs.rounds — valuation rounds from FR RUC/CMS tables (dossier task 4)."""
from __future__ import annotations
from dataclasses import replace
from pathlib import Path
from types import SimpleNamespace
import duckdb
import pfs.rounds as rounds_mod
from bib.item import Rule
from bib.store import Store
from pfs.codetables import RoundRow, ensure_tables, read_rounds, write_rounds
from pfs.rounds import (
CITE_FIELDS,
ROUND_PROMPT_VERSION,
ROUND_SCHEMA,
FrTable,
_percentiles,
apply_paragraph_rules,
candidate_paragraphs,
decision_of,
family_as_proposed,
family_rounds,
field_cites,
find_tables,
fr_text_path,
guard,
has_content,
merge_rounds,
model_rounds,
para_values,
regex_rounds,
rounds_inventory,
rows_from_output,
ruc_as_proposed,
rule_items,
split_pages,
table_rounds,
)
from pfs.runlog import RunLog
FIXTURE = (Path(__file__).parent / "fixtures" / "fr_table_cy2008_t14.txt").read_text()
CODES = {"99326", "99343", "99350", "93325"}
def _t14() -> FrTable:
[t] = [t for t in find_tables(FIXTURE) if t.title.startswith("Table 14")]
return t
def _rounds(**kw):
args = dict(
family="HOME-VISITS",
item_key="54GTKR5G",
rule_year=2008,
rule_kind="final",
codes={"99326", "99343", "99350"},
)
args.update(kw)
return table_rounds(_t14(), **args)
def test_table_rounds_cy2008_home_visits():
rows = {r.code: r for r in _rounds()}
r = rows["99343"]
assert (
r.proposed_work_rvu,
r.requested_work_rvu,
r.ruc_work_rvu,
r.final_work_rvu,
) == (2.27, 2.65, 2.27, 2.53)
assert (r.decision, r.page, r.source) == ("refine", 66361, "fr-table")
assert rows["99326"].final_work_rvu == 2.63
assert rows["99350"].final_work_rvu == 3.28
assert "92557" not in rows
assert r.table_title.startswith("Table 14")
assert r.rationale_quote == "Refinement." and r.pe_note == ""
def test_refinement_basis_with_ruc_equal_final_keeps_accept_and_notes_it():
rows = {r.code: r for r in _rounds(codes={"92557"})}
r = rows["92557"]
assert (r.ruc_work_rvu, r.final_work_rvu) == (0.6, 0.6)
assert r.decision == "accept"
assert r.rationale_quote == "Refinement."
assert r.pe_note == "basis says Refinement.; work RVU equals RUC"
def test_continuation_lines_do_not_create_rows():
t = _t14()
assert [row[0] for row in t.rows] == ["92557", "99326", "99343", "99350", "93325"]
assert t.rows[1][2] == "Domicil/r-home visit new pat."
assert t.header[3].lower().startswith("proposed")
def test_non_numeric_ruc_is_none():
[r] = _rounds(codes={"93325"})
assert r.ruc_work_rvu is None and r.final_work_rvu == 0.07
assert r.decision == ""
def test_agree_maps_to_accept():
t = FrTable(
"Table 9.--x",
10,
("code", "RUC recommendation", "2009 work RVU", "Basis for decision"),
(("99211", "2.35", "2.35", "Agree."),),
)
[r] = table_rounds(
t, family="F", item_key="K", rule_year=2009, rule_kind="final", codes={"99211"}
)
assert r.decision == "accept" and r.final_work_rvu == 2.35
assert r.proposed_work_rvu is None
def test_header_roles_proposed_vs_final():
t = FrTable(
"Table 1.--x",
1,
("code", "proposed work RVU", "2008 work RVU", "CMS"),
(("99211", "1.00", "1.50", "x"),),
)
[r] = table_rounds(
t, family="F", item_key="K", rule_year=2008, rule_kind="final", codes={"99211"}
)
assert (r.proposed_work_rvu, r.final_work_rvu) == (1.0, 1.5)
def test_table_without_roles_skipped_and_short_rows_ignored():
t = FrTable("Table 2.--y", 1, ("code", "descriptor"), (("99211", "x"),))
assert (
table_rounds(
t, family="F", item_key="K", rule_year=1, rule_kind="final", codes={"99211"}
)
== []
)
t2 = FrTable("Table 3.--z", 1, ("code", "RUC rec", "final"), (("99211",),))
assert (
table_rounds(
t2,
family="F",
item_key="K",
rule_year=1,
rule_kind="final",
codes={"99211"},
)
== []
)
def test_decision_of():
assert decision_of(1.0, 2.0, "interim values") == "interim"
assert decision_of(1.0, 2.0, "Agree.") == "accept"
assert decision_of(2.0, 2.0, "") == "accept"
assert decision_of(1.0, 2.0, "Refinement.") == "refine"
assert decision_of(None, 2.0, "Disagree") == "refine"
assert decision_of(1.0, 2.0, "") == "refine"
assert decision_of(None, 2.0, "We reject the RUC") == "reject"
assert decision_of(None, None, "Comments.") == ""
def test_interim_from_title():
t = FrTable(
"Table 5.--Interim work RVUs",
1,
("code", "RUC rec", "final"),
(("99211", "1.0", "1.2"),),
)
[r] = table_rounds(
t, family="F", item_key="K", rule_year=1, rule_kind="final", codes={"99211"}
)
assert r.decision == "interim"
def test_split_pages_numbers_text():
pages = split_pages("front\n[[Page 7]]\nabc\n[[Page 8]]\ndef")
assert [p for p, _ in pages] == [0, 7, 8]
assert "abc" in pages[1][1] and "def" in pages[2][1]
assert split_pages("[[Page 3]]\nx")[0][0] == 3
def test_find_tables_spanning_pages_and_no_table():
assert find_tables("nothing here") == []
text = (
"[[Page 5]]\nTable 1.--Spans\n" + "-" * 30 + "\n"
" Code Value\n" + "-" * 30 + "\n"
"11111.... 1.00\n[[Page 6]]\n22222.... 2.00\n" + "-" * 30 + "\n"
)
[t] = find_tables(text)
assert t.page == 5 and [r[0] for r in t.rows] == ["11111", "22222"]
assert t.row_pages == (5, 6)
# an unterminated / headerless title is dropped
assert find_tables("Table 2.--x\nno rules\n") == []
assert find_tables("Table 2.--x\n" + "-" * 20 + "\nHdr Val\n") == []
rule = "-" * 20
assert find_tables(f"Table 2.--x\n{rule}\nHdr Val\n{rule}\n11111 1.0\n") == []
# a stray indented line before any row is ignored
[t] = find_tables(
f"Table 2.--x\n{rule}\nHdr Val\n{rule}\n stray\n11111 1.0\n{rule}\n"
)
assert t.rows == (("11111", "1.0"),)
# a continuation under a short (two-cell) row appends to its last cell
[t] = find_tables(
f"Table 2.--x\n{rule}\nHdr Val\n{rule}\n11111 1.0\n more\n{rule}\n"
)
assert t.rows == (("11111", "1.0 more"),)
def test_row_pages_flow_to_rounds():
t = FrTable(
"Table 1.--x",
5,
("code", "RUC rec", "final"),
(("11111", "1.0", "1.0"), ("22222", "2.0", "2.5")),
(5, 6),
)
got = table_rounds(
t,
family="F",
item_key="K",
rule_year=1,
rule_kind="final",
codes={"11111", "22222"},
)
assert [r.page for r in got] == [5, 6]
def test_mid_table_rule_between_row_groups_keeps_all_rows():
rule = "-" * 20
text = (
f"Table 3.--Groups\n{rule}\nCode Val\n{rule}\n"
f"11111... 1.0\n{rule}\n\n[[Page 9]]\n22222... 2.0\n"
f"{rule}\n\\1\\ footnote text.\n"
)
[t] = find_tables(text)
assert [r[0] for r in t.rows] == ["11111", "22222"]
assert t.row_pages == (0, 9)
def test_closing_rule_before_text_or_eof_still_ends_table():
rule = "-" * 20
base = f"Table 3.--G\n{rule}\nCode Val\n{rule}\n11111... 1.0\n{rule}\n"
for tail in ("", " indented\n", "Plain prose sentence here.\n", rule + "\n"):
[t] = find_tables(base + tail)
assert [r[0] for r in t.rows] == ["11111"]
def test_fr_text_path_prefers_attachment(tmp_path, monkeypatch):
s = Store(":memory:", storage_dir=tmp_path / "st")
key = s.create(Rule(title="CY 2008 PFS", document_number="07-0001"))
assert fr_text_path(s, key) is None
assert fr_text_path(s, "NOSUCH") is None
dl = tmp_path / "dl"
dl.mkdir()
(dl / "07-0001.txt").write_text("fallback")
monkeypatch.setattr("pfs.rounds.ROOT", tmp_path)
(tmp_path / "data").mkdir()
(tmp_path / "data" / "fr_downloads").symlink_to(dl)
s._con().execute(
"INSERT INTO fr_anchor_docs (item_key, document_number, html_url, start_page,"
" end_page, fr_volume) VALUES (?,'07-0001','u',1,2,72)",
(key,),
)
assert fr_text_path(s, key) == tmp_path / "data/fr_downloads/07-0001.txt"
pdf = tmp_path / "x.pdf"
pdf.write_bytes(b"%PDF")
s.attach_file(key, pdf) # a non-txt attachment is skipped
assert fr_text_path(s, key) == tmp_path / "data/fr_downloads/07-0001.txt"
(dl / "07-0001.txt").unlink() # anchor row but no file on disk
assert fr_text_path(s, key) is None
(dl / "07-0001.txt").write_text("fallback")
att = tmp_path / "att.txt"
att.write_text("attached")
s.attach_file(key, att)
p = fr_text_path(s, key)
assert p is not None and p.read_text() == "attached"
def test_rounds_roundtrip():
con = duckdb.connect(":memory:")
ensure_tables(con)
rows = _rounds()
assert write_rounds(con, "HOME-VISITS", rows) == 3
assert write_rounds(con, "HOME-VISITS", rows) == 3 # idempotent
got = read_rounds(con, "HOME-VISITS")
assert got == sorted(
rows, key=lambda r: (r.code, r.rule_year, r.rule_kind, r.source)
)
assert read_rounds(con, "OTHER") == []
assert isinstance(got[0], RoundRow)
def test_cy2008_nprm_table_10_parses_with_the_data_row_columns():
"""4PYQ2WWF (07-3274) Table 10: stacked header cells split finer than
the data, so the first data row fixes the columns."""
[t] = find_tables(
(
Path(__file__).parent / "fixtures" / "fr_table_cy2008_nprm_t10.txt"
).read_text()
)
assert t.header[3:] == (
"2007 work RVU",
"Requested work RVU",
"RUC REC",
"CMS proposal (agree/ disagree)",
"2008 Proposed work RVU \\2\\",
)
rows = {
r.code: r
for r in table_rounds(
t,
family="F",
item_key="4PYQ2WWF",
rule_year=2008,
rule_kind="proposed",
codes={"99343", "99344"},
)
}
r = rows["99343"]
assert (r.proposed_work_rvu, r.requested_work_rvu, r.ruc_work_rvu) == (
2.27,
2.65,
2.27,
)
assert (r.final_work_rvu, r.decision, r.rationale_quote, r.page) == (
None,
"accept",
"Agree",
38147,
)
assert rows["99344"].proposed_work_rvu == 3.03
# ── paragraph source ─────────────────────────────────────────────────
DESC = "( Home or residence visit for the evaluation and management of a patient)"
P1676 = (
"The RUC survey time includes pre-service time 3 days before the date of "
"encounter. These eight CPT codes were reviewed at the October 2021 RUC "
"meeting. The RUC recommended the survey 25th percentile value for all CPT "
"codes in the Home or Residence Services code family, except for CPT code "
"99350, for which the RUC recommended the median value. We proposed the "
"RUC-recommended work RVU for all eight CPT codes in the Home or Residence "
"Services CPT code family. "
f"We proposed a work RVU of 1.00 for CPT code 99341 {DESC}, "
f"a work RVU of 1.65 for CPT code 99342 {DESC}, "
f"and a work RVU of 3.60 for CPT code 99350 {DESC}."
)
P877 = (
"In February 2021, the CPT Editorial Panel deleted the nine CPT codes in "
"the Domiciliary, Rest Home family (CPT codes 99324-99328, and "
"99334-99337), and one CPT code in the Home Services family (CPT code "
"99343), to merge these services with the eight remaining home visit "
"services. The eight remaining home services CPT codes (99341, 99342, "
"99344, 99345, and 99347-99350) were revised. These eight CPT codes were "
"reviewed at the October 2021 RUC meeting. The RUC recommended the survey "
"25th percentile value for all CPT codes in the Home or Residence "
"Services code family, except for CPT code 99350, for which the RUC "
"recommended the median value."
)
P1685 = (
"After consideration of the public comments, we are finalizing the work RVU "
"values for the Home or Residence Services code family as proposed. We are "
"also finalizing the direct PE inputs for these codes as proposed."
)
TITLE23 = "Medicare Program; CY 2023 PFS Final Rule"
TITLE23P = "Medicare Program; CY 2023 PFS Proposed Rule"
def para(
p_id, text, *, item="PETM225D", title=TITLE23, date="2022-11-18", kind="final"
):
return {
"item_key": item,
"p_id": p_id,
"page": 69608,
"text": text,
"title": title,
"date_published": date,
"rule_kind": kind,
"context": ["earlier one", "earlier two"],
}
def pp(text, kind="proposed"):
return para(
1, text, title="CY 2027 PFS Proposed Rule", date="2026-07-16", kind=kind
)
CODES23 = {"99341", "99342", "99350"}
def test_final_rule_we_proposed_is_proposed():
vals = dict(
((c, f), v)
for c, f, v in para_values(P1676, {"99341", "99350"}, rule_kind="final")
)
assert vals[("99341", "proposed_work_rvu")] == 1.00
assert vals[("99350", "proposed_work_rvu")] == 3.60
assert not any(f == "final_work_rvu" for (_, f) in vals)
def test_para_values_verbs_patterns_and_filters():
t = (
"The RUC-recommended work RVU of 2.27 for CPT code 99343 was reviewed. "
"We are finalizing a work RVU of 2.53 for CPT code 99343 and "
"assigning 1.10 work RVUs to HCPCS code G0001, "
"a work RVU of 9.99 for CPT code 11111 (not ours)."
)
assert para_values(t, {"99343", "G0001"}, rule_kind="final") == [
("99343", "ruc_work_rvu", 2.27),
("99343", "final_work_rvu", 2.53),
("G0001", "final_work_rvu", 1.10),
]
t2 = "We are assigning 1.10 work RVUs to HCPCS code G0001."
assert para_values(t2, {"G0001"}, rule_kind="final") == [
("G0001", "final_work_rvu", 1.10)
]
assert (
para_values("a work RVU of 1.00 for CPT code 99341", {"99341"}, rule_kind="x")
== []
)
twice = "We propose a work RVU of 1.00 for CPT code 99341. " * 2
assert len(para_values(twice, {"99341"}, rule_kind="proposed")) == 1
def test_we_proposed_the_ruc_recommended_work_rvu_is_proposed_and_ruc():
t = "We proposed the RUC-recommended work RVU of 1.00 for CPT code 99341."
got = para_values(t, {"99341"}, rule_kind="final")
assert sorted(got) == [
("99341", "proposed_work_rvu", 1.0),
("99341", "ruc_work_rvu", 1.0),
]
t = "We are finalizing the RUC-recommended work RVU of 1.10 for CPT code 99341."
assert sorted(para_values(t, {"99341"}, rule_kind="final")) == [
("99341", "final_work_rvu", 1.1),
("99341", "ruc_work_rvu", 1.1),
]
# N1: in a proposed rule the whole sentence is a recap: no value at all
assert para_values(t, {"99341"}, rule_kind="proposed") == []
rounds = regex_rounds(pp(t), {"99341"}, family="F")
assert rounds == []
# a model "final" equal to that proposal is dropped
out = {
"codes": [_d("99341", final_work_rvu=1.0, proposed_work_rvu=1.0)],
"surveying_societies": [],
"rationale_quote": "",
}
t = "We proposed the RUC-recommended work RVU of 1.00 for CPT code 99341."
[r] = rows_from_output(out, para(3, t), {"99341"}, family="F", model="m")
assert (r.proposed_work_rvu, r.final_work_rvu) == (1.0, None)
def test_proposed_rules_make_no_finals_and_negation_gives_none():
t = "In the CY 2023 PFS final rule, we finalized a work RVU of 1.00 for CPT code 99341."
assert para_values(t, {"99341"}, rule_kind="proposed") == []
assert [x.final_work_rvu for x in regex_rounds(pp(t), {"99341"}, family="F")] == []
for neg in (
"We are not finalizing the proposed work RVU of 1.00 for CPT code 99341.",
"We are not adopting a work RVU of 1.00 for CPT code 99341.",
"We do not finalize a work RVU of 1.00 for CPT code 99341.",
):
assert para_values(neg, {"99341"}, rule_kind="final") == [], neg
t = "We disagree with the RUC recommendation and are proposing a work RVU of 1.50 for CPT code 99341."
assert para_values(t, {"99341"}, rule_kind="proposed") == [
("99341", "proposed_work_rvu", 1.5)
]
def test_paragraph_rules_only_in_final_rules():
prop = regex_rounds(
pp("We are proposing a work RVU of 1.10 for CPT code 99341."),
{"99341"},
family="F",
)
hist = pp(
"In the CY 2023 PFS final rule, we finalized the work RVU values for the Home or "
"Residence Services code family as proposed (87 FR 69608).",
kind="proposed",
)
[r] = apply_paragraph_rules(prop, [hist])
assert (r.proposed_work_rvu, r.final_work_rvu, r.decision) == (1.1, None, "")
# R22a: a proposal verb sets the RUC value in a proposed rule too ...
ruc = pp(
"We are proposing the RUC-recommended work RVU for all eight codes: 99341, 99342.",
kind="proposed",
)
[r] = apply_paragraph_rules(prop, [ruc])
assert (r.ruc_work_rvu, r.proposed_work_rvu, r.decision) == (1.1, 1.1, "accept")
# ... and the decision compares ruc with the proposal when there is no final
diff = replace(prop[0], ruc_work_rvu=1.0)
[r] = apply_paragraph_rules([diff], [])
assert (r.final_work_rvu, r.decision) == (None, "refine")
# ... but a "finalized the RUC-recommended ..." recap in a proposed rule is nothing
recap = pp(
"In the CY 2023 final rule we finalized the RUC-recommended work RVU for all eight codes: 99341.",
kind="proposed",
)
[r] = apply_paragraph_rules(prop, [recap])
assert (r.ruc_work_rvu, r.decision) == (None, "")
assert ruc_as_proposed(recap["text"], rule_kind="final")
assert not ruc_as_proposed(recap["text"], rule_kind="proposed")
assert ruc_as_proposed(ruc["text"], rule_kind="proposed")
assert not ruc_as_proposed(P1685)
def test_model_accept_without_a_ruc_value_is_dropped():
t = "We are proposing a work RVU of 1.10 for CPT code 99341."
out = _reply(_d("99341", proposed_work_rvu=1.1, decision="accept"))
p = pp(t)
[r] = rows_from_output(out, p, {"99341"}, family="F", model="m")
assert (r.proposed_work_rvu, r.ruc_work_rvu, r.decision) == (1.1, None, "")
def test_paragraph_predicates():
assert family_as_proposed(P1685) and not family_as_proposed(P1676)
assert ruc_as_proposed(P1676) and not ruc_as_proposed(P1685)
def _regex_1676():
return regex_rounds(para(1676, P1676), CODES23, family="HOME-VISITS")
def test_regex_rounds_carry_anchor_year_and_percentile():
rows = {r.code: r for r in _regex_1676()}
r = rows["99341"]
assert (r.rule_year, r.rule_kind, r.p_id, r.page) == (2023, "final", 1676, 69608)
assert (r.source, r.extractor, r.proposed_work_rvu) == ("fr-para", "regex", 1.00)
assert (r.final_work_rvu, r.ruc_percentile, r.final_p_id) == (None, "25th", 0)
assert rows["99350"].ruc_percentile == "median"
fin = regex_rounds(
para(7, "We are finalizing a work RVU of 2.0 for CPT code 99341."),
{"99341"},
family="F",
)
assert (fin[0].final_work_rvu, fin[0].final_p_id) == (2.0, 7)
def _applied():
paras = [para(1676, P1676), para(1685, P1685)]
return {
x.code: x for x in apply_paragraph_rules(merge_rounds(_regex_1676()), paras)
}
def test_family_finalize_as_proposed_propagates_and_cites_the_finalizing_paragraph():
r = _applied()
assert (r["99341"].final_work_rvu, r["99350"].final_work_rvu) == (1.00, 3.60)
assert (r["99341"].final_p_id, r["99341"].rationale_p_id) == (1685, 1685)
assert r["99341"].rationale_quote.startswith("After consideration")
assert r["99341"].p_id == 1676 # the proposal stays cited to its paragraph
def test_ruc_recommended_proposal_sets_ruc():
r = _applied()["99341"]
assert (r.ruc_work_rvu, r.final_work_rvu, r.decision) == (1.00, 1.00, "accept")
assert r.proposed_work_rvu == 1.00 and r.ruc_percentile == "25th"
def test_paragraph_rules_leave_other_rules_and_existing_values_alone():
rows = _regex_1676()
other = replace(rows[0], item_key="OTHER", code="99341", final_work_rvu=None)
kept = replace(rows[1], final_work_rvu=7.0, ruc_work_rvu=9.0)
bare = replace(rows[2], proposed_work_rvu=None)
out = apply_paragraph_rules(
[other, kept, bare], [para(1676, P1676), para(1685, P1685)]
)
assert out[0].final_work_rvu is None and out[0].ruc_work_rvu is None
assert (out[1].final_work_rvu, out[1].ruc_work_rvu) == (7.0, 9.0)
assert out[2].final_work_rvu is None and out[2].decision == ""
assert out[1].decision == "refine"
assert apply_paragraph_rules([], [para(1, P1685)]) == []
# ── percentiles (R21) ────────────────────────────────────────────────
def test_percentiles_real_1676_text():
p = _percentiles(P1676)
assert p["99350"] == "median"
assert p == {c: "25th" for c in ("99341", "99342")} | {"99350": "median"}
def test_percentiles_real_877_deleted_codes_get_none():
p = _percentiles(P877)
for dead in (
"99324",
"99325",
"99326",
"99327",
"99328",
"99334",
"99335",
"99336",
"99337",
"99343",
):
assert dead not in p, dead
assert {
c: p[c] for c in ("99341", "99342", "99344", "99345", "99347", "99348", "99349")
} == {
c: "25th"
for c in ("99341", "99342", "99344", "99345", "99347", "99348", "99349")
}
assert p["99350"] == "median"
def test_percentiles_probe_sentences():
both = "The RUC recommended the 25th percentile for CPT code 99341 and the median for CPT code 99342."
assert _percentiles(both) == {"99341": "25th", "99342": "median"}
# a bare exception names no value for 99350
assert "99350" not in _percentiles(
"The RUC recommended the 25th percentile for all codes except CPT code 99350."
)
# a stray "all" is not collective; 99341 gets nothing
stray = "After reviewing all survey data, the RUC recommended the median work RVU for CPT code 99350."
assert _percentiles(stray) == {"99350": "median"}
# median as a time statistic
assert (
_percentiles(
"The survey median intra-service time for CPT code 99343 was 45 minutes."
)
== {}
)
# P2: "<code> at the <term>"
assert _percentiles(
"The RUC set CPT code 99341 at the 75th percentile, and CPT code 99342 at the median."
) == {"99341": "75th", "99342": "median"}
# contradictory statements give nothing
assert (
_percentiles(
"The 25th percentile for CPT code 99341. The median for CPT code 99341."
)
== {}
)
assert _percentiles("No survey statistic here for CPT code 99341.") == {}
# a list of codes
assert _percentiles(
"The 75th percentile for CPT codes 99341, 99342 and 99344."
) == {"99341": "75th", "99342": "75th", "99344": "75th"}
# ── guard ────────────────────────────────────────────────────────────
def _row(**kw):
base = replace(
regex_rounds(para(1676, P1676), CODES23, family="F")[0], extractor="m"
)
return replace(base, **kw)
def test_guard_drops_numbers_not_in_text():
r = guard(_row(code="99341", ruc_work_rvu=1.75, proposed_work_rvu=1.0), P1676)
assert r.ruc_work_rvu is None and r.proposed_work_rvu == 1.0
def test_guard_keeps_median_only_when_stated():
assert (
guard(_row(code="99350", ruc_percentile="median"), P1676).ruc_percentile
== "median"
)
assert guard(_row(code="99350", ruc_percentile="25th"), P1676).ruc_percentile == ""
assert (
guard(_row(code="99341", ruc_percentile="25th"), P1676).ruc_percentile == "25th"
)
for bad in ("median", "75th"):
assert guard(_row(code="99341", ruc_percentile=bad), P1676).ruc_percentile == ""
assert (
guard(_row(code="99341", ruc_percentile="25th"), "no stats").ruc_percentile
== ""
)
def test_guard_quote_societies_comparators():
quote = "These eight CPT codes were reviewed at the October 2021 RUC meeting."
r = guard(
_row(
rationale_quote=quote,
rationale_p_id=1676,
surveying_societies="American Academy of Family Physicians; Made Up Society",
comparators="99350; 99999",
),
P1676 + " The American Academy of Family Physicians surveyed.",
)
assert r.rationale_quote == quote and r.rationale_p_id == 1676
assert r.surveying_societies == "American Academy of Family Physicians"
assert r.comparators == "99350"
bad = guard(_row(rationale_quote="invented", rationale_p_id=1676), P1676)
assert bad.rationale_quote == "" and bad.rationale_p_id == 0
# ── model rows ───────────────────────────────────────────────────────
class FakeAsk:
model = "qwen-test"
def __init__(self, out):
self.out, self.calls = out, []
def __call__(self, system, user, schema):
self.calls.append((system, user, schema))
return self.out
def _d(code, **kw):
base = {
"code": code,
"ruc_work_rvu": None,
"ruc_percentile": "none",
"survey_intra_min": None,
"survey_total_min": None,
"comparators": [],
"proposed_work_rvu": None,
"final_work_rvu": None,
"decision": "none",
"pe_note": "",
}
return {**base, **kw}
def _reply(*codes, quote="", societies=()):
return {
"codes": list(codes),
"surveying_societies": list(societies),
"rationale_quote": quote,
}
def test_schema_accepts_a_good_reply():
from llm.structured import validate
assert validate(_reply(_d("99341")), ROUND_SCHEMA) == []
assert ROUND_PROMPT_VERSION == "rounds-v1"
def test_model_rounds_guards_and_maps_enums():
out = _reply(
_d(
"99341",
ruc_work_rvu=1.75,
ruc_percentile="75th",
proposed_work_rvu=1.0,
decision="accept",
comparators=["99350"],
pe_note="removed SK062",
),
_d(
"99350",
ruc_percentile="25th",
final_work_rvu=3.6,
proposed_work_rvu=3.6,
decision="none",
),
_d("99342", ruc_percentile="other", proposed_work_rvu=1.65),
_d("99999", ruc_work_rvu=1.0),
quote="We proposed the RUC-recommended work RVU for all eight CPT codes in the Home or Residence Services CPT code family.",
societies=["AAFP"],
)
ask = FakeAsk(out)
rows = {
r.code: r for r in model_rounds(para(1676, P1676), CODES23, ask, family="HV")
}
assert set(rows) == CODES23 # 99999 is not a family code
r = rows["99341"]
assert r.ruc_work_rvu is None # 1.75 is not in the text
# the percentile is the paragraph's (25th), not the model's 75th
assert (r.ruc_percentile, r.proposed_work_rvu, r.decision) == (
"25th",
1.0,
"",
) # no RUC value: the model's "accept" is dropped
assert r.comparators == "99350" and r.surveying_societies == ""
assert r.extractor == "model:qwen-test@rounds-v1"
assert (r.p_id, r.rationale_p_id, r.source) == (1676, 1676, "fr-para")
assert rows["99350"].ruc_percentile == "median" and rows["99350"].decision == ""
assert rows["99350"].final_work_rvu is None # "we proposed" 3.60 is the proposal
assert rows["99342"].ruc_percentile == "25th"
assert "99341, 99342, 99350" in ask.calls[0][1]
def test_model_only_values_need_a_verb_in_the_codes_sentence():
"""R20: the CY2020 99348 "would need to increase from 1.56 to 2.00" case."""
t = (
"To maintain the same proportional value, the work RVU for CPT code 99348 "
"would need to increase from 1.56 to 2.00. We are proposing a work "
"RVU of 1.25 for CPT code 99349 and we propose 4.5 for CPT code 99350. "
"In CY2020 we finalized 3.0 for CPT code 99342. We are not finalizing 2.5 for CPT code 99344."
)
out = _reply(
_d("99348", proposed_work_rvu=2.0, final_work_rvu=1.56, decision="accept"),
_d("99349", proposed_work_rvu=1.25),
_d("99350", proposed_work_rvu=4.5),
_d("99342", final_work_rvu=3.0),
_d("99344", final_work_rvu=2.5, comparators=["G2211"], pe_note="x"),
)
codes = {"99348", "99349", "99350", "99342", "99344"}
p = para(
9, t, title="CY 2020 PFS Proposed Rule", date="2019-08-14", kind="proposed"
)
rows = {r.code: r for r in rows_from_output(out, p, codes, family="F", model="m")}
assert (
rows["99348"].proposed_work_rvu is None and rows["99348"].final_work_rvu is None
)
assert rows["99348"].decision == "" # a bare label with no value is no round
assert rows["99349"].proposed_work_rvu == 1.25
assert rows["99350"].proposed_work_rvu == 4.5 # its sentence says "we propose"
assert rows["99342"].final_work_rvu is None # a proposed rule's recap
assert not any(
has_content(r) for k, r in rows.items() if k in ("99348", "99342", "99344")
)
# the same paragraph in a final rule: finals need the finalize verb
pf = {**p, "rule_kind": "final"}
rows = {r.code: r for r in rows_from_output(out, pf, codes, family="F", model="m")}
assert rows["99342"].final_work_rvu == 3.0 and rows["99342"].final_p_id == 9
assert rows["99344"].final_work_rvu is None # negated
assert rows["99348"].final_work_rvu is None
def test_has_content_ignores_comparators_and_pe_notes():
r = _row(
code="99341",
proposed_work_rvu=None,
ruc_work_rvu=None,
final_work_rvu=None,
comparators="G2211",
pe_note="removed SK062",
decision="",
)
assert not has_content(r)
assert has_content(replace(r, decision="accept"))
assert has_content(replace(r, survey_intra_min=30.0))
assert not rounds_mod.has_value(replace(r, decision="accept"))
def test_model_failed_and_empty_replies():
assert model_rounds(para(1, P1676), CODES23, FakeAsk(None), family="HV") == []
assert rows_from_output({}, para(1, P1676), CODES23, family="HV", model="m") == []
[r] = rows_from_output(
{"codes": [{"code": "99341"}]}, para(1, P1676), CODES23, family="HV", model="m"
)
assert r.decision == "" and r.comparators == "" and r.rationale_p_id == 0
assert r.ruc_percentile == "25th"
def test_model_final_with_both_numbers_gets_a_numeric_decision():
text = (
"The RUC recommended a work RVU of 2.27 for CPT code 99326; we are "
"finalizing a work RVU of 2.63 for CPT code 99326 after refinement."
)
out = _reply(
_d(
"99326",
ruc_work_rvu=2.27,
final_work_rvu=2.63,
decision="accept",
comparators=["99326", "99350"],
pe_note="none (no PE inputs)",
)
)
[r] = rows_from_output(out, para(9, text), {"99326"}, family="HV", model="m")
assert (r.decision, r.pe_note, r.comparators) == ("refine", "", "")
out["codes"][0]["pe_note"] = "removed SK062"
[r] = rows_from_output(out, para(9, text), {"99326"}, family="HV", model="m")
assert r.pe_note == "removed SK062"
def test_model_prompt_expands_code_ranges():
ask = FakeAsk(_reply())
model_rounds(para(877, P877), {"99349", "99350", "99341"}, ask, family="HV")
user = ask.calls[0][1]
assert "99347-99350 = 99347 99348 99349 99350" in user and "Code ranges:" in user
assert "99341, 99349, 99350" in user # range members are named codes
assert "Code ranges" not in rounds_mod.model_user(para(1, "no ranges"), [])
# ── merge ────────────────────────────────────────────────────────────
def _tbl(**kw):
return replace(
_row(code="99341", item_key="K"),
source="fr-table",
extractor="fr-table",
p_id=0,
rationale_p_id=0,
ruc_p_id=0,
final_p_id=0,
page=66361,
**kw,
)
def test_merge_prefers_table_over_model():
table = _tbl(
final_work_rvu=2.53,
decision="refine",
ruc_work_rvu=2.27,
proposed_work_rvu=None,
ruc_percentile="",
)
model = replace(
_tbl(
final_work_rvu=2.60,
ruc_work_rvu=2.27,
decision="accept",
proposed_work_rvu=2.27,
ruc_percentile="25th",
pe_note="pe says",
),
source="fr-para",
extractor="model:m@rounds-v1",
p_id=77,
page=1,
rationale_p_id=77,
rationale_quote="because",
final_p_id=77,
)
[m] = merge_rounds([table], [], [model])
assert m.final_work_rvu == 2.53 and m.decision == "refine"
assert m.proposed_work_rvu == 2.27 and m.ruc_percentile == "25th"
assert (m.p_id, m.rationale_p_id, m.rationale_quote, m.final_p_id) == (
77,
77,
"because",
77,
)
assert m.page == 66361 and m.source == "fr-table"
assert "conflict: final_work_rvu 2.6 (model:m@rounds-v1)" in m.pe_note
assert "conflict: decision accept (model:m@rounds-v1)" in m.pe_note
assert "pe says" in m.pe_note and "conflict: ruc_work_rvu" not in m.pe_note
assert m.extractor == "fr-table+model:m@rounds-v1"
def test_merge_proposal_keeps_its_own_paragraph_anchor():
"""A percentile-only row from ¶877 must not become the proposal's citation."""
pct = replace(
_row(code="99341", item_key="K"), proposed_work_rvu=None, p_id=877, page=100
)
prop = replace(
_row(code="99341", item_key="K"), proposed_work_rvu=1.0, p_id=889, page=101
)
[m] = merge_rounds([pct], [prop])
assert (m.p_id, m.page, m.proposed_work_rvu) == (889, 101, 1.0)
# a table row keeps its table page; the anchor still moves to the paragraph
tbl = _tbl(proposed_work_rvu=None)
[m] = merge_rounds([tbl], [prop])
assert (m.p_id, m.page, m.source) == (889, 66361, "fr-table")
def test_merge_keys_and_same_extractor():
a, b = _tbl(), replace(_tbl(), code="99342")
out = merge_rounds([a, b], [replace(a, pe_note="x")], [replace(a, item_key="Z")])
assert [(r.code, r.item_key) for r in out] == [
("99341", "K"),
("99342", "K"),
("99341", "Z"),
]
assert out[0].extractor == "fr-table" and out[0].pe_note == "x"
assert merge_rounds() == []
# ── store-backed: candidates, family run ─────────────────────────────
def _store(tmp_path, with_text=True, dup=False):
s = Store(":memory:", storage_dir=tmp_path / "st")
key = s.create(
Rule(title=TITLE23, document_number="22-0001", date_published="2022-11-18")
)
con = s._con()
anchors = [
(key, 1, 10, 1, "Unrelated opening paragraph."),
(key, 2, 10, 2, "Context two."),
(key, 1676, 69608, 3, P1676),
(key, 1685, 69609, 4, P1685),
(key, 1690, 69609, 5, "Code 99341 is named in passing here."),
]
keys = [key]
if dup: # a second bib item for the same FR document
k2 = s.create(
Rule(title=TITLE23, document_number="22-0001", date_published="2022-11-19")
)
keys.append(k2)
anchors += [(k2, 1676, 69608, 3, P1676)]
# a correction notice quoting the rule
kc = s.create(
Rule(
title="CY 2023 PFS Final Rule; Correction",
document_number="22-9",
date_published="2023-01-01",
)
)
anchors.append((kc, 5, 1, 1, P1676))
con.executemany(
"INSERT INTO fr_anchors (item_key, p_id, page, ordinal, text) VALUES (?,?,?,?,?)",
anchors,
)
for k in keys:
con.execute(
"INSERT INTO fr_anchor_docs (item_key, document_number, html_url, start_page, end_page, fr_volume)"
" VALUES (?,'22-0001','u',1,2,87)",
(k,),
)
con.commit()
if with_text:
txt = tmp_path / "rule.txt"
txt.write_text(FIXTURE.replace("99343", "99341").replace("2008", "2023"))
s.attach_file(key, txt)
return s, key
FAM = SimpleNamespace(
key="HOME-VISITS",
codes=("99341", "99342", "99350"),
synonyms=("Home or Residence Services",),
)
def test_rule_items_dedupe_documents_and_drop_corrections(tmp_path):
s, key = _store(tmp_path, dup=True)
items = rule_items(s, [])
assert [i["item_key"] for i in items] == [key] # dup and correction gone
assert items[0]["kind"] == "final"
# PFS-titled items with anchors are included even when no anchor names a code
assert [i["item_key"] for i in rule_items(s, [])] == [key]
def test_candidate_paragraphs_filter_and_context(tmp_path):
s, key = _store(tmp_path, dup=True)
assert candidate_paragraphs(s, (), ()) == []
got = candidate_paragraphs(s, FAM.codes, FAM.synonyms)
assert [(p["item_key"], p["p_id"]) for p in got] == [(key, 1676), (key, 1685)]
assert got[0]["context"] == ["Unrelated opening paragraph.", "Context two."]
assert got[0]["rule_kind"] == "final" and got[0]["title"] == TITLE23
assert [p["p_id"] for p in candidate_paragraphs(s, (), FAM.synonyms)] == [
1676,
1685,
]
assert [p["p_id"] for p in candidate_paragraphs(s, FAM.codes, ())] == [1676]
def _good_reply():
return _reply(
_d("99341", ruc_percentile="25th", proposed_work_rvu=1.0),
_d("99350", ruc_percentile="median", proposed_work_rvu=3.6),
)
def test_family_rounds_end_to_end_and_resume(tmp_path):
s, key = _store(tmp_path, with_text=False)
ask = FakeAsk(_good_reply())
log = RunLog(tmp_path / "ledger.jsonl")
rows, counts = family_rounds(None, s, FAM, ask=ask, log=log)
assert counts == {"done": 1, "skipped": 0, "failed": 0, "failed_pending": 0}
by = {r.code: r for r in rows}
r = by["99341"]
assert (r.proposed_work_rvu, r.final_work_rvu, r.ruc_work_rvu) == (1.0, 1.0, 1.0)
assert (r.ruc_percentile, r.decision, r.rule_year) == ("25th", "accept", 2023)
assert (r.p_id, r.final_p_id) == (1676, 1685)
assert by["99350"].ruc_percentile == "median"
assert len(ask.calls) == 1
again, counts = family_rounds(None, s, FAM, ask=ask, log=log)
assert counts["skipped"] == 1 and counts["done"] == 0 and len(ask.calls) == 1
assert again == rows
bad = FakeAsk(None)
log2 = RunLog(tmp_path / "l2.jsonl")
rows2, c2 = family_rounds(None, s, FAM, ask=bad, log=log2)
assert c2["failed"] == 1 and {x.extractor for x in rows2} == {"regex"}
_, c3 = family_rounds(None, s, FAM, ask=bad, log=log2)
assert c3["failed_pending"] == 1 and len(bad.calls) == 1
_, c4 = family_rounds(None, s, FAM, ask=bad, log=log2, retry_failed=True)
assert c4["failed"] == 1 and len(bad.calls) == 2
def test_no_llm_and_tables_only_replay_the_finished_ledger(tmp_path):
s, key = _store(tmp_path, with_text=False)
ask = FakeAsk(_good_reply())
log = RunLog(tmp_path / "ledger.jsonl")
full, _ = family_rounds(None, s, FAM, ask=ask, log=log)
assert any("model:" in r.extractor for r in full)
for kw in ({}, {"tables_only": True}):
replay, counts = family_rounds(
None, s, FAM, ask=None, log=log, replay_model="qwen-test", **kw
)
assert replay == full and counts["done"] == 0
assert len(ask.calls) == 1
# a different model has no recorded outputs: regex rows only, nothing deleted that regex made
other, _ = family_rounds(None, s, FAM, ask=None, log=log, replay_model="other")
assert {r.extractor for r in other} == {"regex"} or all(
"model:" not in r.extractor for r in other
)
# no model name at all
plain, _ = family_rounds(None, s, FAM, ask=None, log=None)
assert all("model:" not in r.extractor for r in plain)
def test_family_rounds_default_ledger(tmp_path, monkeypatch):
s, _ = _store(tmp_path, with_text=False)
monkeypatch.setattr(
rounds_mod, "ledger_path", lambda t, f: tmp_path / f"{t}-{f}.jsonl"
)
ask = FakeAsk(_good_reply())
family_rounds(None, s, FAM, ask=ask, log=None)
assert (tmp_path / "rounds-HOME-VISITS.jsonl").exists()
def test_family_rounds_reads_the_fr_table_and_merges_it_first(tmp_path):
s, key = _store(tmp_path)
rows, counts = family_rounds(None, s, FAM, ask=None, log=None)
[r] = [r for r in rows if r.code == "99341"]
assert r.source == "fr-table" and (r.rule_year, r.rule_kind) == (2023, "final")
assert r.final_work_rvu == 2.53 and r.p_id == 1676 # table + paragraph anchor
s2, _ = _store(tmp_path / "x", with_text=False)
assert all(
r.source != "fr-table"
for r in family_rounds(None, s2, FAM, ask=None, log=None)[0]
)
def test_rounds_inventory(tmp_path):
s, _ = _store(tmp_path)
paras, table = rounds_inventory(s, FAM)
assert [p["p_id"] for p in paras] == [1676]
assert any(r.code == "99341" for r in table)
def test_final_p_id_roundtrips_and_old_tables_gain_the_column():
con = duckdb.connect(":memory:")
con.execute("CREATE SCHEMA pfs")
con.execute(
"CREATE TABLE pfs.valuation_round (family VARCHAR, code VARCHAR, rule_year INTEGER, item_key VARCHAR,"
" rule_kind VARCHAR, p_id INTEGER, page INTEGER, source VARCHAR, table_title VARCHAR,"
" ruc_work_rvu DOUBLE, ruc_percentile VARCHAR, survey_intra_min DOUBLE, survey_total_min DOUBLE,"
" comparators VARCHAR, surveying_societies VARCHAR, requested_work_rvu DOUBLE,"
" proposed_work_rvu DOUBLE, final_work_rvu DOUBLE, decision VARCHAR, rationale_p_id INTEGER,"
" rationale_quote VARCHAR, pe_note VARCHAR, extractor VARCHAR)"
)
ensure_tables(con)
cites = '{"final_work_rvu": 1685, "ruc_work_rvu": "page:66361"}'
row = replace(_row(code="99341"), final_p_id=1685, ruc_p_id=877, field_cites=cites)
write_rounds(con, "F", [row])
got = read_rounds(con, "F")[0]
assert (got.final_p_id, got.ruc_p_id, got.field_cites) == (1685, 877, cites)
assert field_cites(got) == {"final_work_rvu": 1685, "ruc_work_rvu": "page:66361"}
assert got == row
# ── fix round 3 (N1, N2, R26) ────────────────────────────────────────
def test_numbers_decide_the_label_in_proposed_and_final_rules():
"""N2: ruc 2.27, proposed 2.50, model "accept" -> refine."""
t = "The RUC recommended a work RVU of 2.27 for CPT code 99343. We are proposing a work RVU of 2.50 for CPT code 99343."
out = _reply(
_d("99343", ruc_work_rvu=2.27, proposed_work_rvu=2.5, decision="accept")
)
[m] = rows_from_output(out, pp(t), {"99343"}, family="F", model="m")
assert (m.ruc_work_rvu, m.proposed_work_rvu, m.decision) == (2.27, 2.5, "refine")
[r] = apply_paragraph_rules([replace(m, decision="accept")], [])
assert r.decision == "refine"
# a final rule: the final arrives from family_as_proposed in another paragraph
row = replace(
regex_rounds(
para(
1,
"We proposed the RUC-recommended work RVU of 2.27 for CPT code 99343.",
),
{"99343"},
family="F",
)[0],
decision="refine",
)
assert row.ruc_work_rvu == 2.27 and row.final_work_rvu is None
[r] = apply_paragraph_rules([row], [para(2, P1685)])
assert (r.final_work_rvu, r.decision) == (2.27, "accept")
# a table's own label stands; a table row without one is derived
tb = _tbl(ruc_work_rvu=2.27, final_work_rvu=2.53, decision="accept")
assert apply_paragraph_rules([tb], [])[0].decision == "accept"
assert apply_paragraph_rules([replace(tb, decision="")], [])[0].decision == "refine"
def test_rationale_comes_from_a_paragraph_with_a_value_or_decision():
"""R26a: ¶294-style misvaluation boilerplate is not the rationale."""
boiler = replace(
_row(
code="99341",
item_key="K",
proposed_work_rvu=None,
ruc_work_rvu=None,
final_work_rvu=None,
ruc_percentile="",
decision="",
),
p_id=294,
rationale_p_id=294,
rationale_quote="misvalued codes boilerplate",
)
valued = replace(
_row(code="99341", item_key="K", proposed_work_rvu=1.0, decision=""),
p_id=878,
rationale_p_id=0,
rationale_quote="",
)
[m] = merge_rounds([boiler], [valued])
assert (m.rationale_p_id, m.rationale_quote) == (0, "")
[m] = merge_rounds([valued], [boiler]) # boilerplate arriving later is skipped too
assert (m.rationale_p_id, m.rationale_quote) == (0, "")
assert m.p_id == 878
quoted = replace(
valued, rationale_p_id=878, rationale_quote="we propose it", extractor="x"
)
[m] = merge_rounds([boiler], [quoted])
assert (m.rationale_p_id, m.rationale_quote) == (878, "we propose it")
def test_ruc_p_id_follows_the_ruc_statement():
"""R26b: percentile in ¶877, proposal in ¶878 -> p_id 878, ruc_p_id 877."""
pct = regex_rounds(
para(877, P877, item="K", kind="proposed", title=TITLE23P, date="2022-07-29"),
{"99341"},
family="F",
)
prop = regex_rounds(
para(
878,
"We are proposing the RUC-recommended work RVU of 1.00 for CPT code 99341.",
item="K",
kind="proposed",
title=TITLE23P,
date="2022-07-29",
),
{"99341"},
family="F",
)
assert pct[0].ruc_p_id == 877 and prop[0].ruc_p_id == 878
[m] = merge_rounds(pct, [replace(prop[0], ruc_work_rvu=None, ruc_p_id=0)])
assert (m.p_id, m.ruc_p_id, m.ruc_percentile, m.proposed_work_rvu) == (
878,
877,
"25th",
1.0,
)
# ruc_as_proposed anchors the RUC value to its paragraph when nothing else does
bare = replace(prop[0], ruc_work_rvu=None, ruc_p_id=0)
ruc_para = para(
900,
"We are proposing the RUC-recommended work RVU for all eight codes: 99341.",
item="K",
kind="proposed",
title=TITLE23P,
date="2022-07-29",
)
[r] = apply_paragraph_rules([bare], [ruc_para])
assert (r.ruc_work_rvu, r.ruc_p_id) == (1.0, 900)
# a table's RUC value is cited by its page: a paragraph does not take over ruc_p_id
tbl = _tbl(ruc_work_rvu=2.27)
[m] = merge_rounds([tbl], [replace(pct[0], item_key="K", code="99341")])
assert m.ruc_p_id == 0 and m.ruc_percentile == "25th"
# model rows carry it for survey data / percentile
out = _reply(_d("99341", survey_intra_min=30.0, proposed_work_rvu=1.0))
t = "The RUC survey intra-service time was 30 minutes for CPT code 99341. We proposed a work RVU of 1.0 for CPT code 99341."
[r] = rows_from_output(out, para(5, t), {"99341"}, family="F", model="m")
assert r.ruc_p_id == 5
out = _reply(_d("99341", proposed_work_rvu=1.0))
t = "We proposed a work RVU of 1.0 for CPT code 99341."
[r] = rows_from_output(out, para(5, t), {"99341"}, family="F", model="m")
assert r.ruc_p_id == 0
def test_recaps_of_another_years_rule_yield_nothing_in_both_kinds():
for kind in ("proposed", "final"):
for t in (
"In the CY 2023 PFS final rule, we finalized a work RVU of 1.00 for CPT code 99341.",
"In the CY 2023 PFS proposed rule, we proposed a work RVU of 1.00 for CPT code 99341.",
"(CY 2023 PFS final rule) we proposed a work RVU of 1.00 for CPT code 99341.",
):
assert para_values(t, {"99341"}, rule_kind=kind, rule_year=2027) == [], (
kind,
t,
)
# the rule's own year is not a recap
t = "In the CY 2027 PFS proposed rule, we proposed a work RVU of 1.00 for CPT code 99341."
assert para_values(t, {"99341"}, rule_kind="proposed", rule_year=2027) == [
("99341", "proposed_work_rvu", 1.0)
]
# the model path too
out = _reply(_d("99341", proposed_work_rvu=1.0, ruc_work_rvu=1.0))
t = "In the CY 2023 PFS proposed rule, we proposed a work RVU of 1.00 for CPT code 99341."
[r] = rows_from_output(out, pp(t), {"99341"}, family="F", model="m")
assert r.proposed_work_rvu is None
def test_disagree_with_ruc_values_and_maintain_goes_to_the_main_verb():
t = (
"We disagree with the AMA RUC-recommended values for these services and are "
"maintaining the current work RVUs for the related predecessor codes of 0.17 RVUs for CPT "
"code 90460 and 0.15 work RVUs for CPT code 90461."
)
assert para_values(t, {"90461", "90460"}, rule_kind="final") == [
("90461", "final_work_rvu", 0.15)
]
assert para_values(t, {"90461"}, rule_kind="proposed") == [
("90461", "proposed_work_rvu", 0.15)
]
t2 = "We disagree with the RUC-recommended values and are finalizing 0.15 work RVUs for CPT code 90461."
assert para_values(t2, {"90461"}, rule_kind="final") == [
("90461", "final_work_rvu", 0.15)
]
assert para_values(
t2.replace("finalizing", "proposing"), {"90461"}, rule_kind="final"
) == [("90461", "proposed_work_rvu", 0.15)]
def test_printed_page_markers_are_not_codes():
p = _percentiles(P877 + " ( printed page 46000)")
assert "46000" not in p
assert rounds_mod._named(
"see ( printed page 46000) 99341", rounds_mod.code_pattern(["99341"])
) == ("99341",)
sent = rounds_mod._sentences(
"We proposed a ( printed page 123) work RVU of 1.00 for CPT code 99341."
)
assert "printed" not in sent[0]
def test_apply_rules_with_no_rows_and_a_ruc_paragraph():
ruc = para(
900,
"We are proposing the RUC-recommended work RVU for all eight codes: 99341.",
kind="proposed",
)
assert apply_paragraph_rules([], [ruc]) == []
def test_hypothetical_sentences_yield_no_regex_values():
for k, t in (
(
"final",
"If we were to finalize a work RVU of 2.00 for CPT code 99348, payment would rise.",
),
(
"proposed",
"We are proposing that, if the RUC recommends it, we would assign a work RVU of 2.00 for CPT code 99348.",
),
(
"proposed",
"Were we to propose a work RVU of 2.00 for CPT code 99348, it would be consistent.",
),
):
assert para_values(t, {"99348"}, rule_kind=k) == [], t
real = "We agree that it would be more accurate to finalize the RUC-recommended work RVU of 1.25 for CPT code 99348."
assert ("99348", "final_work_rvu", 1.25) in para_values(
real, {"99348"}, rule_kind="final"
)
# N3-c: "would be the/consistent/..." states the value (5ITGVDJV ¶429, JCWARTJD ¶833)
genuine = "Similarly, the RUC-recommended work RVU of 1.40 for CPT code 92979 would be the second highest work RVU for add-on codes."
for k in ("proposed", "final"):
assert para_values(genuine, {"92979"}, rule_kind=k) == [
("92979", "ruc_work_rvu", 1.4)
]
t = "We are proposing a work RVU of 1.50 for CPT code 99348, which would be consistent with the RUC survey."
assert para_values(t, {"99348"}, rule_kind="proposed") == [
("99348", "proposed_work_rvu", 1.5)
]
# ── fix round 4 (R27 field cites, R28 model gates, R29 minors) ───────
P878 = (
"We are proposing the RUC-recommended work RVU for all eight CPT codes in the "
"Home or Residence Services CPT code family. We are proposing a work RVU of "
f"3.88 for CPT code 99345 {DESC}, and a work RVU of 3.60 for CPT code 99350 {DESC}."
)
P880 = (
"This means that for CPT code 99345, assuming we finalize the RUC-recommended "
"total time of 126 minutes, prolonged services would be reportable once 141 or "
"more minutes are spent by a physician or NPP providing home or residence "
"services. Likewise, for CPT code 99350, assuming we finalize the "
"RUC-recommended total time of 97 minutes, prolonged services would be "
"reportable once 112 or more minutes are spent by a physician or NPP providing "
"home or residence services."
)
def _p23(p_id, text):
return para(
p_id, text, item="MZ24MX5S", kind="proposed", title=TITLE23P, date="2022-07-29"
)
def _cited_fields(r):
return {f for f in CITE_FIELDS if getattr(r, f) not in (None, "")}
def test_field_cites_cite_each_value_to_the_paragraph_stating_it():
"""R26b/R27: CY2023 proposed 99350 — percentile ¶877, proposal and RUC
value ¶878, total time ¶880 (only ¶880 states the minutes)."""
codes = {"99345", "99350"}
paras = [_p23(877, P877), _p23(878, P878), _p23(880, P880)]
regex = [r for p in paras for r in regex_rounds(p, codes, family="F")]
out = _reply(
_d("99345", ruc_work_rvu=3.88, proposed_work_rvu=3.88, survey_total_min=126),
_d(
"99350",
ruc_work_rvu=3.6,
proposed_work_rvu=3.6,
survey_total_min=97,
decision="accept",
),
)
model = rows_from_output(out, paras[2], codes, family="F", model="m")
rows = {
r.code: r for r in apply_paragraph_rules(merge_rounds([], regex, model), paras)
}
r = rows["99350"]
assert (
r.ruc_work_rvu,
r.proposed_work_rvu,
r.survey_total_min,
r.ruc_percentile,
r.decision,
) == (3.6, 3.6, 97, "median", "accept")
assert field_cites(r) == {
"ruc_percentile": 877,
"ruc_work_rvu": 878,
"proposed_work_rvu": 878,
"survey_total_min": 880,
"decision": 878,
}
assert (r.p_id, r.ruc_p_id) == (878, 877) # row-level anchors unchanged
assert field_cites(rows["99345"])["survey_total_min"] == 880
for row in rows.values():
assert set(field_cites(row)) == _cited_fields(row)
def test_field_cites_final_rule_family_as_proposed():
"""CY2023 final 99341: the final, the decision and the rationale cite ¶1685."""
r = _applied()["99341"]
assert field_cites(r) == {
"ruc_percentile": 1676,
"proposed_work_rvu": 1676,
"ruc_work_rvu": 1676,
"final_work_rvu": 1685,
"decision": 1685,
"rationale_quote": 1685,
}
def test_field_cites_for_table_cells_and_model_rows():
rows = {r.code: r for r in _rounds(codes={"99343", "92557"})}
page = "page:66361"
assert field_cites(rows["99343"]) == {
f: page
for f in (
"ruc_work_rvu",
"requested_work_rvu",
"proposed_work_rvu",
"final_work_rvu",
"decision",
"rationale_quote",
)
}
assert field_cites(rows["92557"])["pe_note"] == page
out = _reply(
_d(
"99341",
proposed_work_rvu=1.0,
comparators=["99350"],
pe_note="removed SK062",
),
quote="We proposed the RUC-recommended work RVU for all eight CPT codes in the Home or Residence Services CPT code family.",
societies=["American Academy of Family Physicians"],
)
text = P1676 + " The American Academy of Family Physicians surveyed."
[r] = rows_from_output(out, para(1676, text), {"99341"}, family="F", model="m")
assert field_cites(r) == {
f: 1676
for f in (
"ruc_percentile",
"proposed_work_rvu",
"comparators",
"surveying_societies",
"rationale_quote",
"pe_note",
)
}
def test_merge_carries_the_winning_sources_cite_and_lists_pe_note_sources():
[table] = _rounds(codes={"99343"})
table = replace(table, item_key="K", proposed_work_rvu=None)
model = rounds_mod._stamp(
replace(
table,
source="fr-para",
extractor="model:m@rounds-v1",
p_id=77,
proposed_work_rvu=2.27,
final_work_rvu=2.6,
ruc_percentile="25th",
survey_intra_min=40.0,
rationale_quote="",
pe_note="pe says",
decision="",
field_cites="",
),
77,
)
later = rounds_mod._stamp(
replace(model, p_id=78, pe_note="more pe", final_work_rvu=2.7), 78
)
[m] = merge_rounds([table], [], [model, later])
c = field_cites(m)
assert c["final_work_rvu"] == c["ruc_work_rvu"] == c["decision"] == "page:66361"
assert c["proposed_work_rvu"] == c["ruc_percentile"] == c["survey_intra_min"] == 77
assert c["rationale_quote"] == "page:66361"
# pe_note joins three notes: the model's two and the conflicts they raised
assert m.pe_note.startswith("conflict: final_work_rvu 2.6")
assert c["pe_note"] == [77, 78]
assert set(c) == _cited_fields(m)
# one source -> a single cite; rows without cites merge without any
[one] = merge_rounds([model])
assert field_cites(one)["pe_note"] == 77
bare = replace(model, field_cites="")
[m] = merge_rounds([bare], [replace(bare, pe_note="x")])
assert field_cites(m) == {}
def test_model_values_from_another_years_rule_are_dropped_in_both_kinds():
"""R28 (R26c): a recap sentence yields no model value and no label."""
t = (
"In the CY 2020 PFS final rule (84 FR 62796), we finalized the "
"RUC-recommended work RVU of 0.48 for CPT code 99281."
)
out = _reply(
_d(
"99281",
ruc_work_rvu=0.48,
final_work_rvu=0.48,
decision="accept",
comparators=["99281", "99282"],
survey_total_min=0.48,
),
quote=t,
)
for kind, title, date in (
("proposed", "CY 2027 PFS Proposed Rule", "2026-07-16"),
("final", "CY 2027 PFS Final Rule", "2026-11-09"),
):
# the only other sentence names the code but states no number
p = para(
1, t + " CPT code 99281 is related.", title=title, date=date, kind=kind
)
[r] = rows_from_output(out, p, {"99281"}, family="F", model="m")
assert not has_content(r), kind
assert (r.ruc_work_rvu, r.decision, r.comparators, r.rationale_quote) == (
None,
"",
"",
"",
)
assert not any(
has_content(x) for x in apply_paragraph_rules(merge_rounds([r]), [p])
)
def test_model_numbers_need_a_plain_sentence_covering_the_code():
t = (
"The RUC recommended a work RVU of 2.00 for CPT code 99348. "
"If we were to accept a survey intra-service time of 30 minutes for CPT code "
"99348, the value would rise. CPT code 99349 is similar. "
"The RUC recommended a total time of 60 minutes for all eight CPT codes in the family. "
"For all codes except CPT code 99350 the RUC recommended 45 minutes of post-service time."
)
out = _reply(
_d("99348", ruc_work_rvu=2.0, survey_intra_min=30, survey_total_min=60),
_d("99349", ruc_work_rvu=2.0, survey_intra_min=45),
)
rows = {
r.code: r
for r in rows_from_output(out, pp(t), {"99348", "99349"}, family="F", model="m")
}
assert (rows["99348"].ruc_work_rvu, rows["99348"].survey_intra_min) == (2.0, None)
assert rows["99348"].survey_total_min == 60 # a collective sentence covers it
# 2.00 is 99348's; 45 sits in a sentence naming another code
assert (rows["99349"].ruc_work_rvu, rows["99349"].survey_intra_min) == (None, None)
# a hypothetical rationale quote is dropped too
q = "If we were to accept a survey intra-service time of 30 minutes for CPT code 99348, the value would rise."
[r] = rows_from_output(
_reply(_d("99348", ruc_work_rvu=2.0), quote=q),
pp(t),
{"99348"},
family="F",
model="m",
)
assert (r.ruc_work_rvu, r.rationale_quote, r.rationale_p_id) == (2.0, "", 0)
def test_model_label_needs_ruc_and_the_acted_value():
"""R28: a final rule's label needs a final; a proposed rule's a proposal."""
t = "We proposed the RUC-recommended work RVU of 2.00 for CPT code 99348."
out = _reply(
_d("99348", ruc_work_rvu=2.0, proposed_work_rvu=2.0, decision="refine")
)
[r] = rows_from_output(out, para(1, t), {"99348"}, family="F", model="m")
assert (r.ruc_work_rvu, r.proposed_work_rvu, r.final_work_rvu) == (2.0, 2.0, None)
assert r.decision == "" and "decision" not in field_cites(r)
t = "We are proposing the RUC-recommended work RVU of 2.00 for CPT code 99348."
[r] = rows_from_output(out, pp(t), {"99348"}, family="F", model="m")
assert r.decision == "accept" and field_cites(r)["decision"] == 1
def test_stated_interim_or_reject_labels_are_not_overridden():
"""N3-d: numbers do not override "interim"/"reject" the sentence says."""
t = "On an interim basis, we are finalizing the RUC-recommended work RVU of 2.00 for CPT code 99348."
out = _reply(_d("99348", ruc_work_rvu=2.0, final_work_rvu=2.0, decision="interim"))
[r] = rows_from_output(out, para(1, t), {"99348"}, family="F", model="m")
assert r.decision == "interim"
assert apply_paragraph_rules([r], [])[0].decision == "interim"
t = (
"We reject the RUC-recommended work RVU of 2.00 for CPT code 99348 and are "
"finalizing a work RVU of 1.80 for CPT code 99348."
)
out = _reply(_d("99348", ruc_work_rvu=2.0, final_work_rvu=1.8, decision="reject"))
[r] = rows_from_output(out, para(1, t), {"99348"}, family="F", model="m")
assert r.decision == "reject"
# the word is not in the sentence: the numbers decide
t = "We are finalizing the RUC-recommended work RVU of 2.00 for CPT code 99348."
for label in ("interim", "reject"):
out = _reply(_d("99348", ruc_work_rvu=2.0, final_work_rvu=2.0, decision=label))
[r] = rows_from_output(out, para(1, t), {"99348"}, family="F", model="m")
assert r.decision == "accept", label
def test_maintain_only_as_cms_own_verb():
"""N3-a: NV3Q43B7 ¶1598/¶1602 are recommendations, not CMS finals."""
for t in (
"The AMA RUC referred CPT code 24160 to the CPT Editorial Panel for revision of "
"the descriptor and recommended that we maintain the current (CY 2012) work RVU "
"of 8.00 for CPT code 24160 for CY 2013.",
"Commenters recommended we maintain the 2009 valued AMA RUC work RVU of 0.60 for CPT code 24160.",
"Commenters recommended that we will maintain a work RVU of 0.60 for CPT code 24160.",
"Commenters recommend we are maintaining a work RVU of 0.60 for CPT code 24160.",
):
assert para_values(t, {"24160"}, rule_kind="final") == [], t
# bare "we maintain that" is an argument: no proposal or final (the RUC's
# own figure is still the RUC's)
t = "We maintain that the RUC-recommended work RVU of 2.00 for CPT code 99348 overstates the work."
assert para_values(t, {"99348"}, rule_kind="final") == [
("99348", "ruc_work_rvu", 2.0)
]
t = "We will maintain a work RVU of 1.56 for CPT code 99348."
assert para_values(t, {"99348"}, rule_kind="final") == [
("99348", "final_work_rvu", 1.56)
]
assert para_values(t, {"99348"}, rule_kind="proposed") == [
("99348", "proposed_work_rvu", 1.56)
]
t = "Therefore we are maintaining 2.8 work RVUs for CPT code 43219."
assert para_values(t, {"43219"}, rule_kind="final") == [
("43219", "final_work_rvu", 2.8)
]
def test_recap_lead_ins_and_prefixes():
"""N3-b: "Response:"/"Comment:" lead-ins and "CY 20XX final rule" without "PFS"."""
for t in (
"Response: In the CY 2023 PFS final rule, we finalized a work RVU of 1.00 for CPT code 99341.",
"Comment: In the CY 2023 final rule, we finalized a work RVU of 1.00 for CPT code 99341.",
"In the CY 2023 final rule, we finalized a work RVU of 1.00 for CPT code 99341.",
"In the CY 2023 proposed rule, we proposed a work RVU of 1.00 for CPT code 99341.",
):
for kind in ("proposed", "final"):
assert para_values(t, {"99341"}, rule_kind=kind, rule_year=2027) == [], t
# mid-sentence references and "Comment on the ..." headings still state values
kept = (
(
"The AMA RUC recommended 3.61 work RVUs for CPT code 17106, which we "
"accepted in the CY 2009 PFS final rule with comment.",
2010,
("17106", "ruc_work_rvu", 3.61),
),
(
"Comment on the CY 2016 PFS final rule with comment period: Commenters "
"urged CMS to accept the RUC-recommended work RVU of 9.00 for CPT code 43210.",
2017,
("43210", "ruc_work_rvu", 9.0),
),
(
"Similarly, we are finalizing a work RVU of 8.94 for CPT code 43276 based "
"upon the formula described in the CY 2014 final rule with comment period.",
2015,
("43276", "final_work_rvu", 8.94),
),
)
for t, year, triple in kept:
assert para_values(t, {triple[0]}, rule_kind="final", rule_year=year) == [
triple
], t
def test_percentiles_skip_recaps_and_hypotheticals():
recap = "In the CY 2020 PFS final rule, the RUC recommended the 25th percentile for CPT code 99341."
assert _percentiles(recap, rule_year=2027) == {}
assert _percentiles(recap) == {"99341": "25th"} # no rule year, no recap test
hypo = "If the RUC recommended the median for CPT code 99341, the value would rise."
assert _percentiles(hypo) == {}
[r] = regex_rounds(
para(
1,
recap + " We proposed a work RVU of 1.00 for CPT code 99341.",
title="CY 2027 PFS Final Rule",
date="2026-11-09",
),
{"99341"},
family="F",
)
assert (r.ruc_percentile, r.proposed_work_rvu) == ("", 1.0)
assert guard(replace(r, ruc_percentile="25th"), recap).ruc_percentile == ""
def test_family_rounds_cite_every_value(tmp_path):
s, _ = _store(tmp_path)
ask = FakeAsk(_good_reply())
rows, _ = family_rounds(None, s, FAM, ask=ask, log=RunLog(tmp_path / "l.jsonl"))
assert rows
for r in rows:
assert set(field_cites(r)) == _cited_fields(r), r.code
def test_rule_items_keep_an_item_without_an_fr_document_row(tmp_path):
s = Store(":memory:", storage_dir=tmp_path / "st")
key = s.create(Rule(title=TITLE23, date_published="2022-11-18"))
s._con().execute(
"INSERT INTO fr_anchors (item_key, p_id, page, ordinal, text) VALUES (?,?,?,?,?)",
(key, 1, 1, 1, P1685),
)
assert [i["item_key"] for i in rule_items(s, [])] == [key]
def test_percentiles_code_mentions_without_a_survey_value():
t = (
"The RUC recommended the 25th percentile work RVU after CPT code 99342 was "
"surveyed, with CPT code 99341 at the median intra-service time."
)
assert _percentiles(t) == {}