docs(spec): CY2026 PFS proposed rule — capture, ingest, APM analysis (P36)
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
# CY2026 PFS Proposed Rule (CMS-1832-P) — capture, ingestion, APM analysis
|
||||
|
||||
**Status:** Designed — approved in-session 2026-08-12
|
||||
**Date:** 2026-08-12
|
||||
**Part of:** P36 (CY2026 PFS Proposed Rule)
|
||||
**Related:** P30 (comment analysis, #254–#256 still open), P33/P34 (llm module), #514 (lake PFS build-out)
|
||||
|
||||
## Goal
|
||||
|
||||
Bring the CY2026 PFS proposed rule (CMS-1832-P, docket CMS-2025-0304, FR doc
|
||||
`2025-13271`, published 2025-07-16) fully into the stack: capture its files,
|
||||
process them into queryable tables, log calculation-parameter ingestion with
|
||||
provenance, finish farming its comments, ingest the rule text into the RAG
|
||||
index, and analyze the financial changes — with particular focus on Advanced
|
||||
APM requirement changes — in a marimo notebook. Work is tracked as Gitea
|
||||
milestone P36 and landed via version control like prior milestones.
|
||||
|
||||
## Current state (verified 2026-08-12)
|
||||
|
||||
- The rule is already discovered in bib (item `2KVJ2HKX`, tags
|
||||
`cms-rule:CMS-1832-P`, `reg-docket:CMS-2025-0304`); 14,092 comments are
|
||||
fetched, but only ~2,905 have `.state/comments/.../combined.md` extractions
|
||||
and none are embedded in pgvector.
|
||||
- `src/rex/comments/classify.py` hard-codes docket CMS-2025-0304 as the CY2026
|
||||
**OPPS** rule (CMS-1834-P). Verified against the regulations.gov docket
|
||||
abstract: CMS-2025-0304 is the **PFS** NPRM (CMS-1832-P). The label is wrong
|
||||
(skin-substitute payment moved into the PFS rule in CY2026, the likely source
|
||||
of confusion).
|
||||
- `pfs.rules.RULES[2026]` holds only **final**-rule parameters (non-APM CF
|
||||
33.4009, APM CF 33.5675, 90 FR 101174). No proposed-rule parameters exist
|
||||
anywhere.
|
||||
- `data/fr_downloads/` lacks `2025-13271.{pdf,txt}`; `llm.source` can index
|
||||
comments and bib corpus but has no rule-text source; no docket beyond
|
||||
CMS-2017-0092 is indexed.
|
||||
- There is no ingest-log table (only JSONL logs + pincites) and no QPP /
|
||||
MIPS / Advanced-APM model in the repo (`cf_qp` is the only APM-aware field).
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
federalregister.gov ──> data/fr_downloads/2025-13271.{pdf,txt} ──┐
|
||||
CMS PUFs (NPRM addenda) ──> bib/Zotero (sup:2026_PFS_NPRM) ──┐ │
|
||||
│ │
|
||||
pfs.pipe (existing, final files) │ │
|
||||
pfs.nprm loader (new) <─────────────────┘ │
|
||||
│ │
|
||||
v v
|
||||
aco.duckdb + lake: pfs.rvu_proposed, cms.ingest_log llm.source.iter_rule_docs (new)
|
||||
│ │
|
||||
v v
|
||||
pfs.rules.ProposedRule + src/qpp registry pgvector `rules` collection
|
||||
│ pgvector `comments` (docket indexed)
|
||||
v
|
||||
notebooks/cy2026_pfs_proposed_rule.py (financial deltas + Advanced APM)
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### 1. Docket-identity fix (`rex.comments`)
|
||||
Correct `classify.py`'s CMS-2025-0304 ↔ CMS-1834-P mislabel to CMS-1832-P and
|
||||
add a `DOCKET_RULES` mapping constant with a regression test asserting it
|
||||
agrees with bib's `reg-docket:`/`cms-rule:` tag pairs for the dockets we track.
|
||||
|
||||
### 2. Rule-text capture (`bib` / fetch script)
|
||||
Download FR doc `2025-13271` (PDF + full-text TXT) into `data/fr_downloads/`
|
||||
via `dev/scripts/fetch_fr_attachments.py` and attach both to bib item
|
||||
`2KVJ2HKX`. Same for the final rule `2025-19787` (needed for
|
||||
proposed-vs-final deltas; the bib item `NHRGIHGD` exists but the text files
|
||||
don't).
|
||||
|
||||
### 3. NPRM addenda capture (`pfs`)
|
||||
Download the CMS-1832-P public-use files (proposed Addendum B RVUs; specialty
|
||||
impact table if published as data) and register them in bib/Zotero with tags
|
||||
`sup:2026_PFS_NPRM`, `module:pfs`, `file:rvu`, following the existing
|
||||
`sup:2026_PFS_FR` convention so `pfs.pipe`'s Zotero-driven discovery can see
|
||||
them without picking them up as final-rule files.
|
||||
|
||||
### 4. Proposed-parameter registry (`pfs.rules`)
|
||||
New `ProposedRule` pydantic model hung off `RuleYear` as
|
||||
`proposed: ProposedRule | None`: `cms_rule_id`, `fr_document_number`,
|
||||
`federal_register_citation`, `published`, `comment_close`,
|
||||
`conversion_factor`, `cf_qp`, `anesthesia_cf`, `anesthesia_cf_qp`,
|
||||
`budget_neutrality_adjustor`, `telehealth_originating_site_fee`, `notes`.
|
||||
CY2026 values are transcribed from the captured NPRM text (not from memory)
|
||||
and cited with a pincite to the bib attachment. Existing final-rule fields
|
||||
and `pfs.pipe`'s CF-matched RVU-file selection are untouched.
|
||||
|
||||
### 5. Proposed Addendum B loader (`pfs.nprm` + ingest script)
|
||||
A dedicated loader (not `pipe.load_all`) that reads the NPRM Addendum B from
|
||||
bib storage into DuckDB table `pfs.rvu_proposed` (same column shape as
|
||||
`pfs.rvu` plus `cms_rule_id`), delete-and-reload per rule id, published to the
|
||||
replica and the DuckLake `pfs` schema alongside the existing tables, via
|
||||
`dev/scripts/ingest_pfs.py` (a `--nprm` step) under the `duckdb_batch` lock
|
||||
preflight.
|
||||
|
||||
### 6. QPP / Advanced APM module (`src/qpp`)
|
||||
New namespace package + `stack[qpp]` extra. Registry-style like `pfs.rules`:
|
||||
`QppYear` per performance year with QP thresholds (payment-amount %,
|
||||
patient-count %), partial-QP thresholds, Advanced APM criteria parameters
|
||||
(CEHRT-use requirement, marginal-risk / total-risk financial standards,
|
||||
revenue-based nominal-amount standard %, expenditure-based %), and payment-year
|
||||
consequences (APM incentive % where applicable; CY2026+ QP CF differential by
|
||||
reference to `pfs.rules`). Each year cites its FR/statutory source; CY2026
|
||||
carries both current-law values and what CMS-1832-P proposed to change
|
||||
(`proposed: QppProposed | None`). Pure data + small lookup API + tests; no
|
||||
service, no ingestion.
|
||||
|
||||
### 7. Calculation-ingestion log (`cms.ingest_log`)
|
||||
DuckDB table `cms.ingest_log` in `aco.duckdb` (and published to the lake):
|
||||
`run_id, ingested_at, module, table_name, rule_id, source_file, sha256, rows,
|
||||
fr_citation, pincite_key`. Writer helper in `src/cms/ingest_log.py` used by
|
||||
`dev/scripts/ingest_pfs.py` (final + NPRM steps). Append-only; JSONL logs
|
||||
(`src/cms/log.py`) continue unchanged.
|
||||
|
||||
### 8. Rule-text RAG ingestion (`llm`)
|
||||
`iter_rule_docs(store, rule_keys | docket)` in `llm/source.py`: yields FR rule
|
||||
full text (prefer the TXT attachment; fall back to PDF-extracted text) with
|
||||
metadata `{doctype: "rule", cms_rule_id, fr_document_number, year, item_key}`.
|
||||
Indexed through the existing incremental `index_docs` into a new pgvector
|
||||
collection `rules`; `stack llm index --collection rules` CLI path. The chunker
|
||||
is reused as-is (markdown/plain-text aware, 2000-char chunks).
|
||||
|
||||
### 9. Comment-farm completion + indexing (ops)
|
||||
Resumable batches, in order: `stack bib backfill-comments` scoped to
|
||||
CMS-2025-0304 → `stack comments extract` / `extract-ocr` for the remaining
|
||||
~11k → verify `_index.csv` counts → `stack llm index --collection comments
|
||||
--docket CMS-2025-0304` and `--collection rules` for the CY2026 rule texts on
|
||||
the Ollama host pool. Code changes only where gaps surface; progress and
|
||||
final counts recorded on the Gitea issue.
|
||||
|
||||
### 10. Financial-changes notebook (`notebooks/cy2026_pfs_proposed_rule.py`)
|
||||
Marimo, narrative style of `skin_sub_budget_neutrality.py`, reading
|
||||
`connect.ducklake()` + the registries:
|
||||
- CF walk: CY2025 → CY2026 proposed → CY2026 final, including the first-ever
|
||||
QP / non-QP CF split and anesthesia CFs; BN adjustor decomposition.
|
||||
- RVU-level deltas: `pfs.rvu_proposed` vs CY2025 `pfs.rvu` (and vs CY2026
|
||||
final `pfs.rvu`): winners/losers by HCPCS, specialty-level impact.
|
||||
- **Advanced APM section**: QP threshold and financial-risk-standard changes
|
||||
proposed in CMS-1832-P vs current law (from `src/qpp`), the QP CF
|
||||
differential in dollars for example services, and what was finalized.
|
||||
- Every figure carries its FR citation; charts follow the dataviz guidance.
|
||||
Registered with the notebook quality gates (nb-watcher + nightly integration).
|
||||
|
||||
## Milestones and issues
|
||||
|
||||
### P36: CY2026 PFS Proposed Rule — capture, ingest, APM analysis
|
||||
|
||||
1. `comments: fix CMS-2025-0304 docket↔rule mapping (OPPS→PFS)` — component 1
|
||||
2. `bib: capture CY2026 PFS rule text (2025-13271, 2025-19787) into fr_downloads + bib` — component 2
|
||||
3. `pfs: capture CMS-1832-P proposed addenda into bib/Zotero (sup:2026_PFS_NPRM)` — component 3
|
||||
4. `pfs: ProposedRule registry with CY2026 NPRM parameters + pincites` — component 4
|
||||
5. `pfs: NPRM Addendum B loader → pfs.rvu_proposed (duckdb + lake)` — component 5
|
||||
6. `qpp: module scaffold + QP/Advanced-APM registry incl. CY2026 proposed changes` — component 6
|
||||
7. `cms: ingest_log provenance table + writer, wired into ingest_pfs` — component 7
|
||||
8. `llm: rule-text source + rules pgvector collection + CLI` — component 8
|
||||
9. `comments: complete CMS-2025-0304 farm (backfill/extract/OCR) and index comments + rule text` — component 9
|
||||
10. `notebooks: CY2026 proposed-rule financial changes + Advanced APM notebook` — component 10
|
||||
|
||||
Dependency order: 1–3 independent; 4 needs 2; 5 needs 3+4+7; 6 needs 2;
|
||||
8 needs 2; 9 needs 1+8; 10 needs 4+5+6.
|
||||
|
||||
## Testing
|
||||
|
||||
- pytest under the 99% coverage bar, stdlib mocking only: `tests/rex/comments/`
|
||||
(mapping regression), `tests/pfs/test_rules.py` (ProposedRule), new
|
||||
`tests/pfs/test_nprm.py`, `tests/qpp/`, `tests/cms/test_ingest_log.py`,
|
||||
`tests/llm/test_source.py` (rule docs), CLI tests for new flags.
|
||||
- Cross-registry consistency tests: CY2026 `proposed` vs final CF fields both
|
||||
present; `qpp` CY2026 references `pfs.rules` CFs without duplication drift.
|
||||
- Notebook validated headlessly via `dev/scripts/nb_integration.py`; the
|
||||
ingest scripts run end-to-end on the host before merge (row counts logged to
|
||||
`cms.ingest_log`).
|
||||
- RAG spot-check: retrieval from `rules` and `comments` collections returns
|
||||
CY2026 chunks with correct metadata.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- The CY2026 OPPS rule (CMS-1834-P) beyond fixing the mislabel.
|
||||
- Comment classification/sentiment work (remains P30, #254–#256).
|
||||
- MIPS scoring/measure modeling beyond the QP/Advanced-APM parameters above.
|
||||
- Cloud LLM usage — the llm module stays local-only per project policy.
|
||||
- Modeling every NPRM addendum; only Addendum B (RVUs) is tabled.
|
||||
Reference in New Issue
Block a user