| | @@ -21,12 +21,11 @@ Shippable on its own: a public profile that renders signed out. |
| 21 | 21 | - [x] Settle URL shape — handles at the root, routes grouped under prefixes |
| 22 | 22 | ([0004](decisions/0004-root-handles-grouped-routes.md)) |
| 23 | 23 | - [x] Reserved-handle denylist in `OrgName::new`; auth routes moved under `/auth/` |
| 24 | | −- [ ] **Spike: route precedence.** Does `/auth/login` still win once `/{handle}` exists |
| 25 | | − at the root? Most routers prefer a static segment over a parameterised one, but |
| 26 | | − this is load-bearing and unverified — check before designing around it |
| 27 | | −- [ ] Decide `module_router!` vs explicit `#[page]` paths, informed by the spike |
| 24 | +- [ ] Decide `module_router!` vs explicit `#[page]` paths |
| 28 | 25 | - [ ] `PublicProfile` read model + `view_profile` use case — **must not carry email** |
| 29 | | −- [ ] `/{handle}` page: renders label and handle, 404 on unknown, case-insensitive |
| 26 | +- [ ] `/{handle}` page: label, handle, bio, and the section frame; 404 on unknown, |
| 27 | + case-insensitive |
| 28 | +- [ ] Assert `/auth/login` and `/api/me` still route once `/{handle}` exists at the root |
| 30 | 29 | - [ ] `/` redirects to the owner's profile once claimed, retiring the placeholder |
| 31 | 30 | - [ ] `/api/users/{handle}` — same read model, public JSON |
| 32 | 31 | |
| | @@ -38,7 +37,7 @@ A profile you can't change is a stub. This is what makes it a portfolio page. |
| 38 | 37 | - [ ] Flash messages — the first edit form needs success and failure feedback, and |
| 39 | 38 | every form after it inherits whatever we build here |
| 40 | 39 | - [ ] `/{handle}/settings` — edit display name and bio, owner only, enforced in the use |
| 41 | | − case |
| 40 | + case (settings belong to the org, and this scales to organisations) |
| 42 | 41 | - [ ] Owner-only affordances on the profile (edit link) |
| 43 | 42 | |
| 44 | 43 | ### Done when |
| | @@ -54,7 +53,10 @@ on the page. `/api/users/{handle}` returns the same public view. |
| 54 | 53 | profile needs its **own** read model; reusing `Identity` would publish the owner's |
| 55 | 54 | email address to anonymous visitors. This is the single most likely mistake in this |
| 56 | 55 | milestone. |
| 57 | | −- **Route precedence** between `/auth/login` and `/{handle}`. Verify, don't assume. |
| 56 | +- **Route precedence** between static routes and `/{handle}`. The reserved list stops a |
| 57 | + user *owning* `auth`, but it does not stop the router matching `/api/me` against |
| 58 | + `/{handle}/{x}` and shadowing the real route. Static-over-parameterised is near |
| 59 | + universal, so this is an assertion when the route lands, not a blocking spike. |
| 58 | 60 | - **Case-insensitive handles.** Storage is `collate nocase` and `OrgName::new` |
| 59 | 61 | lowercases, so `/JamesGill` must resolve rather than 404. |
| 60 | 62 | - **Authorization on settings** belongs in the use case, taking an `Actor` — not in the |
| | @@ -65,14 +67,14 @@ on the page. `/api/users/{handle}` returns the same public view. |
| 65 | 67 | - **Reserve handles early.** Adding to the denylist later is a breaking change for |
| 66 | 68 | whoever holds that handle ([0004](decisions/0004-root-handles-grouped-routes.md)). |
| 67 | 69 | |
| 68 | | −### Open questions |
| 70 | +### Settled |
| 69 | 71 | |
| 70 | | −- **Where do settings live?** `/{handle}/settings` is consistent with grouping and |
| 71 | | − scales to organisations; `/settings` is simpler for a single user but would need |
| 72 | | − another reserved word (already reserved). Leaning `/{handle}/settings`. |
| 73 | | −- **Empty sections or none?** Rendering "Repositories (none yet)" makes the frame |
| 74 | | − visible but is speculative scaffolding for things that don't exist. Leaning towards |
| 75 | | − omitting them until the content type lands. |
| 72 | +- **Settings live at `/{handle}/settings`.** They belong to the organisation, which |
| 73 | + scales to real organisations in Milestone 7 without moving. |
| 74 | +- **Render the full frame from the start**, including sections with nothing in them. |
| 75 | + A profile page that renders almost nothing is a poor start for a portfolio-first |
| 76 | + product; the shape of the page is part of what is being built, not scaffolding for |
| 77 | + it. |
| 76 | 78 | |
| 77 | 79 | ### Carried over — small, unblocked |
| 78 | 80 | |