fix(notebooks,qpp): partition-filter P36 notebook; post-#620 narrative; cron-safe refarm (refs #616 #620 #615)
This commit is contained in:
@@ -6,18 +6,22 @@
|
|||||||
# Every step is resumable/incremental — safe to re-run on partial failure.
|
# Every step is resumable/incremental — safe to re-run on partial failure.
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
cd "$(dirname "$0")/../.."
|
cd "$(dirname "$0")/../.."
|
||||||
|
export PATH="$HOME/.local/bin:$PATH"
|
||||||
set -a; source .env; set +a
|
set -a; source .env; set +a
|
||||||
|
|
||||||
|
mkdir -p .state/comments
|
||||||
LOG=.state/comments/refarm-CMS-2026-2377.log
|
LOG=.state/comments/refarm-CMS-2026-2377.log
|
||||||
exec >>"$LOG" 2>&1
|
exec >>"$LOG" 2>&1
|
||||||
echo "=== re-farm run $(date -Is) ==="
|
echo "=== re-farm run $(date -Is) ==="
|
||||||
|
|
||||||
uv run stack bib fetch-pfs-comments || echo "WARN: fetch-pfs-comments rc=$?"
|
FAILURES=0
|
||||||
uv run stack bib backfill-comments || echo "WARN: backfill rc=$?"
|
|
||||||
uv run stack comments extract --docket CMS-2026-2377 || echo "WARN: extract rc=$?"
|
uv run stack bib fetch-pfs-comments || { echo "WARN: fetch-pfs-comments rc=$?"; FAILURES=$((FAILURES+1)); }
|
||||||
uv run stack comments extract-ocr --docket CMS-2026-2377 || echo "WARN: ocr rc=$?"
|
uv run stack bib backfill-comments || { echo "WARN: backfill rc=$?"; FAILURES=$((FAILURES+1)); }
|
||||||
|
uv run stack comments extract --docket CMS-2026-2377 || { echo "WARN: extract rc=$?"; FAILURES=$((FAILURES+1)); }
|
||||||
|
uv run stack comments extract-ocr --docket CMS-2026-2377 || { echo "WARN: ocr rc=$?"; FAILURES=$((FAILURES+1)); }
|
||||||
uv run stack llm index --collection comments --docket CMS-2026-2377 \
|
uv run stack llm index --collection comments --docket CMS-2026-2377 \
|
||||||
|| echo "WARN: index rc=$?"
|
|| { echo "WARN: index rc=$?"; FAILURES=$((FAILURES+1)); }
|
||||||
|
|
||||||
COUNTS=$(uv run python - <<'EOF'
|
COUNTS=$(uv run python - <<'EOF'
|
||||||
from conf import connect
|
from conf import connect
|
||||||
@@ -30,14 +34,20 @@ total, enriched = con.execute(
|
|||||||
print(f"comments={total} enriched={enriched}")
|
print(f"comments={total} enriched={enriched}")
|
||||||
s.close()
|
s.close()
|
||||||
EOF
|
EOF
|
||||||
)
|
) || { echo "WARN: counts rc=$?"; FAILURES=$((FAILURES+1)); }
|
||||||
echo "$COUNTS"
|
echo "$COUNTS"
|
||||||
|
|
||||||
|
if [ "$FAILURES" -eq 0 ]; then
|
||||||
|
RESULT_MSG="Automated post-close re-farm ran $(date -I): $COUNTS."
|
||||||
|
else
|
||||||
|
RESULT_MSG="Automated post-close re-farm ran $(date -I) — completed with $FAILURES failed steps — check log. $COUNTS."
|
||||||
|
fi
|
||||||
|
|
||||||
curl -sf -X POST \
|
curl -sf -X POST \
|
||||||
"https://git.fhirworx.io/api/v1/repos/homelab/stack/issues/615/comments" \
|
"https://git.fhirworx.io/api/v1/repos/homelab/stack/issues/615/comments" \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "User-Agent: curl/8.5.0" \
|
-H "User-Agent: curl/8.5.0" \
|
||||||
-d "{\"body\": \"Automated post-close re-farm ran $(date -I): $COUNTS. Log: .state/comments/refarm-CMS-2026-2377.log. Review counts, spot-check indexing, then close this issue and milestone P37.\"}" \
|
-d "{\"body\": \"$RESULT_MSG Log: .state/comments/refarm-CMS-2026-2377.log. Review counts, spot-check indexing, then close this issue and milestone P37.\n\nIf complete: close #615, close milestone P37, and REMOVE the crontab line (re-fires annually).\"}" \
|
||||||
|| echo "WARN: gitea comment failed"
|
|| echo "WARN: gitea comment failed"
|
||||||
echo "=== done $(date -Is) ==="
|
echo "=== done $(date -Is) ==="
|
||||||
|
|||||||
@@ -295,7 +295,8 @@ def _(q):
|
|||||||
ELSE 'Other'
|
ELSE 'Other'
|
||||||
END AS category
|
END AS category
|
||||||
FROM pfs.rvu_proposed
|
FROM pfs.rvu_proposed
|
||||||
WHERE (mod IS NULL OR mod = '')
|
WHERE cms_rule_id = 'CMS-1832-P'
|
||||||
|
AND (mod IS NULL OR mod = '')
|
||||||
AND status_code = 'A'
|
AND status_code = 'A'
|
||||||
AND non_fac_pe_rvu IS NOT NULL
|
AND non_fac_pe_rvu IS NOT NULL
|
||||||
QUALIFY row_number() OVER (PARTITION BY hcpcs ORDER BY hcpcs) = 1
|
QUALIFY row_number() OVER (PARTITION BY hcpcs ORDER BY hcpcs) = 1
|
||||||
@@ -433,13 +434,13 @@ def _(mo, rvu_delta):
|
|||||||
def _(con, mo):
|
def _(con, mo):
|
||||||
_new = con.execute("""
|
_new = con.execute("""
|
||||||
SELECT count(*) FROM pfs.rvu_proposed p
|
SELECT count(*) FROM pfs.rvu_proposed p
|
||||||
WHERE (p.mod IS NULL OR p.mod='') AND p.status_code='A' AND p.non_fac_pe_rvu IS NOT NULL
|
WHERE p.cms_rule_id = 'CMS-1832-P' AND (p.mod IS NULL OR p.mod='') AND p.status_code='A' AND p.non_fac_pe_rvu IS NOT NULL
|
||||||
AND NOT EXISTS (SELECT 1 FROM pfs.rvu f WHERE f.year=2025 AND f.hcpcs=p.hcpcs AND (f.mod IS NULL OR f.mod=''))
|
AND NOT EXISTS (SELECT 1 FROM pfs.rvu f WHERE f.year=2025 AND f.hcpcs=p.hcpcs AND (f.mod IS NULL OR f.mod=''))
|
||||||
""").fetchone()[0]
|
""").fetchone()[0]
|
||||||
_dropped = con.execute("""
|
_dropped = con.execute("""
|
||||||
SELECT count(*) FROM pfs.rvu f
|
SELECT count(*) FROM pfs.rvu f
|
||||||
WHERE f.year=2025 AND (f.mod IS NULL OR f.mod='') AND f.status_code='A'
|
WHERE f.year=2025 AND (f.mod IS NULL OR f.mod='') AND f.status_code='A'
|
||||||
AND NOT EXISTS (SELECT 1 FROM pfs.rvu_proposed p WHERE p.hcpcs=f.hcpcs AND (p.mod IS NULL OR p.mod=''))
|
AND NOT EXISTS (SELECT 1 FROM pfs.rvu_proposed p WHERE p.cms_rule_id = 'CMS-1832-P' AND p.hcpcs=f.hcpcs AND (p.mod IS NULL OR p.mod=''))
|
||||||
""").fetchone()[0]
|
""").fetchone()[0]
|
||||||
mo.md(f"""
|
mo.md(f"""
|
||||||
> **Coverage caveat:** {_new} HCPCS codes appear in `pfs.rvu_proposed`
|
> **Coverage caveat:** {_new} HCPCS codes appear in `pfs.rvu_proposed`
|
||||||
@@ -603,7 +604,7 @@ def _(QPP, pl):
|
|||||||
def _(alt, apm_thresholds, mo):
|
def _(alt, apm_thresholds, mo):
|
||||||
_df = apm_thresholds.to_pandas()
|
_df = apm_thresholds.to_pandas()
|
||||||
_df["apm_incentive_pct"] = _df["apm_incentive_pct"].fillna(0.0)
|
_df["apm_incentive_pct"] = _df["apm_incentive_pct"].fillna(0.0)
|
||||||
_df["status"] = _df["apm_incentive_pct"].apply(lambda v: "Paid" if v > 0 else "Sunset")
|
_df["status"] = _df["apm_incentive_pct"].apply(lambda v: "Paid" if v > 0 else "None under current law")
|
||||||
|
|
||||||
incentive_chart = (
|
incentive_chart = (
|
||||||
alt.Chart(_df)
|
alt.Chart(_df)
|
||||||
@@ -611,10 +612,14 @@ def _(alt, apm_thresholds, mo):
|
|||||||
.encode(
|
.encode(
|
||||||
x=alt.X("payment_year:O", title="Payment year"),
|
x=alt.X("payment_year:O", title="Payment year"),
|
||||||
y=alt.Y("apm_incentive_pct:Q", title="APM incentive payment (% of base-year Part B paid claims)"),
|
y=alt.Y("apm_incentive_pct:Q", title="APM incentive payment (% of base-year Part B paid claims)"),
|
||||||
color=alt.Color("status:N", title=None, scale=alt.Scale(domain=["Paid", "Sunset"])),
|
color=alt.Color(
|
||||||
|
"status:N",
|
||||||
|
title=None,
|
||||||
|
scale=alt.Scale(domain=["Paid", "None under current law"]),
|
||||||
|
),
|
||||||
tooltip=["performance_year", "payment_year", "apm_incentive_pct", "status"],
|
tooltip=["performance_year", "payment_year", "apm_incentive_pct", "status"],
|
||||||
)
|
)
|
||||||
.properties(title="APM Incentive Payment Sunset", width=600, height=300)
|
.properties(title="APM Incentive Payment — Not a Clean Sunset", width=600, height=300)
|
||||||
)
|
)
|
||||||
|
|
||||||
_citations = ", ".join(sorted(set(_df["citation"])))
|
_citations = ", ".join(sorted(set(_df["citation"])))
|
||||||
@@ -623,12 +628,18 @@ def _(alt, apm_thresholds, mo):
|
|||||||
[
|
[
|
||||||
incentive_chart,
|
incentive_chart,
|
||||||
mo.md(f"""
|
mo.md(f"""
|
||||||
Payment year 2026 (QP Performance Period 2024) is, under
|
The lump-sum APM Incentive Payment does **not** sunset
|
||||||
current law, the **last** year with a lump-sum APM incentive
|
cleanly after payment year 2026. Payment year 2027 (QP
|
||||||
payment (1.88% of base-year paid claims). From payment year
|
Performance Period 2025) has no incentive under current
|
||||||
2027 onward, QP status stops paying a bonus and starts
|
law. The Consolidated Appropriations Act, 2026 (CAA 2026,
|
||||||
(instead) determining *which conversion factor applies* — see
|
Pub. L. 119-75) then **revives a 3.1% incentive for payment
|
||||||
below.
|
year 2028 only** (`QPP[2026]`) — CMS-1848-P proposes to
|
||||||
|
codify that revival into 42 CFR 414.1450(b)(1) (91 FR
|
||||||
|
44218-44219, 44286), superseding the CY2026 Final Rule's
|
||||||
|
then-accurate "no incentive after 2026" narrative reflected
|
||||||
|
in the disposition table below. So the pattern across
|
||||||
|
payment years 2025-2028 is paid / paid / gap / revived, not
|
||||||
|
a single cutoff.
|
||||||
|
|
||||||
**Sources:** `qpp.QPP` performance years 2023–2026
|
**Sources:** `qpp.QPP` performance years 2023–2026
|
||||||
({_citations}).
|
({_citations}).
|
||||||
@@ -642,8 +653,21 @@ def _(alt, apm_thresholds, mo):
|
|||||||
def _(QPP, mo):
|
def _(QPP, mo):
|
||||||
_cy = QPP[2026]
|
_cy = QPP[2026]
|
||||||
_proposed = _cy.proposed
|
_proposed = _cy.proposed
|
||||||
_qp = _cy.qp_thresholds
|
|
||||||
_partial = _cy.partial_qp_thresholds
|
# As of the CY2026 PFS Final Rule (90 FR 49980), the payment-year-2028
|
||||||
|
# QP/Partial QP thresholds were understood to be 75%/50% (QP) and
|
||||||
|
# 50%/35% (Partial QP) -- CMS-1832-P proposed, and the Final Rule
|
||||||
|
# finalized, "no numeric change" from those then-current-law figures.
|
||||||
|
# These two literals are a CITED TRANSCRIPTION of that now-superseded
|
||||||
|
# disposition, not a live read of `QPP[2026].qp_thresholds` /
|
||||||
|
# `partial_qp_thresholds` -- those fields were corrected under task
|
||||||
|
# #620 (CAA 2026 legislatively restored 50%/35% QP, 40%/25% Partial
|
||||||
|
# QP for payment year 2028; see the `qpp` module docstring "CAA 2026
|
||||||
|
# supersession"). Pinning the historical values here rather than
|
||||||
|
# reading the registry live preserves what CMS actually said in this
|
||||||
|
# disposition table instead of retroactively rewriting it.
|
||||||
|
_qp_then = "75%/50%"
|
||||||
|
_partial_then = "50%/35%"
|
||||||
mo.md(f"""
|
mo.md(f"""
|
||||||
### CY2026 NPRM proposed QPP changes — proposed vs. finalized disposition
|
### CY2026 NPRM proposed QPP changes — proposed vs. finalized disposition
|
||||||
|
|
||||||
@@ -658,7 +682,7 @@ def _(QPP, mo):
|
|||||||
| Individual-level QP determination (new Threshold Score calc, Sec. 414.1425(b)(3)) | **Finalized as proposed** (90 FR 49923–49928, 49980) |
|
| Individual-level QP determination (new Threshold Score calc, Sec. 414.1425(b)(3)) | **Finalized as proposed** (90 FR 49923–49928, 49980) |
|
||||||
| Attribution-eligible beneficiary definition — expand 6th criterion beyond E/M services | **Finalized WITH MODIFICATION** — dual E/M + covered-professional-services methodology (90 FR 49980) |
|
| Attribution-eligible beneficiary definition — expand 6th criterion beyond E/M services | **Finalized WITH MODIFICATION** — dual E/M + covered-professional-services methodology (90 FR 49980) |
|
||||||
| Sunset the Medical Home Model 50-clinician limit (Sec. 414.1415(c)(7)) after the 2025 QP Performance Period | **Finalized as proposed** (90 FR 49929) |
|
| Sunset the Medical Home Model 50-clinician limit (Sec. 414.1415(c)(7)) after the 2025 QP Performance Period | **Finalized as proposed** (90 FR 49929) |
|
||||||
| QP / Partial QP percentage thresholds | **No numeric change proposed** — remain `qp_thresholds` {_qp.payment_amount_pct:.0f}%/{_qp.patient_count_pct:.0f}% (QP), `partial_qp_thresholds` {_partial.payment_amount_pct:.0f}%/{_partial.patient_count_pct:.0f}% (Partial QP) |
|
| QP / Partial QP percentage thresholds | **No numeric change proposed** — remained `qp_thresholds` {_qp_then} (QP), `partial_qp_thresholds` {_partial_then} (Partial QP) as understood at CY2026 Final Rule publication (90 FR 49980) — **CITED TRANSCRIPTION, since SUPERSEDED for payment year 2028 by CAA 2026; see `QPP[2026].qp_thresholds`/`partial_qp_thresholds` for current law** |
|
||||||
| Nominal-amount risk standards (42 CFR 414.1415(c)(3)(i)) / CEHRT requirement | **No change proposed** — cross-referenced by section only |
|
| Nominal-amount risk standards (42 CFR 414.1415(c)(3)(i)) / CEHRT requirement | **No change proposed** — cross-referenced by section only |
|
||||||
|
|
||||||
Full sourced narrative: `qpp.QPP[2026].proposed.changes`.
|
Full sourced narrative: `qpp.QPP[2026].proposed.changes`.
|
||||||
|
|||||||
@@ -751,16 +751,13 @@ QPP: dict[int, QppYear] = {
|
|||||||
"subparagraph list for readability, with no "
|
"subparagraph list for readability, with no "
|
||||||
"substantive change to any other year's percentage "
|
"substantive change to any other year's percentage "
|
||||||
"(NPRM 91 FR 44218-44219; RIA 91 FR 44260; amendatory "
|
"(NPRM 91 FR 44218-44219; RIA 91 FR 44260; amendatory "
|
||||||
"text 91 FR 44286). CONCERN: this amendatory text lists "
|
"text 91 FR 44286). This amendatory text lists no "
|
||||||
"no applicable percentage for payment years 2027 or "
|
"applicable percentage for payment years 2027 or 2029 "
|
||||||
"2029 (i.e., no incentive payment in those years) and "
|
"(i.e., no incentive payment in those years) and 3.1 "
|
||||||
"3.1 percent for payment year 2028 -- the latter "
|
"percent for payment year 2028 -- the latter directly "
|
||||||
"directly bears on QPP[2026].apm_incentive_pct "
|
"bears on QPP[2026].apm_incentive_pct (payment year "
|
||||||
"(payment year 2028), which is currently coded as "
|
"2028), which was corrected under task #620; see "
|
||||||
"None; this transcription task is scoped not to "
|
"module docstring 'CAA 2026 supersession'."
|
||||||
"silently correct that entry -- see module docstring "
|
|
||||||
"'Concern - PY2026 ... restatement discrepancy' and "
|
|
||||||
"task-7-report.md."
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -152,6 +152,10 @@ DOCKET_RULES: dict[str, str] = {
|
|||||||
"CMS-2026-2377": "CMS-1848-P",
|
"CMS-2026-2377": "CMS-1848-P",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# This prompt is scoped to docket CMS-2025-0304 only (skin-substitute
|
||||||
|
# reclassification). DOCKET_RULES above maps more dockets (e.g.
|
||||||
|
# CMS-2026-2377) to their CMS rule IDs, but do not reuse this prompt
|
||||||
|
# for CMS-2026-2377 or any other docket without rewriting it.
|
||||||
_SYSTEM = """\
|
_SYSTEM = """\
|
||||||
You classify public comments submitted on CMS's CY2026 PFS proposed rule
|
You classify public comments submitted on CMS's CY2026 PFS proposed rule
|
||||||
(CMS-1832-P, docket CMS-2025-0304), specifically regarding the proposal to
|
(CMS-1832-P, docket CMS-2025-0304), specifically regarding the proposal to
|
||||||
|
|||||||
Reference in New Issue
Block a user