steid

@jamesgill /

steid/plans/current.md
5.0 KBCode·Blame·Raw
ab7fea9chore: plans setup1mo
1# Current
2
3> Keep this file short. One active step, one ordered backlog. Completed work moves to
4> [progress.md]progress.md. If this file starts reading like a changelog, it has
5> drifted — that's exactly what went wrong last time.
6
076dbc9docs: close milestone 1, open milestone 21mo
7## Active: Milestone 2 — Profile page
ab7fea9chore: plans setup1mo
8
076dbc9docs: close milestone 1, open milestone 21mo
9**Goal:** `/{owner}` becomes the real home page, replacing the Milestone 0
10placeholder. This is the frame the rest of the product hangs in — repos, writing, and
11projects all appear on it later.
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
12
076dbc9docs: close milestone 1, open milestone 21mo
13**Explicitly out of scope:** repos and posts don't exist yet, so the page shows
14identity plus empty sections. Editing display name / bio is Milestone 2 only if it
15stays small.
ab7fea9chore: plans setup1mo
16
17### Steps
18
076dbc9docs: close milestone 1, open milestone 21mo
19- [ ] Settle routing: `module_router!` vs explicit `#[page]` paths, and `path_param!`
20 for `{owner}` — deferred from Milestone 0, and the URL space is about to grow
21- [ ] `/{owner}` — public page, resolves the org by handle, 404 when unknown
22- [ ] `/` redirects to the owner's profile once claimed
23- [ ] Owner-only affordances visible when the viewer is the owner
24- [ ] `/api/orgs/{owner}` — the API surface for the same read model
ab7fea9chore: plans setup1mo
25
26### Done when
27
076dbc9docs: close milestone 1, open milestone 21mo
28`/{owner}` renders for a signed-out visitor, shows extra affordances to the owner, and
29an unknown handle 404s rather than erroring.
88583f2docs: bring tracking docs up to date with milestone 11mo
30
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
31### Watch for
d7b99d9docs: record milestone 0 progress and routing findings1mo
32
076dbc9docs: close milestone 1, open milestone 21mo
33- **Handle lookups are case-insensitive** in storage (`collate nocase`) and lowercased
34 by `OrgName::new`. A URL with different casing must resolve, not 404.
35- **Reserved handles.** `/setup`, `/login`, `/logout`, `/api` are real routes. A user
36 claiming the handle `login` would shadow or be shadowed by one. Nothing prevents
37 this yet — decide before multi-user registration in Milestone 7, or sooner if it's
38 cheap.
39- **A profile is public.** It is the first page rendering for anonymous visitors by
40 design, so anything private must be gated explicitly rather than by assuming a
41 session exists.
42
43### Carried over — small, unblocked, worth doing alongside
44
45- **Flash messages.** A wrong password or setup token bounces with no explanation.
46 Deliberate on the security side, but indistinguishable from a broken form. Every
47 form added from here inherits the problem.
48- **No rate limiting** on `/login` or `/setup`.
49- **`sweep_expired` is never called**, so expired session rows accumulate. Expiry is
50 enforced on read, so this is tidiness, not a hole.
51- **CSRF.** `SameSite=Lax` covers the common case; whether forms also want tokens is
52 still undecided.
53- **Styling.** Everything is unstyled HTML. Topcoat ships Tailwind without Node, and
54 `steid-backup/AGENTS/UI.md` has a full OKLCH system to mine. Cheaper at two pages
55 than at ten — see [ui.md]ui.md.
ab7fea9chore: plans setup1mo
56
57## Backlog
58
59Ordered. Pull from the top.
60
076dbc9docs: close milestone 1, open milestone 21mo
611. **Milestone 3 — Writing.** Posts, markdown rendering, `/{owner}/{slug}`.
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
62 *Open question: is writing actually the first portfolio feature, or is it
63 projects/showcases?*
076dbc9docs: close milestone 1, open milestone 21mo
642. **Milestone 4 — Repo model.** `Repository` entity, `Visibility`, `create_repo`,
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
65 bare repo on disk at `{data_dir}/{org}/{repo}.git`. Watch the DB-plus-filesystem
66 atomicity problem — see [architecture.md]architecture.md#db-plus-filesystem-writes.
076dbc9docs: close milestone 1, open milestone 21mo
673. **Milestone 5 — Git over HTTP.** `git http-backend` subprocess, PATs over HTTP
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
68 Basic. See [0001]decisions/0001-git-over-http-not-ssh.md.
ab7fea9chore: plans setup1mo
69
70## Open questions
71
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
72- **Topcoat is early** (v0.5.0, first released 2026-07-22, breaking changes expected
73 by its own authors). Expect churn that isn't feature work.
74- Body size limits will reject large pushes at Milestone 5 — `topcoat-router` has a
75 `body_limit` layer that needs raising on the git routes. Recorded here because it
76 will surface as a confusing failure rather than a clear one.
77- Topcoat ships Tailwind without Node, which reopens the design system attempt #1
78 dropped purely to avoid an npm build step — see [ui.md]ui.md.
79
80## Routing findings (Milestone 0)
81
82- **Topcoat 0.5 requires rustc ≥ 1.95.** On an older toolchain `cargo add topcoat`
83 silently resolves to an empty `topcoat v0.0.0` placeholder instead of failing. Local
84 stable is now 1.97.1.
85- `Router::builder().discover()` collects `#[page]`-annotated items **at link time**,
86 so pages can live in any module. Layering is our choice, not the framework's.
87- `module_router!` derives each URL from the module tree rather than a path string.
88 Still deferred — Steid's URL space is parameterised at the root (`/{owner}`,
89 `/{owner}/{repo}`), which means `path_param!` declarations inside route modules.
90 Worth designing at Milestone 2 when the profile page makes it concrete.
91- Path and query params are read from `Cx` via `path_param!` / `#[query_params]`, not
92 injected as handler arguments. Parses are memoized per request.
93- Layouts wrap by path prefix and nest outermost-first, and a layout can catch a page's
94 `NotFoundError` to render a branded 404.
95- `HOST` / `PORT` configure the bind address, so `STEID_LISTEN_ADDR` is gone.
96- `Body` is a boxed `http_body::Body` used for both requests and responses, with
97 `into_data_stream()` to read and `Body::new()` to wrap a stream — pack data can
98 stream both directions without buffering. This is what makes Milestone 5 viable.