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

9.0 KiB
Raw Blame History

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).