docs: news relaunch migration plan (link/redirect map + interim routing)
Settled plan (user + Codex) for standing up /news as the feed's home and cutting /home3 → / without breaking the feed, deep links, or SEO. Drives the upcoming implementation; next build is the feed extraction (pure refactor). Includes the four Codex amendments: /news noindex during transition, explicit prototype 301s, explicit legacy-view mapping (shim before render + /news?view=today alias), and the footer coverage inventory (FeedbackModal stays in the global layout). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,103 @@
|
|||||||
|
# News relaunch — link/redirect map + interim-routing plan
|
||||||
|
|
||||||
|
Scope: stand up `/news` (the feed's new home) and cut `/home3` → `/` (hub) without breaking
|
||||||
|
the feed, deep links, or SEO. Verified against the codebase 2026-06-28. Settled with the
|
||||||
|
user + Codex; amendments folded in. **Next build = the extraction (§A, pure refactor).**
|
||||||
|
|
||||||
|
## A. Interim routing — no broken window, no duplicated impl
|
||||||
|
|
||||||
|
The feed currently IS `routes/+page.svelte` (~1,065 lines: views, BottomNav, MoodNav,
|
||||||
|
LanePicker, SavedFlyout, search). Don't clone it. Extract once, mount twice.
|
||||||
|
|
||||||
|
1. **Extract** the feed UI from `routes/+page.svelte` → `lib/components/NewsFeed.svelte`
|
||||||
|
(verbatim move; URL base becomes `/news`). `parseView`/`urlForView` switch base `/` → `/news`.
|
||||||
|
2. **Mount at both** during transition:
|
||||||
|
- `routes/news/+page.svelte` → `<NewsFeed/>`
|
||||||
|
- `routes/+page.svelte` → `<NewsFeed/>` (interim; identical component)
|
||||||
|
One implementation, both routes live, zero interim breakage.
|
||||||
|
- **`/news` stays hidden during transition**: `X-Robots-Tag: noindex, follow` (so we don't
|
||||||
|
publish a duplicate indexable feed). Removed at cutover, when `/news` is added to the sitemap.
|
||||||
|
3. **Parity test** at `/news` (every view, deep link, Back/Forward, account action) — see §F.
|
||||||
|
4. **Restyle** `NewsFeed` once (CD editorial + reusable card — replaces `ArticleCard`).
|
||||||
|
5. **Behavior fix** (separate, deliberate, post-parity): Latest stays chronological; Highlights
|
||||||
|
gets prefs + geo scope.
|
||||||
|
6. **Cutover**: `routes/+page.svelte` → hub content (move from `home3`); add the legacy-query
|
||||||
|
redirect shim (§C); drop `/news` noindex + add to sitemap; `/news` stays the feed.
|
||||||
|
|
||||||
|
## B. Link/redirect map
|
||||||
|
|
||||||
|
Rule of thumb: **brand/logo/"home"/"back to UB" → `/` (hub)**; **anything "more news /
|
||||||
|
browse / a feed view" → `/news`**; **feed-internal view URLs → `/news?…` base**;
|
||||||
|
**`/home3` fallbacks → `/`**.
|
||||||
|
|
||||||
|
### B1. Brand / home / back → `/` (KEEP)
|
||||||
|
- `Header.svelte:9` brand `/`; `account:103` brand `/`, `:108` back `/`; `admin:678` brand `/`,
|
||||||
|
`:28` non-admin bounce `/`; `auth/verify:24` post-signin `/` (DECIDED: always hub; future
|
||||||
|
optional start-screen setting), `:38` back `/`; `zen:59` brand `/`;
|
||||||
|
`share.py:197,327` brand logo `/`, `:354` "Back to Upbeat Bytes" `/`; `api.py:781` unsubscribe back `/`.
|
||||||
|
|
||||||
|
### B2. News CTAs → `/news` (CHANGE)
|
||||||
|
- `home3:198` "Read more good news" `/` → **`/news`** (the loop bug)
|
||||||
|
- `share.py:210` "Explore Upbeat Bytes →" → **`/news`**
|
||||||
|
- `share.py:332` "Browse more on Upbeat Bytes →" → **`/news`**
|
||||||
|
|
||||||
|
### B3. Feed-internal view URLs → `/news?…` base (CHANGE, via the extraction)
|
||||||
|
- `routes/+page.svelte` (→ NewsFeed): `urlForView` builds `/?source=`,`/?tag=`,`/?view=`
|
||||||
|
(`:39-41`), search `goto('/?q=')` (`:50`), clear `goto('/')` (`:689`), `urlForView('today')`
|
||||||
|
returns `/` (`:344,503`) → all rebase to `/news`.
|
||||||
|
- `account:196` Following `/?view=following` → **`/news?view=following`**
|
||||||
|
- `share.py:30` source link `/?source={id}` → **`/news?source={id}`**
|
||||||
|
|
||||||
|
### B4. `/home3` fallbacks → `/` (CHANGE)
|
||||||
|
- `HubBar:18` home nav `/home3` → **`/`**; `:28` brand `/home3` → **`/`**
|
||||||
|
- `HubShell:17` back fallback `goto('/home3')` → **`/`**
|
||||||
|
- `art:41`, `play:213` back fallback `goto('/home3')` → **`/`**
|
||||||
|
|
||||||
|
## C. Redirects
|
||||||
|
- **Legacy root-query shim** on the new hub `/` — runs **before the hub fetches/renders** (no
|
||||||
|
hub flash), `replaceState`:
|
||||||
|
- `/?view=today` → `/news?view=highlights`
|
||||||
|
- `/?view=latest` → `/news` (Latest is the default)
|
||||||
|
- `?tag=`, `?source=`, `?q=`, other `?view=` → carry across to `/news?…`
|
||||||
|
- `/news?view=today` remains an accepted **alias** (old links never break)
|
||||||
|
- **`/home2`, `/home2.html`, `/home3`, `/home3.html` → `/` permanent (301)** (Caddy). A bare
|
||||||
|
route delete would just serve the SPA fallback — redirect explicitly.
|
||||||
|
|
||||||
|
## D. Infra
|
||||||
|
- **Caddy `@hidden`** (currently `/home2 /home3 /word /word.html /quote /quote.html
|
||||||
|
/onthisday /onthisday.html /admin /admin.html`):
|
||||||
|
- REMOVE `/word* /quote* /onthisday*` (indexable at launch)
|
||||||
|
- `/home3*`,`/home2*` → 301 redirects (out of @hidden)
|
||||||
|
- **`/news` carries `X-Robots-Tag: noindex, follow` until cutover**, then removed
|
||||||
|
- KEEP `/admin*`; `/a/*` still routed to FastAPI
|
||||||
|
- **Sitemap** (`api.py sitemap()`): raise `LIMIT 5000` → ~50000; gate on having a real summary
|
||||||
|
(skip ~31 incomplete; ~+1,000 URLs); add static `/news` (at cutover) + `/art /play /word
|
||||||
|
/quote /onthisday` (keep `/`,`/today`). Fix **`HEAD /sitemap.xml`** (currently 404).
|
||||||
|
- **Head patcher** (`patch-static-heads.mjs`): add `/news` (title/desc/canonical/OG).
|
||||||
|
- **PWA description** (`manifest.webmanifest` + `app.html` description/og/twitter): currently
|
||||||
|
news-only — broaden to the hub (news + daily art + games + small resets).
|
||||||
|
- **Footer**: ONE shared `Footer.svelte` — consistent core (**motto + Send feedback**) + a
|
||||||
|
default **slot for per-section extras**. **`FeedbackModal` stays in the global layout**;
|
||||||
|
only the layout's `<footer.site>` *markup* is removed (and HubShell's `<footer.foot>`).
|
||||||
|
**Coverage inventory** — the shared footer must be explicitly added to every public surface:
|
||||||
|
**Hub, News, Play, Art, HubShell details, Account, Zen** (if dev-visible). Admin/auth get a
|
||||||
|
deliberate **minimal** treatment (explicit, not by omission).
|
||||||
|
|
||||||
|
## E. Behavior (deliberate, post-parity)
|
||||||
|
- **Latest** = newest accepted after safety/boundary exclusions; **stop passing `home`** (or add
|
||||||
|
an explicit "Local first" lane) so Latest ≠ local-first.
|
||||||
|
- **Highlights** = ranked around interests + geo scope dial (kept at launch).
|
||||||
|
- `trackVisit()` stays global; `markBriefSeen()` only inside Highlights.
|
||||||
|
|
||||||
|
## F. Cutover checklist (rehearsal first, hidden)
|
||||||
|
1. **GSC review** (coverage / manual-actions / crawl) BEFORE rehearsal.
|
||||||
|
2. **Parity** at `/news`: each view (today/latest/following/tag/source/search/mood/topic),
|
||||||
|
deep links, Back/Forward single-history, account actions (save/follow/hide/replace), PWA.
|
||||||
|
3. **Legacy redirects**: `/?view=today`→`/news?view=highlights`, `/?view=latest`→`/news`,
|
||||||
|
`/?tag=…`,`/?source=…`,`/?q=…`→`/news?…`; `/home2*`,`/home3*`→`/`.
|
||||||
|
4. **SEO**: `/` 200 + indexable + canonical; `/a/*` 200 + self-canonical (unchanged); `/today`
|
||||||
|
200 indexable; `/news` noindex dropped + in sitemap; joy/art/play noindex removed + in
|
||||||
|
sitemap; sitemap GET + HEAD 200.
|
||||||
|
5. **Caches**: anon Latest/Brief edge-cacheable (45s) intact; personalized private/no-store.
|
||||||
|
SW is a kill-switch (no bump) — just verify.
|
||||||
|
6. **Promote** → live 200/301/canonical/cache checks; resubmit sitemap in GSC.
|
||||||
Reference in New Issue
Block a user