8.5 KiB
CY2027 PFS Proposed Rule (P37) Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Run CY2027's NPRM (CMS-1848-P, docket CMS-2026-2377, FR doc 2026-14327, pub 2026-07-16) through the pipeline P36 built, with the four deltas from the spec (PROPOSED registry, #607 rename first, multi-rule ingest, open-comment-period farm + scheduled re-farm).
Architecture / Tech Stack: identical to P36 — see docs/superpowers/plans/2026-08-12-cy2026-pfs-proposed-rule-p36.md for the detailed patterns; this plan states only what differs.
Tracker: milestone P37 (id 38) — issues #607–#616. Spec: docs/superpowers/specs/2026-08-13-cy2027-pfs-proposed-rule-design.md
Global Constraints
Same as P36's plan (branch p37-cy2027-rule off main; conventional commits refs #NNN, no Co-Authored-By trailers; 99% coverage, stdlib mocking; ruff E,F,I/88; lazy conf imports; generated workflows untouched; llm local-only; duckdb_batch for writes; never transcribe rule parameters from memory — grep the captured text with -a (HTML-wrapped, hard-wrapped lines, stray NULs) and pincite everything; transcription-then-test allowed only for FR values). Plus, learned in P36: stage only your own files (git add <paths>, never -A); retry once on commit HEAD/index races; pyproject-touching commits trigger the full-suite hook (~14 min parallel) — serialize around them.
Task 1: Align rvu_proposed column names (#607)
Files: Modify src/pfs/nprm.py, tests/pfs/test_nprm.py, notebooks/cy2026_pfs_proposed_rule.py (its SQL aliases).
Produces: pfs.rvu_proposed columns exactly hcpcs, mod, description, status_code, work_rvu, non_fac_pe_rvu, fac_pe_rvu, mp_rvu, cms_rule_id (audit against pfs.pipe._RVU_COLUMNS and match pfs.rvu verbatim — verify with DESCRIBE pfs.rvu on the replica first).
- Update tests to the new names (they pin the schema) → run → FAIL → rename in
nprm.py(target columns + DDL + normalization map) → PASS. - Simplify the P36 notebook's join SQL (drop the alias juggling);
uvx marimo check+ layout tests. - Host run
uv run python dev/scripts/ingest_pfs.py --nprm --no-lake(delete-and-reload recreates the table with new names;DROP TABLE pfs.rvu_proposedfirst since CREATE IF NOT EXISTS won't alter columns — do it inside the same duckdb_batch), then full run with lake; verify replica + lake schema. - Commit
fix(pfs): align rvu_proposed columns with pfs.rvu (refs #607).
Task 2: Docket mapping (#608)
Files: src/rex/comments/classify.py, tests/rex/comments/test_classify.py.
- Failing test:
DOCKET_RULES["CMS-2026-2377"] == "CMS-1848-P"→ add the entry (comment: CY2027 PFS NPRM, verified via FR API 2026-08-13) → PASS → commitfeat(comments): register CMS-2026-2377 (CY2027 PFS NPRM) (refs #608).
Task 3: Discover + capture rule text (#609) — ops
uv run stack bib discover-pfs-rules(registers/tags the CY2027 items; note the new item key for 2026-14327 — find viastore.list_items(item_type="rule")filtering document_number).uv run python dev/scripts/fetch_fr_attachments.py --key <KEY>; verify PDF+TXT in data/fr_downloads and attachments on the item;grep -a -c "conversion factor" data/fr_downloads/2026-14327.txt> 0.- Record the item key in the ledger + issue comment (later tasks need it). No commit.
Task 4: NPRM addenda capture (#610)
Files: Modify dev/scripts/fetch_nprm_addenda.py (add --year selecting a per-year config dict: detail-page URL, ZIP URL, dest dir, rule item key, tag set; 2026 entry keeps current values; 2027 entry → CMS-1848-P page, data/cms/pfs_nprm/2027/, tags sup:2027_PFS_NPRM, module:pfs, file:rvu, year:2027).
- Resolve the CMS-1848-P addenda ZIP URL from
https://www.cms.gov/medicare/payment/fee-schedules/physician/federal-regulation-notices/cms-1848-p(fallback: the notices index). Download, verify Addendum B columns,--register(idempotency guard already exists — parameterize it by item key), tag-scoped zotero sync. - Commit
feat(pfs): CY2027 NPRM addenda capture — fetch_nprm_addenda --year (refs #610).
Task 5: PROPOSED registry + CY2027 parameters (#611)
Files: src/pfs/rules/__init__.py, tests/pfs/test_rules.py.
Produces: PROPOSED: dict[int, ProposedRule]; proposed_for(year) -> ProposedRule | None (RULES[year].proposed if year in RULES else PROPOSED.get(year)); PROPOSED[2027] populated.
- Transcribe from
data/fr_downloads/2026-14327.txt(grep-a, short fragments): proposed CFs (QP/non-QP + anesthesia pair), BN adjustor, telehealth originating-site fee, published/comment-close (DATES block — this close date drives #615's schedule), FR citation from the header ([Pages …]). Record verbatim quotes + file lines + FR pages in the report. - Structural tests first (helper resolves both paths; 2027 in PROPOSED not RULES), then exact-value tests pinning transcriptions. Docstrings follow the file's
:pincite:style citing the new item key. - Commit
feat(pfs): PROPOSED registry + proposed_for — CY2027 NPRM parameters (refs #611).
Task 6: Multi-rule NPRM ingest (#612)
Files: src/pfs/nprm.py, dev/scripts/ingest_pfs.py, tests/pfs/test_nprm.py.
Produces: _discover_addendum_b(tag: str); NPRM_SOURCES: list of (year, sup_tag, cms_rule_id) (2026 + 2027); load_rvu_proposed(con, *, year) resolving tag/rule/citation via NPRM_SOURCES + proposed_for(year); --nprm loads every source, one ingest_log row each, same run_id.
- Tests: two fixture partitions load side by side; reloading one rule leaves the other intact; discovery raises naming the missing tag.
- Host run (both partitions; verify
SELECT cms_rule_id, count(*) FROM pfs.rvu_proposed GROUP BY 1shows CMS-1832-P + CMS-1848-P) + replica + lake;cms.ingest_logrows for both. - Commit
feat(pfs): multi-rule NPRM ingest — CY2026+CY2027 partitions (refs #612).
Task 7: qpp PY2027 (#613)
Files: src/qpp/__init__.py, tests/qpp/test_rules.py.
- Transcribe PY2027 current-law thresholds/standards and CMS-1848-P's proposed Advanced-APM changes from
2026-14327.txt(both greps and quotes in the report; re-verify PY2026 only where restated).QPP[2027]+QppProposed(cms_rule_id="CMS-1848-P", …); unstated values →None+ notes (P36 precedent). - Structural + exact-value tests; commit
feat(qpp): PY2027 registry + CMS-1848-P proposed changes (refs #613).
Task 8: Initial comment farm (#614) — ops, long-running
uv run stack bib fetch-pfs-comments(discovers CMS-1848-P → resolves docket → pulls comments to date; rate-limited ~970/hr — with an open period expect a partial set; run detachedsetsid nohup, log to.state/comments/fetch-CMS-2026-2377.log, Monitor for errors).- Then
backfill-comments(export REGULATIONS_GOV_API_KEY),comments extract --docket CMS-2026-2377,extract-ocr, thenstack llm index --collection comments --docket CMS-2026-2377and--collection rules --key <2027 item key>(detached + monitored; all resumable). - Post counts on #614. No commit.
Task 9: Post-close re-farm (#615) — scheduled, blocks milestone close
- After Task 5 lands, set the local cron reminder for close-date+1 (CronCreate) to re-run Task 8's commands verbatim; comment the exact date + commands on #615.
- On firing: re-run, post final counts, close #615, then close milestone P37.
Task 10: CY2027 notebook (#616)
Files: notebooks/cy2027_pfs_proposed_rule.py.
- Clone the P36 notebook's structure (dataviz skill first): CF walk CY2026-final → CY2027-proposed (columns labeled "final rule pending"), RVU deltas
rvu_proposed[CMS-1848-P]vs CY2026 finalpfs.rvu(aligned columns from Task 1), APM section fromqppPY2027 viafor_payment_year(2029)/QPP[2027], provenance fromcms.ingest_log. Registry-sourced values only; FR citation attached to every figure (P36 review standard); GPCI=1.0 scope note. - marimo check + layout tests + scoped headless integration; commit
feat(notebooks): CY2027 proposed-rule financial changes + APM (refs #616).
Task 11: Close-out
- Full gates; merge
p37-cy2027-rule→ main (Merge P37: …), push (remote isgitea). - Close #607–#614, #616 with substantiation; #615 + milestone stay open until the post-close re-farm.
- Append
## P37 build outcomesto the spec; update ledger; keep the re-farm cron armed.