Captures two recurring review processes as repeatable Claude skills so they don't have to be reinvented from scratch each time: - /ia-review: walks the route tables + nav surfaces, builds the sitemap, flags drift, and writes/refreshes docs/information-architecture.md. Refresh mode preserves the user's accumulated decisions and implementation backlog while only rewriting the snapshot sections. - /spec-drift-review: walks every spec in openspec/specs/, compares it to shipped code, and produces a categorized drift report (high/medium/low severity) plus code-without-spec findings and structural suggestions (split/merge/new specs, CAPABILITIES.md catch-up). Excludes specs touched by active openspec changes since that mismatch is intended in-flight work, not drift. Also extends the IA doc with the items resolved/deferred during the review (observations 1-10), three new streams (D: drop redundant Feed button on logged-in /, E: break Settings apart, F: propose /explore spec), and an "Open exploration" section for the routes-vs-activities terminology question. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
562 lines
24 KiB
Markdown
562 lines
24 KiB
Markdown
# Information Architecture Review
|
||
|
||
*Snapshot date: 2026-04-26.* If the navbar, route table, or feed model has
|
||
shifted since then, treat this doc as stale and refresh against
|
||
`apps/journal/app/routes.ts` + `apps/journal/app/root.tsx`.
|
||
|
||
To regenerate this doc (fresh or refresh), invoke the **`/ia-review`**
|
||
skill — it walks the route tables + nav surfaces, builds the sitemap,
|
||
flags drift since this snapshot, and preserves the decisions/backlog
|
||
already captured below. The companion **`/spec-drift-review`** skill
|
||
does the equivalent for `openspec/specs/` against shipped code.
|
||
|
||
A snapshot of where every page lives, who sees it, and how visitors navigate
|
||
between them. Intended for review — flag anything that doesn't make sense or
|
||
should change.
|
||
|
||
## Apps
|
||
|
||
trails.cool ships two front-ends:
|
||
|
||
- **Journal** (`trails.cool`) — user accounts, social, content. Most of the IA
|
||
question lives here.
|
||
- **Planner** (`planner.trails.cool`) — anonymous, ephemeral. Five routes
|
||
total; not a real IA concern. Listed at the bottom for completeness.
|
||
|
||
---
|
||
|
||
## Journal sitemap
|
||
|
||
### Public surface (logged-out)
|
||
|
||
```
|
||
/ Anonymous home (hero + marketing + public feed)
|
||
/users/:username Public profile (full or locked stub)
|
||
/users/:username/followers Followers list (404 on private profile)
|
||
/users/:username/following Following list (404 on private profile)
|
||
/activities/:id Public activity (or 404)
|
||
/routes/:id Public route (or 404)
|
||
|
||
/auth/register Sign-up
|
||
/auth/login Sign-in
|
||
/auth/verify Magic-link click-through landing
|
||
/auth/accept-terms Re-accept gate (used after a Terms version bump)
|
||
|
||
/legal/imprint Imprint (German required)
|
||
/legal/privacy Privacy policy
|
||
/legal/terms Terms of service
|
||
/privacy 301 → /legal/privacy
|
||
```
|
||
|
||
### Authenticated surface (logged-in)
|
||
|
||
```
|
||
/ Personal dashboard ("your activities" stream)
|
||
/feed Social feed (people you follow, accepted)
|
||
/notifications Notifications log (4 event types, cursor paginated)
|
||
/follows/requests Incoming Pending follow requests inbox
|
||
|
||
/users/:username Any profile (own, followed, or locked stub)
|
||
/users/:username/followers Gated by locked-account rule
|
||
/users/:username/following Gated by locked-account rule
|
||
|
||
/routes Your routes
|
||
/routes/new Create route
|
||
/routes/:id View
|
||
/routes/:id/edit Edit (handoff to Planner via JWT callback)
|
||
|
||
/activities Your activities
|
||
/activities/new Create activity
|
||
/activities/:id View
|
||
|
||
/settings Profile + email + passkeys + sync + danger zone
|
||
|
||
/sync/import/:provider Wahoo import flow
|
||
/auth/logout Logout
|
||
```
|
||
|
||
### Navigation surfaces
|
||
|
||
**Top navbar (logged-in)** — in document order:
|
||
|
||
```
|
||
[trails.cool] Feed Routes Activities ... 🔔 Follow requests <username> Settings Logout
|
||
└─ unread badge └─ pending badge
|
||
```
|
||
|
||
**Top navbar (logged-out):**
|
||
|
||
```
|
||
[trails.cool] ... Login [Register]
|
||
```
|
||
|
||
**Footer (everywhere):** Imprint · Privacy · Terms · Source (GitHub) · "Alpha".
|
||
|
||
**Profile self-link in nav** points to `/users/<self>`. There is no separate
|
||
"My profile" route; you reach your own profile via your own username.
|
||
|
||
---
|
||
|
||
## Logged-in vs logged-out home
|
||
|
||
`/` does double duty:
|
||
|
||
| Visitor | What `/` shows |
|
||
|---|---|
|
||
| Anonymous | Hero · Sign-up CTAs · "Try the Planner" · marketing cards (flagship only) · **public instance feed** |
|
||
| Signed-in | "Welcome, X" · Feed button · "New activity" CTA · **personal stream of your own activities** |
|
||
|
||
The two surfaces share no layout — they're effectively two pages behind one
|
||
URL. `home.tsx` branches on `user`.
|
||
|
||
---
|
||
|
||
## Two feed surfaces, three views
|
||
|
||
**Decision (2026-04-26):** merge Social and Public into a single `/feed`
|
||
page with a Followed / Public toggle. `/` stays "your content," `/feed`
|
||
becomes "everyone else's content."
|
||
|
||
| Surface | View | Audience |
|
||
|---|---|---|
|
||
| `/` (logged-in) | **Personal** — your own activities | You |
|
||
| `/feed` | **Followed** (default) — accepted-followed users' public activities | Signed-in |
|
||
| `/feed?view=public` | **Public** — instance-wide public activities | Signed-in |
|
||
| `/` (logged-out) | Hero + marketing + public feed (visitor entry point) | Anonymous |
|
||
|
||
Signed-in users can now see the public instance feed without logging out.
|
||
The logged-out `/` keeps its public feed as the anonymous visitor's first
|
||
impression.
|
||
|
||
Implementation notes (for whoever picks this up):
|
||
|
||
- `listSocialFeed` and `listRecentPublicActivities` already exist in
|
||
`apps/journal/app/lib/activities.server.ts` — the toggle just picks one.
|
||
- URL shape: `?view=followed` (default, can omit) | `?view=public`. Keeps the
|
||
toggle bookmarkable and avoids two routes.
|
||
- Empty-state in the Followed view today links to `/` ("see the public feed
|
||
there") — that link should become the in-page Public toggle.
|
||
- Anonymous request to `/feed` keeps redirecting to `/auth/login`; the
|
||
public feed remains reachable for them on `/`.
|
||
|
||
---
|
||
|
||
## Notifications vs follow requests
|
||
|
||
Two adjacent inboxes, two navbar entries:
|
||
|
||
| | `/notifications` | `/follows/requests` |
|
||
|---|---|---|
|
||
| Purpose | Read-only event log | Actionable Approve/Reject |
|
||
| Types covered | follow_received, follow_request_received, follow_request_approved, activity_published | Only Pending follows targeting you |
|
||
| Navbar surface | 🔔 icon + red unread badge | "Follow requests" text + red pending count |
|
||
| State | `read_at` per row | None — Approve/Reject mutates the follow row |
|
||
|
||
A `follow_request_received` notification points its "Review request" link at
|
||
`/follows/requests`, so the two surfaces are explicitly cross-linked rather
|
||
than merged. Mastodon makes the same split.
|
||
|
||
---
|
||
|
||
## Settings
|
||
|
||
`/settings` is a single scrollable page with five concerns stacked top to
|
||
bottom:
|
||
|
||
1. Profile (display name, bio, profile visibility)
|
||
2. Email change (with re-verification)
|
||
3. Passkeys (list, add, delete)
|
||
4. Connected services (Wahoo today, Strava/Garmin later)
|
||
5. Danger zone (delete account)
|
||
|
||
Spec was recently split into three (`profile-settings`, `account-management`,
|
||
`connected-services`) but the UI is still one page.
|
||
|
||
---
|
||
|
||
## Cross-app linking
|
||
|
||
- **Journal → Planner:** "Edit in Planner" on a saved route generates a JWT
|
||
callback URL and opens `planner.trails.cool/session/<id>`. Logged-out home
|
||
also has a "Try the Planner" link.
|
||
- **Planner → Journal:** the post-edit "Save" flow POSTs back to
|
||
`/api/routes/:id/callback` on the Journal that issued the JWT.
|
||
- The Planner has no link back to a specific Journal otherwise — it's
|
||
intentionally instance-agnostic.
|
||
|
||
---
|
||
|
||
## Planner sitemap
|
||
|
||
```
|
||
/ Anonymous home (CTA: "Plan a route")
|
||
/new Create a fresh anonymous session
|
||
/session/:id Collaborative editor
|
||
```
|
||
|
||
No accounts, no profiles, no settings. IA is essentially trivial.
|
||
|
||
---
|
||
|
||
## Observations worth discussing
|
||
|
||
These are tensions or surprises I noticed while mapping. None are bugs — but
|
||
each is a deliberate IA choice that's worth confirming.
|
||
|
||
1. **`/` is two different products.** Logged-in `/` is "your stuff," logged-out
|
||
`/` is "the instance." Discoverable? Or should logged-in `/` keep showing
|
||
*something* of the public surface (e.g., a "Discover" tab)?
|
||
*Status: open — not sure yet if this needs resolving.*
|
||
|
||
2. ~~**Signed-in users can't see the public instance feed.**~~ *Resolved:
|
||
merged into `/feed` with a Followed / Public toggle (see above).*
|
||
|
||
3. ~~**The navbar's account cluster is busy.**~~ *Folded into Stream C
|
||
(navbar redesign): regroup the cluster behind an avatar dropdown.*
|
||
|
||
4. ~~**`/feed` is reachable from both the navbar AND a button on the home
|
||
page.**~~ *Decision: drop the button on logged-in `/` — the navbar
|
||
entry is enough. See Stream D.*
|
||
|
||
5. ~~**🔔 vs "Follow requests" are visually inconsistent.**~~ *Resolved by
|
||
Stream B — Follow requests folded into the bell as a Requests tab, so
|
||
there's a single inbox icon in the navbar.*
|
||
|
||
6. **Routes and Activities are siblings, not nested.** That's correct today —
|
||
an activity can exist without a route, and vice versa. *Flagged for a
|
||
broader review: the concept of routes-vs-activities, plus the impending
|
||
word collision with social "activities" (comment, like, publish) once
|
||
federation lands. Tracked separately — see "Open exploration"
|
||
below.*
|
||
|
||
7. ~~**`/settings` is one page.**~~ *Decision: break it apart. See Stream E.*
|
||
|
||
8. ~~**No `/explore`, no `/users` directory, no search.**~~ *Decision:
|
||
propose an `/explore` spec. See Stream F.*
|
||
|
||
9. ~~**No mobile breakpoint for the navbar.**~~ *Decision: include mobile
|
||
responsiveness in Stream C (navbar redesign).*
|
||
|
||
10. **Profile vs identity.** Your own profile is at `/users/<you>` not
|
||
`/me` or `/profile`. Reachable via the navbar self-link. Fine, but means
|
||
"view as logged-out visitor" is non-trivial — opening incognito is the
|
||
only way to see your locked stub.
|
||
*Status: open — no decision yet.*
|
||
|
||
---
|
||
|
||
## Open IA questions
|
||
|
||
If the answer to any of these is "I don't know yet," that's a signal it's
|
||
worth discussing before the navbar redesign locks in shapes:
|
||
|
||
- ~~Should `/` and `/feed` merge for signed-in users?~~ *Resolved:
|
||
no — `/` stays "you," `/feed` becomes "everyone else" with a
|
||
Followed / Public toggle.*
|
||
- ~~Where does an `/explore` or `/discover` page live?~~ *Resolved: it's
|
||
the Public view inside `/feed`, no new top-level destination needed.*
|
||
|
||
---
|
||
|
||
## Implementation backlog
|
||
|
||
Decisions captured above translate into two work-streams. Items marked
|
||
*needs decision* are blockers — confirm before starting that stream.
|
||
|
||
### Stream A — Merge Social and Public feeds into `/feed`
|
||
|
||
**Code changes:**
|
||
|
||
1. **`apps/journal/app/routes/feed.tsx`**
|
||
- Loader reads `?view=` query (`"followed"` default, `"public"` accepted;
|
||
anything else falls back to default).
|
||
- Branch the fetch: `listSocialFeed(user.id, 50)` for Followed,
|
||
`listRecentPublicActivities(50)` for Public — both already exist in
|
||
`apps/journal/app/lib/activities.server.ts`.
|
||
- Render a toggle (two pills/tabs) at the top of the page; active state
|
||
highlighted; toggle uses `<Link to="?view=public">` so it's plain HTTP,
|
||
SSR-friendly, bookmarkable, and works without JS.
|
||
- Per-view `<meta>` title: "Following — trails.cool" / "Public — trails.cool".
|
||
- Per-view empty state: Followed view's existing "see the public feed"
|
||
escape now links to `?view=public` instead of `/`.
|
||
|
||
2. **Translation keys (`apps/journal/app/locales/{en,de}/journal.json`)**
|
||
- `social.feed.toggle.followed`, `social.feed.toggle.public`
|
||
- `social.feed.public.heading`, `social.feed.public.empty`
|
||
- Drop `social.feed.publicFeedLink` once the empty-state link is rewired.
|
||
|
||
3. **No change** to `apps/journal/app/routes/home.tsx` — logged-in `/` keeps
|
||
showing the personal stream; the "Feed" button still targets `/feed`
|
||
(defaults to Followed view).
|
||
|
||
4. **No change** to anonymous `/feed` behavior — it continues to redirect to
|
||
`/auth/login`. The public feed remains visible to anonymous visitors on `/`.
|
||
|
||
**Spec updates after Stream A ships:**
|
||
|
||
- `openspec/specs/social-follows/spec.md` — the **Social activity feed**
|
||
requirement currently scopes `/feed` to followed users only. Update it (or
|
||
split into a new "Feed views" requirement) to add scenarios for the Public
|
||
view and the toggle behavior.
|
||
- `openspec/specs/activity-feed/spec.md` — the **Instance-wide public
|
||
activity feed** requirement says it powers the home page. Add a scenario
|
||
noting it now also powers `/feed?view=public` for signed-in users.
|
||
- `openspec/specs/journal-landing/spec.md` — no change. Logged-in `/`
|
||
already shows the personal stream; this is unchanged.
|
||
|
||
### Stream B — Merge Follow requests into Notifications ✅ Shipped (PR #316)
|
||
|
||
**Decision (2026-04-26):** fold `/follows/requests` into `/notifications`
|
||
as a tabbed sub-page (Activity / Requests). The bell icon stays the
|
||
single inbox surface; the Requests tab shows a dot when there are pending
|
||
follows still needing Approve/Reject. The Notifications spec's existing
|
||
"no inline Approve/Reject on the activity log" rule is preserved — the
|
||
Requests tab is the actionable surface, the Activity tab is the log.
|
||
|
||
**Code changes:**
|
||
|
||
1. **`apps/journal/app/routes/notifications.tsx`**
|
||
- Loader reads `?tab=` (`"activity"` default, `"requests"` accepted).
|
||
- For Activity tab: existing behavior (paginated rows, `?before=` cursor).
|
||
- For Requests tab: load `listPendingFollowRequests(user.id)` and
|
||
`countPendingFollowRequests(user.id)` (already exist).
|
||
- Page renders a tab strip at the top with both labels and an unread/
|
||
pending dot per tab; active tab highlighted.
|
||
- "Mark all read" button only appears on the Activity tab.
|
||
|
||
2. **`apps/journal/app/routes.ts`**
|
||
- `route("follows/requests", ...)` becomes a redirect to
|
||
`/notifications?tab=requests` (301). New file
|
||
`routes/follows.requests.tsx` reduces to one `loader` returning a
|
||
redirect, mirroring the existing `routes/privacy.tsx` pattern.
|
||
|
||
3. **`apps/journal/app/lib/notifications/link-for.ts`**
|
||
- `follow_request_received` now resolves to
|
||
`/notifications?tab=requests` instead of `/follows/requests`. Update
|
||
the unit test in `link-for.test.ts`.
|
||
|
||
4. **`apps/journal/app/root.tsx`**
|
||
- Drop the `Follow requests` navbar entry entirely.
|
||
- Drop `pendingFollowRequests` from the root loader (no longer needed
|
||
for navbar). The Requests tab loader inside `/notifications`
|
||
fetches it on-demand.
|
||
- Bell unread badge logic unchanged — `follow_request_received`
|
||
already creates an unread row, so the existing unread count
|
||
implicitly covers pending requests.
|
||
|
||
5. **i18n updates** (`packages/i18n/src/locales/{en,de}.ts`)
|
||
- New keys: `notifications.tabs.activity`, `notifications.tabs.requests`.
|
||
- The `settings.profile.visibility.privateHelp` string mentions
|
||
`/follows/requests` — update to point at the new surface.
|
||
|
||
6. **e2e tests**
|
||
- `e2e/social.test.ts`: the `/follows/requests redirects anonymous
|
||
visitors to login` test changes to verify the new redirect target,
|
||
and the "B sees the request in /follows/requests" step navigates
|
||
to `/notifications?tab=requests` instead.
|
||
- `e2e/notifications.test.ts`: same — replace `bPage.goto("/follows/requests")`
|
||
with `bPage.goto("/notifications?tab=requests")`.
|
||
|
||
**Spec updates after Stream B ships:**
|
||
|
||
- `openspec/specs/notifications/spec.md` — extend the **Notifications
|
||
page and unread count** requirement to describe the tabbed structure;
|
||
refine the "follow_request_received card links to /follows/requests,
|
||
not inline Approve/Reject" scenario to reflect the new URL and the tab
|
||
separation. The `linkFor` scenarios need the updated path.
|
||
- `openspec/specs/social-follows/spec.md` — update the **Pending follow
|
||
request management** requirement: navigation moves to
|
||
`/notifications?tab=requests`; the navbar count badge requirement is
|
||
retired (it's now a dot inside the Requests tab).
|
||
- `openspec/specs/journal-landing/spec.md` — the **Notifications entry
|
||
in the navbar** requirement should clarify that this is the only
|
||
inbox-style entry (no separate Follow requests entry).
|
||
|
||
### Stream C — Navbar redesign
|
||
|
||
**Scope (decided):**
|
||
|
||
- Regroup the account cluster (`<username>` + Settings + Logout) behind
|
||
an avatar dropdown.
|
||
- Treat mobile responsiveness as a first-class concern in the redesign,
|
||
not a phase-2 follow-up. Today the navbar wraps badly on phones.
|
||
- The bell + Requests tab from Stream B already gives the navbar a
|
||
consistent inbox surface; nothing further needed there.
|
||
|
||
**Needs decision before implementation:**
|
||
|
||
- **Avatar dropdown content.** Profile · Settings · Logout — any others?
|
||
(Theme toggle? Language toggle? Account switcher when federation lands?)
|
||
- **Mobile pattern.** Hamburger drawer? Bottom tab bar? Condensed top
|
||
bar with the dropdown absorbing most controls?
|
||
- **Self-link vs avatar.** Today the navbar has a `<username>` text link
|
||
to your profile. After the avatar dropdown, does the avatar take that
|
||
role (click → profile, dropdown chevron → menu), or does Profile live
|
||
only inside the dropdown?
|
||
|
||
### Stream D — Drop the redundant "Feed" button on logged-in `/`
|
||
|
||
**Decision (2026-04-26):** logged-in `/` no longer needs a "Feed" button
|
||
in the page header — the navbar entry is the single discoverable path.
|
||
|
||
**Code changes:**
|
||
|
||
- `apps/journal/app/routes/home.tsx` — remove the `<a href="/feed">`
|
||
button next to the "New Activity" CTA in the signed-in branch
|
||
(currently lines ~170–183). Keep "New Activity" as the only header
|
||
action.
|
||
- No spec changes; `journal-landing/spec.md` has a **Social feed link
|
||
for signed-in users** requirement that mentions a "Feed (or
|
||
equivalent) link" — that requirement should be retired.
|
||
|
||
Tiny PR, ~5 lines + a spec update.
|
||
|
||
### Stream E — Break Settings apart
|
||
|
||
**Decision (2026-04-26):** `/settings` becomes a sectioned area rather
|
||
than a single scrollable page. Spec was already split into three
|
||
(`profile-settings`, `account-management`, `connected-services`); the UI
|
||
should follow.
|
||
|
||
**Needs decision before implementation:**
|
||
|
||
- **Layout pattern.** Tab strip on `/settings` (single URL, JS-driven
|
||
tabs)? Nested routes (`/settings/profile`, `/settings/security`,
|
||
`/settings/connections`, `/settings/danger`)? Sidebar nav?
|
||
- **Section list.** The current page has five concerns:
|
||
1. Profile (display name, bio, profile visibility)
|
||
2. Email (with re-verification)
|
||
3. Passkeys
|
||
4. Connected services (Wahoo today)
|
||
5. Danger zone (delete account)
|
||
Keep five? Merge "Email" into "Profile"? Pull "Danger zone" into
|
||
"Account" alongside Email?
|
||
|
||
**Spec impact:** `profile-settings`, `account-management`, and
|
||
`connected-services` are already separate specs — they describe behavior,
|
||
not URL structure. Whichever URL pattern wins, only `journal-landing`
|
||
(or wherever the navbar currently sits) needs to know.
|
||
|
||
### Stream F — Propose an `/explore` spec
|
||
|
||
**Decision (2026-04-26):** the gap between "I want to find people to
|
||
follow" and "I have a username from outside" needs an in-app path.
|
||
Federation (Phase 2) makes this more valuable, but local-only `/explore`
|
||
is useful on day one.
|
||
|
||
**Approach:** kick off an OpenSpec proposal via `/opsx:propose` rather
|
||
than diving into code. The proposal phase decides:
|
||
|
||
- What `/explore` actually shows. A directory of all local users? A
|
||
curated "active in the last N days" list? A randomized rotation? Just
|
||
the public activity feed (which is currently buried on logged-out `/`)?
|
||
- Whether search is part of v1 or a follow-up.
|
||
- Where the link lives in the navbar (its own entry vs. inside `/feed`).
|
||
- Privacy: private profiles need to be excluded from any directory; this
|
||
is the same locked-account access rule that already gates
|
||
followers/following lists.
|
||
|
||
**Spec impact:** new spec file at
|
||
`openspec/specs/explore/spec.md` (or similar — name TBD by the proposal),
|
||
plus an entry in `CAPABILITIES.md`.
|
||
|
||
**Code changes (assuming avatar dropdown + icon-only Follow requests + no
|
||
mobile work yet):**
|
||
|
||
1. **`apps/journal/app/root.tsx`**
|
||
- Add `displayName` to the loader's `user` payload (currently only
|
||
`id` + `username`).
|
||
- Replace the `<username> · Settings · Logout` cluster with a single
|
||
avatar trigger + dropdown menu.
|
||
- Replace the "Follow requests" text link with an icon-only button +
|
||
badge (matching the bell's visual treatment).
|
||
- Confirm bell + Follow requests sit on the same baseline / same gap.
|
||
|
||
2. **New components in `apps/journal/app/components/`**
|
||
- `Avatar.tsx` — initials fallback when no image (none of our 4 prod
|
||
users have an avatar field today, so this is initials-only initially).
|
||
- `NavDropdown.tsx` — click-outside + Escape-to-close. Simple
|
||
headless impl, no library.
|
||
|
||
3. **Loader query**
|
||
- `getSessionUser` already returns `displayName`; just expose it in the
|
||
loader return shape.
|
||
|
||
**Spec updates after Stream B ships:**
|
||
|
||
- `openspec/specs/journal-landing/spec.md` — currently has only the
|
||
**Notifications entry in the navbar** requirement. Add a new
|
||
requirement (or extend that one) for the account dropdown shape and the
|
||
Follow-requests icon. Or factor the navbar out into its own spec —
|
||
plausible if the cluster keeps growing.
|
||
- `openspec/specs/social-follows/spec.md` — the **Pending follow request
|
||
management** requirement says "the 'Follow requests' link in the navbar
|
||
renders with a small red count badge". Update the wording to reflect
|
||
the icon treatment.
|
||
- `openspec/specs/notifications/spec.md` — the **Notifications page and
|
||
unread count** requirement already says "navbar entry renders with a
|
||
count badge"; verify the wording still applies cleanly to the icon-only
|
||
treatment.
|
||
|
||
### Out of scope (deliberately)
|
||
|
||
These came up in the IA review but aren't part of this round:
|
||
|
||
- **`/me` alias for own profile.** Marginal value; navbar self-link is
|
||
enough.
|
||
- **Mobile breakpoints across the app.** Bigger than just the navbar.
|
||
Stream C handles the navbar; the rest of the app is a separate effort.
|
||
|
||
---
|
||
|
||
## Open exploration
|
||
|
||
Items that aren't ready for an implementation backlog because the
|
||
underlying *concept* still needs work, not just the UI.
|
||
|
||
### Routes vs Activities — terminology and model
|
||
|
||
The IA review flagged that Routes and Activities are sibling top-level
|
||
concepts. Two reasons to revisit this beyond the URL structure:
|
||
|
||
1. **Conceptual overlap.** A *route* is a planned path. An *activity*
|
||
is a recorded outing (often along a route). Some apps (Strava,
|
||
Komoot) collapse these into one timeline; we keep them split. Worth
|
||
confirming the split still earns its keep.
|
||
2. **Word collision with social "activities".** Once federation lands
|
||
(and arguably already, with the `activity_published` notification
|
||
type), "activity" will be overloaded:
|
||
- **Athletic activity:** a bike ride, a hike, a run.
|
||
- **Social activity:** a comment, a like, a follow, a publish event.
|
||
This is the ActivityPub sense — and it's the one that will appear
|
||
in feeds, notifications, and remote inboxes.
|
||
|
||
The collision is going to bite. ActivityPub uses "Activity" as a
|
||
technical term throughout; the user-facing "Activity" (a ride) will
|
||
constantly be next to ActivityPub Activities (a follow, a like) in
|
||
logs, diagrams, and possibly UI strings.
|
||
|
||
**Plan:** dedicate a separate review doc (e.g.
|
||
`docs/routes-vs-activities.md`) to think this through before any spec
|
||
or code change. Open questions for that doc:
|
||
|
||
- Do we rename the user-facing "Activity" to something else (Outing?
|
||
Trip? Ride? Record? Trace?) before federation cements the social
|
||
meaning?
|
||
- Or do we keep "Activity" for the user-facing object and use "Event"
|
||
or "Stream item" for the social/ActivityPub sense?
|
||
- Are Routes still needed as a top-level object, or could they be a
|
||
subordinate concept of Activities (a "saved planned version" of
|
||
something you may eventually ride)?
|
||
- What does the export/import story look like — GPX is route-shaped;
|
||
Strava's TCX is activity-shaped; how do we want the model to talk
|
||
about each?
|
||
|
||
Not urgent, but worth resolving before federation is far enough along
|
||
that renaming is a migration headache.
|
||
- **Is "Follow requests" a sub-page of Notifications or a sibling?** Today
|
||
it's a sibling (separate navbar item). Could be a tab inside `/notifications`.
|
||
- **Avatar dropdown content.** Profile · Settings · Logout — anything else?
|
||
(Theme toggle? Language toggle? Account switcher when federation lands?)
|
||
- **Mobile.** Hamburger menu? Bottom tab bar? Nothing yet — what's the target?
|
||
- **Search.** None today. Adding it changes the navbar. When does it land?
|