Files
stack/docs/superpowers/specs/2026-08-12-cy2026-pfs-proposed-rule-design.md

10 KiB
Raw Blame History

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.