From adcf4b9df67762c82f93c3e7b956d6db3fe1357b Mon Sep 17 00:00:00 2001 From: Justin Visser Date: Sun, 9 Aug 2026 20:56:13 +0200 Subject: [PATCH] chore: establish standards in AGENTS.md --- .gitignore | 17 +++++++++++ AGENTS.md | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 1 + README.md | 41 +++++++++++++++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 120000 CLAUDE.md create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1c3d9b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# secrets +.env + +# python +__pycache__/ +*.pyc +.venv/ +.mypy_cache/ +.ruff_cache/ +.pytest_cache/ + +# node +node_modules/ +frontend/dist/ + +# eval: raw (unredacted) recordings never enter the repo +eval/fixtures/raw/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..a7727a0 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,82 @@ +# Engineering standards + +This file is the contract for every contributor to this repository — human or +agent. `CLAUDE.md` is a symlink to it: one canonical standard, read by every +tool. `ruff`, `mypy`, `eslint`, `prettier`, and `vue-tsc` enforce mechanically +what they can; this document carries the rest. + +## Architecture + +Monorepo with a ports-and-adapters seam. The dependency direction is enforced +by review: + +``` +api → pipeline → ports ← adapters domain imports nothing app-level +``` + +- `backend/app/domain/` — pure models and logic (`Track`, `TasteProfile`, + fuzzy matching, profile compression). Imports nothing app-level. +- `backend/app/ports/` — `Protocol` definitions (`MusicCatalog`, + `Recommender`, `PlaylistWriter`). +- `backend/app/adapters/` — implementations: `spotify/` (auth, thin httpx + client, DTO mapping), `anthropic/` (both LLM calls), `demo/` + (fixture-replay implementations of the same ports). +- `backend/app/pipeline/` — the orchestrator composes the stages; stages never + import each other. +- Spotify JSON never escapes `adapters/spotify/mapping.py`. +- Pipeline tunables (counts, thresholds, budgets, model id, effort, TTLs) live + in `app/config.py` (pydantic-settings) — no magic numbers in code. +- LLM prompts are versioned template files under `app/prompts/`, not inline + strings. +- `eval/scenarios.yaml` is one source of truth for three consumers: golden + eval queries, demo fixture keys, and UI suggestion chips. + +## Naming + +Human-readable, descriptive, full words. Code reads as prose. + +- Python: `snake_case` functions/variables, `PascalCase` classes, + `UPPER_SNAKE` constants. Functions verb-first (`resolve_candidate`), + variables noun-first (`grounded_tracks`), booleans as predicates + (`is_grounded`, `should_stream`). +- TypeScript/Vue: `camelCase` variables/functions, `PascalCase` components and + `.vue` filenames, composables `use`-prefixed, types without `I`-prefix. +- Abbreviations: only universally idiomatic ones (`id`, `url`, `api`, `db`, + `llm`, `sse`). Never invented ones. +- Single-letter names only in lambdas/comprehensions whose whole scope is + ≤ ~3 lines. +- Modules named for what they contain, singular nouns. `utils.py`, + `helpers.py`, `misc.py` are banned names. + +## Sizing, DRY, modularity + +- Soft cap ~300 lines per module, hard cap 500 — split by responsibility. + Functions target ≤ ~40 lines. +- One responsibility per module: if it needs "and" to describe, split it. +- DRY by the rule of three: extract on the third occurrence, not the second. + No premature abstraction, no helpers for one-shot operations, no designing + for hypothetical future requirements. +- Validate at boundaries only (user input, Spotify responses, LLM output); + trust internal code — no defensive re-checking between our own modules. + +## Comments & docstrings + +Sparse and load-bearing: + +- One-line docstring on every module and public function/class; add + Args/Returns only where the signature genuinely doesn't tell the story. +- Inline comments state only what the code cannot — constraints, API quirks, + non-obvious whys. Never narrate the next line. +- Banned: commented-out code, drive-by TODOs, decorative section banners. +- Type hints everywhere; `mypy --strict` is the enforcement. + +## Enforcement + +- Backend: `ruff` (lint + format) everywhere. `mypy --strict` on `domain/`, + `ports/`, `pipeline/`; default strictness on `adapters/` and `api/` — + SDK/streaming typing archaeology is not where the effort goes. Every + `# type: ignore` carries an error code and a reason. +- Frontend: `eslint` + `prettier` + `vue-tsc --noEmit`; no `any`. The stream + event types are a discriminated union mirrored against the Pydantic + response schemas. +- Commit messages describe the change, conventional-commit style. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d693161 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# discovery-by-llm + +Muziekontdekking met een LLM als aanbevelingsmotor, op de Spotify Web API. + +**These:** Spotify heeft zijn aanbevelings- en audio-intelligentielaag uit de +API verwijderd (recommendations en audio features zijn weg voor nieuwe apps). +Deze proof-of-concept beantwoordt de vraag of een LLM die rol kan overnemen, +met Spotify teruggebracht tot resolver, verifier, personalisatiebron en +afspeeloppervlak. Een naïeve-zoekbaseline maakt het antwoord meetbaar. + +> Werk in uitvoering — dit bestand wordt gevuld tijdens de bouw. +> Chronologisch verslag: [docs/logboek.md](docs/logboek.md). + +## Demo + +*(volgt: gehoste instantie + lokale `docker compose up --build`, met en zonder +API-sleutels)* + +## Hoe het werkt + +*(volgt: pipeline-diagram en module-overzicht)* + +## Keuzes + +*(volgt)* + +## Performance en optimalisaties + +*(volgt)* + +## Waar te kijken + +*(volgt)* + +## Werkwijze + +*(volgt)* + +## Wat ik heb geschrapt / wat ik hierna zou doen + +*(volgt)*