# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Commands ```bash # Development make dev # start backend + frontend in parallel make dev-backend # cargo run (port 3000) make dev-frontend # pnpm dev (port 5173, /api proxied to :3000) # Linting & Quality make lint # runs cargo fmt, clippy (-D warnings), and svelte-check # Build make build # runs lint, then pnpm build and cargo build --release make compose-up # docker compose build + start # Testing make test # runs lint, then cargo test (backend unit tests) make test-e2e # test-up + pnpm test:e2e in one step # Demo data make seed-demo # wipe dev.db and reseed from backend/demo/demo_seed.sql ``` ## Architecture TutorTool is a **Rust + SvelteKit attendance tracker** for tutoring sessions. ### Backend (`backend/`) - **Framework**: Axum (async) on Tokio, port 3000 - **Database**: SQLite via SQLx — all queries use the runtime `sqlx::query()` / `sqlx::query_as::<_, T>()` (not compile-time macros); no `DATABASE_URL` needed for `cargo build`/`cargo check` - **Auth**: Secure JWT-based authentication. The backend sets an `httpOnly`, `SameSite=Strict` cookie named `token`. The `TutorClaims` extractor in `auth.rs` enforces authentication by reading this cookie. - **Shared State**: Axum handlers use `State` (or `State` via `FromRef`) which caches the `JWT_SECRET` and DB pool. - **Static serving**: `tower_http::ServeDir` serves compiled frontend from `frontend/build/` with SPA fallback to `index.html` - **Migrations**: auto-run via `sqlx::migrate!` at startup from `backend/migrations/` - **`PRAGMA foreign_keys = ON`** is enforced on every connection in `db.rs` Route handlers live in `backend/src/routes/` and are merged in `routes/mod.rs`. Route modules: `auth_routes`, `checkin`, `courses`, `rooms`, `sessions`, `attendance`, `notes`, `export`, `tutors`, and `test_reset` (mounted only when `TT_TEST_MODE=1` AND in debug builds). The `/health` route always returns `"ok"` and is used by the test pipeline to wait for startup. ### Frontend (`frontend/`) - **Framework**: SvelteKit 5 (Svelte runes, `$state`/`$derived`) with TypeScript. - **Auth state**: Managed by the `auth` object in `$lib/auth.svelte.ts`. - **Adapter**: `adapter-static` → single-page app, `fallback: 'index.html'` - **API client**: `src/lib/api.ts` — all fetch calls go through here; relies on browser automatic cookie handling. - **Types**: `src/lib/types.ts` mirrors the Rust models exactly — keep them in sync when changing the API Routes: - `routes/admin/login/` — public login - `routes/admin/` — all tutor-facing pages (guarded by `+layout.svelte` auth check) - `attendance/`, `courses/`, `export/`, `live/`, `rooms/`, `sessions/`, `students/`, `tutors/` - `routes/s/[code]/` — public student check-in page ### Data Model (SQLite, 9 tables) ``` tutors ──< tutor_courses >── courses ──< students └──< sessions ──< slots ──< attendances └──< notes rooms (layout_json) ←── slots.room_id ``` Key slot status transitions: `closed` → `open` → `locked`. The `code` field on slots is the public check-in token students use at `/s/[code]`. ### Testing E2E tests run Playwright against a dedicated test backend daemon. The test backend uses a separate DB and is started with `TT_TEST_MODE=1`, which enables `POST /__test__/reset` for fast state resets (~10–50 ms) without restarting the process. Each git worktree gets its own deterministic port and DB path (no collisions when testing across branches). See `docs/testing.md` for the full guide including seed data, MCP-driven verification, and worktree isolation details. ### Container / K8s / CI - `Dockerfile`: 3-stage build (Node 22/pnpm frontend → Rust 1.95 backend → Debian slim runtime, non-root) - `deploy/`: Helm chart — Deployment, Service, HTTPRoute (Gateway API), PVC, CronJob for nightly vacuum + backup rotation - Live at `tutor.puchstein.dev` (tenant-5, ITSH Cloud); image at `registry.itsh.dev/s0wlz/tutortool` - CI: Gitea Actions at `.gitea/workflows/ci.yml` — runs `make lint`, `cargo test`, `pnpm build`, `make test-e2e`, and a no-push Docker build on every non-main push and PR - Release: `.gitea/workflows/release.yml` — triggered by `v*.*.*` tags; builds + pushes image, then `helm upgrade` ## Conventions - Rust toolchain is pinned to 1.95.0 via `rust-toolchain.toml`. - Frontend indentation: 2 spaces (Svelte/TS files). Backend: standard `rustfmt` defaults. - All SQLx queries are runtime (`sqlx::query_as::<_, T>()`); no compile-time macros are used, so `DATABASE_URL` is not required for `cargo build` or `cargo check`. - **Zero Warnings Policy**: All code must pass `make lint` (clippy, fmt, svelte-check) without warnings before committing.