docs(spec): CY2027 PFS proposed rule — P36 pattern repeat w/ deltas (P37)

This commit is contained in:
kert
2026-08-13 16:36:14 -04:00
parent 53b97b8a60
commit 4ce8975c3a

View File

@@ -0,0 +1,121 @@
# CY2027 PFS Proposed Rule (CMS-1848-P) — capture, ingestion, APM analysis
**Status:** Designed — repeat of the P36 pattern per standing directive
**Date:** 2026-08-13
**Part of:** P37 (CY2027 PFS Proposed Rule)
**Related:** P36 (#588–#597, merged 53b97b8), #607 (rvu_proposed column alignment — folded in here), spec `2026-08-12-cy2026-pfs-proposed-rule-design.md`
## Goal
Run the CY2027 PFS proposed rule (CMS-1848-P, docket CMS-2026-2377, FR doc
`2026-14327`, published 2026-07-16) through the pipeline P36 built: capture
files, ingest proposed parameters and Addendum B with provenance, farm the
comments (period is **open** — initial pull now, scheduled re-farm after
close), index the rule text for RAG, extend the QPP/Advanced-APM registry to
PY2027, and analyze financial changes in a notebook. Tracked as Gitea
milestone P37.
## Current state (verified 2026-08-13)
- CY2027 NPRM published 2026-07-16: FR `2026-14327`, CMS-1848-P, reg.gov
docket CMS-2026-2377 (per the FR API). Not yet in bib; no comments pulled;
comment period open (close date to be transcribed from the DATES block).
- P36 infrastructure exists and is merged: `ProposedRule` model,
`pfs.nprm` loader + `pfs.rvu_proposed` (keyed by `cms_rule_id` —
designed for multi-rule), `cms.ingest_log`, `qpp` registry,
`llm.source.iter_rule_docs` + `rules` pgvector collection,
`rex.comments.classify.DOCKET_RULES`, notebook pattern.
- **No CY2027 final rule exists**, so there is no `RULES[2027]` to hang a
`proposed` block on — and `RuleYear` requires final fields
(`conversion_factor` is load-bearing for `pfs.pipe`'s RVU-file
selection). The registry needs a home for proposed-only years.
- #607 is open: `pfs.rvu_proposed` column names deviate from `pfs.rvu`
(`modifier`/`mod`, `nonfac_pe_rvu`/`non_fac_pe_rvu`); fixing it before a
second consumer cycle joins against the table halves the migration cost.
## Design deltas vs P36
### A. Proposed-only registry home (`pfs.rules`)
`PROPOSED: dict[int, ProposedRule]` module-level registry for years whose
final rule hasn't published, plus `proposed_for(year) -> ProposedRule | None`
returning `RULES[year].proposed` when the year exists in `RULES`, else
`PROPOSED.get(year)`. When the CY2027 final rule ships, `RULES[2027]` is
created and the entry migrates onto it (P36 shape); `proposed_for` keeps
consumers stable across that move. `RuleYear` itself is untouched — no
optional-final-fields surgery, `pfs.pipe` semantics unchanged.
### B. Column alignment first (#607)
Rename `pfs.rvu_proposed` columns to match `pfs.rvu` (`mod`,
`non_fac_pe_rvu`, `fac_pe_rvu` — audit the full set against `_RVU_COLUMNS`)
in `pfs.nprm`, the P36 notebook's SQL, and tests, then reload the table
(delete-and-reload makes this cheap). Done before any CY2027 consumer exists.
### C. Multi-rule NPRM ingest
`load_rvu_proposed` is parameterized by rule already; generalize discovery:
`_discover_addendum_b(tag)` takes the supplement tag (`sup:2027_PFS_NPRM`),
and `dev/scripts/ingest_pfs.py --nprm` loads every configured
(tag, cms_rule_id) pair — CY2026 and CY2027 partitions coexist keyed by
`cms_rule_id`, each with its own `ingest_log` row and FR citation from
`proposed_for(year)`.
### D. Open comment period
Initial farm now (`stack bib fetch-pfs-comments` discovers CMS-1848-P →
resolves CMS-2026-2377 → pulls comments to date), then backfill/extract/
index. A **post-close re-farm** (same commands — everything is resumable/
incremental) is scheduled after the DATES close date; the P37 milestone
stays open until that re-farm's counts are posted. A local cron reminder
fires the re-farm; the Gitea issue carries the close date.
### E. Everything else is a repeat
- Rule-text capture: `fetch_fr_attachments.py --key <2027 item>` after
`discover-pfs-rules` registers/tags the item (`cms-rule:CMS-1848-P`).
- Addenda: CMS-1848-P detail page → `data/cms/pfs_nprm/2027/`; Addendum B
attached + tagged `sup:2027_PFS_NPRM`, `module:pfs`, `file:rvu`,
`year:2027` (fetch script gains a `--year 2027` mode instead of a clone).
- `DOCKET_RULES["CMS-2026-2377"] = "CMS-1848-P"` + regression test.
- CY2027 `ProposedRule` values transcribed from the captured text with
pincites — never memory; comment-close date transcribed from DATES.
- `qpp`: PY2027 `QppYear` (current law) + `QppProposed` for CMS-1848-P
changes, both-file citations; re-verify PY2026 values only where the
CY2027 NPRM restates them.
- Rule text → `rules` collection (`stack llm index --collection rules
--key <item>`); comments → `comments` collection scoped to the docket.
- Notebook `notebooks/cy2027_pfs_proposed_rule.py`: CY2026-final →
CY2027-proposed CF walk (no final column — labeled "final rule pending");
RVU deltas `rvu_proposed[CMS-1848-P]` vs CY2026 final `pfs.rvu`;
Advanced-APM section from `qpp` PY2027; provenance section. Same
registry-sourced/citation-per-figure rules the P36 review enforced.
## Milestones and issues
### P37: CY2027 PFS Proposed Rule — capture, ingest, APM analysis
1. `pfs: align rvu_proposed column names with pfs.rvu` — existing #607, added to P37 (delta B)
2. `comments: register CMS-2026-2377 ↔ CMS-1848-P mapping` — delta E
3. `bib: discover + capture CY2027 rule text (2026-14327) into fr_downloads + bib` — delta E
4. `pfs: capture CMS-1848-P proposed addenda (sup:2027_PFS_NPRM)` — delta E
5. `pfs: PROPOSED registry + proposed_for helper, CY2027 NPRM parameters + pincites` — delta A
6. `pfs: multi-rule NPRM ingest (CY2026 + CY2027 partitions)` — delta C
7. `qpp: PY2027 registry + CMS-1848-P proposed changes` — delta E
8. `comments: initial farm of CMS-2026-2377 (fetch/backfill/extract/index)` — delta D
9. `comments: post-close re-farm + re-index of CMS-2026-2377` — delta D (blocks milestone close; scheduled)
10. `notebooks: CY2027 proposed-rule financial changes + Advanced APM notebook` — delta E
Dependency order: 1 first (schema); 2–4 independent; 5 needs 3; 6 needs
1+4+5; 7 needs 3; 8 needs 2; 9 after close date; 10 needs 5+6+7.
## Testing
Same bars as P36: pytest 99% coverage, stdlib mocking; exact-value tests
pin transcribed CY2027 parameters; multi-rule loader tests cover both
partitions + the renamed columns; qpp structural tests extend to PY2027;
notebook through layout tests + marimo check + headless integration; RAG
spot-checks on the docket and rule-text chunks. Regression: P36 notebook
still green after the #607 rename.
## Out of scope
- CY2027 OPPS/other rules; the CY2027 *final* rule (a future milestone
creates `RULES[2027]` and migrates the PROPOSED entry).
- Comment classification/sentiment (P30).
- Cloud LLM usage (llm stays local-only).