# AGENTS.md — Bookhoard server Standing rules for any agent or developer working in this repo. Project-wide context lives in the sibling repos' AGENTS.md files (../bookhoard-app/AGENTS.md is the Android client's). ## Commit as each unit of work completes — ALWAYS apply Commit when a logically complete piece of the project is done — a fix, a feature slice, a schema change, a docs/test batch — before moving on to the next piece. Do not hold a whole session's work for one giant end-of-session commit, and never push uncommitted work aside until the tree is clean. - **One commit per related edit.** If the changed files serve more than one purpose, they are separate commits; only share a commit when you are sure every file in it is the same related edit. Generated artifacts (templ codegen, sqlc output, vite/tailwind build output, bruno/openapi mirrors) commit with the source change that produced them. - **Detailed messages**, in the repo's conventional-commit style (`fix(scanner): …`, `feat(issues): …`) with a body that says WHY the change exists, what behavior changed, and any precedent it follows — not just what the diff shows. The body is how the next agent (and git history) arbitrates "test stale vs code changed". - **Ordering**: stack commits so each tree compiles where practical (a service before the handler that calls it, a guard before the feature that relies on it). - **Committing ≠ pushing.** Commits are local bookkeeping and land as work completes; pushing (once, after the last commit) is the author's call. Never push half-finished units just because they were committed. ## Rendering ownership (templ vs TypeScript) — ALWAYS apply The point of templ + htmx + Alpine is: the server renders the UI, and JavaScript is kept minimal. The rule that keeps it that way: > **JavaScript decides what shows. templ decides what it looks like.** - Content derived from **server data** (DB rows, search results, match lists, conflict cards, any list/card/badge/table) is rendered by templ. When it changes client-side, fetch a **templ partial** from an SSR route and swap it in (`htmx.ajax(...)`), never rebuild it from a JSON payload with `innerHTML` string templates. - **Pure functions of client-local state** (open/closed panels, tabs, password strength from a typed value, toasts) may render in TS/Alpine — no server round trip needed. - `web/src` is **TypeScript only** — no new `.js` source files. Vendored third-party and vite build output under `web/static` are exempt. - Inline `