| | @@ -0,0 +1,93 @@ |
| 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 | +| File | Holds | |
| 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 # expect zero warnings |
| 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. |