| | @@ -6,7 +6,7 @@ |
| 6 | 6 | |
| 7 | 7 | ## Active: Milestone 2 — Profile page |
| 8 | 8 | |
| 9 | | −**Goal:** `/{owner}` becomes the real home page, replacing the Milestone 0 |
| 9 | +**Goal:** `/user/{handle}` becomes the real profile page, replacing the Milestone 0 |
| 10 | 10 | placeholder. This is the frame the rest of the product hangs in — repos, writing, and |
| 11 | 11 | projects all appear on it later. |
| 12 | 12 | |
| | @@ -16,26 +16,28 @@ stays small. |
| 16 | 16 | |
| 17 | 17 | ### Steps |
| 18 | 18 | |
| 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 |
| 19 | +- [x] Settle URL shape — scoped under `/user/{handle}`, see |
| 20 | + [0003](decisions/0003-scoped-urls.md) |
| 21 | +- [ ] Settle routing mechanics: `module_router!` vs explicit `#[page]` paths, and |
| 22 | + `path_param!` for `{handle}` — deferred from Milestone 0 |
| 23 | +- [ ] Migration: `orgs.kind` (`personal` | `shared`), backfilled `personal` |
| 24 | +- [ ] `/user/{handle}` — public page, resolves the org by handle, 404 when unknown |
| 22 | 25 | - [ ] `/` redirects to the owner's profile once claimed |
| 23 | 26 | - [ ] Owner-only affordances visible when the viewer is the owner |
| 24 | | −- [ ] `/api/orgs/{owner}` — the API surface for the same read model |
| 27 | +- [ ] `/api/users/{handle}` — the API surface for the same read model |
| 25 | 28 | |
| 26 | 29 | ### Done when |
| 27 | 30 | |
| 28 | | −`/{owner}` renders for a signed-out visitor, shows extra affordances to the owner, and |
| 29 | | −an unknown handle 404s rather than erroring. |
| 31 | +`/user/{handle}` renders for a signed-out visitor, shows extra affordances to the |
| 32 | +owner, and an unknown handle 404s rather than erroring. |
| 30 | 33 | |
| 31 | 34 | ### Watch for |
| 32 | 35 | |
| 33 | 36 | - **Handle lookups are case-insensitive** in storage (`collate nocase`) and lowercased |
| 34 | 37 | 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. |
| 38 | +- ~~**Reserved handles.**~~ Resolved by [0003](decisions/0003-scoped-urls.md): handles |
| 39 | + are scoped under `/user/`, so they cannot collide with application routes and no |
| 40 | + denylist is needed. |
| 39 | 41 | - **A profile is public.** It is the first page rendering for anonymous visitors by |
| 40 | 42 | design, so anything private must be gated explicitly rather than by assuming a |
| 41 | 43 | session exists. |
| | @@ -58,7 +60,7 @@ an unknown handle 404s rather than erroring. |
| 58 | 60 | |
| 59 | 61 | Ordered. Pull from the top. |
| 60 | 62 | |
| 61 | | −1. **Milestone 3 — Writing.** Posts, markdown rendering, `/{owner}/{slug}`. |
| 63 | +1. **Milestone 3 — Writing.** Posts, markdown rendering, `/user/{handle}/posts/{slug}`. |
| 62 | 64 | *Open question: is writing actually the first portfolio feature, or is it |
| 63 | 65 | projects/showcases?* |
| 64 | 66 | 2. **Milestone 4 — Repo model.** `Repository` entity, `Visibility`, `create_repo`, |
| | @@ -85,9 +87,9 @@ Ordered. Pull from the top. |
| 85 | 87 | - `Router::builder().discover()` collects `#[page]`-annotated items **at link time**, |
| 86 | 88 | so pages can live in any module. Layering is our choice, not the framework's. |
| 87 | 89 | - `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. |
| 90 | + Still deferred. Now that URLs are scoped ([0003](decisions/0003-scoped-urls.md)) the |
| 91 | + module tree and the URL tree line up — `user/handle/repos/name` — which makes |
| 92 | + `module_router!` a much better fit than it was under root-level handles. |
| 91 | 93 | - Path and query params are read from `Cx` via `path_param!` / `#[query_params]`, not |
| 92 | 94 | injected as handler arguments. Parses are memoized per request. |
| 93 | 95 | - Layouts wrap by path prefix and nest outermost-first, and a layout can catch a page's |