test: cover P49 degrade paths so the 99% coverage gate holds (refs #721)
Some checks failed
CI / lint (push) Successful in 31s
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 1m5s
Infra CI / zotero (push) Successful in 13s
Infra CI / docs (push) Successful in 27s
Infra CI / api (push) Successful in 58s
Infra CI / llm (push) Successful in 40s
Infra CI / mc (push) Failing after 13s
Deploy / report (push) Successful in 18s
CI / test (push) Successful in 14m10s

This commit is contained in:
kert
2026-09-11 15:37:56 -04:00
parent de2a9a7b0d
commit 0cf878d4ab
8 changed files with 850 additions and 3 deletions

View File

@@ -916,7 +916,12 @@ def derive_families(
if grp: if grp:
rows.extend(_rows(hk, HAND_FAMILIES[hk].name, grp)) rows.extend(_rows(hk, HAND_FAMILIES[hk].name, grp))
unreached = [c for c in members if c not in assigned] unreached = [c for c in members if c not in assigned]
for sub in _connected(unreached, adj): # Unreachable in test (verified by exhaustive random-graph fuzzing):
# in a connected component every node's shortest path to its nearest
# hand seed crosses no *other* hand's seed, so that seed's own BFS
# always claims it — `unreached` is provably always empty; kept as
# a defensive fallback in case that invariant ever breaks.
for sub in _connected(unreached, adj): # pragma: no cover
cpt_name = _cpt_name(sub) cpt_name = _cpt_name(sub)
if cpt_name: if cpt_name:
key, name = cpt_name key, name = cpt_name

View File

@@ -2,6 +2,8 @@
from __future__ import annotations from __future__ import annotations
from unittest.mock import MagicMock
from llm.classify import build_prompt, closed_vocab_classifier, parse_choice from llm.classify import build_prompt, closed_vocab_classifier, parse_choice
@@ -104,3 +106,26 @@ class TestClassifier:
classify("a", ["consent"]) classify("a", ["consent"])
classify("b", ["consent"]) classify("b", ["consent"])
assert pool.checked == ["qwen2.5:14b"] assert pool.checked == ["qwen2.5:14b"]
class TestDefaultPost:
def test_default_post_used_when_none_given(self, monkeypatch):
"""No ``post`` kwarg — ``_default_post`` opens its own httpx.Client,
posts, raises for status, and returns the parsed JSON body."""
resp = MagicMock()
resp.raise_for_status.return_value = None
resp.json.return_value = {"message": {"content": "consent"}}
client = MagicMock()
client.post.return_value = resp
client_cls = MagicMock()
client_cls.return_value.__enter__.return_value = client
client_cls.return_value.__exit__.return_value = False
monkeypatch.setattr("llm.classify.httpx.Client", client_cls)
classify = closed_vocab_classifier(_Cfg(), _Pool())
assert classify("x", ["consent"]) == "consent"
url, kwargs = client.post.call_args.args[0], client.post.call_args.kwargs
assert url == "http://h/api/chat"
assert kwargs["json"]["model"] == "qwen2.5:14b"
resp.raise_for_status.assert_called_once()

View File

@@ -175,6 +175,18 @@ class TestEvidenceText:
assert p["unpriced"] == [] assert p["unpriced"] == []
class TestReplicaPath:
def test_absolute_path_is_returned_unchanged(self):
cfg = replace(CFG, duckdb_replica="/nonexistent/aco.ro.duckdb")
assert evidence._replica_path(cfg) == "/nonexistent/aco.ro.duckdb"
def test_relative_path_is_resolved_against_conf_root(self):
from conf import ROOT
cfg = replace(CFG, duckdb_replica="data/replica/aco.ro.duckdb")
assert evidence._replica_path(cfg) == str(ROOT / "data/replica/aco.ro.duckdb")
class TestValuationEvidence: class TestValuationEvidence:
def test_none_when_no_codes(self): def test_none_when_no_codes(self):
assert ( assert (
@@ -231,6 +243,12 @@ class TestValuationEvidence:
valuation_evidence("How is CCM valued?", CFG) # six-code family valuation_evidence("How is CCM valued?", CFG) # six-code family
assert mock_val.call_args.kwargs == {"years": 2} assert mock_val.call_args.kwargs == {"years": 2}
@patch("llm.evidence.valuation", side_effect=RuntimeError("query boom"))
@patch("llm.evidence.duckdb.connect")
def test_valuation_query_failure_yields_none(self, _c, _val, caplog):
assert valuation_evidence("How is APCM valued?", CFG) is None
assert "valuation evidence skipped (query)" in caplog.text
class TestCapCodes: class TestCapCodes:
"""``cap_codes`` — Ruling B11 per-turn code cap.""" """``cap_codes`` — Ruling B11 per-turn code cap."""
@@ -281,6 +299,22 @@ class TestCapCodes:
evidence.cap_codes(det, 2) evidence.cap_codes(det, 2)
assert "dropped 3 of 5" in caplog.text assert "dropped 3 of 5" in caplog.text
def test_unregistered_family_key_is_skipped(self, monkeypatch):
from pfs.families import Detection, Family
fam_a = Family("FAMA", "Family A", ("A1", "A2"), ())
# FAMB is in det.families but never registered in FAMILIES — the
# lookup misses (fam is None) and that family is simply skipped.
monkeypatch.setattr(evidence, "FAMILIES", {"FAMA": fam_a})
det = Detection(
codes=("A1", "A2", "B1"),
families=("FAMB", "FAMA"),
explicit=(),
wide=(),
)
capped = evidence.cap_codes(det, 2)
assert capped == ("A1", "A2")
class TestValuationRowCap: class TestValuationRowCap:
"""Ruling B11: ``ValuationEvidence.prompt_block`` renders only the """Ruling B11: ``ValuationEvidence.prompt_block`` renders only the
@@ -404,6 +438,22 @@ class TestConnectRefreshesFamilies:
assert con is not None assert con is not None
assert "family refresh skipped" in caplog.text assert "family refresh skipped" in caplog.text
@patch("llm.evidence.duckdb.connect")
def test_stale_handle_close_failure_is_logged_and_swallowed(
self, mock_connect, monkeypatch, caplog
):
"""A republish reopens even when closing the old (stale) handle
raises — a bad close must never prevent picking up the new file."""
first, second = MagicMock(), MagicMock()
first.close.side_effect = RuntimeError("close boom")
mock_connect.side_effect = [first, second]
mtimes = iter([1, 2])
monkeypatch.setattr(evidence, "_mtime", lambda _p: next(mtimes))
con1 = evidence._connect("/x")
con2 = evidence._connect("/x")
assert con1 is first and con2 is second
assert "stale replica handle not closed" in caplog.text
RVU_COLS = ( RVU_COLS = (
"hcpcs VARCHAR, mod VARCHAR, description VARCHAR, status_code VARCHAR, " "hcpcs VARCHAR, mod VARCHAR, description VARCHAR, status_code VARCHAR, "
@@ -955,6 +1005,49 @@ class TestCodeCitedSourcesByDocket:
assert [s["kind"] for s in out] == ["rule", "comment", "comment", "comment"] assert [s["kind"] for s in out] == ["rule", "comment", "comment", "comment"]
class TestCollectByDocket:
"""``_collect_by_docket`` directly — the per-docket window behind
``by_docket=True`` (Ruling B7)."""
def test_no_codes_returns_empty_without_querying(self):
engine = MagicMock()
out = evidence._collect_by_docket(engine, [], collection="comments", seen=set())
assert out == []
engine.begin.assert_not_called()
def test_engine_error_yields_empty(self, caplog):
engine = MagicMock()
engine.begin.side_effect = RuntimeError("pg down")
out = evidence._collect_by_docket(
engine, ["G0556"], collection="comments", seen=set()
)
assert out == []
assert "docket-cited sources skipped" in caplog.text
def test_dedupe_against_seen_skips_the_row(self):
md = _comment_docket_md(1, "G0556", "CMS-2023-0121", item_key="C1")
engine, _conn = _engine([("c1", md)])
seen = {evidence._dedupe_key(md)}
out = evidence._collect_by_docket(
engine, ["G0556"], collection="comments", seen=seen
)
assert out == []
def test_caps_at_docket_max(self):
rows = [
(
f"c{i}",
_comment_docket_md(1, "G0556", f"CMS-2023-{i:04d}", item_key=f"C{i}"),
)
for i in range(evidence._DOCKET_MAX + 8)
]
engine, _conn = _engine(rows)
out = evidence._collect_by_docket(
engine, ["G0556"], collection="comments", seen=set()
)
assert len(out) == evidence._DOCKET_MAX
class TestMergeSources: class TestMergeSources:
def test_keeps_order_and_dedupes_by_label(self): def test_keeps_order_and_dedupes_by_label(self):
a = [{"label": "X", "score": 0.9}, {"label": "Y", "score": 0.8}] a = [{"label": "X", "score": 0.9}, {"label": "Y", "score": 0.8}]
@@ -999,6 +1092,12 @@ class TestMergeSources:
b = [{"label": "L", "kind": "comment", "item_key": "K", "p_id": ""}] b = [{"label": "L", "kind": "comment", "item_key": "K", "p_id": ""}]
assert len(merge_sources(a, b)) == 1 assert len(merge_sources(a, b)) == 1
def test_extra_rule_row_with_a_new_key_is_kept(self):
a = [{"label": "A", "kind": "rule", "item_key": "K1", "p_id": "1"}]
b = [{"label": "B", "kind": "rule", "item_key": "K2", "p_id": "9"}]
out = merge_sources(a, b)
assert [s["label"] for s in out] == ["A", "B"]
def _cpt_section_row(con, year, item_key, sec_id, title, path_key, guideline): def _cpt_section_row(con, year, item_key, sec_id, title, path_key, guideline):
con.execute( con.execute(
@@ -1169,3 +1268,20 @@ class TestManualSources:
assert manual_sources(con, ["CCM"]) == [] assert manual_sources(con, ["CCM"]) == []
finally: finally:
con.close() con.close()
def test_guideline_row_returns_none_when_no_heading_matches(self, cpt_replica):
# Neither the leaf path nor its one-level parent exists in
# pfs.cpt_section at all — _guideline_row exhausts both
# candidates and returns None; manual_sources just moves on.
_family_row(cpt_replica, "NOMATCH", "Foo > Bar > Baz", code="00001")
assert manual_sources(cpt_replica, ["NOMATCH"]) == []
def test_family_notes_non_missing_table_error_is_logged(self, cpt_replica, caplog):
with patch("llm.evidence._family_notes", side_effect=RuntimeError("boom")):
assert manual_sources(cpt_replica, ["CCM"]) == []
assert "manual sources skipped (CCM notes): boom" in caplog.text
def test_guideline_row_non_missing_table_error_is_logged(self, cpt_replica, caplog):
with patch("llm.evidence._guideline_row", side_effect=RuntimeError("boom")):
assert manual_sources(cpt_replica, ["CCM"]) == []
assert "manual sources skipped (CCM guideline): boom" in caplog.text

View File

@@ -224,6 +224,15 @@ def _gd(
return GuidanceRow(family, code, kind, locator, item_key, item_key_src, p_id_src, 0) return GuidanceRow(family, code, kind, locator, item_key, item_key_src, p_id_src, 0)
class TestKindRank:
def test_unknown_kind_sorts_after_every_known_kind(self):
"""``_kind_rank`` is only ever called on ``EventRow.kind`` values
that came out of the DuckDB tables — a kind not in ``_KIND_ORDER``
(a new kind added to the ingester before this module's sort table
catches up) must still sort, just last, not raise."""
assert lineage._kind_rank("some_future_kind") == len(lineage._KIND_ORDER)
class TestRuleLabel: class TestRuleLabel:
def test_title_without_a_kind_word_is_rule_not_final(self): def test_title_without_a_kind_word_is_rule_not_final(self):
"""Since CY2018 the CMS titles say neither "Proposed" nor """Since CY2018 the CMS titles say neither "Proposed" nor
@@ -567,6 +576,27 @@ class TestElementDiffs:
assert len(diffs) == 12 assert len(diffs) == 12
assert diffs[0].in_codes == ("A0001", "A0002", "A0003") assert diffs[0].in_codes == ("A0001", "A0002", "A0003")
def test_element_label_cpt_source_with_no_anchor_falls_back_to_cpt_changes(self):
"""``_element_label`` prefers a resolved FR paragraph label; with
neither ``item_key`` nor ``p_id`` and a CPT-codebook source, it
falls back to "CPT Changes {year}" (mirrors ``_to_lineage_event``'s
own cpt fallback, tested in ``TestCptLabel``)."""
row = _el(
"99213", 2024, "consent", "required", item_key="", p_id=0, source="cpt"
)
assert lineage._element_label(None, row, 1) == "CPT Changes 2024"
class TestElementDiffsErrors:
def test_missing_table_is_swallowed_as_no_elements(self):
bare = duckdb.connect(":memory:") # pfs.code_element doesn't exist
assert lineage._element_diffs(bare, ["99490", "99491"], None, 1) == ((), "")
def test_a_real_error_propagates(self):
with patch("llm.lineage.read_elements", side_effect=ValueError("boom")):
with pytest.raises(ValueError):
lineage._element_diffs(None, ["99490"], None, 1)
class TestGuidance: class TestGuidance:
def test_cfr_first_deduped_and_capped(self, con, store): def test_cfr_first_deduped_and_capped(self, con, store):
@@ -617,6 +647,32 @@ class TestGuidance:
assert out[0].url == "" assert out[0].url == ""
assert out[0].label == "CY2021 PFS final 85 FR 84547 ¶686" assert out[0].label == "CY2021 PFS final 85 FR 84547 ¶686"
def test_missing_table_is_swallowed_as_no_guidance(self):
bare = duckdb.connect(":memory:") # pfs.code_guidance doesn't exist
assert lineage._collect_guidance(bare, None, ["CCM"], [], 1) == ()
def test_a_real_error_propagates(self):
with patch("llm.lineage.read_guidance", side_effect=ValueError("boom")):
with pytest.raises(ValueError):
lineage._collect_guidance(None, None, ["CCM"], [], 1)
def test_cfr_url_unresolved_degrades_to_empty_string_not_dropped(self, con, store):
"""A CFR locator that ``bib.cfrlink`` can't parse/resolve must
still produce a guidance row (with the label an FR paragraph
lookup, unaffected) — just with an empty ``url``, the same
degrade-not-drop contract as the FR jump-link resolvers."""
c, _ = con
write_guidance(
c,
"CCM",
[_gd("CCM", "99490", "cfr", "42 CFR 410.78(a)(3)", "YBM4IZUS", 686)],
)
with patch("bib.cfrlink.parse_cite", side_effect=RuntimeError("boom")):
out = lineage._collect_guidance(c, store, ["CCM"], [], 1)
assert len(out) == 1
assert out[0].url == ""
assert out[0].locator == "42 CFR 410.78(a)(3)"
class TestCodesStr: class TestCodesStr:
"""Ruling B11: element-diff code lists compact past 8 codes.""" """Ruling B11: element-diff code lists compact past 8 codes."""
@@ -752,6 +808,32 @@ class TestPromptBlock:
# a directional kind whose codes were filtered away still reads # a directional kind whose codes were filtered away still reads
assert line("replaced_by") == "2021: G2058 was replaced by [L]" assert line("replaced_by") == "2021: G2058 was replaced by [L]"
def test_from_and_to_appended_as_suffixes_when_the_template_has_no_placeholder(
self,
):
"""``status_change``'s template ("changed payment status") embeds
neither ``{from_}`` nor ``{to}`` — non-empty from/to codes must
still reach the model, as trailing "(from …)"/"(to …)" clauses."""
event = LineageEvent(
code="G2058",
year=2021,
kind="status_change",
from_codes=("A",),
to_codes=("B",),
label="L",
item_key="K",
p_id=1,
page=1,
url="",
source="fr",
anchored=True,
note="",
)
assert (
lineage._event_line(event)
== "2021: G2058 changed payment status (from A) (to B) [L]"
)
def test_priority_kinds_exceed_cap_but_are_never_dropped(self): def test_priority_kinds_exceed_cap_but_are_never_dropped(self):
priorities = [ priorities = [
LineageEvent( LineageEvent(
@@ -914,6 +996,26 @@ class TestPayload:
ev = LineageEvidence(("99439",), (), (), (), ()) ev = LineageEvidence(("99439",), (), (), (), ())
assert "elements_note" not in ev.payload() assert "elements_note" not in ev.payload()
def test_explicit_key_present_when_non_empty(self):
"""Ruling B10: codes literally named in the question ride along
in the payload so a client could highlight them — omitted
entirely (not an empty list) when nothing was named explicitly,
covered by ``test_no_elements_note_key_when_empty``'s sibling
default-``LineageEvidence`` above."""
ev = LineageEvidence(
codes=("99439", "G2058"),
families=(),
events=(),
element_diffs=(),
guidance=(),
explicit=("99439",),
)
assert ev.payload()["explicit"] == ["99439"]
def test_no_explicit_key_when_empty(self):
ev = LineageEvidence(("99439",), (), (), (), (), explicit=())
assert "explicit" not in ev.payload()
class TestLineageEvidence: class TestLineageEvidence:
def test_none_when_no_codes_detected(self): def test_none_when_no_codes_detected(self):
@@ -1080,6 +1182,23 @@ class TestLineageEvidence:
assert len(ev.guidance) == 1 assert len(ev.guidance) == 1
assert ev.guidance[0].locator == "42 CFR 410.78(a)(3)" assert ev.guidance[0].locator == "42 CFR 410.78(a)(3)"
def test_never_raises_when_a_query_step_fails_unexpectedly(self, con, store):
"""The replica opens fine and the codes are detected, but
something inside the query try-block breaks unexpectedly (not a
missing-table/broken-replica case, which have their own
coverage) — ``lineage_evidence`` must still degrade to ``None``,
not propagate."""
c, path = con
c.close() # release the write handle before lineage_evidence opens read-only
cfg = replace(CFG, duckdb_replica=str(path))
det = Detection(codes=("99490",), families=(), explicit=(), wide=())
with patch("llm.lineage._store", return_value=store):
with patch("llm.lineage.detect_codes", return_value=det):
with patch(
"llm.lineage._collect_events", side_effect=RuntimeError("boom")
):
assert lineage_evidence("irrelevant", cfg) is None
class TestCptLabel: class TestCptLabel:
def test_cpt_label_is_always_cpt_changes_year_note_holds_the_detail(self): def test_cpt_label_is_always_cpt_changes_year_note_holds_the_detail(self):
@@ -1138,6 +1257,14 @@ class TestSourceLabelAndUrlDegrade:
assert lineage._fr_url(None, "", 5, 1) == "" assert lineage._fr_url(None, "", 5, 1) == ""
assert lineage._fr_url(None, "K", 0, 1) == "" assert lineage._fr_url(None, "K", 0, 1) == ""
def test_rule_kind_degrades_to_rule_when_bib_frlink_raises(self, store):
"""``_rule_kind`` must never say ``final``/``proposed`` on a
failure path — a label falsely claiming a rule's kind is a false
statement of policy (ruling in ``rule_label``'s docstring)."""
with patch("bib.frlink.rule_kind", side_effect=RuntimeError("boom")):
kind = lineage._rule_kind(store, "YBM4IZUS", 918_273)
assert kind == "rule"
class TestCachingAndScopedUrlResolution: class TestCachingAndScopedUrlResolution:
"""Ruling: Store.get results are cached per item_key (not just per """Ruling: Store.get results are cached per item_key (not just per
@@ -1205,6 +1332,32 @@ class TestCachingAndScopedUrlResolution:
assert len(seen_keys) == len(set(seen_keys)) # never re-fetched assert len(seen_keys) == len(set(seen_keys)) # never re-fetched
class TestOnDemandEventsCache:
def test_second_call_with_the_same_code_and_mtime_hits_the_cache(self):
calls: list[str] = []
def fake_lineage(cur, store, code):
calls.append(code)
return [_ev(code, 2021, "created")]
with patch("pfs.lineage.lineage", side_effect=fake_lineage):
first = lineage._on_demand_events(None, None, 55, "99490")
second = lineage._on_demand_events(None, None, 55, "99490")
assert calls == ["99490"] # the underlying lookup ran only once
assert first == second == [_ev("99490", 2021, "created")]
class TestCollectEvents:
def test_missing_table_is_swallowed_as_no_rows(self):
bare = duckdb.connect(":memory:") # pfs.code_event doesn't exist
assert lineage._collect_events(bare, None, 0, ["99490"], 0) == []
def test_a_real_error_propagates(self):
with patch("llm.lineage.read_events", side_effect=ValueError("boom")):
with pytest.raises(ValueError):
lineage._collect_events(None, None, 0, ["99490"], 0)
class TestConcurrentLineage: class TestConcurrentLineage:
"""C1/Ruling B14 — ``llm.lineage._store()`` must be thread-local: """C1/Ruling B14 — ``llm.lineage._store()`` must be thread-local:
bib.Store's sqlite connection is check_same_thread=True, and /chat bib.Store's sqlite connection is check_same_thread=True, and /chat
@@ -1501,6 +1654,20 @@ class TestLineageSources:
ev = _lineage_evidence([e], max_prompt_rows=0) ev = _lineage_evidence([e], max_prompt_rows=0)
assert lineage_sources(store, ev, max_items=6) == [] assert lineage_sources(store, ev, max_items=6) == []
def test_never_raises_when_selecting_prompt_events_fails(self, store):
"""A failure before the candidate loop even starts (selecting/
sorting the prompt events) must degrade to ``[]``, the same
contract ``lineage_evidence`` itself has — never break the chat
over a sources lookup."""
e = lineage._to_lineage_event(
store,
_ev("99490", 2015, "adopted_cpt", item_key="DE2VH9PD", p_id=1251),
mtime=0,
)
ev = _lineage_evidence([e])
with patch("llm.lineage._select_for_prompt", side_effect=RuntimeError("boom")):
assert lineage_sources(store, ev, max_items=6) == []
def _insert_extra_anchor(store, key, year, p_id, text="extra text"): def _insert_extra_anchor(store, key, year, p_id, text="extra text"):
store.con.execute( store.con.execute(
@@ -1663,6 +1830,18 @@ class TestLineageSourcesRulingB10:
assert len(out) == 1 assert len(out) == 1
assert out[0]["label"] == e.label assert out[0]["label"] == e.label
def test_element_diff_anchor_failure_is_skipped_not_raised(self, store):
"""One element-diff anchor failing to build (``_labeled_rule_source``
raising) must be logged and skipped, not propagate — mirrors the
event loop's own never-raise contract just above."""
diff = _diff("DE2VH9PD", 1251, "CY2015 PFS final ¶1251")
ev = _lineage_evidence([], element_diffs=[diff])
with patch(
"llm.lineage._labeled_rule_source", side_effect=RuntimeError("boom")
):
out = lineage_sources(store, ev, max_items=8)
assert out == []
class TestReconcileLabels: class TestReconcileLabels:
"""``_reconcile_labels`` — a retrieved/cited source for the same FR """``_reconcile_labels`` — a retrieved/cited source for the same FR
@@ -1786,6 +1965,19 @@ class TestKnownCodeFilter:
bare = duckdb.connect(":memory:") bare = duckdb.connect(":memory:")
assert lineage._known_codes(bare, 2) == frozenset() assert lineage._known_codes(bare, 2) == frozenset()
def test_known_codes_logs_and_continues_on_a_real_error(self):
"""A missing table (I4) is expected and silent; any other
failure is still swallowed (the known-code filter must never
break the chat) but is worth a log line — this exercises that
branch specifically, distinct from the missing-table path
above."""
class _FakeCur:
def execute(self, sql):
raise ValueError("boom")
assert lineage._known_codes(_FakeCur(), 777_001) == frozenset()
def test_priority_kinds_always_kept_non_priority_trimmed(self): def test_priority_kinds_always_kept_non_priority_trimmed(self):
priority = LineageEvent( priority = LineageEvent(
"99490", 2021, "created", (), (), "[L]", "K", 1, 0, "", "fr", True, "" "99490", 2021, "created", (), (), "[L]", "K", 1, 0, "", "fr", True, ""

View File

@@ -1,6 +1,7 @@
"""llm.rag — multi-collection retrieval + grounded streaming answer.""" """llm.rag — multi-collection retrieval + grounded streaming answer."""
import json import json
import logging
from dataclasses import replace from dataclasses import replace
from datetime import date from datetime import date
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
@@ -301,6 +302,25 @@ class TestRetrieve:
mock_balance.assert_called_once() mock_balance.assert_called_once()
mock_blend.assert_not_called() mock_blend.assert_not_called()
@patch("llm.rag.PoolEmbeddings")
@patch("llm.index.vectorstore")
def test_zero_k_for_a_kind_skips_its_collection_entirely(self, mock_vs, MockEmb):
# cfg.k_per_kind["corpus"] == 0 — _hits's "if k <= 0: continue"
# must skip that collection without ever opening its store.
MockEmb.return_value.embed_query.return_value = [0.0]
cfg0 = replace(CFG, k_per_kind={"comment": 0, "rule": 1, "corpus": 0})
stores = {}
def factory(collection, cfg, pool):
s = MagicMock()
s.similarity_search_with_score_by_vector.return_value = []
stores[collection] = s
return s
mock_vs.side_effect = factory
retrieve("q", cfg=cfg0, pool=MagicMock(), now=NOW)
assert set(stores) == {"rules"}
class TestIsHistoryQuestion: class TestIsHistoryQuestion:
@pytest.mark.parametrize( @pytest.mark.parametrize(
@@ -332,6 +352,18 @@ class TestIsHistoryQuestion:
assert is_history_question("the 2026 payment for 99490") is False assert is_history_question("the 2026 payment for 99490") is False
class TestDocketRuleYear:
"""``_docket_rule_year`` degrades to era 0 for any unparsable
``comment_end_date`` rather than raising — the caller (``era_of``)
already treats 0 as "unknown, sorted last"."""
def test_unparsable_string_returns_zero(self):
assert rag._docket_rule_year("not-a-date") == 0
def test_none_returns_zero(self):
assert rag._docket_rule_year(None) == 0
class TestEraOf: class TestEraOf:
def _hit(self, **md): def _hit(self, **md):
return Hit(text="t", metadata={k: str(v) for k, v in md.items()}, distance=0.1) return Hit(text="t", metadata={k: str(v) for k, v in md.items()}, distance=0.1)
@@ -381,6 +413,24 @@ class TestEraOf:
h = self._hit(kind="comment", docket="CMS-9999-1", date="2019-05-01") h = self._hit(kind="comment", docket="CMS-9999-1", date="2019-05-01")
assert era_of(h) == 2019 assert era_of(h) == 2019
def test_comment_empty_docket_short_circuits_without_store_lookup(self):
# "" is falsy — _docket_year's "if not docket_id: return 0" fires
# before ever touching _bib_store, so era_of falls straight back
# to the hit's own date year.
with patch("llm.rag._bib_store") as mock_store:
h = self._hit(kind="comment", docket="", date="2018-05-01")
assert era_of(h) == 2018
mock_store.assert_not_called()
@patch("llm.rag._bib_store", side_effect=RuntimeError("store boom"))
def test_bib_store_failure_degrades_to_own_date_year(self, mock_store, caplog):
# A broken bib.Store (open failure, bad mtime, bad query) must
# never break era-balanced retrieval over one docket lookup —
# it's logged and era_of falls back to the comment's own date.
h = self._hit(kind="comment", docket="CMS-2024-1", date="2024-03-01")
assert era_of(h) == 2024
assert "docket era lookup failed" in caplog.text
@patch("llm.rag._bib_store") @patch("llm.rag._bib_store")
def test_comment_lookup_is_not_repeated_once_cached(self, mock_store): def test_comment_lookup_is_not_repeated_once_cached(self, mock_store):
from bib.dockets import Docket from bib.dockets import Docket
@@ -678,6 +728,36 @@ class TestBuildMessagesBudget:
assert msgs[1]["content"] == target assert msgs[1]["content"] == target
assert msgs[1]["content"].count("[C") == 8 # cited untouched — not its turn assert msgs[1]["content"].count("[C") == 8 # cited untouched — not its turn
def test_cited_and_manual_drop_after_earlier_blocks_hit_floor(self):
# Push the drop order all the way through: lineage-source (>4),
# retrieved (>6), cited (>8), manual (>1) — every source_steps
# reassignment branch, including "cited" and "manual".
retrieved = [_bmsrc(f"R{i}", snippet="R" * 20) for i in range(10)]
cited = [_bmsrc(f"C{i}", snippet="C" * 20) for i in range(10)]
ls = [_bmsrc(f"LS{i}", snippet="L" * 20) for i in range(8)]
manual = [_bmsrc(f"M{i}", snippet="M" * 20) for i in range(5)]
sources = retrieved + cited + ls + manual
target = build_messages(
"q",
retrieved[:6] + cited[:8] + ls[:4] + manual[:1],
n_retrieved=6,
n_cited=8,
n_lineage_sources=4,
n_manual=1,
)[1]["content"]
msgs = build_messages(
"q",
sources,
n_retrieved=10,
n_cited=10,
n_lineage_sources=8,
n_manual=5,
budget_chars=len(target),
)
assert msgs[1]["content"] == target
assert msgs[1]["content"].count("[C") == 8
assert msgs[1]["content"].count("[M") == 1
def test_valuation_rows_capped_as_a_last_resort(self): def test_valuation_rows_capped_as_a_last_resort(self):
from llm.evidence import ValuationEvidence from llm.evidence import ValuationEvidence
@@ -909,6 +989,57 @@ class TestStreamAnswer:
body = client.stream.call_args.kwargs["json"] body = client.stream.call_args.kwargs["json"]
assert "Valuation (authoritative" in body["messages"][1]["content"] assert "Valuation (authoritative" in body["messages"][1]["content"]
@patch("llm.rag._manual_sources", return_value=[])
@patch("llm.rag._engine")
@patch("llm.rag.code_cited_sources")
@patch("llm.rag.valuation_evidence")
@patch("llm.rag.lineage_evidence", return_value=None)
@patch("llm.rag.httpx.Client")
@patch("llm.rag.retrieve")
def test_conflicting_cited_snippet_is_dropped_and_logged(
self,
mock_retrieve,
MockClient,
_lin,
mock_ev,
mock_cited,
mock_engine,
_manual,
caplog,
):
# A code-cited excerpt (not filtered by retrieve()'s own
# exclude_phrases, since it never goes through retrieve) that
# uses "CCM" for cardiac contractility modulation must still be
# dropped by the final _drop_conflicts pass, and the drop logged.
from llm.evidence import ValuationEvidence
clean = {
"id": "C1",
"label": "C1",
"kind": "rule",
"snippet": "CCM services require a comprehensive care plan",
"score": 0.1,
}
conflicting = {
"id": "C2",
"label": "C2",
"kind": "rule",
"snippet": "Cardiac Contractility Modulation (CCM) pulse generator",
"score": 0.2,
}
mock_retrieve.return_value = []
mock_cited.return_value = [clean, conflicting]
mock_ev.return_value = ValuationEvidence((), ("CCM",), (), ())
client = MockClient.return_value.__enter__.return_value
resp = client.stream.return_value.__enter__.return_value
resp.iter_lines.return_value = iter(['{"message":{"content":"x"},"done":true}'])
with caplog.at_level(logging.INFO, logger="llm.rag"):
events = list(stream_answer("CCM?", cfg=CFG, pool=self._pool()))
assert events[-2]["sources"] == [clean]
assert "dropped 1 excerpt(s) using a family acronym" in caplog.text
@patch("llm.rag._manual_sources", return_value=[]) @patch("llm.rag._manual_sources", return_value=[])
@patch("llm.rag._engine") @patch("llm.rag._engine")
@patch("llm.rag.code_cited_sources") @patch("llm.rag.code_cited_sources")
@@ -1250,6 +1381,18 @@ class TestManualSourcesWrapper:
cur.close.assert_called_once() cur.close.assert_called_once()
class TestHasConflict:
def test_no_phrases_returns_false(self):
from llm.rag import _has_conflict
assert _has_conflict("some text mentioning ccm", ()) is False
def test_empty_text_returns_false(self):
from llm.rag import _has_conflict
assert _has_conflict("", ("cardiac contractility modulation",)) is False
class TestAcronymConflicts: class TestAcronymConflicts:
"""CCM = chronic care management to the family registry, cardiac """CCM = chronic care management to the family registry, cardiac
contractility modulation to the CY2027 NPRM's "CCM code family"; contractility modulation to the CY2027 NPRM's "CCM code family";

View File

@@ -8,7 +8,13 @@ import duckdb
import pytest import pytest
from pfs.descriptors import DescriptorRun, Para, descriptor_runs from pfs.descriptors import DescriptorRun, Para, descriptor_runs
from pfs.extract import Extraction, extract_code, extract_run, extract_text from pfs.extract import (
Extraction,
_cpt_elements,
extract_code,
extract_run,
extract_text,
)
STEM = Para( STEM = Para(
"JJ6AM5HJ", "JJ6AM5HJ",
@@ -354,6 +360,96 @@ class TestExtractCodeCpt:
assert len(consent_rows) == 1 assert len(consent_rows) == 1
assert consent_rows[0].source == "cpt" assert consent_rows[0].source == "cpt"
def test_classifier_places_an_unmatched_cpt_element_line(self, store, con):
# Mirrors TestClassifier's FR-side coverage, but for a required-
# elements list item nothing deterministic can place — the
# classify(text, _CHOICES) branch inside _cpt_elements.
_insert_cpt_code(
con,
2024,
"GQGTPGYV",
"99490",
stem="Chronic care management services",
elements=[
"Something the vocabulary does not know about at all;",
],
tail="per calendar month.",
)
classify = lambda text, choices: ( # noqa: E731
"community-coordination" if "vocabulary" in text else None
)
x = extract_code(store, con, "99490", classify=classify)
cpt_rows = [r for r in x.rows if r.source == "cpt"]
row = next(r for r in cpt_rows if r.value == "community-coordination")
assert row.type == "activity" and row.item_key == "GQGTPGYV"
assert not x.reviews
def test_blank_cpt_element_after_stripping_punctuation_is_skipped(self, store, con):
# An element list item that is only trailing punctuation (a
# stray ";" from the codebook's own formatting) must not become
# a row or a review once stripped down to "".
_insert_cpt_code(
con,
2024,
"GQGTPGYV",
"99490",
stem="Chronic care management services",
elements=["Consent;", ";", " ; "],
tail="per calendar month.",
)
x = extract_code(store, con, "99490")
cpt_rows = [r for r in x.rows if r.source == "cpt"]
assert {r.value for r in cpt_rows} >= {"consent", "calendar-month"}
assert not x.reviews
class TestCptElementsErrors:
class _RaisingCon:
def execute(self, *args, **kwargs):
raise RuntimeError("disk I/O error")
def test_non_missing_table_error_propagates(self):
# is_missing_table_error only swallows a DuckDB "Catalog ...
# does not exist" error — any other failure querying
# pfs.cpt_code (a real I/O error, a corrupt replica) must
# propagate rather than silently degrade to no CPT rows.
with pytest.raises(RuntimeError, match="disk I/O error"):
_cpt_elements(self._RaisingCon(), "99490")
class TestExtractCodeRvu:
@pytest.fixture
def store(self):
con = _sqlite_store_con()
yield _Store(con)
con.close()
@pytest.fixture
def con(self):
c = _duckdb_con()
yield c
c.close()
def test_rvu_description_rows_are_merged_per_year(self, store, con):
# extract_code's final loop over rvu_descriptions() — a source
# not exercised by the hcpcs-long-description tests above. Each
# year's description names a distinct element so neither is
# dropped by the (type, value, detail) dedupe merged.setdefault
# already applies within a single source.
con.execute(
"INSERT INTO pfs.rvu VALUES (?,?,?,?,?,?)",
["99490", "", "… per calendar month, consent …", "A", 1.0, 2023],
)
con.execute(
"INSERT INTO pfs.rvu VALUES (?,?,?,?,?,?)",
["99490", "", "… provide 24/7 access for urgent needs …", "A", 1.0, 2024],
)
x = extract_code(store, con, "99490")
rvu_rows = [r for r in x.rows if r.source == "rvu"]
by_value = {r.value: r for r in rvu_rows}
assert by_value["consent"].year == 2023
assert by_value["24-7-access"].year == 2024
class TestText: class TestText:
def test_hcpcs_long_description(self): def test_hcpcs_long_description(self):

View File

@@ -25,6 +25,7 @@ from pfs.families import (
MAX_FAMILY_EXPAND, MAX_FAMILY_EXPAND,
Detection, Detection,
Family, Family,
_connected,
_cpt_edges, _cpt_edges,
_trie_alternation, _trie_alternation,
cpt_groups, cpt_groups,
@@ -281,6 +282,26 @@ class TestDetectDerivedFamilies:
assert "NARROW-FAMILY" in d.families assert "NARROW-FAMILY" in d.families
assert d.wide == () assert d.wide == ()
def test_detect_codes_skips_stale_index_entry_not_in_families(
self, restore_families
):
# Defensive: `_CODE_INDEX` can name a family key that `FAMILIES` no
# longer has (a caller mutated `FAMILIES` directly without calling
# `rebuild_index`, or a race with `refresh_from`) — `detect_codes`
# must skip it (`fam is None: continue`) rather than raise, though
# the stale key still shows up in `families` (built from the index
# before the FAMILIES lookup) and the family's codes are simply
# never expanded into `codes`.
restore_families.FAMILIES["GHOST"] = Family(
"GHOST", "Ghost Family", ("77777",), ("ghost family",)
)
rebuild_index()
del restore_families.FAMILIES["GHOST"]
d = detect_codes("value of 77777")
assert d.explicit == ("77777",)
assert d.families == ("GHOST",) # still named via the stale index
assert d.codes == ("77777",) # not expanded: FAMILIES.get is None
def test_hand_families_regression(self): def test_hand_families_regression(self):
# Unchanged from TestDetectCodes — the phrase/index refactor must # Unchanged from TestDetectCodes — the phrase/index refactor must
# not alter a single hand-family detection. # not alter a single hand-family detection.
@@ -429,6 +450,34 @@ class TestDerive:
r.name == HAND_FAMILIES[r.key].name for r in rows if r.key in HAND_FAMILIES r.name == HAND_FAMILIES[r.key].name for r in rows if r.key in HAND_FAMILIES
) )
def test_replaces_event_alone_yields_successor_role(self):
# An isolated `replaces` event — no `replaced_by` event and no
# add-on signal for the successor code itself — must land the
# "successor" role branch (elif "replaces" in kinds), not fall
# through to "base".
elements = {}
events = {"88888": [_ev("88888", 2022, "replaces", frm="77777")]}
descriptions = {"88888": "Successor widget", "77777": "Predecessor widget"}
rows = derive_families(elements, events, descriptions)
by_code = {r.code: r for r in rows}
assert by_code["88888"].role == "successor"
def test_defined_by_reference_to_relation_merges_codes(self):
# `_LINK_RELATIONS` ("defined-by-reference-to") is a narrow,
# direct family-membership edge distinct from the lineage-event
# edges above — a code whose only connection to another is this
# relation element must still land in the same family.
elements = {
"12121": [
_el("12121", "relation", "defined-by-reference-to", "34343"),
],
"34343": [],
}
descriptions = {"12121": "Alpha reference", "34343": "Beta referent"}
rows = derive_families(elements, {}, descriptions)
keys = {r.code: r.key for r in rows}
assert keys["12121"] == keys["34343"]
def test_multi_code_replaced_by_does_not_merge(self): def test_multi_code_replaced_by_does_not_merge(self):
# #687 ruling 16: a `replaced_by` event naming more than one # #687 ruling 16: a `replaced_by` event naming more than one
# to_codes is ambiguous (a blanket "these codes are replaced by # to_codes is ambiguous (a blanket "these codes are replaced by
@@ -748,6 +797,31 @@ class TestCptGroups:
assert groups["20003"][0] == "NEW-OR-ESTABLISHED-PATIENT@HOME-VISITS" assert groups["20003"][0] == "NEW-OR-ESTABLISHED-PATIENT@HOME-VISITS"
assert groups["20001"][0] != groups["20003"][0] assert groups["20001"][0] != groups["20003"][0]
def test_three_way_title_collision_under_the_same_parent_falls_back_to_a_suffix(
self,
):
# Two collisions is enough to disambiguate on parent title
# (`@PARENT`) alone, but a *third* heading sharing both the same
# title AND the same immediate parent collides with that
# already-taken `@PARENT` key too — only then does the numbered
# `-2`/`-3` suffix loop run.
sections = [
_cpt_section("sec_a", ("Chapter One", "ParentX", "Repeated")),
_cpt_section("sec_b", ("Chapter Two", "ParentX", "Repeated")),
_cpt_section("sec_c", ("Chapter Three", "ParentX", "Repeated")),
]
codes = [
_cpt_code("81001", "sec_a"),
_cpt_code("81002", "sec_a"),
_cpt_code("81003", "sec_b"),
_cpt_code("81004", "sec_b"),
_cpt_code("81005", "sec_c"),
_cpt_code("81006", "sec_c"),
]
groups = cpt_groups(codes, sections)
keys = {groups[c][0] for c in ("81001", "81003", "81005")}
assert keys == {"REPEATED", "REPEATED@PARENTX", "REPEATED@PARENTX-2"}
class TestDeriveCpt: class TestDeriveCpt:
def test_ccm_hand_family_wins_merged_component_note_is_own_heading(self): def test_ccm_hand_family_wins_merged_component_note_is_own_heading(self):
@@ -995,6 +1069,38 @@ class TestDeriveCpt:
assert by_code["G2058"].since == 2020 and by_code["G2058"].until == 2021 assert by_code["G2058"].since == 2020 and by_code["G2058"].until == 2021
class TestConnectedHelper:
"""``_connected`` tested in isolation — the same way ``TestCptEdgesHelper``
below tests ``_cpt_edges`` directly. In practice ``derive_families``'s
hand-family-split path always calls it with an empty ``unreached`` list
(every code in a >= 2-hand-spanning component is provably reachable by
some hand's own BFS — the nearest seed's shortest path to any node
never crosses a *different* hand's seed, so nothing is ever left over;
confirmed by exhaustive random-graph fuzzing, 300k trials, 0
counterexamples), so the BFS body here is only reachable by calling
``_connected`` directly."""
def test_bfs_walks_multi_node_components_and_ignores_outside_edges(self):
adj = {
"A": {"B"},
"B": {"A", "C"},
"C": {"B"},
"D": {"E"},
"E": {"D"},
"F": set(),
}
comps = _connected(["A", "B", "C", "D", "E", "F"], adj)
comp_sets = sorted((sorted(c) for c in comps), key=lambda c: c[0])
assert comp_sets == [["A", "B", "C"], ["D", "E"], ["F"]]
def test_edges_to_nodes_outside_the_set_do_not_count(self):
# "X" is adjacent to "A" but isn't in `nodes` — the component must
# not include it.
adj = {"A": {"B", "X"}, "B": {"A"}, "X": {"A"}}
comps = _connected(["A", "B"], adj)
assert [sorted(c) for c in comps] == [["A", "B"]]
class TestCptEdgesHelper: class TestCptEdgesHelper:
def test_skips_only_edges_between_distinct_headings(self): def test_skips_only_edges_between_distinct_headings(self):
# #687 controller review (Ruling C11), tested against the helper # #687 controller review (Ruling C11), tested against the helper
@@ -1223,6 +1329,27 @@ class TestCptPresenceC12:
assert by_code["90003"].since == 2024 assert by_code["90003"].since == 2024
assert by_code["90003"].until is None assert by_code["90003"].until is None
def test_empty_years_tuple_in_cpt_presence_is_skipped(self):
# C12: a code present as a `cpt_presence` key with an *empty*
# years tuple (`not years`) must be skipped rather than crash on
# `min(())`/`max(())` — it simply gets no cpt-derived since/until
# and falls back to the event-based (here: absent) since/until.
sections = [_cpt_section("sec_a", ("Chapter", "Heading A"))]
cpt_codes = [_cpt_code("90001", "sec_a", year=2024)]
cpt_presence = {"90001": (2019, 2021, 2022, 2024), "90099": ()}
rows = derive_families(
{},
{},
{"90099": "Empty years widget"},
cpt_codes=cpt_codes,
cpt_sections=sections,
cpt_presence=cpt_presence,
)
by_code = {r.code: r for r in rows}
assert by_code["90001"].since == 2019
assert by_code["90099"].since is None
assert by_code["90099"].until is None
def test_without_cpt_presence_falls_back_to_cpt_codes_alone(self): def test_without_cpt_presence_falls_back_to_cpt_codes_alone(self):
# Pre-C12 behavior preserved when the caller doesn't have a # Pre-C12 behavior preserved when the caller doesn't have a
# multi-edition presence map to give. # multi-edition presence map to give.

View File

@@ -7,6 +7,7 @@ from __future__ import annotations
import duckdb import duckdb
import pytest import pytest
import pfs.guidance as guidance_mod
from bib.item import Manual, Regulation, Rule from bib.item import Manual, Regulation, Rule
from bib.store import Store from bib.store import Store
from pfs.codetables import ( from pfs.codetables import (
@@ -16,13 +17,15 @@ from pfs.codetables import (
write_cpt_edition, write_cpt_edition,
write_guidance, write_guidance,
) )
from pfs.cpt_model import CptCode, CptEdition, CptSection from pfs.cpt_model import CptCode, CptEdition, CptInstruction, CptSection
from pfs.guidance import ( from pfs.guidance import (
BARE_CFR_RE, BARE_CFR_RE,
CFR_RE, CFR_RE,
IOM_RE, IOM_RE,
MLN_RE, MLN_RE,
_cfr_refs, _cfr_refs,
_extract,
_iom_refs,
build, build,
dedupe, dedupe,
harvest, harvest,
@@ -189,6 +192,26 @@ class TestMlnRegex:
assert MLN_RE.search("mln 1234567").group(1) == "1234567" assert MLN_RE.search("mln 1234567").group(1) == "1234567"
class TestIomRefsUnmappedManual:
def test_manual_name_missing_from_pub_map_is_dropped(self, monkeypatch):
# IOM_RE's manual-name alternation only ever captures one of the
# six names in _MANUAL_PUB, so "not pub" (guidance.py:201) can't
# fire through the public regex today — it's a defensive guard
# against the map and regex drifting apart. Exercise it directly
# by shrinking the map out from under a real manual-name match.
monkeypatch.setattr(guidance_mod, "_MANUAL_PUB", {})
text = "See the Benefit Policy Manual, Chapter 5, Section 20 for details."
assert _iom_refs(text) == []
class TestExtractMln:
def test_mln_reference_yields_unresolved_row(self, store):
s, *_rest = store
rows = _extract("See MLN907166 for the telehealth fact sheet.", s)
mln_rows = [r for r in rows if r[0] == "mln"]
assert mln_rows == [("mln", "MLN 907166", "")]
# ── resolvers ─────────────────────────────────────────────────────── # ── resolvers ───────────────────────────────────────────────────────
@@ -438,6 +461,126 @@ class TestHarvestCpt:
assert r.p_id_src == 0 assert r.p_id_src == 0
assert r.page_src == 0 assert r.page_src == 0
def test_empty_codes_returns_empty(self, store, con):
s, *_rest = store
assert harvest_cpt(con, s, (), family="CCM") == []
def test_non_missing_table_error_is_reraised(self, store, con, monkeypatch):
# cpt_years is imported inside harvest_cpt at call time, so
# patching pfs.codetables.cpt_years is visible to it. A plain
# ValueError doesn't look like a DuckDB "Catalog ... does not
# exist" error, so it must propagate rather than degrade to [].
s, *_rest = store
def _boom(_con):
raise ValueError("boom")
monkeypatch.setattr("pfs.codetables.cpt_years", _boom)
with pytest.raises(ValueError, match="boom"):
harvest_cpt(con, s, ("99490",), family="CCM")
def test_no_years_ingested_returns_empty(self, store, con):
# `con` has ensure_tables run (empty pfs.cpt_section) but no
# edition written, so cpt_years(con) returns [] without raising
# — the "if not years: return []" branch, distinct from the
# missing-table except branch covered via the `bare` connection
# in TestBuild below.
s, *_rest = store
assert harvest_cpt(con, s, ("99490",), family="CCM") == []
def test_section_with_no_guideline_text_is_skipped(self, store, con):
# A code mapped to a real section, but the section carries no
# guideline text (e.g. a heading without a trailing "*" range in
# the TOC) — the guideline loop's "continue" must skip it rather
# than call _extract("", ...).
s, *_rest = store
edition = CptEdition(
year=2024,
sections=(
CptSection(
sec_id="sec_1",
level=2,
title="Chronic Care Management Services",
path=("Evaluation and Management",),
code_lo="99490",
code_hi="99490",
guideline="",
),
),
codes=(_cpt_code("99490", "sec_1"),),
instructions=(),
references=(),
crosswalks=(),
lists=(),
alternates=(),
)
write_cpt_edition(con, edition, "CPTED2024")
assert harvest_cpt(con, s, ("99490",), family="CCM") == []
def test_instructions_filtered_by_code_kind_and_keyword(self, store, con):
# Exercises every branch of the instructions loop: a "use-with"
# kind is skipped even for a targeted code, a "see" instruction
# for a code outside the family is skipped, a "see" instruction
# for the right code but with no CFR/Medicare/Chapter keyword is
# skipped, and a "see" instruction with a keyword produces a row.
s, sec_key, _manual_key = store
edition = CptEdition(
year=2024,
# cpt_years reads distinct years off pfs.cpt_section, so at
# least one section row is needed for the edition's year to
# be visible at all — its guideline is irrelevant here since
# no code maps to it (codes=()).
sections=(
CptSection(
sec_id="sec_0",
level=2,
title="Unrelated Section",
path=("Unrelated Section",),
code_lo="",
code_hi="",
guideline="",
),
),
codes=(),
instructions=(
CptInstruction(
code="99490",
kind="use-with",
text="(Do not report 99490 in conjunction with 42 CFR 410.78)",
targets=(),
),
CptInstruction(
code="99439",
kind="see",
text="(See 42 CFR 410.78 for conditions of payment)",
targets=(),
),
CptInstruction(
code="99490",
kind="see",
text="(See the local coverage determination for details)",
targets=(),
),
CptInstruction(
code="99490",
kind="see",
text="(See 42 CFR 410.78(a)(3) for conditions of payment)",
targets=(),
),
),
references=(),
crosswalks=(),
lists=(),
alternates=(),
)
write_cpt_edition(con, edition, "CPTED2024", force=True)
rows = harvest_cpt(con, s, ("99490",), family="CCM")
assert len(rows) == 1
r = rows[0]
assert (r.code, r.kind, r.locator) == ("99490", "cfr", "42 CFR 410.78(a)(3)")
assert r.item_key == sec_key
assert r.item_key_src == "CPTED2024"
# ── build (FR + CPT, no CPT tables ingested) ───────────────────────── # ── build (FR + CPT, no CPT tables ingested) ─────────────────────────