Files
stack/docs/superpowers/specs/2026-08-13-cy2027-pfs-proposed-rule-design.md
kert f57d77f5f8
All checks were successful
CI / lint (push) Successful in 34s
CI / notebooks-smoke (push) Successful in 1m24s
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 1m4s
Infra CI / zotero (push) Successful in 14s
Infra CI / docs (push) Successful in 1m25s
Infra CI / api (push) Successful in 1m1s
Infra CI / llm (push) Successful in 1m7s
Infra CI / mc (push) Successful in 16s
Package Supply Chain / pkg-supply-chain (push) Successful in 1m19s
Deploy / report (push) Successful in 13s
CI / test (push) Successful in 14m17s
docs(spec): P37 build outcomes (refs #607-#616 #620)
2026-08-14 11:10:12 -04:00

163 lines
9.0 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.
# 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); 24 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).
## P37 build outcomes (2026-08-14)
Issues #607#616 + interstitial #620, milestone P37 (10 commits,
d726dca..a75b4c7, branch `p37-cy2027-rule` in a worktree — main checkout
was concurrently owned by #618). #615 and the milestone remain open until
the scheduled post-close re-farm.
- **CY2027 NPRM parameters** (91 FR 43842, item 5ITGVDJV, all values
byte-verified by two agents): proposed CF 32.8409 non-QP / 33.1693 QP,
anesthesia 20.2143/20.4165, BN 1.0053, telehealth $32.65, comment close
**2026-09-14**. Housed in the new `PROPOSED` registry via
`proposed_for(year)` (no RULES[2027] until the final rule). The NPRM's
own Summary section mislabels its CY2026 baselines (verbatim carry-over
of CY2026's *proposed* QP CF); documented in notes, resolved via the RIA.
- **Multi-rule ingest**: `pfs.rvu_proposed` now partitioned by cms_rule_id
(CMS-1832-P 14,169 + CMS-1848-P 14,518 rows) with per-rule ingest_log
provenance; columns aligned with `pfs.rvu` (#607) behind a durable
schema-migration guard.
- **QPP correction (#620, adjudicated)**: P36 had mis-keyed the statutory
threshold schedule (payment years read as performance years) —
QPP[2023]/[2024] corrected to 50/35 (partial 40/25); QPP[2026] was
superseded by CAA 2026 (50/35, partial 40/25, **3.1% incentive revived
for payment year 2028**); [2025]/[2027] confirmed correct at 75/50. Both
notebooks self-corrected (live registry reads); the P36 notebook's
narrative and partition filters were fixed in the final-review wave
(d3750ce). CMS's own drafting error at 414.1430(b)(2)(i)(D) documented.
- **Comment farm**: initial pull complete — **2,727 comments to date**
(period open); extraction/indexing follow incrementally; post-close
re-farm scheduled via host crontab 2026-09-15 03:17 →
`dev/scripts/refarm_cms_2026_2377.sh` (cron-hardened: PATH, honest
failure reporting), which posts final counts to #615.
- **Notebook**: `notebooks/cy2027_pfs_proposed_rule.py` — CF walk
(final pending), RVU deltas vs CY2026 final, payment-year-labeled APM
section incl. the CAA-2026 "not a clean sunset" story, provenance;
headless-validated via staged worktree copy.
- **Also landed en route**: #617 (FR text normalized at capture — first
live use: the CY2027 txt arrived clean), #618 (4 tracemalloc-verified
sqlite leaks fixed + `error::ResourceWarning` gate), #619 filed
(publish_replica symlink bug, workaround applied), #623 filed (docs
tag-list drift from controller tag ops).