Skip to main content
← Back to list
01Issue
BugShippedSwamp CLIPublic
Assigneesskunk-ape

Relationships

#2427 Lab tab title goes stale on client-side navigation between issues

Opened by skunk-ape · 9/23/2026· Shipped 9/23/2026

The browser tab title never changes while navigating inside the Lab. Open the issue list, click an issue, click another: the <title> keeps whatever the server set when the document was first served. Reloading shows the correct title for the issue in the URL.

Steps to reproduce

  1. Load /lab — tab reads "Lab - Swamp Club".
  2. Click any issue. URL becomes /lab/{n}, the detail pane renders, tab title is unchanged.
  3. Click a second issue. Same.
  4. Reload. Tab now reads Lab #{n}: {title} — correct.

Cause

/lab and /lab/{n} are the same island, islands/lab/LabShell.tsx, which is a hand-rolled client router. Clicking an issue never reaches the server, and the only writer of the tab title is server-side:

  • routes/_app.tsx:58 renders <title>{title}</title> from ctx.state.title, once per document response.
  • routes/lab/[slug].tsx:185 sets Lab #{number}: {title} for a detail URL.
  • routes/lab/[slug].tsx:302 / routes/lab/index.tsx:54 set the list titles.

document.title appears nowhere in the repo, and there is no <Head> usage — Fresh 2 has no client-side head management. So after hydration nothing can change the title.

The island intercepts the navigation instead:

  • islands/lab/LabShell.tsx:510 handleIssueClick calls e.preventDefault() on the issue anchor for any plain left-click (anchors at :880 board, :1555 list).
  • islands/lab/LabShell.tsx:493 navigateTo sets the selection, calls history.pushState, and calls loadDetail. It never touches the title.
  • islands/lab/LabShell.tsx:406 loadDetail fetches /api/v1/lab/issues/{n} as JSON. JSON has no head.

A reload fixes it because that is a real document request to routes/lab/[slug].tsx, whose handler sets ctx.state.title for the URL the island's pushState already corrected. The server has always been right; it is just never consulted on the fast path.

Also affected, same cause

Every pushState in the island: closing the detail (navigateTo(null), :602/:2070) leaves an issue title on a list view; toggleFilter (:479), clearFilters (:487) and setViewMode (:852) change the filter URL without the matching Lab — {filter} title; the popstate handler (:552) restores selection on back/forward but sets no title. Because loadDetail re-runs off the SSE stream when the open issue changes (:689), a rename also leaves the old title up.

Suggested fix

One effect in LabShell, keyed on the selected number and activeDetail — written in an effect, not render scope, since the island SSRs before it hydrates. activeDetail.value.issue already carries number and title (LabShellDetail, :58), so no extra fetch is needed.

The one real cost: the list-side vocabulary (filterTitle and its STATUS_LABEL / TYPE_LABEL maps) is route-local at routes/lab/[slug].tsx:95-111, and an island cannot import from routes/. It needs extracting into the shared kernel (lib/*.ts) with the route re-pointed at it, or the client and server titles drift the next time a status label changes. ctx.state.description / ogImage can stay server-only — crawlers get a real document response.

Scope elsewhere in the app

islands/ExtensionRegistry.tsx:471, islands/FeedFilterBar.tsx:81 and islands/LeaderboardBrowser.tsx:76 do the same pushState-without-title, so the gap is app-wide. Their host routes set static titles (routes/extensions/index.tsx:50, routes/feed/index.tsx:51), so there is nothing per-URL to go stale and no visible defect there today. Lab is the only place where a server title varies per URL behind a client router.

Environment

swamp-club web app, main at ec005fc0. Reproduced by reading the code; the mechanism is confirmed against the files and lines above.

02Bog Flow
✓OPEN✓TRIAGED✓IN PROGRESS✓SHIPPED+ 1 MOREASSIGNED+ 8 MOREREVIEW+ 6 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

9/23/2026, 8:19:09 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
skunk-ape assigned skunk-ape9/23/2026, 6:57:57 PM

Sign in to post a ripple.