docs(sp42): skills cyclone-api-router/frontend-page/edi — refresh router count, page count, SP number

This commit is contained in:
Nora
2026-07-08 23:20:46 -06:00
parent 542a55fdbe
commit 0156051ca7
3 changed files with 38 additions and 26 deletions
+35 -23
View File
@@ -5,23 +5,27 @@ description: "Cyclone FastAPI router conventions (api_routers/, api_helpers.py,
# cyclone-api-router # cyclone-api-router
Cyclone splits its FastAPI surface two ways: small resource-group Cyclone splits its FastAPI surface two ways: resource-group routers
routers live in `backend/src/cyclone/api_routers/<topic>.py` and are live in `backend/src/cyclone/api_routers/<topic>.py` and are mounted
mounted bare into `api.py`; the high-traffic streaming and parse bare into `api.py`; the parse endpoints (`/api/parse-837`,
endpoints still live as top-level decorators in `api.py` itself. This `/api/parse-835`, plus the SP40 `/api/admin/validate-837` etc.) stay
skill codifies the conventions so additions stay consistent with the as top-level decorators in `api.py` because they span multiple store
four routers already shipped (`acks`, `admin`, `health`, `ta1_acks`). modules. This skill codifies the conventions so additions stay
consistent with the routers already shipped.
As of this writing: **4 router modules** under As of this writing: **22 router modules** under
`backend/src/cyclone/api_routers/`, **one shared helpers module** at `backend/src/cyclone/api_routers/` (`acks`, `activity`, `admin`,
`backend/src/cyclone/api_helpers.py` (248 lines, NDJSON primitives + `batches`, `claim_acks`, `claims`, `clearhouse`, `config`,
content negotiation + `tail_events`), and ~30 routes still inlined `dashboard`, `eligibility`, `health`, `inbox`, `parse`, `payers`,
in `backend/src/cyclone/api.py`. The next refactor target is the `providers`, `rebill`, `reconciliation`, `remittances`, `submission`,
parse endpoints. `ta1_acks`, plus a `_shared.py` for cross-router helpers), **one
shared helpers module** at `backend/src/cyclone/api_helpers.py`
(NDJSON primitives + content negotiation + `tail_events`), and
`api.py` itself is now 378 LOC (down from ~3,145 pre-SP36).
## Auth gate (SP24) ## Auth gate (SP23)
Every router declared in `backend/src/cyclone/api_routers/` **must** carry `dependencies=[Depends(matrix_gate)]` at the `APIRouter(...)` declaration — not on each individual endpoint. The gate lives at `backend/src/cyclone/auth/deps.py:107` and the role matrix is at `backend/src/cyclone/auth/permissions.py`. The roles are `admin / user / viewer`; `matrix_gate` returns 401 when there's no session and 403 when the role is below the endpoint's required role. When `AUTH_DISABLED` is True (conftest autouse fixture flips it; `CYCLONE_AUTH_DISABLED=1` in prod-by-mistake), the gate short-circuits to a synthetic admin — see the SP24 spec for the threat-model implications. New routers get the gate by default; the auth-aware convention is `router = APIRouter(dependencies=[Depends(matrix_gate)])`. Every router declared in `backend/src/cyclone/api_routers/` **must** carry `dependencies=[Depends(matrix_gate)]` at the `APIRouter(...)` declaration — not on each individual endpoint. The gate lives at `backend/src/cyclone/auth/deps.py:107` and the role matrix is at `backend/src/cyclone/auth/permissions.py`. The roles are `admin / user / viewer`; `matrix_gate` returns 401 when there's no session and 403 when the role is below the endpoint's required role. When `AUTH_DISABLED` is True (conftest autouse fixture flips it; `CYCLONE_AUTH_DISABLED=1` in prod-by-mistake), the gate short-circuits to a synthetic admin — see the SP23 spec for the threat-model implications. New routers get the gate by default; the auth-aware convention is `router = APIRouter(dependencies=[Depends(matrix_gate)])`.
## When to use ## When to use
@@ -45,11 +49,13 @@ Every router declared in `backend/src/cyclone/api_routers/` **must** carry `depe
1. **No new top-level routes in `api.py` for resource groups.** Any 1. **No new top-level routes in `api.py` for resource groups.** Any
endpoint grouped under a resource (`/api/<resource>` and its endpoint grouped under a resource (`/api/<resource>` and its
`/{id}` detail) lives in `backend/src/cyclone/api_routers/<topic>.py` `/{id}` detail) lives in `backend/src/cyclone/api_routers/<topic>.py`
as an `APIRouter`. The streaming list endpoints (`/api/claims/stream`, as an `APIRouter`. The streaming list endpoints
`/api/remittances/stream`, `/api/activity/stream`) and the parse (`/api/claims/stream`, `/api/remittances/stream`,
endpoints (`/api/parse-*`) currently stay in `api.py` because they `/api/activity/stream`, `/api/acks/stream`, `/api/ta1-acks/stream`)
span multiple store modules — don't move them unless you're also were split into their per-resource routers as part of SP36; only
restructuring the store split. the cross-resource parse endpoints (`/api/parse-*` and the SP40
`validate-837`) stay in `api.py` because they span multiple store
modules.
2. **Reuse `api_helpers.py`.** NDJSON primitives (`ndjson_line`, 2. **Reuse `api_helpers.py`.** NDJSON primitives (`ndjson_line`,
`ndjson_stream_list`, `ndjson_stream_837`, `ndjson_stream_835`), `ndjson_stream_list`, `ndjson_stream_837`, `ndjson_stream_835`),
content negotiation (`client_wants_json`, `wants_ndjson`), the content negotiation (`client_wants_json`, `wants_ndjson`), the
@@ -80,8 +86,9 @@ Every router declared in `backend/src/cyclone/api_routers/` **must** carry `depe
`StreamingResponse(media_type="application/x-ndjson")` and feed it `StreamingResponse(media_type="application/x-ndjson")` and feed it
either `ndjson_stream_list(items, total, returned, has_more)` (for either `ndjson_stream_list(items, total, returned, has_more)` (for
list pages) or `tail_events(request, bus, kinds)` (for live-tail list pages) or `tail_events(request, bus, kinds)` (for live-tail
pages). See `acks.py:62-66` for the list-stream skeleton and pages). See `api_routers/acks.py:62-66` for the list-stream
`api.py:1357-1401` for the full live-tail pattern (snapshot → skeleton and `api_routers/claims.py` (the `/api/claims/stream`
handler) for the full live-tail pattern (snapshot →
`snapshot_end` → subscription → heartbeats). See `cyclone-tail` `snapshot_end` → subscription → heartbeats). See `cyclone-tail`
for the wire format. for the wire format.
6. **Tests.** Every new endpoint gets a `test_api_<topic>_<verb>.py` 6. **Tests.** Every new endpoint gets a `test_api_<topic>_<verb>.py`
@@ -170,12 +177,17 @@ just after middleware registration and just before the first
# Resource-group routers. Each module owns its own APIRouter and is # Resource-group routers. Each module owns its own APIRouter and is
# registered below. New resources go in `cyclone.api_routers.<name>` # registered below. New resources go in `cyclone.api_routers.<name>`
# and are wired in here. # and are wired in here.
from cyclone.api_routers import acks, admin, health, ta1_acks # noqa: E402 from cyclone.api_routers import ( # noqa: E402
acks, activity, admin, batches, claim_acks, claims, clearhouse,
config, dashboard, eligibility, health, inbox, parse, payers,
providers, rebill, reconciliation, remittances, submission, ta1_acks,
)
app.include_router(health.router) app.include_router(health.router)
app.include_router(acks.router) app.include_router(acks.router)
app.include_router(ta1_acks.router) app.include_router(activity.router)
app.include_router(admin.router) app.include_router(admin.router)
# ... 18 more include_router calls in api.py ...
``` ```
## Anti-patterns ## Anti-patterns
+1 -1
View File
@@ -7,7 +7,7 @@ description: "Cyclone EDI parser/validator conventions (837P/835/999/270/271/277
Cyclone parses seven X12 EDI transaction types (837P, 835, 999, 270, 271, 277CA, TA1) into typed Pydantic models, then runs per-claim / per-batch validator rules that surface as R-coded `ValidationIssue` records. This skill codifies the conventions so new parsers and rules stay consistent with the seven that already exist. Cyclone parses seven X12 EDI transaction types (837P, 835, 999, 270, 271, 277CA, TA1) into typed Pydantic models, then runs per-claim / per-batch validator rules that surface as R-coded `ValidationIssue` records. This skill codifies the conventions so new parsers and rules stay consistent with the seven that already exist.
As of this writing: **7 parser modules** under `backend/src/cyclone/parsers/parse_<edi>.py`, **~25 per-claim rules** numbered `R010``R100` and `R200``R210` in `validator.py`, plus a parallel set of **835-specific rules** prefixed `R835_*` in `validator_835.py`. The next increment is **SP22**. As of this writing: **7 parser modules** under `backend/src/cyclone/parsers/parse_<edi>.py` (837P, 835, 999, TA1, 270, 271, 277CA), **~25 per-claim rules** numbered `R010``R100` and `R200``R210` in `validator.py`, plus a parallel set of **835-specific rules** prefixed `R835_*` in `validator_835.py`. The most recently shipped increment touching this surface is **SP41** (in-window rebill pipeline); the next free increment after this SP42 doc-pass is **SP43**.
## When to use ## When to use
@@ -5,9 +5,9 @@ description: "Cyclone React page conventions (TanStack Query, use<X> hook, drawe
# cyclone-frontend-page # cyclone-frontend-page
Cyclone pages live at `src/pages/<Name>.tsx`: a `use<X>` hook in `src/hooks/use<X>.ts` does the fetching (and optionally the live-tail subscription), `<Layout>` + `<PageHeader>` + `<Sidebar>` (`src/components/`) provide the app shell, and any right-side detail (claim, remittance) lives in `src/components/<DrawerName>/` whose open/close state is mirrored to the URL via `useDrawerUrlState`. This skill codifies the conventions so additions stay consistent with the eleven pages already shipped. Cyclone pages live at `src/pages/<Name>.tsx`: a `use<X>` hook in `src/hooks/use<X>.ts` does the fetching (and optionally the live-tail subscription), `<Layout>` + `<PageHeader>` + `<Sidebar>` (`src/components/`) provide the app shell, and any right-side detail (claim, remittance) lives in `src/components/<DrawerName>/` whose open/close state is mirrored to the URL via `useDrawerUrlState`. This skill codifies the conventions so additions stay consistent with the twelve pages already shipped (Login added in SP23).
As of this writing: **11 pages** under `src/pages/` (9 of 11 with a `*.test.tsx` sibling — Dashboard, Upload, and BatchDiff are not yet covered; be the first when you refactor them), **~30 hooks** under `src/hooks/`, and **2 drawer modules** at `src/components/{ClaimDrawer,RemitDrawer}/`. The next increment is **SP22**. As of this writing: `**12 pages** under `src/pages/` (10 of 12 with a `*.test.tsx` sibling — Dashboard and BatchDiff are not yet covered; be the first when you refactor them; the Login page does not need a sibling), **~30 hooks** under `src/hooks/`, and **4 drawer modules** at `src/components/{ClaimDrawer,RemitDrawer,ProviderDrawer,AckDrawer}/`. The most recently shipped increment is **SP41** (in-window rebill pipeline); the next free increment after this SP42 doc-pass is **SP43**.
## When to use ## When to use