# Fourier (audio API) — TODO

Resume state for the `audio` sub-API of api.nech.pl (codename **Fourier**).
Snapshot **2026-06-29**. Task IDs map to the session task board; completed work
lives in [`../tasks/completed-archive.md`](../tasks/completed-archive.md).

## ✅ Live in production (api.nech.pl/audio/v1)
- 11 analysis engines (emotion/features/samples/grade/onsets/waveform/loudness/
  spectrum/naming/…), torch-free light CPU. Central auth + hierarchical scopes.
- **Async jobs + worker** (#25), **artifact store** (#27), **`/sources`** yt-dlp
  fetch (#29) — `fourier` + `fourier-worker` containers on erable.
- **`/docs` + `/openapi.json` gated** behind `api:docs` (#48).
- **`@nech/api`** generated TS client published to `npm.nech.pl` (Verdaccio, #44).
- Deployed via build-on-dev → `docker save|gzip|ssh erable docker load`. Recipe +
  gotchas: memory `reference_fourier_deploy`, [`DEPLOY.md`](./DEPLOY.md),
  [`VERDACCIO.md`](./VERDACCIO.md).

## 🔴 Open — the heavy chain (needs a GPU runner)
- [ ] **#30 — `POST /separate`** (demucs/roformer/merge as a heavy job). Wrap
      `foundry.engine.separate` (pluggable REGISTRY + MultiModelBackend merge).
      Stems → FLAC artifacts cached by `(content_id, backend, model)`. `GET
      /separate/{content_id}?backend=` returns stem artifact refs or job status.
      Surface `Backend.available()` so an absent backend → clean 4xx not 500. Keep
      a cloud-GPU-runner adapter seam (no API change later). erable has **no GPU**
      → the route is a deliberate `503 compute_unavailable` until a runner exists.
      Validate: pytest with a fake separator (submit→poll→stem refs; cache hit;
      merge 4-stem contract; unavailable backend → 4xx) + one gated real clip.
      *Blocked by: #29 ✓, #27 ✓ — so unblocked except for the GPU itself.*
- [ ] **#31 — `/loops` `/grade` `/correlate`** (full Foundry chain over the API).
      `/loops {content_id, mode:loops|chops, bars?, top_n?}` over cached stems
      (`?auto_separate=true` chains a separate job); `/grade` wraps
      `engine.grade.grade`; `/correlate` locates a loop's provenance (NCC); `POST
      /loops/export` applies B-rules naming → ZC-snapped FLAC. Validate with the
      foundry fixtures (correlate ~1.0 on known slice, ~0.03 foreign).
      *Blocked by: #30.* **Completing this closes EPIC #21.**

## 🟡 Optional / later
- [ ] **#47 — X-Accel artifact serving** (perf). Replace the app's FileResponse
      with nginx `X-Accel-Redirect`: `FOURIER_X_ACCEL=1` + a per-API `/_artifacts/`
      internal location (DEPLOY.md §5b) → means extending `render-gateway.py` to
      emit per-API extra locations. Only worth it at high throughput / large stems;
      FileResponse is correct + fine at current scale.
- [ ] **#46 — Grafana over Postgres** (`nechapi-db`: usage_log + health_checks).
      Panels: calls/tenant/route, reject reasons + offender IPs, uptime%/latency.
      Visual layer atop the existing `/admin/usage` `/admin/security` `/status`.
      Add grafana.nech.pl (DNS+certbot) or serve under /admin/grafana; also a
      nightly usage_monthly rollup. *Not urgent — /admin/* covers the need.*
- [ ] **`nech_api` Python client** — second generated client (openapi-generator
      python) alongside `@nech/api`, same `npm.nech.pl`-style distribution.
- [ ] **Mint hexa an npm read token** for `@nech/api` (`@nech/*` reads are
      auth-gated). Mint via the Verdaccio user API; hand off with their API bearer.
- [ ] Build the platform `/docs` Scalar portal (basic-auth RTFM/PLN) + fix the
      gateway 404's dangling "See /docs" hint.

## EPIC
- [ ] **#21** closes when the heavy chain (#30 → #31) ships. The analysis MVP is
      already live; this is the separation/loops half.
