ADR 0001 — Initial architecture decisions¶
Status: Accepted · Date: 2025
Context and full rationale live in ../DESIGN.md. This record is
the short list of binding decisions for the scaffold.
Decisions¶
- Reference implementation of the
open-playlistspec. The universalPlaylist/Trackmodel is the internal hub format. - Any-to-any, hub-and-spoke. Providers are spokes; the hub is the universal format. O(N) adapters, not O(N²) pairs.
- Monorepo, hard-separated FE/BE. No shared code. The frontend consumes a client generated from the backend's OpenAPI document only.
- Stack. Backend: Python 3.12 / FastAPI / SQLAlchemy 2 async / Alembic / arq. Frontend: Vite / React / TypeScript. Data: Postgres + Valkey. Infra: docker compose.
- YouTube write path.
ytmusicapi(unofficial) default-on; official YouTube Data API v3 opt-in and off by default (quota ~66 songs/day). - Pipeline order: import → match → review → write.
- Adapters are search-only. The core
MatchServiceowns caching, scoring and promotion; adapters never touch the identity graph. - Identity graph is an evidence graph keyed by an internal UUID. ISRC is evidence, not identity. User confirmations are per-account overlays, promoted to global only on strong evidence.
- Idempotency via an operation ledger. Persist intent → call → observed state; reconcile by reading target state instead of blind retries.
- Central rate limiter (token bucket) using capability cost hints; not per-adapter sleeps.
- Durable, replayable progress. Derived from
job_item, streamed over SSE withLast-Event-IDresume. - Capability descriptors, not booleans. Adapters advertise constraints (batch size, ordering, quota, stability, warnings).
- Fidelity contract. Flat music-track playlists; unsupported items carry an
unsupported_reasonand appear in a per-job lossy report. - Deployment: self-host single-user v1 with SaaS-ready seams. One
OPE_DEPLOYMENT_MODEflag + pluggableKeyProvider; header-paste auth and untrusted plugins are self-host-only.