Development setup¶
Prerequisites¶
- Python 3.12+ (3.11 also works for backend, but plugins test with 3.12).
- Node 24+ (required by Vite 8). Older Node versions will
fail at the build step with
crypto.hash is not a function. - Poetry for Python dependency management. Install:
curl -sSL https://install.python-poetry.org | python3 -. - npm (ships with Node).
- GNU Make for the orchestration targets. The Makefile is the source of truth — every CI command is in there.
Clone + install¶
make install runs:
cd backend && poetry install— backend + plugin path-deps.cd frontend && npm ci— frontend dependencies (Node 24).- Installs every plugin in
plugins/as a path-dep into the backend's venv (develop = trueso edits are live).
If make install fails, the most common culprit is Poetry
picking the wrong Python. Run poetry env use python3.12 in
backend/ (and each plugin if you went deep) and re-install.
Configuration¶
The backend reads its config from a three-layer chain (highest priority wins):
- Environment variables prefixed
ADAPTIVE_LEARNER_*. - User secrets at
~/.config/adaptive_learner/secrets.yaml— auto-generated as a commented template on first start (chmod 0600on POSIX); never committed to git. - Defaults in
backend/config/app.yaml.
Plus per-provider AI key resolution layered on top:
env > secrets.yaml > Fernet-encrypted DB column (set via
the Settings UI), surfaced to the UI as the key_source_*
field on UserSettingsOut.
The one mandatory secret is ADAPTIVE_LEARNER_SECRET_KEY —
used to encrypt user API keys at rest with Fernet. Generate
one with python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())".
Three places to put it (highest priority wins):
ADAPTIVE_LEARNER_SECRET_KEY env var, secret_key: in
secrets.yaml, or make dev-secret for a one-shot dev key.
The app fails hard at startup if the key is unset (no
silent-generated-default footgun — see
docs/configuration.md).
Run¶
Spins up backend on port 18001 and frontend on port 15174, in
parallel. Backend has hot-reload via uvicorn's --reload;
frontend is Vite's dev server. Press Ctrl-C once to stop both.
Background mode:
Tests¶
make test # backend + plugins + frontend Vitest
make test-backend # backend pytest only
make test-frontend # frontend Vitest only
make test-coverage # opt-in coverage run (slow)
E2E tests:
16 smoke spec files at v1.20.0: landing, onboarding + assessment, session (3-chunk SSE), curriculum, settings, mobile viewports, sync pairing, backup roundtrip, multi-cycle auto-loop, import + analysis, MD export, subjects/tags filter, rich-text notes, model picker.
Linting + formatting¶
cd backend && poetry run ruff check . # Python lint
cd backend && poetry run ruff format . # Python format
cd frontend && npx tsc --noEmit # TypeScript check
cd frontend && npm run lint # ESLint
cd frontend && npm run format # Prettier
Pre-commit hooks enforce ruff + formatter checks on every commit:
Docs¶
make docs-install # one-time, installs MkDocs venv in docs/
make docs-serve # serve docs on localhost:8000 with hot-reload
make docs-build # build static site to site/
The docs venv is separate from backend's — MkDocs has its
own docs/pyproject.toml with mkdocs-material +
mkdocs-static-i18n.
Common gotchas¶
make devcrashes with "port already in use": another AdaptiveLearner instance is still running.make dev-downorpkill -f uvicorn.- Tests fail with "duplicate column name": an Alembic
migration changed the schema. Delete
backend/adaptive_learner.dband re-run. npm run buildfails on Node 18: bump to Node 24. Vite 8 requires it.