Dashboard.tsx was reading claims/providers/activity directly from the
zustand `useAppStore`, which returns the hardcoded sample fixtures
(sampleClaims / sampleProviders / sampleActivity) regardless of whether
a backend session is active. The hooks useClaims / useProviders /
useActivity are wired correctly to /api/* when api.isConfigured, so
the fix is to swap the three direct store reads for the hooks.
buildMonthly's parameter is now typed as Claim[] (the live Claim
shape) instead of the store's heterogeneous slice, and the
`useAppStore` import is gone.
Verified end-to-end: after logging in as admin via the live server,
the Dashboard now shows $0 KPIs, "0 events / No activity yet.",
and the live operator name in the greeting — proving the hooks are
hitting /api/* and not falling back to the sample store.
Two issues surfaced by going live against the dev backend:
1. Dashboard greeting was hardcoded 'Good morning, Jordan.' in
src/pages/Dashboard.tsx. The sidebar already pulls from useAuth()
but the dashboard had been missed. Replace with a time-of-day
greeting ('morning' / 'afternoon' / 'evening') plus the live
operator's username from useAuth(). Falls back to 'there' while
/api/auth/me is in flight so we never flash 'undefined' at the
operator.
2. api.isConfigured was returning false whenever VITE_API_BASE_URL
was empty, which is the default in .env.example and the
recommended setting for the Vite dev proxy / nginx. With the flag
false, every hook (useClaims, useBatches, useActivity, ...)
threw notConfiguredError() and silently fell back to the
in-memory zustand store's hardcoded sample data — so 'files not
serving' was actually the Dashboard rendering fixtures, not the
real DB. With the flag now true, joinUrl('') produces relative
URLs that the proxy intercepts and forwards to FastAPI on :8000.
Set VITE_API_BASE_URL=disabled to opt out (useful for Storybook
/ offline UI work).
Also installs @radix-ui/react-tabs which was listed in package.json
but missing from node_modules — Vite was failing pre-transform on
tabs.tsx and refusing to compile downstream pages until deps were
re-installed via 'npm install'.
Two bootability gaps caught by running the live stack:
1. cyclone.auth.bootstrap.run() called SessionLocal() before db.init_db()
had run on a fresh DB, so 'python -m cyclone serve' / 'users create'
/ '--help' all crashed with 'db.init_db() has not been called'.
Add init_db() inside bootstrap (idempotent — no-op once the schema
is current) so first-boot works without manual prep.
2. PERMISSIONS matrix didn't register the inbox endpoints that were
added in SP10 (export.csv) and SP14 (candidates/dismiss,
rejected/resubmit, payer-rejected/acknowledge). The matrix's
longest-prefix match meant /api/inbox/candidates/dismiss matched
the '/api/inbox' prefix but only because that single entry
existed; adding the dedicated write-prefix entries makes the
intent explicit and keeps viewer role out of every inbox write
even if the broader /api/inbox prefix is later broadened.
Verified live:
- admin can login, GET /api/inbox/lanes (200), GET
/api/inbox/export.csv (200), POST /api/inbox/candidates/dismiss
(200).
- viewer can login, GET /api/inbox/lanes (200), GET
/api/inbox/export.csv (200), but POST /api/inbox/candidates/dismiss
and POST /api/inbox/rejected/resubmit both return 403.
- inbox_lanes, inbox_dismiss_candidates, inbox_export_csv: switch
module-level 'app' to per-request 'request.app' so per-endpoint
state stays consistent with the test's TestClient target across
importlib.reload() (test_api.py::test_cors_extra_origins_via_env
reloads the api module to mutate CORS allow-lists; pre-reload
endpoints then mutate the wrong app instance).
- _http_exc_handler: when HTTPException.detail is a dict, wrap under
'detail' so the standard envelope stays stable and callers can
branch on body['detail']['error'].
- conftest._auto_init_db: re-resolve cyclone.api.app each fixture
invocation (instead of caching at module load) so the reload pattern
doesn't leave event_bus set on a stale app.
- test_acks.test_migration_latest_idempotent_on_fresh_db: bump
user_version assertion from 12 to 14 after auth migration renumber
(0013 users+sessions, 0014 audit_log.user_id).
Also installs sqlcipher3 + paramiko into the backend venv so the
capability tests can run; both modules were optional deps that
test_db_crypto and test_sftp_paramiko assume are present.
Backend test results: 1008 passed, 9 skipped (gitignored prodfile
fixtures), 0 failed.
The auth cherry-picks brought in 0010_auth_users_and_sessions.sql
(version: 10) and 0011_audit_log_user_id.sql (version: 11), which
collide with main's 0010_payer_rejected_acknowledged.sql (version: 10)
and 0011_processed_inbound_files.sql (version: 11). The migration
runner is filename-sorted but uses the -- version: N header to gate
'application vs skip', so the second file at each version gets
silently skipped — claims.payer_rejected_acknowledged_at never
got ALTERed, breaking every test that touches the claims table.
Renumber to 0013 and 0014 (and bump the -- version headers to
match) so they apply after 0012_backups.sql and main's existing
SP14 / SP15 migrations.
- New test_existing_endpoints_require_auth.py: spot-check that existing
/api/* endpoints now require auth (gated via Depends(matrix_gate)) when
AUTH_DISABLED is False. Health remains public.
- conftest.py: flip AUTH_DISABLED=True for the suite so the legacy
pre-auth tests keep passing without login. Auth tests flip it back
off via their own autouse fixture (now patched to use monkeypatch
for cleanup).
Verified: 53 auth tests pass; 222 pre-existing non-auth failures are
unchanged.
Re-apply f91d7b3 manually against current Upload.tsx / Inbox.tsx /
Reconciliation.tsx since main's UI text diverged from the original
cherry-pick (Release to 'parse' span, mono uppercase tracking, etc.).
The write affordances are now wrapped in <RoleGate allow={admin,user}>:
- Upload.tsx: dropzone + Parse/Clear buttons
- Inbox.tsx: rejected / payer_rejected / candidates BulkBars
- Reconciliation.tsx: 'Match selected' button
Test fixtures stub useAuth to return an admin user so RoleGate lets
the BulkBars / Match button render synchronously on first paint —
the tests don't need a full AuthProvider + /api/auth/me probe.
Replaces the skipped 63ae0d2 commit: adds matrix_gate to deps.py and
applies it as a dependency on every authenticated FastAPI route, including
extracted routers (acks, ta1_acks, admin) and inline endpoints in api.py.
The PERMISSIONS matrix in auth/permissions.py controls which roles can
hit which (method, path) combinations; matrix_gate is fail-closed by
default (any endpoint not in the matrix returns 403).
Follow-up to 7c1be58. pytest-randomly was added to a PEP 735
[dependency-groups] dev block by 'uv add --dev', leaving the
project with two 'dev' groups (the legacy [project.optional-
dependencies] dev and the new [dependency-groups] dev). To get
the full dev env someone had to run 'uv sync --extra dev --group
dev', which is easy to miss.
This commit moves pytest-randomly>=4.1 into the
[project.optional-dependencies] dev list (where pytest,
pytest-cov, pytest-asyncio, and httpx already live) and drops
the [dependency-groups] block. 'uv sync --extra dev' now
installs the full dev toolchain. uv.lock updated accordingly:
the [package.dev-dependencies] and [package.metadata.requires-
dev] sections are gone, and pytest-randomly is part of the
standard dev extra.
Two follow-ups to the v0.2.0 release:
1. Add cryptography>=49.0,<50 to the [project.dependencies] list.
cyclone.backup / cyclone.backup_service import it at module top
level, so it has to be a hard dep — not an extra — or the test
suite fails to collect on a fresh 'uv sync'. A clean install
(without this commit) gets 28 collection errors with no tracked
file changes. This was latent because the original 99M venv
had cryptography installed out-of-band and masked the missing
dep declaration.
2. Add pytest-randomly>=4.1.0 to the dev dep group.
Used to characterize the test suite's order-dependence: under
alphabetic order the suite shows 7 failures; under seed=12345
it shows 34. That variance is evidence of test-order state
interactions, not venv issues. Keeping pytest-randomly in
the dev deps so this can be re-verified on demand.
Note: pytest-randomly was added with 'uv add --dev', which uses
the PEP 735 [dependency-groups] syntax. pyproject.toml now has
two 'dev' groups — the legacy [project.optional-dependencies] dev
= [pytest, pytest-cov, pytest-asyncio, httpx] and the new
[dependency-groups] dev = [pytest-randomly]. To get the full dev
env, run: uv sync --extra dev --group dev. Worth consolidating
in a follow-up.
Backend:
- New POST /api/batches/{id}/export-837: regenerate X12 837 files
for a list of claim_ids into a ZIP using HCPF file naming standards,
with a unique interchange/group control number per export. Wire
the clearhouse Loop 1000A (NM1*41 + PER) and per-payer receiver
(NM1*40) blocks so the serializer no longer falls back to
CYCLONE / RECEIVER placeholders.
- /api/parse-837 and /api/parse-835 now surface the server-side
batch_id in both JSON and NDJSON response shapes so the frontend
can hit batch-scoped endpoints without an extra listBatches
round-trip.
- Filename helpers and the 837 serializer updated to match the new
HCPF envelope; tests cover batch export, parse batch_id, and the
serializer's control-number uniqueness guarantee.
Frontend:
- New shared components: ClaimCard, ClaimCard837, DominantKpiCard,
EditorialNote, ExportBar, TickerTape, and a charts/ set
(BarChart, HBarChart, SegmentedBar, AgingBars).
- New useBatchExport hook driving ExportBar's download flow against
the new endpoint.
- ClaimDrawer, Lane, and Layout migrated from raw CSS-variable
colors to Tailwind theme tokens (bg-card, text-foreground,
border/60, etc.) for consistency with the rest of the instrument
chrome; the active tab indicator gains a subtle accent glow.
- Upload, Inbox, Batches, BatchDiff, Reconciliation, and Acks pages
reworked to compose the new shared components and consume the new
batch-scoped API surface (notably ExportBar wired into Batches).
Tooling / Docs:
- Add audit-uiux.mjs and a docs/goodclaim.x12 sample fixture.
- Update ClaimDrawer testids and add coverage for the new
components and the useBatchExport hook.
Rolls up into the v0.2.0 release tag.
Phase 5 Task 5.8 (PartiesGrid) and 5.9 (ValidationPanel) added useDrillStack()
calls, so Claims.test.tsx needs a DrillStackProvider wrapper around the
page render to keep the hook happy. Task 5.10 refactored the ClaimDrawer
header onto the shared DrillDrawerHeader, so the deep-link and close-button
tests need to find the title via <h2> and the close button via
aria-label='Close drawer' instead of the now-removed header-id /
header-close testids.
Without this fix the page-level Claims tests report 3 failures
(deep link, URL clear after close, ?-mark with drawer closed) that
were not failing on the Phase 4 base (9a313d2).
The implementer caught that claims.id is a single-column PK, which
prevents the same CLM01 from existing in multiple batches. That
makes the spec's pre-flight 409 workflow unreachable and resubmits
impossible. Migration 0014 relaxes the PKs to composite (batch_id,
id) on both claims and remittances, and updates all FKs that
referenced the old single-column PK. Task 1.3 in the plan is now
the migration; the previous Task 1.3 (helper tightening) is
renumbered to Task 1.4 and will run after 0014 lands.