Skip to content

fix(links): finish the /p/ permalink migration — emit canonical links everywhere, teach the viewer router to parse them#213

Merged
elucid merged 1 commit into
mainfrom
elucid/finish-concept-rename-migration
Jul 5, 2026
Merged

fix(links): finish the /p/ permalink migration — emit canonical links everywhere, teach the viewer router to parse them#213
elucid merged 1 commit into
mainfrom
elucid/finish-concept-rename-migration

Conversation

@elucid

@elucid elucid commented Jul 5, 2026

Copy link
Copy Markdown
Member

The bug

/p/ permalinks were half-implemented in 0.11.0. PR #140 added the /p/:id server routes and made the new publish_post MCP tool emit them — but the viewer's History-API router was never taught to parse them. The result: the server resolves a /p/ link fine (byte-identical response to /s/), the SPA shell loads… and then the client-side router matches nothing, so the user lands on the workspace instead of the post. To anyone clicking an agent-shared /p/ link, the post simply "doesn't resolve."

Reproduced against a live 0.11.0 deployment — the deployed bundle's router only contains:

match(/^\/session\/([^/]+(?:\/s\/…
match(/^\/s\/([^/]+

Meanwhile, most of the app still emitted legacy /s/ links anyway (CLI, viewer copy-link, meta tags), so the canonical shape was both broken on arrival and barely used.

The fix

Parse both, write canonical. The viewer router (viewer/src/host.ts) now parses /[sp]/:id and /session/:id/[sp]/:pid inbound, and writes the canonical /p/ shapes.

Every link emitter now produces /p/:

  • bin/sideshow.js — CLI publish / update output URL
  • server/mcpHttp.ts + mcp/server.tsall MCP tool responses, including the deprecated publish_surface / publish_snippet aliases (the deprecation is about tool names/args; the returned link can be canonical). postResult() loses its seg param since there's only one answer now.
  • viewer/src/api.ts — copy-link (postLink) and open-as-image (postImageLink)
  • server/app.ts<link rel="canonical"> and og:url / og:image preview tags
  • workers/screenshot.ts — the PNG matcher accepts both /p/:id.png and /s/:id.png, so existing /s/ share links keep their previews

Nothing inbound changes: /s/:id, /session/:id/s/:pid, and /s/:id.png remain accepted as legacy aliases and resolve exactly as before. Card iframes still load surfaces from /s/:id?part=N (internal wire, not a user-facing link).

Tests

  • New e2e regression test: goto /p/:id renders the standalone full-page post — the exact scenario that shipped broken (the suite previously only covered /s/:id, which is how this slipped through).
  • Updated unit/e2e assertions to the canonical shapes, keeping explicit legacy-alias coverage (worker screenshot matcher, inbound /s/ gotos, deep-link [sp] tolerance).
  • ✅ 425 unit tests, 166 e2e (chromium + webkit), tsc --noEmit, oxlint.

Changeset included (patch).

…r router to parse them

/p/ links were half-implemented in 0.11.0: the server resolved them but the
viewer's History-API router only parsed /s/ shapes, so canonical links from
publish_post landed on the workspace instead of the post. The router now
parses both spellings and writes /p/; the CLI, viewer copy-link/open-as-image,
OG/canonical meta tags, screenshot worker, and all MCP tool responses
(including deprecated aliases) now emit /p/. Legacy /s/ URLs remain accepted
inbound. Adds a /p/:id standalone e2e regression test.
@elucid elucid merged commit 5d38be6 into main Jul 5, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant