Files
stack/docs/superpowers/specs/2026-09-09-code-family-longitudinal-design.md
kert 28eb50943b
Some checks failed
CI / lint (push) Successful in 28s
CI / notebooks-smoke (push) Successful in 1m37s
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 48s
Infra CI / zotero (push) Successful in 15s
Infra CI / docs (push) Successful in 1m42s
Deploy / report (push) Has been cancelled
CI / test (push) Has been cancelled
Infra CI / llm (push) Has been cancelled
Infra CI / mc (push) Has been cancelled
Infra CI / api (push) Has been cancelled
docs(spec): P49/P50 code families as first-class objects — element model, lineage anchors, longitudinal chat, legal epidemiology (refs #684-#696)
Design for milestone 49 (element vocabulary, extractor, code_event lineage table,
derived families, corpus-wide code:/family: anchors, IOM/eCFR crosswalk, reaction
series, lineage SSE event + era-balanced retrieval, golden eval set) and the scoped
milestone 50 (exposure calendar, Part B PUF, BLS OEWS/CES, event-study notebook).
Every claim is anchored to corpus paragraphs measured 2026-09-09.

Claude-Session: https://claude.ai/code/session_01Aum3pEMAM3yQVdFSdVe6Gc
2026-09-09 10:32:20 -04:00

105 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Code families as first-class objects — element model, lineage anchors, longitudinal chat, legal epidemiology
Tracker: milestone P49 (this spec) and milestone P50 (outcome series). Builds on P48 (`pfs/families.py`, `codes` chunk metadata, valuation event), P47 (dockets/seals), P40/P41 (FR + eCFR jump links), P35 (closed-vocabulary RAG tagging).
## Why
When the physician fee schedule decides coding and payment for a service, the unit of decision is a **code** or a **code family**: a bundle of logical elements that say what the service is and is not. CMS says this itself in the Medicare telehealth review process, where Step 3 is literally "Review the elements of the service as described by the HCPCS code" (CY2026 NPRM, 90 FR 32389, item `2KVJ2HKX` ¶398). The corpus already holds every layer of the record for those elements — 84 PFS rules with paragraph anchors back to CY2002, RVU files 2015–2026, ten comment dockets 2017–2026, 177 IOM chapters, 699 eCFR sections — but nothing ties the layers together *per code*. The chat can price a code (P48) but cannot answer "where did this code come from, what replaced what, how did the elements change, who objected, and what does the manual say".
The goal of P49 is that the chat, and the notebooks, can build a **longitudinal view of any code or code set payable under the PFS**: creation, predecessors and successors, element changes, valuation history, sub-regulatory guidance, and public reaction, each step anchored to a citable paragraph. P50 then joins that timeline to outcome series (utilization, BLS wages, industry markets) so the fee schedule can be studied as law with effects — legal epidemiology.
## What the corpus already shows (measured 2026-09-09)
**The "elements" construct is explicit in the record.**
- CPT 99490 was adopted for CY2015 in place of a CMS G-code; the FR prints its descriptor as a stem plus enumerated required elements — two or more chronic conditions expected to last ≥12 months; conditions placing the patient at significant risk; comprehensive care plan established/implemented/revised/monitored (CY2015 final rule `DE2VH9PD` ¶1244–1247, 79 FR 67716). The commenter-vs-CMS argument was about a single element (per calendar month vs per 30 days, ¶1248–1251).
- HCPCS G0556–G0558 (APCM, CY2025) print as a stem plus ~20 elements and `++` sub-elements: consent, initiation visit, 24/7 access, continuity, alternative care delivery, comprehensive care management, electronic care plan, transitions, community coordination, asynchronous communication, population data analysis, risk stratification, performance measurement (CY2025 final rule `JJ6AM5HJ` ¶1163–1185, 89 FR 97864–97865). G0557 is defined *by reference* to G0556's elements plus a population element (¶1186).
- The telehealth process (first printed in the CY2024 rules, `44F3F6VU`/`JJV3R692`, and repeated every year since) is a three-step element test: Step 1 separately payable under the PFS; Step 2 subject to §1834(m), i.e. is the service "in whole or in part, inherently a face-to-face service"; Step 3 each element capable of being furnished via an interactive telecommunications system as defined in § 410.78(a)(3) (`2KVJ2HKX` ¶394, ¶396, ¶398).
**Lineage is recoverable from the record but only by hand today.**
- G-code → CPT 99490 (CY2015 final `DE2VH9PD` ¶1249–1251).
- G2058 (RVU file 2020 only) → CPT 99439 (2021–): "descriptor identical to G2058", temporary crosswalk value (CY2021 final `YBM4IZUS` ¶1578, 85 FR 84639; ¶686, ¶2369).
- G2064/G2065 (2020–2021) → CPT 99424/99426 (2022–), including the knock-on into the RHC/FQHC G0511 rate (CY2022 final `JE7KYBW3` ¶1100/¶1111, 86 FR 65118; CY2023 final `MZ24MX5S` ¶1305).
- 99441–99443 deleted by the CPT panel, last in the RVU file 2024, replaced by 98008–98015 / 98016 (CY2025 rules `XFGGRBDH`/`JJ6AM5HJ` ¶489/¶765).
- Status changes visible in `pfs.rvu`: 99487/99489 B→A, 99497/99498 I→A, 99091 B→A, G2211 B→A.
- 99490 is discussed in 27 distinct rule documents from CY2015 final to CY2027 proposed; 284 rule chunks already carry `codes ∋ 99490`.
**Public reaction is in the corpus, in two forms.**
- Dockets CMS-2017-0092 … CMS-2026-2377 (10 dockets, 918,484 comment chunks). Abstract-level hits per docket: CCM 5/19/6/11/24/2/5/10/174/27; APCM 19 (CMS-2024-0256, the year it was proposed), 25, 5; G2211 3,353 in CMS-2023-0121 (the year it was activated), then 146 and 190. Telehealth: ~10–13k per docket 2020–2023.
- Before 2017 the only public-comment record is the FR itself: the `Comment:` / `Response:` paragraph pairs (e.g. `DE2VH9PD` ¶1244/¶1249, ¶1255) — a proxy series back to CY2002.
**Sub-regulatory guidance is present but unlinked.** 177 IOM chapters (MCPM ch. 12 `SPJB4FX2` and MBPM ch. 15 `YQFGB6JS` = 988 corpus chunks), 699 eCFR sections (§ 410.78 telehealth `SAJSGG28`, § 410.26 incident-to `Q78GP4Z9`, § 414.22 `T7PXFV4S`), MLN909188 CCM/PCM/APCM booklet (`PV8APQ4A`). None carries a `code:` tag; `comments` and `corpus` chunks carry no `codes` metadata (0 of 1.15 M), only `rules` do (29,595 of 67,340).
**Scale of "any code".** `pfs.rvu` has 18,641 distinct HCPCS over 2015–2026; 8,572 are status A/R/T in CY2026 (7,087 in 2015). The hand registry in `pfs/families.py` covers 5 families / 17 codes.
## Decisions
1. **Elements are a closed, typed vocabulary**, not free text. Element *types* are fixed in code; element *values* are a curated list that grows by review (P35 pattern: local model proposes from a closed list, humans accept). Types: `actor` (clinical staff directed by / personally by physician or QHP; RHC/FQHC), `time` (threshold minutes, "each additional"), `period` (calendar month, 30 days, per visit, 14/7 days post-discharge), `population` (≥2 chronic conditions ≥12 months; QMB; high risk), `activity` (consent; care plan; 24/7 access; transitions; medication reconciliation; …), `modality` (face-to-face; interactive telecommunications; audio-only; asynchronous), `relation` (add-on to; do-not-report-with; replaces; defined-by-reference-to; crosswalk-valued-to), `setting` (facility/non-facility; place of service), `billing` (one practitioner per month; initiating visit).
2. **Lineage is a table of dated events with anchors**, derived from two independent sources that must agree or be flagged: (a) `pfs.rvu`/`rvu_proposed` (first year, last year, status transitions, descriptor changes, RVU changes beyond a threshold) and (b) FR paragraphs whose text carries the code and an event verb (create/adopt/replace/delete/crosswalk/bundle/telehealth-list). Every event row stores `item_key, p_id, page` so the chat cites a paragraph, never a table.
3. **Families are derived, not hand-listed.** A family = codes sharing a stem and differing in `time`/`actor`/`population` elements, plus CMS's own "code family" language in the FR ("The Chronic Care Management code family will be resurveyed", `YBM4IZUS` ¶1578) and `relation` elements (add-on/replaces). The five hand families become the regression fixture.
4. **Anchors live in two places, one per consumer.** Items get `code:<hcpcs>` and `family:<key>` tags in bib/Zotero (browsable, syncable); chunks get `codes`, `families`, and `elements` metadata in pgvector (retrievable). `comments` and `corpus` are re-indexed once with `--force` to stamp them, as P48 did for `rules`.
5. **Longitudinal retrieval is era-balanced, not recency-blended.** A "history of X" question retrieves per era (rule year) with a cap per era, so CY2015 and CY2027 both appear; the P34 recency blend stays for ordinary questions.
6. **Chat presents a timeline the way P48 presents a valuation**: a `lineage` SSE event before the first token, rendered as a table (year, event, codes, anchor link), and a prompt block whose rows carry bracketed labels the model must cite.
7. **Self-hosted inference only** for element extraction and stance tagging (llm module rule; qwen2.5 on the GPU pool).
8. **Outcome series (P50) are separate**: they never write into bib; they join on the lineage table's effective dates.
## Architecture
```
FR rules (fr_anchors) ─┐
RVU files (pfs.rvu) ──┼─► element extractor ─► pfs.code_element (code, year, type, value, anchor)
HCPCS/CPT descriptors ─┘ │
▼
family deriver ─► pfs.code_family (key, code, role, since, until, anchor)
│
rvu first/last/status ─┐ ▼
FR event paragraphs ───┴─► lineage builder ─► pfs.code_event (code, year, kind, from/to, item_key, p_id, page)
│
┌────────────────────┼─────────────────────┐
▼ ▼ ▼
bib tags code:/family: pgvector metadata notebooks
(rules, comments, IOM, codes/families/elements (timeline, reaction series)
eCFR, MLN) on all 3 collections
└────────────────────┬─────────────────────┘
▼
chat: detect codes → lineage(codes) + era-balanced retrieve → `lineage` SSE + prompt block → cited answer
│
P50: event calendar ─► BLS OEWS/CES, market series, Part B utilization ─► event-study notebook
```
## Components (P49)
### `pfs/elements.py` — element model
Pure dataclasses + the closed vocabulary (`ElementType` enum; `VOCAB: dict[ElementType, tuple[str, ...]]` seeded from the 99490, 99487/99489, 99491/99437, 99439, 99495/99496, 99497/99498, 99424–99427, G0556–G0558, 99441–99443/98008–98016, G2211 descriptors in the corpus). Descriptor parser for the deterministic parts: minutes, "each additional", period words, code references inside parentheticals ("Use in conjunction with 99490", "Do not report … 99487, 99489, 99491").
### `pfs/extract.py` — extractor
Input: a descriptor text plus the FR paragraph run that prints it (stem paragraph followed by element paragraphs, as in `DE2VH9PD` ¶1244–1247 and `JJ6AM5HJ` ¶1163–1185). Deterministic parse first; then the local model maps each element paragraph to a vocabulary value or proposes a new value (queued for review, never auto-added). Output rows into DuckDB `pfs.code_element` with the anchor. Runs from `stack pfs elements --code … | --family … | --all-payable`.
### `pfs/lineage.py` — events
Sources: (a) `pfs.rvu`/`rvu_proposed` diffs per code per year (appears, disappears, status change, descriptor change, |ΔRVU| > 10 %); (b) `fr_anchors` paragraphs where `codes ∋ code` and an event pattern matches (`finaliz…creat`, `replace`, `delet`, `crosswalk`, `bundled`, `telehealth services list`). Emits `pfs.code_event`. Cross-check rule: an RVU-file appearance without an FR paragraph within ±1 rule year, or vice versa, is flagged `unanchored` for review. The 5 worked lineages above are the fixture.
### `pfs/families.py` — derive
Keep the API (`FAMILIES`, `detect_codes`, `family_of`) but source `FAMILIES` from `pfs.code_family` at import time when the replica is present, falling back to the hand list. Family key = stem slug; synonyms = descriptor stems + FR family phrases.
### `bib/tag.py` + `bib/sync.py` — anchors on items
New namespaces `code:` and `family:`; tagger walks rules (via `fr_anchors.text`), comments (chunk text via pgvector or abstract), IOM chapters (attachment text), eCFR sections, MLN items. Nightly zotero-sync carries them (P38 tag-merge rules apply; `sup:` tags stay load-bearing).
### `llm/chunk.py` + `llm/index.py` — anchors on chunks
`codes` metadata on all three collections; add `families` (space-joined keys) and `elements` (space-joined `type=value` slugs when a chunk mentions a vocabulary value). One-time `stack llm index --collection all --force`.
### `bib/reaction.py` — reaction series
Per (family, docket): comment count, share of docket, stance (support/oppose/modify — closed vocab, local model, P35 pipeline), top organisations (`org:` tags exist). Pre-2017: `Comment:`/`Response:` pairs from `fr_anchors` where the pair mentions the family's codes. Output table `pfs.code_reaction` and a notebook.
### `llm/evidence.py` + `llm/rag.py` + `llm/web/chat.html` — longitudinal chat
`lineage_evidence(question)` → events + element diffs for the detected codes; `retrieve(..., mode="timeline")` era-balanced; `lineage` SSE event; prompt block; system prompt: cite the event label for every dated claim. UI: timeline table with FR jump links (P40) and eCFR links (P41).
### Evaluation
Golden questions with expected anchors: "history of CCM coding and payment", "what replaced G2058", "how did the APCM elements differ from CCM", "when were audio-only E/M codes payable and why did 99441–99443 end", "what did commenters say about G2211 in 2023 vs 2025", "does 99490 pass telehealth Step 3". Pass = every expected `(item_key, p_id)` appears in sources and the prose cites the event labels.
## P50 — legal epidemiology (scoped, not designed)
- `pfs.code_event` supplies the exposure calendar: effective date = 1 January of the rule year (or mid-year for correction rules), exposure = code/family becomes payable, revalued, or ends.
- Outcome series to ingest, each with its own module and provenance in bib: Medicare Physician & Other Practitioners PUF (utilization and allowed amounts per HCPCS, 2013–), BLS OEWS (occupation wages: 29-1xxx physicians, 29-2xxx/31-9xxx clinical support), BLS CES (health-care employment), market series (sector ETFs; telehealth/RPM/care-management tickers).
- Event-study notebook: pre/post windows around each event with never-treated code sets as controls; output effect sizes with confidence bands, never causal language in the chat.
## Out of scope (P49)
Locality pricing, OPPS, MA/commercial coding, CPT copyrighted long descriptors beyond what the FR prints, cloud LLM APIs.