# Rules Contract for every contributor — human or AI — working in this repo. ## Project Overview `automAIta` is a deployment-ready boilerplate for HuggingFace Spaces. - **Backend:** FastAPI (MVC) managed by `uv`, defined in `backend/pyproject.toml`. - **Frontend:** Vite + React + TypeScript + Tailwind + PWA. No app frameworks. - **Container:** one Docker image, single port `7860`. FastAPI serves the built React bundle as static files at `/` and the API under `/api/*`. - **Deploy:** GitHub `main` → `.github/workflows/sync-to-hf.yml` mirrors the repo to a HuggingFace Space, which rebuilds automatically. ## Scope & Goals - Stay a **boilerplate**. The only "feature" is `App.tsx` proving the `/api/status` round-trip works. - New projects fork this repo and fill it in — they should not have to rewire Docker, ports, the workflow, or the layout. ## Coding Rules - **No hardcoded values.** Backend goes through `app/core/config.py` (env prefix `AUTOMAITA_`). Frontend uses Vite env vars (`import.meta.env.VITE_*`) when needed. - **Backend MVC layering:** - `controllers/` — `APIRouter`s only. Thin: parse, call a service, return. - `services/` — business logic. No FastAPI imports. - `schemas/` — pydantic request/response DTOs. - `models/` — pydantic domain models. - `core/` — config, lifespan, anything cross-cutting. - **Frontend:** - TypeScript `strict` is on. Don't downgrade it. - Tailwind is the only styling system. **No** Material UI, Chakra, Mantine, shadcn, etc., without explicit approval. - **No** state libraries (Redux/Zustand/Jotai) or routers (React Router/TanStack Router) until the project actually needs one. - All API calls go through `src/api/client.ts`. - **Single-port contract.** The container exposes only `7860`. Don't introduce nginx, supervisord, or a second process. FastAPI mounts the static SPA itself. - **Same-origin in prod.** API client uses relative URLs (`/api/...`). Don't hardcode `http://localhost:8000` or any HF URL. ## Agent Behavior Rules - Never bypass the `frontend/` ↔ `backend/` boundary — they communicate over HTTP, not shared imports. - Never break the **single-port** contract or the **non-root UID 1000** Dockerfile. - Never edit `.github/workflows/sync-to-hf.yml` to add deploy logic that belongs inside the container — the workflow's job is mirroring, nothing else. - Preserve the Dockerfile's two-stage shape (`frontend-build` → `runtime`). New build steps go inside an existing stage; don't add stages without need. - When adding a backend route, create a controller in `app/controllers/`, register it in `app/main.py`, and put the logic in `app/services/`. ## Workflow Rules - **Local dev (split):** - Backend: `cd backend && uv sync && uv run uvicorn app.main:app --reload --port 8000` - Frontend: `cd frontend && npm install && npm run dev` (proxy handles `/api`). - **Container parity (run what HF runs):** `docker build -t automaita . && docker run --rm -p 7860:7860 automaita` → http://localhost:7860. - **Deploy:** push to GitHub `main`. Required repo secrets: `HF_TOKEN`, `HF_USER`, `HF_SPACE_NAME`. - Don't push directly to the HF Space repo — let the action do it, otherwise you fork history. ## Conventions - **Env var prefix:** `AUTOMAITA_` (backend), `VITE_` (frontend). - **API prefix:** all backend routes live under `/api/*`. The PWA service worker explicitly excludes `/api/*` from navigation fallback. - **Filenames:** - Backend: snake_case modules, controllers suffixed `_controller.py`, services suffixed `_service.py`. - Frontend: PascalCase for components (`App.tsx`), camelCase for utilities (`client.ts`). - **Health:** `/api/health` is the simplest possible OK probe; `/api/status` is the rich one (used by the SPA on mount). - **Python:** 3.12+, `ruff` for linting (config in `pyproject.toml`). - **Node:** 20+ (matches the Dockerfile build stage). ## HuggingFace Space deployment - The HF Space's README is **generated at CI time** by `.github/workflows/sync-to-hf.yml` from the template at `.github/hf-space.README.tpl.md`. Don't hand-edit the repo's `README.md` expecting it to ship to HF — edit the template instead. The repo's `README.md` is purely for GitHub readers and is overwritten in the workflow's checkout (never pushed back to GitHub). - **Required GitHub repo secrets** (Settings → Secrets and variables → Actions → Secrets): - `HF_TOKEN` — HF user access token, **write** scope. - `HF_USER` — HF username or org that owns the Space. - `HF_SPACE_NAME` — the Space's repo name (no `user/` prefix). - **Optional GitHub repo variables** (Settings → Secrets and variables → Actions → Variables) — each falls back to a default if unset: - `HF_SPACE_TITLE`, `HF_SPACE_EMOJI`, `HF_COLOR_FROM`, `HF_COLOR_TO`, `HF_APP_PORT`, `HF_SHORT_DESCRIPTION`. - **Persistent storage** lives at `/data`. It's mounted at runtime **only if** persistent storage is enabled in the Space's Settings UI. Without it, `/data` still exists and is writable, but writes are wiped on every restart. The boilerplate works in both modes. - `/data` is **runtime-only** per HF docs — never write to `/data` from the Dockerfile (build step). Pre-create + chown is fine; writes are not. - Do **not** use `VOLUME /data` in the Dockerfile — HF Spaces don't honor `VOLUME` declarations and it complicates layer permissions. - Container must run as **UID 1000**. `$HOME` must be writable by that user (we set `HOME=/home/app` in the Dockerfile so libraries like `huggingface_hub` can cache to `~/.cache/huggingface`). - **Build-time secrets** (private model downloads, etc.) use `--mount=type=secret,id=NAME` and are read from `/run/secrets/NAME` per HF docs. **Never** pass runtime secrets through `ARG` — they would bake into image layers. - **Runtime secrets/variables** set in the HF Space Settings UI become regular env vars in the container — read them via `os.environ.get(...)` or via `pydantic-settings` (`AUTOMAITA_*` prefix for our config). - The backend reports storage status under `storage` in `/api/status` (`exists`, `writable`, `persistent`). The `persistent` flag is a heuristic: it's `true` when a marker file from a previous startup survived to the current one. ## Out of Scope - Auth, database, persistence, queues, LLM integrations — add per project, don't bake into the boilerplate. - Tests — the boilerplate has nothing meaningful to test. Add tests when you add logic. - Analytics, telemetry, error tracking — opt in per project. - HF Hub Storage Buckets (`hf://buckets/...`) — that's a separate object-storage product accessed via `huggingface_hub`, not a filesystem mount. Not wired in by default; add the lib + auth if you need it per project.