Tyler 6a65c2b750 fix(backend): spec-bug fixes for T10 — Match.claim_id non-unique + reconcile test dates
Three spec-bug fixes from T10 implementation:

- Match.claim_id: drop UNIQUE constraint (T4 schema error) — reversals add a 2nd row per claim (audit trail). Add explicit non-unique index ix_matches_claim_id to preserve query performance. Mirrored in ORM and migration 0001_initial.sql.
- test_run_orphan_remit_leaves_claim_unmatched: claim PCN-A does not match remit PCN-NEW, so unmatched_claims must be 1, not 0.
- test_run_reversal_flips_paid_to_reversed: claim service_date_from was 9 days before reversal remit service_date (outside default 7-day window) so no match occurred; changed to 5 days apart so match() picks the claim.
- test_match_unique_per_claim: in test_db_models.py asserted the now-removed UNIQUE behavior; renamed and inverted to assert the audit-trail design (two Match rows per claim allowed).
2026-06-19 22:59:20 -06:00

Cyclone

A self-hosted EDI claims management suite for a single billing office. Parses 837P professional claims and 835 ERA remittances (X12 005010X222A1 and 005010X221A1), with a local-only FastAPI backend and a React UI for browsing, filtering, and inspecting the parsed data.

Local-only on purpose: binds to 127.0.0.1, no auth, no internet exposure. Built for one operator, one machine, one trading partner (Colorado Medicaid, currently).

Install

# Backend (Python 3.11+)
cd backend
python -m venv .venv
.venv/bin/pip install -e '.[dev]'

# Frontend (Node 20+)
cd ..
npm install

Dev

Two terminals:

# Terminal 1 — backend
cd backend
.venv/bin/python -m cyclone serve
# (defaults to 127.0.0.1:8000; override with CYCLONE_PORT=...; reload with CYCLONE_RELOAD=1)

# Terminal 2 — frontend
npm run dev
# (Vite on http://localhost:5173)

Then open http://localhost:5173. Drop an .txt 837P or 835 file on the Upload page; navigate to Claims, Remittances, Providers, or Activity to see the parsed data.

The frontend reads its backend URL from VITE_API_BASE_URL (default empty). Create a .env.local at the repo root with:

VITE_API_BASE_URL=http://127.0.0.1:8000

Without that, the UI falls back to its in-memory sample store via the existing data adapter (parses are disabled).

Test

# Backend
cd backend && .venv/bin/pytest

# Frontend type-check + build
npm run typecheck
npm run build

# Frontend unit tests
npm test

Project layout

.
├── backend/
│   ├── src/cyclone/
│   │   ├── api.py             # FastAPI app, GET + parse routes
│   │   ├── store.py           # InMemoryStore, mappers
│   │   ├── __main__.py        # `python -m cyclone serve`
│   │   ├── cli.py             # click CLI
│   │   └── parsers/           # X12 tokenizer, models, validator, writers
│   └── tests/
│       ├── fixtures/          # co_medicaid_*.txt, minimal_*.txt
│       ├── test_api.py        # parse-837/835 round-trip
│       ├── test_api_gets.py   # 6 GET endpoints
│       ├── test_store.py      # store + mappers + iterators
│       ├── test_api_streaming.py
│       └── test_api_parse_persists.py
├── src/                       # React + Vite + TypeScript UI
│   ├── components/
│   │   └── ui/                # Skeleton, EmptyState, ErrorState, FilterChips, Pagination, …
│   ├── pages/                 # Claims, Remittances, Providers, Activity, Upload
│   ├── hooks/                 # useBatches, useClaims, useRemittances, useProviders, useActivity, useParse
│   ├── lib/                   # api.ts (6 GET + parse837/parse835/health), format.ts, utils.ts
│   ├── store/                 # zustand sample-data + parsed-batches store
│   └── types/                 # shared TS types
├── docs/
│   ├── reference/             # condensed 837P/835/X12/CO Medicaid notes
│   └── superpowers/plans/     # implementation plan
├── tailwind.config.js         # shimmer, scan, row-flash keyframes
└── package.json

Roadmap

This is sub-project 1 of 4. The other three are not in this build:

  • Sub-project 2 — DB + reconciliation. Replace the in-memory store with SQLite/Postgres; add 837P ↔ 835 reconciliation matching payouts to original claims.
  • Sub-project 3 — More 837P/835 features. Additional 837P validation rules (REF*G1 enforcement, BHT06), 835 CAS deep-parsing, 999 ACK, and 270/271 eligibility.
  • Sub-project 4 — Frontend features. Per-claim detail drawer, batch diff view, real-time streaming of NDJSON GET responses, advanced filters (date range, multi-status, saved filter sets), keyboard-driven navigation.

License

No license file yet; this is internal-use software. Add a LICENSE file when one is decided.

S
Description
No description provided
Readme 8.6 MiB
Languages
Python 62%
TypeScript 37.1%
CSS 0.4%
JavaScript 0.3%
Shell 0.1%