| 1 | # Steid |
| 2 | |
| 3 | A personal-first gitforge in Rust. Hosts git repos, writing, and projects for one |
| 4 | developer or an organisation. **Portfolio-first, not a Gitea clone** — the profile page |
| 5 | is the product and repos are one kind of thing on it. Use that to break ties. |
| 6 | |
| 7 | ## Read first |
| 8 | |
| 9 | `plans/` is the source of truth and is tracked in git. Read it at the start of a |
| 10 | session rather than inferring intent from the code. |
| 11 | |
| 12 | |
| 13 | |
| 14 | | `plans/ROADMAP.md` | vision, stack, the single milestone ladder | |
| 15 | | `plans/current.md` | the active milestone only — steps, watch-fors, backlog | |
| 16 | | `plans/progress.md` | what shipped, and the decisions worth not rediscovering | |
| 17 | | `plans/architecture.md` | layer rules and conventions | |
| 18 | | `plans/runbook.md` | how to run it, config, manual verification | |
| 19 | | `plans/decisions/` | ADRs; `TEMPLATE.md` defines the format | |
| 20 | |
| 21 | ## Keep the docs current |
| 22 | |
| 23 | **Before reporting work complete, update `plans/`.** This is part of finishing the |
| 24 | work, not a follow-up chore — the project has been restarted three times and the |
| 25 | previous attempts lost their reasoning at exactly these handover points. |
| 26 | |
| 27 | - `current.md` — tick off finished steps; move completed work out to `progress.md`. |
| 28 | If it starts reading like a changelog, it has drifted. |
| 29 | - `progress.md` — record decisions and gotchas that aren't obvious from the code. The |
| 30 | test is: would the next session waste an hour rediscovering this? |
| 31 | - `ROADMAP.md` — only when a milestone's status actually changes. |
| 32 | - `decisions/` — a new ADR when a choice would be expensive to reverse or constrains |
| 33 | future work. Write it when the decision is made; reconstructed rationale is fiction. |
| 34 | |
| 35 | Also update `current.md` when a *new* problem is found — an unfinished item, a |
| 36 | shortcut taken, a hole opened. Carry those forward explicitly at milestone rollover |
| 37 | rather than letting them vanish. |
| 38 | |
| 39 | Skip all of this for typos, formatting, and dependency bumps. |
| 40 | |
| 41 | ## Working style |
| 42 | |
| 43 | - **Commit directly to `main`.** No feature branches — solo repo. Cleanliness comes |
| 44 | from small, self-contained commits that each compile, not from branching. |
| 45 | - Small steps. Build up slowly; prefer a working increment over a big drop. |
| 46 | - Explain *why* in commit messages, not just what. |
| 47 | |
| 48 | ## Conventions |
| 49 | |
| 50 | Full detail in `plans/architecture.md`. The short version: |
| 51 | |
| 52 | - **Layers:** `domain` (no knowledge of HTTP/SQL/git/Topcoat) → `application` (use |
| 53 | cases and ports) → `infrastructure` (adapters, web). Dependencies point inward. |
| 54 | - **Every use case takes an `Actor`** and enforces authorization before any side |
| 55 | effect — one place, reachable from a page, an `/api` route, or a future transport. |
| 56 | - **Typed IDs**, never raw `String` for entity references. |
| 57 | - **`new()` validates, `from_trusted()` doesn't.** Storage adapters use |
| 58 | `from_trusted`; re-validating stored rows makes a tightened rule unreadable. |
| 59 | - **`from_str` returns `Result`, never `Option`.** A silently-defaulted enum surfaces |
| 60 | later as the wrong permissions. |
| 61 | - **Every repository port gets two implementations** — in-memory (what makes use cases |
| 62 | testable without a database) and SQLite. |
| 63 | - Request helpers are **functions taking `cx`**, not middleware or extractors. A page |
| 64 | that forgets to call one gets nothing; a route added without middleware silently |
| 65 | gets someone else's data. |
| 66 | - Safe Rust only. No `unsafe`. |
| 67 | |
| 68 | ## Before saying it's done |
| 69 | |
| 70 | ```bash |
| 71 | cargo test |
| 72 | cargo clippy --all-targets |
| 73 | cargo fmt |
| 74 | ``` |
| 75 | |
| 76 | Report counts accurately — don't state a test number without running it. |
| 77 | |
| 78 | Verify behaviour rather than asserting it. A passing unit test is not evidence that a |
| 79 | page works; the session-cookie bug passed every test and failed silently in the |
| 80 | browser. Say plainly what was checked and what wasn't. |
| 81 | |
| 82 | ## Gotchas |
| 83 | |
| 84 | - **Topcoat 0.5 needs rustc ≥ 1.95.** On older toolchains `cargo add topcoat` silently |
| 85 | resolves to an empty `topcoat v0.0.0` placeholder instead of failing. |
| 86 | - **Topcoat is very new** (first release 2026-07-22) and expects breaking changes. |
| 87 | Check its docs on GitHub rather than assuming an API. |
| 88 | - **`STEID_INSECURE_COOKIES=true`** is set in a gitignored `.env` for local dev, |
| 89 | because a `Secure` cookie is dropped silently over plain-HTTP localhost. Never |
| 90 | deploy it. |
| 91 | - **The setup token is in memory only**, so every restart — including each `topcoat |
| 92 | dev` rebuild — mints a new one. |
| 93 | - Don't leave background servers running; the user drives the app. |