All checks were successful
CI / lint (push) Successful in 45s
CI / notebooks-smoke (push) Successful in 1m33s
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 8m12s
Infra CI / zotero (push) Successful in 20s
Infra CI / docs (push) Successful in 18s
Infra CI / api (push) Successful in 20s
Infra CI / llm (push) Successful in 12s
Infra CI / mc (push) Successful in 14s
Deploy / report (push) Successful in 16s
CI / test (push) Successful in 19m16s
88 lines
8.5 KiB
Markdown
88 lines
8.5 KiB
Markdown
# 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_proposed` first 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 → commit `feat(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 via `store.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 1` shows CMS-1832-P + CMS-1848-P) + replica + lake; `cms.ingest_log` rows 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 detached `setsid 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`, then `stack llm index --collection comments --docket CMS-2026-2377` and `--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 final `pfs.rvu` (aligned columns from Task 1), APM section from `qpp` PY2027 via `for_payment_year(2029)`/`QPP[2027]`, provenance from `cms.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 is `gitea`).
|
||
- [ ] Close #607–#614, #616 with substantiation; #615 + milestone stay open until the post-close re-farm.
|
||
- [ ] Append `## P37 build outcomes` to the spec; update ledger; keep the re-farm cron armed.
|