Commit Graph

90 Commits

Author SHA1 Message Date
Nora 9d8f83d111 docs(plan): implementation plan for SP27 remittances architecture refactor
17 tasks: 1 preflight + 4 handler extracts (999/TA1/277CA/835) +
helpers dedup + INBOUND_RE loosen + SFTP timeouts + status surface +
atomic 835/reconcile + match invariants + chain endpoint +
claim.rejected_after_remit + frontend chain UI + final verify + merge.

Each task ends with live-test + autoreview + commit. Spec:
docs/superpowers/specs/2026-06-29-cyclone-remittances-architecture-refactor-design.md
2026-06-29 09:59:53 -06:00
Nora 454c3598b1 docs(spec): design for SP27 remittances architecture refactor
Tier 1: split scheduler.py into handlers/ subpackage, dedup helpers,
loosen INBOUND_RE, add SFTP operation timeouts, surface SFTP errors
in Scheduler.status().

Tier 2: unify 835 ingest + reconciliation into one critical section,
add GET /api/claims/{id}/chain, guard matched_remittance_id ↔
Remittance.claim_id invariant, emit claim.rejected_after_remit audit
when a 277CA rejection hits a matched claim.

Status: Draft, awaiting user sign-off.
2026-06-29 09:54:33 -06:00
Nora dd7da18279 fix(sftp): swap inbound/outbound paths — FromHPE is inbound, ToHPE is outbound
The dzinesco SP9 seed had `paths.inbound` and `paths.outbound` mapped to
the wrong Gainwell MFT directories:

  - paths.outbound  was  FromHPE/   (HPE sends files FROM here TO us — inbound)
  - paths.inbound   was  ToHPE/     (we send files TO here — outbound)

So `/api/clearhouse/submit` was writing 837P claims to FromHPE (where
HPE puts acks/835s) and the SP16 scheduler was polling ToHPE (where our
claims go). 999 / TA1 / 835 files in the real FromHPE inbox were
unreachable.

Semantics per operator (2026-06-24):
  - FromHPE = HPE/Gainwell → us  = 999, TA1, 835  (inbound)
  - ToHPE   = us → HPE/Gainwell = 837P claims    (outbound)

**Runtime code (the actual fix):**
  - backend/src/cyclone/store.py            — SP9 seed paths flipped
  - backend/src/cyclone/edi/filenames.py    — docstring corrected

**Test fixtures + assertions (would otherwise fail on the new seed):**
  - backend/tests/test_clearhouse_api.py
  - backend/tests/test_providers_seed.py
  - backend/tests/test_sftp_stub.py        — incl. inbound dir paths
  - backend/tests/test_sftp_paramiko.py
  - backend/tests/test_store_update_clearhouse.py
  - backend/tests/test_api_clearhouse_patch.py
  - backend/tests/test_scheduler.py
  - backend/tests/test_api_scheduler.py

**Docs (text-only — keeps the codebase self-consistent):**
  - README.md
  - docs/reference/co-medicaid.md
  - docs/superpowers/specs/2026-06-20-cyclone-multi-payer-npi-sftp-design.md

**Operator action required after merge:** the existing clearhouse row in
`~/.local/share/cyclone/cyclone.db` was seeded with the old wrong
paths. Easiest recovery:
  1. `rm ~/.local/share/cyclone/cyclone.db` and let `ensure_clearhouse_seeded` re-run on next boot, OR
  2. PATCH /api/clearhouse with the new `paths` block (the SP25
     reconfigure hook picks it up live, including by the running scheduler).

**Verification:**
  - 82 tests in the affected files pass (test_clearhouse_api, test_providers_seed,
    test_sftp_stub, test_sftp_paramiko, test_store_update_clearhouse,
    test_api_clearhouse_patch, test_scheduler, test_api_scheduler, test_filenames).
  - Full backend suite: 1029 pass + 36 pre-existing order-dependent flakes
    unrelated to this change (verified by running the same tests in isolation).
2026-06-24 22:05:55 -06:00
tyler b7be9f38bc merge: SP25 + SP26 SFTP polling + secret-file lookup into Version-1.0.0
Bring the SP25 SFTP Polling Enablement (PATCH /api/clearhouse,
scheduler hot-reload, env-var-first secret lookup) and SP26
SFTP Password File Companion (Docker secret _FILE tier) onto
the v1.0.0 release branch so the production stack can actually
flip the Gainwell MFT poll from stub → real.

Conflict resolved in docker-compose.yml: kept both the SP26
CYCLONE_SFTP_PASSWORD_FILE env var and the new 'Always bind
to 0.0.0.0' comment.
2026-06-24 18:09:32 -06:00
tyler edca04868d feat: always bind to 0.0.0.0
Reachability is controlled by the host firewall / compose port
publishing, not the bind address. Backend, compose, and docs all
now default to 0.0.0.0 so the API is reachable from the frontend
container, the host, and the LAN without per-env overrides.

Files:
- backend/src/cyclone/__main__.py: default host = 0.0.0.0
- docker-compose.yml: refresh the comment to match the new posture
- CLAUDE.md / README.md / docs/ARCHITECTURE.md / docs/REQUIREMENTS.md:
  reframe the bind note accordingly
2026-06-24 17:40:18 -06:00
Nora 3c907d17e9 docs(sp26): RUNBOOK — CYCLONE_SFTP_PASSWORD_FILE row + Docker variant 2026-06-24 16:08:12 -06:00
Nora 3075955826 docs(plan): SP26 SFTP Password File Companion — implementation plan 2026-06-24 16:03:51 -06:00
Nora 5b4c1513b6 docs(spec): SP26 SFTP Password File Companion — initial design 2026-06-24 15:52:19 -06:00
Nora 13a3e7c334 docs(sp25): RUNBOOK.md — enable SFTP polling for real Gainwell MFT 2026-06-24 15:38:35 -06:00
Nora 9fda46b891 docs(plan): SP25 SFTP Polling Enablement — implementation plan 2026-06-24 15:28:54 -06:00
Nora 7d2000ec31 docs(spec): SP25 SFTP Polling Enablement — initial design 2026-06-24 15:25:20 -06:00
Nora 5334646992 docs(plan): SP23 — record live verification results
Appends a 'Live verification' section to the plan with the six bugs
the live bring-up surfaced + their fixes, and the end-to-end smoke
results (login, /api/auth/me, /api/parse-837 with a real 837P,
full test suite 1026 passed).
2026-06-23 17:55:54 -06:00
Nora 3ba5ca0849 feat(sp23): live-verification fixes from end-to-end bring-up
After the 8-commit SP23 implementation landed, kicking the tires on
`docker compose build && docker compose up -d` (the gated DOCKER_TESTS=1
live test) surfaced five real bugs that don't show up in unit tests:

1. Backend wheel was built from the stub `__init__.py`, not the real
   source. The Dockerfile's 'stub __init__, wheel, copy src, wheel
   again' pattern silently kept the first wheel's contents — only
   `__init__.py` got re-stubbed. The installed package had an empty
   `__init__.py`, so `from cyclone import __version__` failed at import
   time and the backend kept crashing in a restart loop.
   Fix: single `COPY src/` + single `pip wheel`. Comment explains
   why the stub trick is gone for good.

2. Backend binds to 127.0.0.1 (intentional — local-only by design,
   see CLAUDE.md). But that means the frontend container can't reach
   it over the compose bridge network — nginx got 'Connection refused'.
   Fix: `CYCLONE_HOST` env var, defaults to 127.0.0.1 (preserves local
   posture for non-Docker runs), set to 0.0.0.0 by the docker-compose
   backend service. Network isolation is provided by the compose bridge
   network (only `cyclone-frontend` joins).

3. Healthcheck probed `/api/healthz` (404 — the route is `/api/health`).
   Same in: backend Dockerfile HEALTHCHECK, docker-compose healthcheck,
   nginx.conf doesn't have one (frontend proxies through), RUNBOOK.md,
   scripts/post-deploy.sh, scripts/smoke.sh.
   Fix: `/api/healthz` → `/api/health` everywhere SP23 owns.

4. The auth matrix in `cyclone.auth.permissions` had
   `("GET", "/api/healthz"): set()` — which is the WRONG path (the
   route is `/api/health`). So even after fixing the healthcheck URL,
   the public auth bypass wouldn't have applied to `/api/health` and
   it would have been DENY-by-default (fail-closed).
   Fix: matrix entry updated to `/api/health`.

5. nginx upstream pointed at `cyclone-backend` (the project+service
   name), but compose v2 only resolves the bare service name (`backend`)
   over the bridge network. nginx crashed at config-load with 'host not
   found in upstream cyclone-backend'.
   Fix: `cyclone-backend:8000` → `backend:8000` in nginx.conf + spec
   + plan.

6. Frontend HEALTHCHECK used `http://localhost:8080/`. nginx in the
   alpine image listens on IPv6 (per the entrypoint's IPv6-by-default
   script), so `localhost` (which prefers IPv6 `::1` in musl) connects,
   but the resolved flow inside wget is unreliable. `127.0.0.1` works.
   Fix: HEALTHCHECK uses `http://127.0.0.1:8080/`.

Also moves `frontend/Dockerfile` → `Dockerfile.frontend` and
`frontend/nginx.conf` → `nginx.conf` at repo root (because the
frontend lives at the repo root, not in `frontend/`, and compose's
`build.context: .` needs them at the same root as compose.yml).
The frontend's pre-existing `.dockerignore` was empty/unused, so it's
dropped — the root `.dockerignore` covers it.

Adds `docker-compose.override.yml` for local bring-up testing on a
host without sudo. Production uses `/etc/cyclone/secrets/` directly.

Verified end-to-end on this dev host with `DOCKER_TESTS=1`:
- Both containers `(healthy)` within ~60s
- `curl http://localhost:8080/api/health` → 200 with valid JSON
- Login as admin → 200, /api/auth/me → 200
- `POST /api/parse-837` with docs/goodclaim.x12 → 200, batch created
- Full backend test suite: 1014 passed, 9 skipped (prodfiles gitignored)
2026-06-23 17:53:16 -06:00
Cyclone 364e5d7497 docs(plan): SP23 Ubuntu Docker Deployment implementation plan
12 tasks from worktree bootstrap through single-atomic-merge into main. Backend Dockerfile (multi-stage python:3.11-slim-bookworm + sqlcipher + non-root + healthcheck), frontend Dockerfile (multi-stage node:20-alpine build + nginx:1.27-alpine runtime), docker-compose.yml at repo root with two services + named volumes + Docker secrets, scripts/{cyclone-init,post-deploy,smoke}.sh, RUNBOOK.md, tests/test_docker.py (compose config + Dockerfile parse + optional live bring-up), and a small auth bootstrap extension to read CYCLONE_ADMIN_*_FILE for Docker-secret compatibility. Closes the SP23 spec; see spec section 1.1 for what auth is reused from main vs. what SP23 actually adds.
2026-06-23 17:21:34 -06:00
Cyclone 35c561de20 docs(spec): mark SP23 approved and reconcile with auth on main
Auth landed in main on 2026-06-23 (migrations 0013/0014, cyclone.auth package, bcrypt + matrix_gate, Login.tsx + AuthProvider + RoleGate, CYCLONE_AUTH_DISABLED escape hatch). The original draft was written 2026-06-22 and proposed argon2id, admin/operator/viewer roles, a fresh migration 0012, and an inline require_role dependency. All of that is already in main. Add a 1.1 Delta vs. main section and switch role names from operator to user to match cyclone.auth.permissions.Role.
2026-06-23 17:20:04 -06:00
cyclone c398aa7d29 Merge branch 'sp24-doc-posture-alignment'
Align CLAUDE.md + REQUIREMENTS.md + ARCHITECTURE.md with the auth work
that landed on main on 2026-06-23; emit an AUTH_DISABLED WARNING at boot
when AUTH_DISABLED is true so misconfigured production deploys fail loudly.

Closes requirements R-1 (was Open; auth shipped via the origin/main merge,
SP24 reconciles the docs).

* CLAUDE.md — track in git for the first time
* docs/REQUIREMENTS.md + docs/ARCHITECTURE.md — replace every stale
  'no auth' / 'no authentication' / 'no second party to authenticate'
  claim with the v1 posture (bcrypt + HttpOnly session cookie; first
  admin bootstrapped from CYCLONE_ADMIN_USERNAME + CYCLONE_ADMIN_PASSWORD)
* backend/src/cyclone/__main__.py — boot-time WARNING when AUTH_DISABLED
* ARCHITECTURE §4.2 module map — add cyclone.auth.* package
* ARCHITECTURE §6.1 migrations table — list 0013 + 0014 with renumbering note
* .superpowers/skills/{cyclone-tests,cyclone-api-router,cyclone-spec} —
  add AUTH_DISABLED / matrix_gate / auth-aware threat-model guidance

Spec: docs/superpowers/specs/2026-06-23-cyclone-auth-posture-alignment-design.md
Plan: docs/superpowers/plans/2026-06-23-cyclone-auth-posture-alignment.md
2026-06-23 16:28:28 -06:00
cyclone 616d467c65 docs(reviews): capture 2026-06-23 docset review + CSS reduction experiment
Commit the day's review artifacts so they aren't lost in the working tree:

* 2026-06-23-cyclone-docset-review-A.md — full docset review (Reviewer A:
  requirements + architecture lens; flags the 8-state vs 7-state claim
  lifecycle divergence, FR-20 vs ARCHITECTURE §5.4 tail-endpoint conflict,
  api_routers/ package split status, etc.)
* 2026-06-23-cyclone-docset-review-B.md — code-first counterpart (Reviewer B)
* 2026-06-23-cyclone-groundtruth-audit.md — live-data readiness audit
* 2026-06-23-css-reduction/ — iterative CSS reduction experiment
  (baseline 60,695 B raw / 11,786 B gzip / 9,875 B brotli; 18 prioritized
  candidates; per-iter reports and progress log)

Binary artifacts (PNGs, dist build output) are intentionally not committed —
they're working files for the experiment, not review records.
2026-06-23 16:28:15 -06:00
Tyler 00f11f84b6 feat(sp24): align CLAUDE.md + REQUIREMENTS.md + ARCHITECTURE.md with auth work + AUTH_DISABLED WARNING
Replaces every stale 'no auth' / 'no authentication' / 'no second party to
authenticate' claim in the three top-level docs with the v1 posture: the
auth boundary is HTTP (bcrypt + HttpOnly session cookie; first admin
bootstrapped from CYCLONE_ADMIN_USERNAME + CYCLONE_ADMIN_PASSWORD env
vars); the file-system posture (SQLCipher at rest, macOS Keychain) is
unchanged; the threat model is still a stolen/imaged drive.

Closes requirements §11 R-1 (was Open; now Closed by SP24 — auth shipped
via the origin/main merge on 2026-06-23, SP24 reconciled the docs).

Also:
- backend/src/cyclone/__main__.py — emit WARNING when AUTH_DISABLED is
  True at boot so a misconfigured production deploy fails loudly
- §6.1 migrations table in ARCHITECTURE.md — list 0013 (auth_users_and_
  sessions) and 0014 (audit_log_user_id) with the renumbering note
- §4.2 module map — add the cyclone.auth.* package
- Three skills addenda: cyclone-tests (AUTH_DISABLED conftest bypass is
  mandatory context), cyclone-api-router (every router needs
  Depends(matrix_gate)), cyclone-spec (spec template threat-model is now
  auth-aware)

This also tracks CLAUDE.md in git for the first time (was previously
untracked; the SP24 doc updates are in scope for the increment).

Spec: docs/superpowers/specs/2026-06-23-cyclone-auth-posture-alignment-design.md
Plan: docs/superpowers/plans/2026-06-23-cyclone-auth-posture-alignment.md
2026-06-23 14:49:31 -06:00
Tyler d294b8bbed docs(plan): SP24 — auth posture alignment implementation plan
Plan covers the docs-only reconciliation between the auth work that landed
in main on 2026-06-23 and the three top-level docs (CLAUDE.md, docs/
REQUIREMENTS.md, docs/ARCHITECTURE.md). Includes a 6-line __main__.py edit
for the AUTH_DISABLED startup WARNING and one-paragraph addenda to the
cyclone-tests / cyclone-api-router / cyclone-spec skills.

Spec: docs/superpowers/specs/2026-06-23-cyclone-auth-posture-alignment-design.md
2026-06-23 14:49:26 -06:00
Tyler 9e595503b7 docs(spec): SP24 — align docs and decisions with the auth work in main
The auth-merge that landed on 2026-06-23 (14 commits, a25504b..39ae988)
adds real auth: User/Session models, bcrypt, matrix_gate, AUTH_DISABLED,
a login router, RBAC on every existing endpoint, and a RequireAuth
guard on the React shell. But CLAUDE.md, docs/REQUIREMENTS.md, and
docs/ARCHITECTURE.md (all added/updated on the css-reduction branch)
still describe the pre-auth "no auth, local-only, single operator,
single host" posture. The doc/code divergence is real and would
confuse anyone reading the docs.

This is the spec for SP24 — the alignment. It is intentionally a
docs/skills-only SP: no schema, no role, no gate changes. The plan
section enumerates the doc lines to update; the implementation step
sweeps the markdown for residual "no auth" claims.

Draft, awaiting user sign-off per the SP-N spec flow.
2026-06-23 14:49:21 -06:00
Tyler 1148a03a43 docs(architecture): add top-level technical design doc
Closes round 4 of doc-prep. ARCHITECTURE.md is the day-1 read for
engineers joining Cyclone: process & network topology, tech stack
(backend + frontend + external), backend package layout with
one-line module responsibility map, frontend architecture (route
map, state management, live tail), data model (migrations 0001-0012,
ERD, 7-state claim lifecycle, audit chain), data flow (manual upload,
SFTP polling, auto-match, outbound 837P, ack ingestion), API surface,
cross-cutting concerns (logging, audit, PHI/PII, error model),
operational concerns (startup order, schedulers, health probe,
shutdown), deployment, out-of-scope, and references.

759 lines, 13 top-level sections. Cross-linked from REQUIREMENTS.md
sister-docs block and from README.md Roadmap section.
2026-06-23 14:49:16 -06:00
Tyler cad104a38c docs(requirements): add top-level requirements + DoD
38 functional requirements + 18 non-functional requirements across 13 sections.
Includes traceability matrix mapping FRs to NFRs and to existing
docs/superpowers/specs/* artifacts. Closes round 1 of doc-prep
preparation for Cyclone implementation.
2026-06-23 14:49:12 -06:00
Nora d552d3d3ec docs(plan): auth implementation plan — 29 tasks, ~40 files 2026-06-22 15:32:45 -06:00
Nora 42a826cb73 docs(spec): auth (admin/user/viewer) design — sessions, RBAC, login flow 2026-06-22 15:32:45 -06:00
Tyler 9bca4b608a feat(release): v0.2.0 — batch 837 export, ClaimCard, theme tokens
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.
2026-06-22 11:01:58 -06:00
cyclone 35298907bc docs(spec): add Ubuntu Docker deployment design
Production deployment of Cyclone on a single Ubuntu Linux server:
- Two-container Docker Compose (backend + nginx frontend)
- App-layer auth: username/password, argon2id, session cookies
- 3 fixed roles: admin / operator / viewer with documented action matrix
- SQLCipher encryption at rest, key as Docker secret
- Existing BackupService autostarts (24h interval, 14-day retention)
- LAN-only bind, single operator, VPN for outside access
- Manual update via 'docker compose pull', env-var rollback
- SFTP stays stub (download ZIP + MFT UI); listed as v2

Builds on the parent 2026-06-19 production-readiness spec which
shipped local-only in-memory store + react-query wiring.
2026-06-22 10:43:26 -06:00
Tyler 7290cac643 plan+spec: add migration 0014 to relax PKs; renumber plan Task 1.3 -> 1.4
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.
2026-06-21 18:09:06 -06:00
Tyler 5e8c7b11ea plan: parse-decide workflow implementation for 837/835 upload dedup 2026-06-21 17:53:21 -06:00
Tyler 9c0cec8f0c spec: parse-then-decide workflow for 837/835 upload dedup 2026-06-21 17:47:03 -06:00
Tyler b606e8c9a2 plan: drop claims UNIQUE + 409 UX implementation plan 2026-06-21 17:02:10 -06:00
Tyler ac709c07c3 spec: self-review fixes (explicit claim_id arg, defer_foreign_keys) 2026-06-21 16:58:50 -06:00
Tyler 3f672d5db3 spec: drop claims UNIQUE constraint + 409 UX 2026-06-21 16:54:07 -06:00
Tyler c0b7924aad docs(sp22): drop speculative uv.lock note from deviations 2026-06-21 16:28:24 -06:00
Tyler ebd2834cc4 docs(sp22): backfill plan with 10 as-built deviations
The implementation shipped with 10 inline bug fixes that weren't reflected
in the original plan document. This commit updates the plan so it
matches the as-built code and adds a summary table near the top so a
future reader of the plan knows what was adjusted and why.

- Bug 1a: AckTimeoutError.__init__ signature widened from int to float
  plus adds self.phase attribute for report remediation hints.
- Bug 1b: wait_for passes timeout_s through without int() round-trip.
- Bug 2: Markdown table assertion updated to match the actual format.
- Bug 3: Added ## Remediation section to write_report_md for soft/hard fails.
- Bug 4: 835 expected-by text now says 'typically the following Monday'.
- Bug 5: Removed bogus HealthSnapshot import in test_pipeline.py.
- Bug 6: All phase tests wrapped in 'async with CyclonePipeline(...) as p:'.
- Bug 7: Playwright + UploadPage imports moved to module level in pipeline.py.
- Bug 8: Added _phase_records_to_results() helper for PhaseRecord→PhaseResult.
- Bug 9: Resume test uses _make_stub factory so stubs append to completed_phases.
- Bug 10: CLI uses context_settings={'allow_interspersed_args': True}.
2026-06-21 16:28:07 -06:00
Tyler 9133baa070 plan(SP22): Task 7 — replace assert with RuntimeError in list_*_acks methods 2026-06-21 13:56:14 -06:00
Tyler 646d00adde plan(SP22): Task 6 — apply Task 4/5 fixes (4xx terminal, RuntimeError, retry logging) preemptively 2026-06-21 13:51:42 -06:00
Tyler f6f821e082 plan(SP22): Task 5 — add test_parse_837_does_not_retry_on_5xx to spec (locks in dedup-safety contract) 2026-06-21 13:50:51 -06:00
Tyler 3e00fb3f63 plan(SP22): Task 5 — apply Task 4 fixes (4xx terminal, RuntimeError) to list_claims and parse_837 2026-06-21 13:43:37 -06:00
Tyler 2faf7bfd48 plan(SP22): Task 4 — distinguish 4xx (terminal) from 5xx (retry) + drop dead BACKOFF_S[2]
Update _get_typed in the plan to:
- raise immediately on 4xx (terminal)
- retry on 5xx and RequestError (transient)
- use BACKOFF_S = (1.0, 2.0) instead of (1.0, 2.0, 4.0)
- use RuntimeError instead of assert for the context-manager guard

Also add test_4xx_is_terminal_not_retried to the plan's test list.
2026-06-21 13:43:01 -06:00
Tyler 73be586110 plan(SP22): Task 4 review fix — client() fixture must enter async context
The verbatim plan returned an un-entered CycloneClient from the fixture,
causing all 3 tests to fail with 'use async with CycloneClient(...)'.
Convert fixture to async generator that enters the context. Verified by
running pytest tests/test_api_client.py -v → 3 passed.
2026-06-21 13:37:55 -06:00
Tyler d7f37f845a plan(SP22): Task 3 — add add_logger_name to shared_processors so JSON includes the logger name (matches docstring contract) 2026-06-21 13:31:49 -06:00
Tyler df10b55a34 plan(SP22): Task 3 review fix — get_logger returns BoundLoggerLazyProxy, not BoundLogger
The verbatim plan's test asserted isinstance(log, structlog.stdlib.BoundLogger),
but cache_logger_on_first_use=True makes get_logger return a lazy proxy on
first call. Test now uses duck-typing (hasattr('bind') + callable). Also
dropped the misleading caplog assertion and unused logging/structlog imports.
2026-06-21 13:26:59 -06:00
Tyler 6300280142 plan(SP22): Task 1 review fixes — conftest fixture bug + Python 3.13 note
- conftest.py example now uses SAMPLE_837P constant so sample_837p_bytes
  reads the file (not the directory).
- Note that Python 3.13 satisfies requires-python >= 3.11 if 3.11 isn't
  installed.
2026-06-21 13:13:01 -06:00
Tyler fff000ed2e plan(SP22): pre-flight fix — add _extract_items helper for paginated API responses 2026-06-21 12:58:32 -06:00
Tyler 134eb4f404 plan(SP22): self-review fixes — phase4_at, browser check via shared client, dead code 2026-06-21 12:56:25 -06:00
Tyler 022b229d81 plan(SP22): cyclone-pipeline agent — 26 tasks, TDD-shaped, ~26 commits 2026-06-21 12:55:01 -06:00
Tyler dc70bdacf6 spec(SP22): cyclone-pipeline agent design — upload + SFTP submit + TA1/999 wait + 835 deferred 2026-06-21 12:47:40 -06:00
Tyler 3fa61bb3f6 plan(SP21): universal drill-down — 5 phases, ~40 tasks
Phase 1: drill primitives (DrillStackProvider, DrillableCell, PeekModal,
DrillDrawerHeader) + PayerPeekContent + ValidationRulePeekContent +
/api/payers/{id}/summary backend + Dashboard KPI/provider/denial drills.
Phase 2: ProviderDrawer + activity event routing for claim_* events.
Phase 3: ProviderDrawer tabs (Claims/Activity) + remaining event routing.
Phase 4: RemitDrawer + 4 surfaces (Remittances, BatchDiff, Inbox,
Reconciliation navigate).
Phase 5: AckDrawer + 8 final surfaces (Claims, Batches, Acks, Providers,
ActivityLog, BatchDiff, Inbox, Reconciliation).

Each phase = 1 PR, shippable independently with its own smoke slice.

Spec: docs/superpowers/specs/2026-06-21-cyclone-universal-drilldown-design.md
2026-06-21 11:33:39 -06:00
Tyler 2d7d73a9db spec(SP21): universal drilldown design — drawers + peek modals everywhere (self-review fixes) 2026-06-21 11:22:21 -06:00
Tyler 02b06bf4d9 docs(plan): implementation plan for Cyclone skill catalog (9 tasks, 4 phases) 2026-06-21 11:17:06 -06:00