RAGELITE / rules.md
triflix
initial: automAIta boilerplate (FastAPI + React PWA, single-port HF Space)
a6229e1
# 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.