.offrails/blueprints/AGENTS.md
AGENTS.md
APP: {{app_name}}
FRAMEWORK: Rust off Rails {{version}} (.offrails/, vendored {{date}})
FRONTEND_MODE: {{htmx | inertia | spa | api}}
UI_LIBRARY: {{react | svelte | none}} (none = no JS UI: htmx without islands, or api)
ISLANDS: {{yes | no}}
API: {{none | rest | graphql | both}} (a versioned API for other programs; api mode is never none)
DEV_DATABASE: sqlite (storage/development.sqlite3)
PROD_DATABASE: {{postgres | sqlite}}
DEPLOY_TARGET: {{later | fly | kamal | railway | render}}
ACCOUNTS: {{none | <methods>[ invite]: password | magic-link | password+magic-link, then " invite" when sign-up is closed}} ROLES: {{none | admin+member | …}} TENANCY: {{per-user | teams | shared | none}}
EMAIL: dev: local mailbox (/dev/mailbox); prod: {{smtp via <provider> | not yet (logged only)}}NEVER
Each line is a mistake an agent shipped. The check that catches it is in parentheses.
- NEVER look a record up without the actor.
<model>::find_for(db, &user, id), neverfind(db, id); rawfind/destroystaypub(in crate::models)(guard_test.rs,tenancy_test.rs,offrails check). - NEVER call work done while
sh .offrails/tools/offrails-checkprints aFAIL, or any Definition of Done command fails or was skipped without saying so. - NEVER add a retry, watchdog, reload cap,
sleepor raised limit without evidence (the failing request, its status and protocol) and adocs/decisions.mdline citing it (.offrails/workflow.md#when-something-fails). - NEVER serve a browser an untrusted certificate, or test login with certificate errors ignored. Chrome drops
Set-Cookieafter "Proceed (unsafe)", so login bounces. HTTPS in development only with mkcert (bin/dev-cert), else the http URLbin/devprints; smokes run with no certificate overrides (run.sh login). - NEVER ship a light surface, dark text or faint tone without its
dark:partner (abg-whitecard under a dark body: 1.09:1). The palette is in.offrails/guides/views.md#dark-mode(tests/app/theme_test.rs,run.sh a11yin light and dark). - NEVER guess the running app's state. Ask it:
curl -s $URL/dev/introspect.json(migrations, CSS staleness, protocol, streams) orcargo run -q -- introspect. - NEVER pick files by loose prefix, depend on
HashMaporder, or hash files per request (kernel/assets.rsuniqueness test). - NEVER change a header line (
FRONTEND_MODE,ISLANDS,UI_LIBRARY,TENANCY,FRAMEWORK) without the code to match, and never leaveFRAMEWORKbehind.offrails/VERSION(offrails check). - NEVER tick an acceptance criterion without a test that cites its id, or write a Security row without a test name (
offrails check). - NEVER edit
.offrails/or delete adocs/decisions.mdline. Supersede it with a new line (.offrails/workflow.md#decisions). - NEVER give an
idor*_ida 32-bit column, or let the suite run only on SQLite.big_pk_auto/big_integerandi64;INT4ids made every Postgres write a 500 behind a green SQLite suite (offrails check[ids],[postgres]). - NEVER bind development to
0.0.0.0or drop the Host check to "make it work"./devhas no login: the mailbox holds every sign-in link. A dev container or a phone setsAPP_BIND=0.0.0.0andAPP_ALLOWED_HOSTSon purpose (offrails check[bind],development_refuses_a_rebound_host_name). - NEVER
pkill/killall, or kill by name or pattern. Stop the PID file you started:kill "$(cat storage/pids/dev-$PORT.pid)"forbin/dev(it stops its whole tree),serve-$PORT.pidfor a plainserve(.offrails/cli.md#running-the-app-from-an-agent). Killing onlybin/dev's listener leaves its watchers running.
You are an agent working on a Rust off Rails app
- Read
.offrails/AGENTS.mdnow, then follow its load order. It overrides your general habits. - The human writes no code. Their intent lives in
product/. Update the spec before you build, and ask questions per.offrails/interview.md. - Follow only the
FRONTEND_MODEbranch of each guide, and theUI_LIBRARYbranch of the JS guides. - Done means
.offrails/workflow.md#definition-of-done, green, with each item's command run. Start withsh .offrails/tools/offrails-check. - Log decisions not covered by
.offrails/indocs/decisions.md; supersede, never edit. - Start app code from a snippet or a generator (both tested, both yours once copied); hand-write the rest.
cargo offrails snippets(snippet show <name>to read one),cargo offrails g --help; neither is mandatory. Commit after each run, then edit. Keep a feature in its own small files, and mark each line it adds to a shared file with its name (// posts).
Quick commands
bin/setup # first run
bin/dev # develop: open the URL it prints (https + HTTP/2 with mkcert, else http){{TS modes only: ", or :5173 via Vite"; htmx: nothing}}; tabs reload on save
sh .offrails/tools/offrails-check # mechanical gates: header, specs ↔ tests, tenancy, scoped lookups
cargo offrails verify # after each slice: fmt, CSS, clippy, one build, SQLite + Postgres suites at once
cargo offrails verify --full # before saying done: + offrails-check, coverage, deny, gitleaks, routes, browser smokes incl. every page after a boosted link, and prove (BASE=…)
BASE=… cargo offrails prove # screenshots + checks of every page → docs/proof/PROOF.md: link it in your final message
cargo offrails brand # after editing brand.toml (colors, fonts, radius, logo); components: /dev/components
cargo nextest run <name> # one test while it's red (SQLite)
docker compose up --build # the whole app, production-shaped, on Postgres (http://localhost:3000)
docker compose up -d db # a Postgres for verify (or set TEST_DATABASE_URL in .env)
cargo run -- db migrate # migrate (any DATABASE_URL)
cargo run -q -- introspect # the app's state as JSON (development; live parts: $URL/dev/introspect.json)
cargo run -- routes # list routes (with access levels); rewrites docs/routes.md
cargo offrails snippets # small tested code to copy: snippet show <name> (read), snippet add <name> (copy + wire)
cargo offrails g --help # generators: resource, model, controller (path params), page, api, job, mailer, channel, test
cargo offrails list-parts # parts (mailer, jobs, auth, realtime, dev dashboard): installed or available
cargo offrails add-part <name> # add one when the product needs it (dependencies first; re-running is a no-op)
cargo run -- jobs stats # with the jobs part: job counts per state (dashboard: /admin/jobs; dev tools: /dev)App-specific notes
This app started from the Rust off Rails template (.offrails/template/). Shared files carry one-line samples: <anchor> comments (// samples: config fields, {# samples: nav links #}) where cargo offrails add-part inserts a part's wiring. Keep them; they are inert comments.