| 1 | # movement |
| 2 | |
| 3 | A [rastrillo](https://github.com/rastrilloorg/rastrillo) app. Read the |
| 4 | framework's own SKILL.md (repo root, or in the module cache) before |
| 5 | writing app code — it is the whole app story in ~15KB. The conventions |
| 6 | below are load-bearing; the framework enforces most of them |
| 7 | mechanically. |
| 8 | |
| 9 | ## Layout |
| 10 | |
| 11 | - `internal/<app>/` — the app: `models.go` (plain GORM structs |
| 12 | + Models), `migrations.go` + `migrations/` (Schema, BootSchema), |
| 13 | `app.go` (migrate.Apply + chi router), `handlers.go`, |
| 14 | `render.go` + `templates/`. This is SKILL.md's five-file shape. |
| 15 | - `manifest/` — the declarative path, optional and equal: TOML or .go |
| 16 | Resource manifests, each generating a full screen set under `gen/` |
| 17 | (see manifest/README.md for the mounting recipe). A hand-written |
| 18 | action file at a generated path (carrying `//go:build rastrillo_actions`) |
| 19 | takes that one screen over, by existence. |
| 20 | - `gen/` — generated when manifests are used; never edit. Regenerate |
| 21 | with `rastrillo generate`. |
| 22 | |
| 23 | ## Rules the family holds hard |
| 24 | |
| 25 | - Every query touching user-owned rows goes through `scope.Owned` — |
| 26 | reads AND writes, transactions included. A row that isn't yours 404s, |
| 27 | never 403s. |
| 28 | - Never bind a request body onto a GORM model: explicit |
| 29 | `map[string]any` + `.Select` allowlist. |
| 30 | - Migrations apply at boot, once each, and are never re-run or reversed. |
| 31 | - Schema changes: edit models.go, then run `rastrillo migration generate` |
| 32 | and read the SQL it writes — it may be a full table rebuild, not just |
| 33 | an `ADD COLUMN`. Never edit a migration that has shipped. |
| 34 | Renames are hand-written (`rastrillo migration new rename_x`) because |
| 35 | a rename is indistinguishable from a drop plus an add. |
| 36 | - Money is integer cents. A float never touches a value a person will |
| 37 | be held to. |
| 38 | - Screens work with JavaScript disabled; destructive actions get their |
| 39 | own confirm-page URL. |
| 40 | - The gate is `make ci` (vet + gofmt + tests) — the same definition |
| 41 | CI runs. Run it before every push. `CGO_ENABLED=0` throughout: the |
| 42 | stack is cgo-free by design. |
| 43 | |
| 44 | ## Serving |
| 45 | |
| 46 | `rastrillo.Resolve` + `Serve` speak the CARLOS activation contract |
| 47 | (socket/addr/db argv, LISTEN_FDS, $STATE_DIRECTORY, `sidecar run`) — |
| 48 | the golden deployment target, not a requirement: `./app -addr :8080` |
| 49 | serves anywhere. `GET /healthz` and `GET /api/version` are answered |
| 50 | by the framework, outside app middleware. |
| 51 | |
| 52 | ## UX conventions (seeded from profile: considered) |
| 53 | |
| 54 | Accessible by construction, progressively enhanced, and never dependent on JavaScript to be usable. |
| 55 | |
| 56 | These are rastrillo's own conventions, and the profile is named for what it does rather than after anyone else's work. For wider reading on interface quality, impeccable.style, the WAI-ARIA Authoring Practices and Inclusive Components are all worth your time — none of them is endorsed by, or authoritative for, this framework. |
| 57 | |
| 58 | - [x] Icons — lucide, inline — `internal/<app>/icons` |
| 59 | - [x] Fields — label, hint, help and error in one envelope, wired with aria-describedby — `ui/field, ui/field-select, ui/field-text` |
| 60 | - [x] Destructive actions — their own confirm-page URL, never a modal — `ui/confirm-form` |
| 61 | - [x] State — never colour alone — always a text label too — `ui/status-pill, ui/badge` |
| 62 | - [ ] Dates — relative, with the absolute date in a title attribute |
| 63 | - [ ] Errors — server-rendered beside the field, in plain language |
| 64 | - [x] Empty states — say what the screen is for, not just that it is empty — `ui/empty-state` |
| 65 | |
| 66 | `[x]` is enforced by a vendored component: use it, do not hand-roll an |
| 67 | equivalent. `[ ]` is guidance to apply by hand. A convention absent from |
| 68 | this list is one rastrillo has no opinion about. |
| 69 | |
| 70 | Edit any line. **This file is the source of truth, not the profile name.** |
| 71 | The profile only seeded this list at scaffold time; nothing re-reads it, so |
| 72 | changing a line here changes what the app does. A convention deleted from |
| 73 | this file is not a convention to reinstate. |
| 74 | |