docs(sp3): fix rule IDs (R034/R035) and BHT06 naming in plan+spec

This commit is contained in:
Tyler
2026-06-20 07:18:03 -06:00
parent b47818883a
commit 467087d683
2 changed files with 42 additions and 20 deletions
@@ -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
@@ -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.