diff --git a/docs/superpowers/plans/2026-06-20-cyclone-edi-features.md b/docs/superpowers/plans/2026-06-20-cyclone-edi-features.md index 74b0134..e466a52 100644 --- a/docs/superpowers/plans/2026-06-20-cyclone-edi-features.md +++ b/docs/superpowers/plans/2026-06-20-cyclone-edi-features.md @@ -9,56 +9,78 @@ ## Phase 1 — 837P extra validation rules -### Task 1: REF*G1 enforcement rule (R032) +> **Note on rule IDs:** R032 and R033 are already used in `validator.py` +> for CLM05-2 facility code qualifier and CLM05-1 place of service code +> respectively. The two new rules in this phase use **R034** (REF*G1 +> enforcement) and **R035** (BHT06 transaction type code). + +### Task 1: REF*G1 enforcement rule (R034) **Files:** - Modify: `backend/src/cyclone/parsers/validator.py` +**Detection mechanism:** `ClaimOutput.raw_segments` is already populated +by the parser, so R034 scans `raw_segments` for any segment whose first +two elements are `["REF", "G1", ...]`. No new model field is required. + - [ ] **Step 1: Add failing tests** Append to `backend/tests/test_validator.py`: ```python -def test_r032_ref_g1_required_when_freq_7_no_ref_g1_emits_error(co_medicaid_payer_with_ref_g1_required): - # ... claim with frequency_code=7, no REF*G1 +def test_r034_ref_g1_required_when_freq_7_no_ref_g1_emits_error(strict_ref_g1_payer): + # ... claim with frequency_code=7, no REF*G1 in raw_segments # expect validation error -def test_r032_ref_g1_required_when_freq_8_no_ref_g1_emits_error(co_medicaid_payer_with_ref_g1_required): - # ... claim with frequency_code=8, no REF*G1 +def test_r034_ref_g1_required_when_freq_8_no_ref_g1_emits_error(strict_ref_g1_payer): + # ... claim with frequency_code=8, no REF*G1 in raw_segments # expect validation error -def test_r032_ref_g1_required_when_freq_7_with_ref_g1_passes(co_medicaid_payer_with_ref_g1_required): - # ... claim with frequency_code=7, REF*G1 present +def test_r034_ref_g1_required_when_freq_7_with_ref_g1_passes(strict_ref_g1_payer): + # ... claim with frequency_code=7, REF*G1 present in raw_segments # expect no error -def test_r032_ref_g1_required_when_freq_1_no_ref_g1_passes(co_medicaid_payer_with_ref_g1_required): +def test_r034_ref_g1_required_when_freq_1_no_ref_g1_passes(strict_ref_g1_payer): # ... claim with frequency_code=1, no REF*G1 # expect no error (rule only fires on 7/8) ``` - [ ] **Step 2: Run tests to verify they fail** -- [ ] **Step 3: Implement `_r032_ref_g1_required` in `validator.py`** +- [ ] **Step 3: Implement `_r034_ref_g1_required` in `validator.py`** + - Scan `claim.raw_segments` for `["REF", "G1", *]`; only check if claim + frequency is 7 or 8; only emit if `cfg.require_ref_g1_for_adjustments`. - [ ] **Step 4: Register in `_RULES` list** - [ ] **Step 5: Commit** -### Task 2: BHT06 transaction-set-purpose-code rule (R033) +### Task 2: BHT06 transaction-type-code rule (R035) **Files:** - Modify: `backend/src/cyclone/parsers/validator.py` +- Modify: `backend/src/cyclone/parsers/models.py` (add `Envelope.transaction_type_code: str | None`) +- Modify: `backend/src/cyclone/parsers/parse_837.py` (populate `transaction_type_code` from BHT06 in `_build_envelope`) -- [ ] **Step 1: Add failing tests** +> **Note on naming:** BHT06 in the 837P 005010X222A1 implementation is the +> "Transaction Type Code" element (qualifier 640) — not the "Transaction +> Set Purpose Code" (which is BHT02, qualifier 353). The plan originally +> conflated these. Use `transaction_type_code` for the new `Envelope` field. + +- [ ] **Step 1: Add failing tests** — frequency_code=7/8 with no REF*G1 in raw_segments should NOT fire R034 (R034 is gated on `cfg.require_ref_g1_for_adjustments`, which is False for CO). Test must use a "strict" config. - [ ] **Step 2: Run tests to verify they fail** -- [ ] **Step 3: Implement `_r033_bht06_allowed` in `validator.py`** -- [ ] **Step 4: Register in `_RULES` list** -- [ ] **Step 5: Commit** +- [ ] **Step 3: Add `transaction_type_code: str | None = None` to `Envelope` model** +- [ ] **Step 4: Populate it in `_build_envelope` from `seg[6]` when present** +- [ ] **Step 5: Implement `_r035_bht06_allowed` in `validator.py`** + - If `claim.envelope.transaction_type_code` is set and not in + `cfg.allowed_bht06`, emit error. +- [ ] **Step 6: Register in `_RULES` list** +- [ ] **Step 7: Commit** -### Task 3: Verify existing R031 (REF*G1 optional) keeps warning behavior +### Task 3: Verify existing R031 (REF*G1 optional) keeps no-op behavior **Files:** - Verify: `backend/src/cyclone/parsers/validator.py:54-57` - [ ] **Step 1: Read current `_r031_ref_g1_optional`** -- [ ] **Step 2: Confirm it stays as a warning for default configs** +- [ ] **Step 2: Confirm it stays as a no-op (returns empty tuple)** - [ ] **Step 3: (No commit — verification task)** ### Task 4: Wire `require_ref_g1_for_adjustments` into CO Medicaid factory @@ -66,9 +88,9 @@ def test_r032_ref_g1_required_when_freq_1_no_ref_g1_passes(co_medicaid_payer_wit **Files:** - Verify: `backend/src/cyclone/parsers/payer.py:67` -- [ ] **Step 1: Confirm factory sets the flag for CO** -- [ ] **Step 2: Run existing co_medicaid fixture through new R032** -- [ ] **Step 3: Adjust fixture if false positives appear** +- [ ] **Step 1: Confirm factory sets the flag for CO (currently `False` for v1 leniency)** +- [ ] **Step 2: Run existing co_medicaid fixture through new R034** +- [ ] **Step 3: Adjust fixture or factory if false positives appear** - [ ] **Step 4: Commit if any change** ### Task 5: Phase 1 commit + full suite diff --git a/docs/superpowers/specs/2026-06-20-cyclone-edi-features-design.md b/docs/superpowers/specs/2026-06-20-cyclone-edi-features-design.md index 9de0e00..7752cfd 100644 --- a/docs/superpowers/specs/2026-06-20-cyclone-edi-features-design.md +++ b/docs/superpowers/specs/2026-06-20-cyclone-edi-features-design.md @@ -8,7 +8,7 @@ Four backend EDI feature areas extending cyclone beyond the 837P parser and 835 ERA parser already shipped: -1. **Additional 837P validation rules** — REF\*G1 enforcement for adjustment-frequency claims, BHT06 transaction-set-purpose-code allowlist. +1. **Additional 837P validation rules** — REF\*G1 enforcement for adjustment-frequency claims (R034, gated on `cfg.require_ref_g1_for_adjustments`); BHT06 transaction-type-code allowlist (R035, gated on `cfg.allowed_bht06`). 2. **835 CAS deep-parsing** — surface CARC/RARC reason-code labels alongside the existing CAS persistence. 3. **999 ACK** — parse inbound 999s, serialize outbound 999s, optionally auto-generate a 999 in response to an inbound 837P. 4. **270/271 eligibility** — parse 270 requests and 271 responses, serialize 270 requests.