# AGENTS.md - Do NOT test, run the server, write tests etc. - ESPECIALLY DO NOT make repros, do NOT install Playwright etc. Please instead ask the user to see from dev tools what you need, e.g. to look up something in DOM or log. Use console.log for debugging where needed (and otherwise for permanently kept useful messages in the app). ## What this is Pagerite: a single-user CMS/blog. FastAPI serves HTML rendered in Python with html5tagger; content is persisted in a kanta database and rendered on the fly per request. Vue is used only for interactive bits (editing tools), not for the public pages. See `docs/design-principles.md` for the design. ## Layout - `pagerite/` — the Python backend package (hatchling build target). - Server run by CLI entry point `uv run pagerite` (no auto reloads, build needed) - Dev mode `scripts/devserver.py` (which the user mostly uses for auto reloads, no build needed) - Avoid running the server yourself, ask the user to test - `app.py` — the FastAPI app. FastAPI's built-in API docs are disabled (`docs_url`/`redoc_url`/`openapi_url=None`) because `/docs` belongs to our content. Our own routes (content pages, `/_/api/...`, `/_/f/...`, `/_/admin`) are registered BEFORE `frontend.route(app, "/")` is called: fastapi-vue inserts its file routes at the position where `route()` was called (during `load()` in the lifespan), so anything defined earlier wins. The one exception is the content catch-all `/{path:path}`, registered AFTER `frontend.route()` so that built frontend assets still take priority over content slugs. The `Frontend` is constructed with `spa=False` explicitly: it only serves the built files without a catch-all. The build mirrors the URL space — hashed immutable assets under `/_/assets/`, `favicon.ico` at the site root — and an `index.html` in the build would become a `/` route, so leave it out of the build to keep `/` ours. - `data.py` — msgspec Structs for the kanta database. The site structure is a tree: `Data.menu` maps top-level slugs to `Node`s, each with `children` keyed by slug — the URL path is the slug chain. The front page is whichever top-level node has slug "" (parallel to the other main level pages, not their parent); it cannot have children, and renaming its slug away leaves no front page ("/" redirects to the first nav item). `Node.content` is the Markdown page, or None for a pure category label whose URL renders a placeholder page (while nav links to it point at its first child); every label's title and slug are editable. Siblings order by the fractional `Node.order` key: a moved item gets a fresh key relative to its new siblings, all others keep theirs. `resolve`/`find_slot` walk the tree by path; moves are slot detach/attach carrying the whole subtree. Legacy flat `Data.pages` (pre-tree databases) migrates into `menu` on startup. The app owns the `Data` object; reads are plain attribute access, writes in `kanta.transaction(...)`. `Data.files` is a content-addressed store (blake3[:12] + extension) mapping file names to bytes, served at `/_/f/{name}` with immutable caching; pages reference files by absolute `/_/f/` URLs so hierarchy moves never break them. `Node.banner` is a raw trusted HTML snippet for the header banner (img, styled div, canvas+script...); empty inherits from the node's ancestors (front page last), then the default banner.svg artwork. `Data.version` is bumped on every write and embedded in page ETags so nav-affecting changes invalidate caches. `Data.brand` is the site name (header link + `