From a847ff2e52c62cb3fdb5237e6c37aa63497be7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sun, 26 Apr 2026 08:41:07 +0200 Subject: [PATCH] Add /ia-review and /spec-drift-review process skills 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) --- .claude/skills/ia-review/SKILL.md | 275 ++++++++++++++++++++++ .claude/skills/spec-drift-review/SKILL.md | 216 +++++++++++++++++ docs/information-architecture.md | 190 ++++++++++++--- 3 files changed, 648 insertions(+), 33 deletions(-) create mode 100644 .claude/skills/ia-review/SKILL.md create mode 100644 .claude/skills/spec-drift-review/SKILL.md diff --git a/.claude/skills/ia-review/SKILL.md b/.claude/skills/ia-review/SKILL.md new file mode 100644 index 0000000..8e323f9 --- /dev/null +++ b/.claude/skills/ia-review/SKILL.md @@ -0,0 +1,275 @@ +--- +name: ia-review +description: Take a snapshot of the apps' information architecture (sitemap, navigation, audience-gating per route) and write or refresh `docs/information-architecture.md`. Use when the user wants to review the IA, plan a navigation/surface redesign, or check for drift since the last review. +license: MIT +metadata: + author: trails.cool + version: "1.0" +--- + +Walk the apps' route table + navigation surfaces, build a sitemap, surface +tensions and open questions, and capture the result in +`docs/information-architecture.md` (fresh write or refresh against the +prior snapshot). + +The output is a *review document for the user* — not a unilateral plan. +Decisions are made by the user during the conversation that follows; the +doc captures the snapshot and the open questions that need answering. + +--- + +## When to use + +- The user explicitly asks for an IA review ("review the IA", "what's + the information architecture look like"). +- Before a navigation/surface redesign, so the redesign is informed by + a current snapshot rather than a vibe. +- After a chunk of new features — pages, modals, settings sections — + to check whether the IA is drifting (busy navbar, duplicated surfaces, + orphaned routes). +- When the user says "we should do another IA review" after the prior + doc has aged. + +--- + +## Steps + +1. **Detect mode (fresh vs refresh)** + + Check whether `docs/information-architecture.md` already exists. + + - **No file:** fresh review. Build the snapshot from scratch. + - **File exists:** refresh review. Read it first; preserve the + decisions/backlog the user has accumulated and only update the + snapshot sections (sitemap, navigation, observations, open + questions). Decisions previously crossed out stay crossed out. + + In refresh mode, also read the snapshot date at the top — anything + shipped *since* that date is what your refresh should focus on. + +2. **Identify the apps in scope** + + trails.cool ships two front-ends; the IA question lives mostly in + the Journal: + + - `apps/journal/` — user accounts, social, content. Main IA + surface. + - `apps/planner/` — anonymous, ephemeral. ~5 routes; include for + completeness but don't dwell. + + If the project structure has changed and there's a new app, include + it. + +3. **Read the route tables** + + For each app: + + ``` + apps//app/routes.ts + ``` + + This is the authoritative URL → route-file mapping. Both apps use + explicit registration (per CLAUDE.md), so `routes.ts` is complete. + +4. **Read the navigation surfaces** + + - `apps/journal/app/root.tsx` (and `apps/planner/app/root.tsx` if + it has navigation) — the top navbar lives here. Read both the + loader (to see what data the navbar consumes — counts, badges, + user fields) and the `NavBar` component (to see what entries + render). + - `apps/journal/app/components/Footer.tsx` — the footer. + - Any auth-gate / Terms-gate logic in the root loader. + +5. **Sample key route loaders to understand audience** + + For each top-level route, scan its loader to determine: + + - Does it require a session? (loaders typically `redirect("/auth/login")` + for anonymous visitors when so.) + - Does it serve different content per session? (e.g., `home.tsx` + branches on `user`.) + - Does it have an access rule beyond auth? (locked-account 404s, + visibility checks, etc.) + + You don't need to read every route — pick the top-level ones and + any that look like they might gate differently than the URL hints. + +6. **Build the sitemap** + + Group routes by audience: **Public surface** (anonymous-reachable) + and **Authenticated surface** (signed-in only). Within each group, + order by topic (auth, profile, content, settings, legal, etc.). + + Use plain code blocks with one URL per line and a one-line gloss + per entry. Keep the format scannable; don't repeat what the URL + already says. + +7. **Map navigation surfaces** + + Two short tables/snippets: + + - **Navbar (signed-in)** — entries left-to-right. + - **Navbar (signed-out)** — entries left-to-right. + - **Footer** — links + any meta text. + + Note any entry whose visibility is conditional (badge counts, etc.). + +8. **Identify "feed concepts" and other duplications** + + trails.cool has historically had multiple feed-like surfaces. Any + IA review should ask: how many lists of activities are there? Is + the same data reachable from multiple URLs? Is there a URL that + shows different products to different audiences? + + Capture these in a small table or section if they exist. + +9. **Map cross-app linking** + + Journal ↔ Planner cross-links (JWT callback URLs, "Try the + Planner" buttons, etc.). One short list. + +10. **List observations** + + Walk the snapshot and call out tensions worth discussing. Useful + prompts: + + - **Busy clusters** — three or more controls for the same concept + side-by-side in the navbar. + - **Redundant paths** — same destination reachable from multiple + surfaces with no clear reason. + - **Dead-end routes** — pages reachable only by typing the URL, + no in-app link. + - **Missing surfaces** — common user need with no in-app path + (e.g., "find people to follow" with no `/explore`). + - **Audience mismatch** — same URL serving meaningfully different + products to anon vs auth. + - **Visual inconsistency** — adjacent navbar entries with + different treatment (icon vs text, different baselines). + - **Mobile hazards** — clusters that will wrap badly under + small viewport widths. + + Each observation should be one short paragraph. Each is a + question for the user to answer, not a decision you've made. + +11. **List open IA questions** + + Distinct from observations: these are larger directional choices + where the answer determines what other observations even matter. + Examples: "Should `/` and `/feed` merge for signed-in users?", + "Where does an `/explore` page live, if at all?", "Mobile + pattern — hamburger? Bottom tab bar?" + + Keep these as bullets the user can answer in one line each. + +12. **Write the doc** + + Output to `docs/information-architecture.md`. Use this top + structure: + + ```markdown + # Information Architecture Review + + *Snapshot date: YYYY-MM-DD.* 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`. + + 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 + [...] + + ## Journal sitemap + ### Public surface (logged-out) + [...] + ### Authenticated surface (logged-in) + [...] + + ### Navigation surfaces + [...] + + ## Logged-in vs logged-out home + [...if `/` does double duty...] + + ## [Any "N feed concepts" / duplication sections] + [...] + + ## Cross-app linking + [...] + + ## Planner sitemap + [...short...] + + ## Observations worth discussing + [...] + + ## Open IA questions + [...] + ``` + + Use today's date for the snapshot. Reference the source-of-truth + files at the top so the next review knows what to compare against. + +13. **In refresh mode, preserve the user's accumulated decisions** + + The prior doc may already contain: + + - Resolved observations (struck through with a *Resolved: ...* + note). + - An "Implementation backlog" section with streams. + - An "Open exploration" section. + + These are the *user's work*, not the snapshot. Carry them forward + untouched unless one is plainly obsolete (e.g., the feature it + references no longer exists). When in doubt, leave it and let the + user prune. + + If a previously-flagged observation is no longer present in the + current code (e.g., it was implemented), update its status note + rather than removing it — preserves history. + +14. **Surface a ranked next-action list** + + After writing the doc, summarize in 4–6 lines what changed since + the prior review (or what the most actionable observations are if + fresh). End with a question: which open IA question does the user + want to tackle first? + + Don't start implementation work — this skill is for the + *snapshot*. The decisions and the implementation backlog grow + through the conversation that follows. + +--- + +## What this skill is NOT + +- **Not an implementation skill.** Don't write code, don't open PRs. + The doc is the deliverable; decisions and code follow in normal + conversation. +- **Not a unilateral redesign.** Observations are questions for the + user. Don't bake "decisions" into the snapshot — those go in the + backlog only when the user has actually answered the question. +- **Not a spec change.** OpenSpec specs describe what's shipped; this + doc describes the IA *as it stands* with tensions flagged. Any + resulting spec updates happen during implementation, not during the + review. + +--- + +## Guardrails + +- Always include the snapshot date at the top of the output doc — IA + drifts; future-you needs to know whether to trust the snapshot or + refresh it. +- Reference the source-of-truth files (`routes.ts`, `root.tsx`) so the + next review's diff is mechanical. +- Keep observations as questions, not decrees. The user makes the + call. +- In refresh mode, preserve the user's accumulated decisions verbatim. + Only the snapshot sections are yours to rewrite. +- Don't invent routes or features. If you can't find evidence for it + in the code, don't put it in the snapshot. +- Keep it scannable. The doc is for review; verbose explanations bury + the signal. diff --git a/.claude/skills/spec-drift-review/SKILL.md b/.claude/skills/spec-drift-review/SKILL.md new file mode 100644 index 0000000..bf1cae9 --- /dev/null +++ b/.claude/skills/spec-drift-review/SKILL.md @@ -0,0 +1,216 @@ +--- +name: spec-drift-review +description: Walk every spec in `openspec/specs/`, compare it to the shipped code, and produce a categorized drift report (high/medium/low severity per spec, plus code-without-spec findings and structural suggestions). Use when the user wants to check spec drift, after a chunk of features has shipped, or when planning a spec catch-up PR. +license: MIT +metadata: + author: trails.cool + version: "1.0" +--- + +Walk the specs directory + the shipped code, compare them claim by +claim, and produce a structured drift report. The report is the +deliverable; fixes happen in a follow-up PR after the user has reviewed +the findings. + +The goal is to keep `openspec/specs/` honest — specs are useless if they +don't describe the actual product, and worse than useless if they +contradict it. + +--- + +## When to use + +- The user explicitly asks to check spec drift ("are the specs in sync", + "review specs against code"). +- After a multi-feature chunk has shipped without per-feature spec + promotion — drift accumulates fastest in catch-up phases. +- Before reorganizing the specs directory (split / merge / rename). +- When a spec contradicts the codebase and you're not sure which is + right. + +--- + +## Steps + +1. **Get the lay of the land** + + Run these in parallel: + + ```bash + ls openspec/specs/ + ls openspec/changes/ # in-flight work — NOT drift + cat openspec/CAPABILITIES.md # if it exists, it's the index + openspec list --json # any active changes that explain drift + ``` + + **Important:** any spec referenced in an active openspec change is + *expected* to drift from current code — that drift is the work in + progress. Note these and exclude them from the report. + +2. **Identify the code-side anchors per spec** + + For each spec at `openspec/specs//spec.md`, find the + primary code locations that implement it. Most capability specs map + to one or more of: + + - **Routes:** `apps/journal/app/routes/*.tsx` / `*.ts` — + authoritative for URL behavior, redirects, access gating. + - **Server lib:** `apps/journal/app/lib/.server.ts` — most + business logic. + - **Schema:** `packages/db/src/schema/journal.ts` — table shapes, + visibility values, defaults, indexes. + - **i18n:** `packages/i18n/src/locales/{en,de}.ts` — user-facing + strings, often telling. + - **Tests:** integration tests are a great oracle for the *intended* + behavior; mismatch with the spec usually means the spec is stale. + + Don't read every file. Pick the 1–3 anchors per spec that the + requirements most plausibly map to. + +3. **Compare claim by claim** + + For each requirement in a spec, ask: + + - **Does the code do this?** If not, is it because the requirement + was retired or because it was never shipped? + - **Does the code do *more* than this?** New scenarios shipped + without a spec update. + - **Does the code do this *differently*?** Different URL, + different default, different status code, different rule. + - **Does this requirement reference a name that no longer exists?** + Renamed routes, deleted helpers, removed tables. + + Track findings with severity: + + | Severity | What it means | + |----------|---------------| + | **High** | Spec actively misleads — claims a behavior the code does not exhibit. A reader implementing against the spec would write the wrong code. | + | **Medium** | Spec is incomplete — code has scenarios the spec doesn't describe. Reader gets less information than they should but isn't actively misled. | + | **Low** | Wording drift — comments/cross-refs mention a renamed thing, but the requirement statements are still accurate. | + +4. **Find code-without-spec** + + Walk the route tree in `apps/journal/app/routes.ts` and the topic + files in `apps/journal/app/lib/`. For each top-level concept ask: + + - Is this concept covered by a spec? + - If yes, does the spec mention this surface? + - If no, should it be? + + Genuine "no spec" cases are usually: new feature shipped without + spec promotion, or piece of infrastructure deemed too internal for a + spec. Both are valid; flag the former, leave the latter alone. + +5. **Structural review** + + Spec organization itself can drift: + + - **Specs that grew too big** — multiple unrelated requirements + under one spec. Candidates for a split (we previously split + `account-settings` into `profile-settings`, `account-management`, + `connected-services`). + - **Specs that overlap** — the same requirement appears in two + specs, or one spec keeps cross-referencing another. Candidate for + a merge or a clearer ownership boundary. + - **Specs that should exist but don't** — a capability is shipped + and substantial enough to merit its own spec but is currently + squeezed into another. (We added `sse-broker` and `notifications` + this way.) + - **`CAPABILITIES.md` drift** — if the index exists, check that + every spec dir has an entry and every entry points at a real + spec. The index is the easy thing to forget when adding a spec. + +6. **Compose the report** + + Output to the conversation as a markdown structure. Don't write a + doc unless the report is unusually large and the user asks for one — + most drift reports get acted on inside a single PR and don't need a + long-lived artifact. + + Suggested structure: + + ```markdown + # Spec drift review — YYYY-MM-DD + + **Active changes excluded from this review:** + - (touches: ) + + ## High-severity drift + + ### `` + - **Requirement: ** says ; code at `` does . + [link / one-line action] + - … + + ## Medium-severity drift + + ### `` + - + - … + + ## Low-severity drift (wording / cross-refs) + + - ``: + - … + + ## Code-without-spec + + - at `` — should this be in , or a new + spec? Recommendation: <…> + + ## Structural suggestions + + - Split: , + - Merge: + + - New spec: covering + - `CAPABILITIES.md` updates: + ``` + +7. **Propose next actions, then stop** + + End the review with three concrete options the user can pick from: + + - **Ship a catch-up PR for everything** — works when drift is + mostly low/medium and the fix is mechanical. + - **Fix high-severity first, defer the rest** — works when high + items are urgent and the rest can wait for natural per-feature + spec updates. + - **Restructure first, then catch up** — works when structural + suggestions (split / merge / new spec) are large enough that + fixing claims inside the wrong spec shape would just have to be + redone. + + Don't pick for them. Don't start fixing yet. + +--- + +## What this skill is NOT + +- **Not a fixer.** This skill produces a report. Apply happens after + the user has decided which findings to act on. +- **Not a CI check.** It's interactive — judgment calls (severity, + splits, merges) are part of the value, not an automation target. +- **Not for in-flight work.** Active openspec changes legitimately + cause spec/code mismatch; flag them in the "excluded" section and + move on. +- **Not a code review.** The question is "does the spec match the + code", not "is the code good." Code-quality observations belong in + PR review, not here. + +--- + +## Guardrails + +- Always start by reading active openspec changes — drift caused by + in-flight work is not drift. +- Don't write spec edits during the review. The user picks which + findings to act on; edits happen after. +- When code and spec disagree, the prevailing rule on this project is + **code is source of truth** unless the user says otherwise. Surface + the conflict; don't preemptively decide. +- Skip generated/scaffold files (e.g. `.react-router/types/**`) — they + are derived, not product. +- Don't pad the report with low-severity wording drift unless the user + asks for it. Concentrate signal. +- Keep observations specific (file + line + claim), not abstract. A + finding without an anchor isn't actionable. diff --git a/docs/information-architecture.md b/docs/information-architecture.md index cd0ab0f..656d18c 100644 --- a/docs/information-architecture.md +++ b/docs/information-architecture.md @@ -4,6 +4,12 @@ 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. @@ -200,44 +206,42 @@ 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.** `` + `Settings` + `Logout` - is three controls for the same concept. Already on the redesign list — - collapsing into an avatar dropdown is the natural fix. +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.** - The button on logged-in `/` (currently labelled "Feed") is mildly redundant - with the navbar entry. Worth keeping if Personal and Followed feel like - separate destinations; worth dropping if the navbar's "Feed" link is - obvious enough on its own. +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.** One is an icon, the - other is a text link. Either both icons (consistent) or both text - (discoverable) would be more legible. Mobile wrap is already going to be a - problem. +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. Worth flagging only - because some apps merge them into a single "history" timeline. + 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.** Fine for now (5 sections, ~6 controls per - section). At ~10 sections it becomes a tab strip; at ~15 it becomes a left - nav. Not urgent. +7. ~~**`/settings` is one page.**~~ *Decision: break it apart. See Stream E.* -8. **No `/explore`, no `/users` directory, no search.** A signed-in user who - wants to *find* people to follow has no in-app path — they need a username - from outside. Federation (Phase 2) makes this worse before it gets better. +8. ~~**No `/explore`, no `/users` directory, no search.**~~ *Decision: + propose an `/explore` spec. See Stream F.* -9. **No mobile breakpoint for the navbar.** The cluster of 7 links on the - right will wrap on phones today. Tied to the navbar redesign. +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/` 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.* --- @@ -300,7 +304,7 @@ Decisions captured above translate into two work-streams. Items marked - `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 +### 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 @@ -370,17 +374,91 @@ Requests tab is the actionable surface, the Activity tab is the log. ### Stream C — Navbar redesign -*Pending tomorrow. Principles agreed in the IA review; specifics still -need decisions before implementation.* +**Scope (decided):** -**Needs decision:** +- Regroup the account cluster (`` + 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 target.** Hamburger? Bottom tab bar? "Phase 2, just don't break"? -- **Self-link vs avatar.** Today the navbar has a `` text link to - your profile. After the avatar dropdown, does the avatar take that role, - or does Profile live only inside the dropdown? +- **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 `` 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 `` + 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):** @@ -424,12 +502,58 @@ mobile work yet):** These came up in the IA review but aren't part of this round: -- **Search / `/explore` directory.** No way to find users in-app. Defer - until federation (Phase 2) makes it more painful. -- **Settings sectioning.** Single scrollable page is fine at 5 sections. - **`/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?