Skip to content

feat(examples-chat): URL-based thread routing — /<mode>/:threadId#500

Merged
blove merged 1 commit into
mainfrom
claude/url-thread-routing
May 21, 2026
Merged

feat(examples-chat): URL-based thread routing — /<mode>/:threadId#500
blove merged 1 commit into
mainfrom
claude/url-thread-routing

Conversation

@blove
Copy link
Copy Markdown
Contributor

@blove blove commented May 21, 2026

Summary

Shareable, deep-linkable URLs for the canonical demo. Spec: docs/superpowers/specs/2026-05-20-url-thread-routing-design.md.

URL becomes the source of truth for the active thread:

  • /embed → new chat
  • /embed/<id> → load that thread
  • /popup/<id> / /sidebar/<id> → same, in those modes

Behaviors:

  • Pasting an invalid/deleted thread id silently redirects to the bare mode path (uses replaceUrl: true so back button doesn't loop)
  • Clicking a thread in the sidenav updates the URL
  • Switching modes (Embed → Popup) preserves the active thread id
  • Browser back/forward walks through visited threads
  • Drops the localStorage threadId persistence — URL IS persistence (browsers reopen last URL); cleaner than fighting URL vs storage

New API

LangGraphThreadsAdapter.getThread(id) — validates pasted ids. Returns null for both 404 (no such thread) AND 422 (malformed UUID); rethrows other errors so transport failures aren't masked.

Test plan

  • 4 new unit tests for getThread() pass (vitest run threads-adapter)
  • nx build langgraph + nx build examples-chat-angular green
  • Local Chrome MCP smoke verified all four flows:
    • /embed bare → stays bare
    • /embed/<valid-id> → URL preserved
    • /embed/<bogus> → redirected to /embed
    • Mode toggle while active thread → /popup/<same-id>
  • CI green

Out of scope (separate)

  • Server-rendered <title> / og:* tags for richer link previews
  • Restoring scroll position to last-read message on reload
  • Auth — these URLs are public on the demo by design

🤖 Generated with Claude Code

Shareable, deep-linkable URLs for the canonical demo. Spec at
docs/superpowers/specs/2026-05-20-url-thread-routing-design.md.

URL becomes the source of truth for the active thread:

- /embed              → new chat (no persisted thread on first load)
- /embed/<id>         → load that thread
- /popup/<id>         → same, in popup mode
- /sidebar/<id>       → same, in sidebar mode

Behaviors:

- Pasting an invalid/deleted thread id silently redirects to the
  bare mode path (replaceUrl: true so back button doesn't loop)
- Clicking a thread in the sidenav updates the URL
- Switching modes (Embed → Popup) preserves the active thread id
- Browser back/forward walks through visited threads
- Drops the localStorage threadId persistence — URL IS persistence
  (browsers reopen last URL); cleaner than fighting URL vs storage

Adds LangGraphThreadsAdapter.getThread(id) to validate pasted ids.
Treats both 404 (no such thread) and 422 (malformed UUID) as
"missing"; rethrows other errors so transport failures aren't
masked. 4 new unit tests cover the new method.

Verified locally via Chrome MCP: all four flows (bare, valid id,
bogus id redirect, mode-switch preserves id) pass end-to-end.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
threadplane Ready Ready Preview, Comment May 21, 2026 12:03am

Request Review

@blove blove enabled auto-merge (squash) May 21, 2026 00:01
@blove blove merged commit 1201712 into main May 21, 2026
37 of 38 checks passed
blove added a commit that referenced this pull request May 21, 2026
…omponent instance (#504)

PR #500 added a route entry per (mode, hasThreadId) — six entries total,
two per mode. Navigating from `/embed` to `/embed/<id>` was a route
CHANGE (different entry), which tore down EmbedMode and remounted it,
killing the active stream when the agent auto-created a thread mid-send.

Symptom: `examples/chat — e2e` test "failed stream surfaces an alert and
the next send recovers" + "core controls expose expected accessible names"
both fail because the assistant message never renders (stream died at
remount time). Vercel deploy gates on e2e, so prod has been stuck on the
pre-#500 bundle.

Fix: collapse the per-mode pair into a single route entry via UrlMatcher.
Both `/embed` and `/embed/<threadId>` now resolve to the same route, so
the component instance survives the navigation and the stream keeps
flowing.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
blove added a commit that referenced this pull request May 21, 2026
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