Short, immutable records of the decisions that shaped this project. Each entry states context, the decision, its consequences, and the alternatives rejected. A record is superseded by a new one (status change), never rewritten.
| ADR | Decision | Status |
|---|---|---|
| 0001 | Custom app calling the Jellyfin REST API, not a Jellyfin plugin | Accepted |
| 0002 | Write fields directly; do not use Jellyfin’s metadata provider pipeline | Accepted |
| 0003 | Treat UpdateItem as a full overwrite with a fixed write whitelist |
Accepted |
| 0004 | Snapshot the writable fields before every write; revert replays it | Accepted |
| 0005 | Related artists live in our DB only, never written to Jellyfin | Accepted |
| 0006 | Map Last.fm styles onto BaseItemDto.Tags; genres onto Genres |
Accepted |
| 0007 | Optimistic concurrency on Etag/DateLastSaved |
Accepted |
| 0008 | Persist every Last.fm request and response in a permanent archive | Accepted |
| 0009 | The raw archive is the source of truth; derived data is rebuildable | Accepted |
| 0010 | Content-address response bodies; log every observation separately | Accepted |
| 0011 | Measure the ToS cap; never silently delete archive data | Accepted |
| 0012 | Read-only Last.fm usage: API key only, no user auth flow | Accepted |
| 0013 | Single container serving the SPA; Postgres 18 pinned as 18-trixie |
Accepted |
| 0014 | PremiereDate is a wire string everywhere outside comparison |
Accepted |
| 0015 | Genre matching is exact and case-insensitive; a comma is never guessed at | Accepted |
| 0016 | Operator policy is stored in the database, not only in the environment | Accepted |
| 0017 | Facet filters are id-validated; exclusion patterns are literal globs | Accepted |