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

13 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: 13 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.

P36 build outcomes (2026-08-13)

Issues #588#597, milestone P36 (17 commits, 4a9be6f..a444d75, branch p36-cy2026-rule). Deviations and measurements vs the design:

  • Comment farm was already complete — the design's "only ~2,905 of 14,092 extracted" understated prior work: all 2,905 attachment-bearing comments had extractions; the remaining ~11.2k are body-only (abstract fallback covers them), and the whole corpus (164k comments, 726,620 chunks incl. 62,486 for CMS-2025-0304) was already embedded post-P33. #596 reduced to rule-text indexing.
  • Rules collection: 5,769 chunks (2,254→2,208 NPRM CMS-1832-P, 3,611→3,561 final CMS-1832-F) after an inline-HTML-strip fix round and force reindex; final-rule bib item gained cms-rule:CMS-1832-F.
  • pfs.rvu_proposed: 14,169 rows (NPRM Addendum B), in aco.duckdb + replica + DuckLake; cms.ingest_log provenance (18 rows/2 runs) with sha256, FR citation, pincite. Column names deviate from pfs.rvu (modifier/mod, nonfac_pe_rvu/non_fac_pe_rvu) — follow-up #607.
  • CY2026 NPRM parameters (all source-verified at 90 FR 32352 et seq.): proposed CF 33.4209 non-QP / 33.5875 QP, anesthesia 20.5728/20.6754, BN adjustor 1.0055, telehealth originating-site fee $31.85, comment close 2025-09-12.
  • qpp module: QP thresholds 75%/50% (partial 50%/35% — patient-count value is a disclosed inference), revenue nominal-risk standard 8% (414.1415(c)(3)(i)(A), established 83 FR / 2018-24170); the benchmark-based standard (c)(3)(i)(B) is stated nowhere in the corpus as a bare percentage → None with notes. APM incentive sunset and the first-year QP/non-QP CF split (payment year 2026) encoded with citations at 90 FR 49928/4997949980.
  • Citation correction found by final review: RULES[2026]'s final-rule citation had been "90 FR 101174" — that is the OPPS final rule's citation; the PFS final rule is 90 FR 4926650481 (CFs at 49961). Corrected in registry + notebook (a444d75). Design note: the spec itself restated the wrong citation as current state; future rule cycles should re-verify pre-existing registry citations against newly captured text.
  • Interstitials landed on the branch: #598 (test poisoned global polars.DataFrame → duckdb replacement-scan cache corruption; full suite 14,023 green post-fix), #599 (stale .git/hooks copies removed), CVE bumps aiohttp 3.14.3 / pyasn1 0.6.4 / cryptography 50.0.0 (#581#587 closed with per-CVE exploitability rationale).
  • Notebook: notebooks/cy2026_pfs_proposed_rule.py, 5 sections, all values registry/lake-sourced (thresholds interpolated after review fix), 18/18 headless integration green. CMS Table 92 (specialty impact) is a scanned graphic → clearly-labeled CPT-range proxy.