12 KiB
Sub-project 3 — Backend EDI Features: Implementation Plan
Date: 2026-06-20
Status: Approved
Branch: edi-features
Spec: docs/superpowers/specs/2026-06-20-cyclone-edi-features-design.md
30 tasks across 5 phases. Follows the same task pattern as the SP2 plan ([ ] Step 1 → Step N → commit).
Phase 1 — 837P extra validation rules
Task 1: REF*G1 enforcement rule (R032)
Files:
-
Modify:
backend/src/cyclone/parsers/validator.py -
Step 1: Add failing tests
Append to backend/tests/test_validator.py:
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
# 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
# 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
# expect no error
def test_r032_ref_g1_required_when_freq_1_no_ref_g1_passes(co_medicaid_payer_with_ref_g1_required):
# ... 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_requiredinvalidator.py - Step 4: Register in
_RULESlist - Step 5: Commit
Task 2: BHT06 transaction-set-purpose-code rule (R033)
Files:
-
Modify:
backend/src/cyclone/parsers/validator.py -
Step 1: Add failing tests
-
Step 2: Run tests to verify they fail
-
Step 3: Implement
_r033_bht06_allowedinvalidator.py -
Step 4: Register in
_RULESlist -
Step 5: Commit
Task 3: Verify existing R031 (REF*G1 optional) keeps warning 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 3: (No commit — verification task)
Task 4: Wire require_ref_g1_for_adjustments into CO Medicaid factory
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 4: Commit if any change
Task 5: Phase 1 commit + full suite
.venv/bin/pytest -q
Expected: 205 + 6 = ~211 pass.
Phase 2 — 835 CAS deep-parsing (reason-code lookup)
Task 6: parsers/cas_codes.py with CARC dict
Files:
-
Create:
backend/src/cyclone/parsers/cas_codes.py -
Create:
backend/tests/test_cas_codes.py -
Step 1: Add failing tests for known/unknown lookup, count > 100
-
Step 2: Run tests to verify they fail
-
Step 3: Implement
reason_label+all_known_codeswith ~150 CARC codes -
Step 4: Run tests to verify they pass
-
Step 5: Commit
Task 7: Extend GET /api/remittances/{id} to surface adjustments
Files:
-
Modify:
backend/src/cyclone/store.py -
Modify:
backend/src/cyclone/api.py -
Modify:
backend/tests/test_api_gets.py -
Step 1: Add failing tests (empty adjustments returns
[], populated returns{group, reason, label, amount, quantity}) -
Step 2: Run tests to verify they fail
-
Step 3: Add a
get_remit_with_adjustmentsmethod toCycloneStore -
Step 4: Update
to_ui_remittance(or addto_ui_remittance_detail) to include adjustments array -
Step 5: Update endpoint to use the new mapper
-
Step 6: Run tests to verify they pass
-
Step 7: Commit
Task 8: Frontend Remit type + Remittances page adjustments expansion
Files:
-
Modify:
src/types/index.ts -
Modify:
src/pages/Remittances.tsx -
Step 1: Add
adjustments?: { group, reason, label, amount, quantity }[]toRemittance -
Step 2: Update Remittances page to render adjustments expansion
-
Step 3: Add test in Remittances.test.tsx
-
Step 4: Verify build + tests pass
-
Step 5: Commit
Task 9: Phase 2 commit + full suite
.venv/bin/pytest -q && npm test -- --run
Expected: ~215 backend, ~9 frontend.
Phase 3 — 999 ACK transaction set
Task 10: New Pydantic models for 999
Files:
-
Create:
backend/src/cyclone/parsers/models_999.py -
Modify:
backend/src/cyclone/parsers/__init__.py -
Step 1: Implement model classes per spec §4.1
-
Step 2: Wire lazy re-exports
-
Step 3: Verify import + simple instantiation works (1 sanity test)
-
Step 4: Commit
Task 11: 999 parser
Files:
-
Create:
backend/src/cyclone/parsers/parse_999.py -
Step 1: Add failing tests with minimal_999.txt fixture (basic happy path)
-
Step 2: Run tests to verify they fail
-
Step 3: Implement orchestrator following
parse_835.pypattern -
Step 4: Run tests to verify they pass
-
Step 5: Commit
Task 12: 999 serializer (outbound)
Files:
-
Create:
backend/src/cyclone/parsers/serialize_999.py -
Step 1: Add failing round-trip test (parse → serialize → parse → assert equal)
-
Step 2: Run test to verify it fails
-
Step 3: Implement
serialize_999(ISA/GS/ST/AK1/[AK2/AK3/AK4/AK5*]/AK9/SE/GE/IEA) -
Step 4: Run test to verify it passes
-
Step 5: Commit
Task 13: acks table + ORM + migration
Files:
-
Create:
backend/src/cyclone/migrations/0002_acks.sql -
Modify:
backend/src/cyclone/db.py -
Step 1: Add migration SQL per spec §4.4
-
Step 2: Add ORM model
Acktodb.py -
Step 3: Add
CycloneStore.add_ack(...)method -
Step 4: Run migration on a fresh DB; verify table exists
-
Step 5: Commit
Task 14: POST /api/parse-999 endpoint
Files:
-
Modify:
backend/src/cyclone/api.py -
Step 1: Add failing tests (parse + persist, returns summary)
-
Step 2: Run tests to verify they fail
-
Step 3: Add endpoint mirroring
parse-835JSON path -
Step 4: Run tests to verify they pass
-
Step 5: Commit
Task 15: Auto-generate 999 on 837 ingest via ?ack=true
Files:
-
Modify:
backend/src/cyclone/api.py -
Modify:
backend/tests/test_api_parse_persists.py -
Step 1: Add 3 failing tests (default no ack, ack=true happy path, ack=true with rejected claims)
-
Step 2: Run tests to verify they fail
-
Step 3: Add
ack: bool = Query(False)param toparse_837_endpoint -
Step 4: Build
BatchAckBuilderthat converts validation report to 999 -
Step 5: Persist
Ackrow, attach to response body -
Step 6: Run tests to verify they pass
-
Step 7: Commit
Task 16: Frontend /acks page + sidebar entry
Files:
-
Modify:
src/lib/api.ts -
Modify:
src/hooks/(newuseAcks.ts) -
Modify:
src/types/index.ts -
Create:
src/pages/Acks.tsx -
Create:
src/pages/Acks.test.tsx -
Modify:
src/components/Sidebar.tsx -
Step 1: Add
Acktype tosrc/types/index.ts -
Step 2: Add
api.listAcks()+api.getAck(id)tosrc/lib/api.ts -
Step 3: Create
useAckshook -
Step 4: Create
Acks.tsxpage with empty state, populated list, download button -
Step 5: Add sidebar nav entry with
CheckCircle2icon -
Step 6: Add tests (1-2 cases: empty + populated)
-
Step 7: Verify typecheck + build + tests pass
-
Step 8: Commit
Task 17: Phase 3 commit + full suite
.venv/bin/pytest -q && npm test -- --run
Expected: ~225 backend, ~10 frontend.
Phase 4 — 270/271 eligibility
Task 18: 270 request models
Files:
-
Create:
backend/src/cyclone/parsers/models_270.py -
Step 1: Implement model classes per spec §4.2
-
Step 2: Wire lazy re-exports
-
Step 3: Sanity test
-
Step 4: Commit
Task 19: 271 response models + service-type-code dict
Files:
-
Create:
backend/src/cyclone/parsers/models_271.py -
Step 1: Implement model classes per spec §4.3
-
Step 2: Add inline
SERVICE_TYPE_CODESdict (~50 codes) -
Step 3: Add
service_type_description(code)helper -
Step 4: Sanity test
-
Step 5: Commit
Task 20: 270 request parser
Files:
-
Create:
backend/src/cyclone/parsers/parse_270.py -
Step 1: Add failing tests with minimal_270.txt fixture
-
Step 2: Run tests to verify they fail
-
Step 3: Implement orchestrator
-
Step 4: Run tests to verify they pass
-
Step 5: Commit
Task 21: 271 response parser
Files:
-
Create:
backend/src/cyclone/parsers/parse_271.py -
Step 1: Add failing tests with minimal_271.txt fixture (2 EB segments)
-
Step 2: Run tests to verify they fail
-
Step 3: Implement orchestrator with EB segment handling
-
Step 4: Run tests to verify they pass
-
Step 5: Commit
Task 22: 270 request serializer (outbound)
Files:
-
Create:
backend/src/cyclone/parsers/serialize_270.py -
Step 1: Add failing round-trip test
-
Step 2: Run test to verify it fails
-
Step 3: Implement
serialize_270 -
Step 4: Run test to verify it passes
-
Step 5: Commit
Task 23: POST /api/eligibility/request endpoint
Files:
-
Modify:
backend/src/cyclone/api.py -
Create:
backend/tests/test_api_eligibility.py -
Step 1: Add failing test
-
Step 2: Run test to verify it fails
-
Step 3: Implement endpoint
-
Step 4: Run test to verify it passes
-
Step 5: Commit
Task 24: POST /api/eligibility/parse-271 endpoint
Files:
-
Modify:
backend/src/cyclone/api.py -
Step 1: Add 2 failing tests (happy path + parse error)
-
Step 2: Run tests to verify they fail
-
Step 3: Implement endpoint
-
Step 4: Run tests to verify they pass
-
Step 5: Commit
Task 25: Fixtures + cross-parser round-trip test
Files:
-
Create:
backend/tests/fixtures/minimal_270.txt -
Create:
backend/tests/fixtures/minimal_271.txt -
Step 1: Write minimal_270.txt fixture (small valid 270 request)
-
Step 2: Write minimal_271.txt fixture (small valid 271 response with 2 EB segments)
-
Step 3: Add round-trip integration test (parse 270 → serialize → parse → assert equal)
-
Step 4: Verify tests pass
-
Step 5: Commit
Task 26: Phase 4 commit + full suite
.venv/bin/pytest -q
Expected: ~229 backend, ~10 frontend.
Phase 5 — Docs + smoke
Task 27: Spec doc already committed (T0)
Verify docs/superpowers/specs/2026-06-20-cyclone-edi-features-design.md is on branch.
Task 28: Update root README
Files:
-
Modify:
README.md -
Step 1: Update Roadmap section to mark SP3 shipped and describe 4 areas
-
Step 2: Commit
Task 29: End-to-end smoke
- Step 1: Clean DB, start server, parse 837 with
?ack=true - Step 2: Confirm 999 in response +
acksrow in DB - Step 3: Parse 999 fixture, confirm endpoint round-trips
- Step 4: Build 270 request via API, paste into parser
- Step 5: Commit empty
smoke: end-to-end SP3 flow passes
Task 30: Final cleanup + merge worktree to main
- Step 1: Verify worktree is clean
- Step 2: Discard main's stale uncommitted changes (if any)
- Step 3: Fast-forward merge
edi-featuresintomain - Step 4: Remove worktree + delete branch
Test count targets
| Phase | New backend | New frontend | Running total |
|---|---|---|---|
| Start | 205 | 8 | — |
| P1 end | +6 | 0 | 211 / 8 |
| P2 end | +4 | +1 | 215 / 9 |
| P3 end | +10 | +2 | 225 / 11 |
| P4 end | +4 | 0 | 229 / 11 |
Risk reminders
- 270/271 scope creep — keep tight to EQ/EB surface.
- 999 auto-gen semantics — "rejected" = validation errors.
- CARC codes drift —
LAST_UPDATEDconstant for tracking. - Migration ordering —
0002_acks.sqlafter0001_initial.sql, idempotent.