9.0 KiB
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:
ProposedRulemodel,pfs.nprmloader +pfs.rvu_proposed(keyed bycms_rule_id— designed for multi-rule),cms.ingest_log,qppregistry,llm.source.iter_rule_docs+rulespgvector collection,rex.comments.classify.DOCKET_RULES, notebook pattern. - No CY2027 final rule exists, so there is no
RULES[2027]to hang aproposedblock on — andRuleYearrequires final fields (conversion_factoris load-bearing forpfs.pipe's RVU-file selection). The registry needs a home for proposed-only years. - #607 is open:
pfs.rvu_proposedcolumn names deviate frompfs.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>afterdiscover-pfs-rulesregisters/tags the item (cms-rule:CMS-1848-P). - Addenda: CMS-1848-P detail page →
data/cms/pfs_nprm/2027/; Addendum B attached + taggedsup:2027_PFS_NPRM,module:pfs,file:rvu,year:2027(fetch script gains a--year 2027mode instead of a clone). DOCKET_RULES["CMS-2026-2377"] = "CMS-1848-P"+ regression test.- CY2027
ProposedRulevalues transcribed from the captured text with pincites — never memory; comment-close date transcribed from DATES. qpp: PY2027QppYear(current law) +QppProposedfor CMS-1848-P changes, both-file citations; re-verify PY2026 values only where the CY2027 NPRM restates them.- Rule text →
rulescollection (stack llm index --collection rules --key <item>); comments →commentscollection 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 deltasrvu_proposed[CMS-1848-P]vs CY2026 finalpfs.rvu; Advanced-APM section fromqppPY2027; 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
pfs: align rvu_proposed column names with pfs.rvu— existing #607, added to P37 (delta B)comments: register CMS-2026-2377 ↔ CMS-1848-P mapping— delta Ebib: discover + capture CY2027 rule text (2026-14327) into fr_downloads + bib— delta Epfs: capture CMS-1848-P proposed addenda (sup:2027_PFS_NPRM)— delta Epfs: PROPOSED registry + proposed_for helper, CY2027 NPRM parameters + pincites— delta Apfs: multi-rule NPRM ingest (CY2026 + CY2027 partitions)— delta Cqpp: PY2027 registry + CMS-1848-P proposed changes— delta Ecomments: initial farm of CMS-2026-2377 (fetch/backfill/extract/index)— delta Dcomments: post-close re-farm + re-index of CMS-2026-2377— delta D (blocks milestone close; scheduled)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).
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
PROPOSEDregistry viaproposed_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_proposednow partitioned by cms_rule_id (CMS-1832-P 14,169 + CMS-1848-P 14,518 rows) with per-rule ingest_log provenance; columns aligned withpfs.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::ResourceWarninggate), #619 filed (publish_replica symlink bug, workaround applied), #623 filed (docs tag-list drift from controller tag ops).