From eee8195662d251e002d57083f4601d3ca964ffb7 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 | 35 +++++++++++++++++++++++ 4 files changed, 135 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..65b4f0a --- /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. + +- 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 and comprehensions whose whole scope is + a few lines. +- Modules are named for what they contain, singular nouns. `utils.py`, + `helpers.py`, and `misc.py` are banned names. + +## Sizing, DRY, modularity + +- Soft cap ~300 lines per module, hard cap 500. Split by responsibility. + Functions target 40 lines or less. +- 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 unless it's a clear necessity. + +## Comments and 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/`, since + SDK and 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..37706ce --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# discovery-by-llm + +This is a demo that serves as a proof of concept for LLM utilization for music discovery, specifically using the Spotify API. The form-factor is an LLM-chat like experience, with direct Spotify integration. + +> Work in progress. This file is filled in during the build. +> Chronological build log (Dutch): [docs/logboek.md](docs/logboek.md). + +## Demo + +*(to follow: hosted instance + local `docker compose up --build`, with and +without API keys)* + +## How it works + +*(to follow: pipeline diagram and module map)* + +## Choices + +*(to follow)* + +## Performance and optimisations + +*(to follow)* + +## Where to look + +*(to follow)* + +## Method + +*(to follow)* + +## What I cut / what I would do next + +*(to follow)*