feat(chat): route all chats through workflow; drop legacy /api/chat branch#1755
feat(chat): route all chats through workflow; drop legacy /api/chat branch#1755sweetmantech wants to merge 1 commit into
Conversation
…ranch
Post Phase 2 backfill, existing chats can use the workflow path too, so
the legacy /api/chat conditional is no longer needed.
- (a) /chat/[roomId] resolves its sessionId via GET /api/chats/[id]
(ExistingChatBootstrap / useExistingChatBootstrap) before mounting <Chat>;
not-found/no-session rooms render a graceful state.
- (b) History loads from chat_messages via GET /api/sessions/{sessionId}/chats/{chatId}
instead of the memories-backed /api/chats/[id]/messages.
- (c) Removed the if(!sessionId) legacy branch in useChatTransport; sessionId
is now required through useVercelChat / VercelChatProvider / Chat. Migrated
the home route (/) to bootstrap a session like /chat (NewChatBootstrap), and
moved useAutoLogin into NewChatBootstrap so anonymous landings still prompt
sign-in instead of hanging on the spinner.
Merges to main only inside the held cutover bundle, after the final backfill
re-run (prod still writes rooms/memories until the cutover lands on main).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR migrates the chat application from client-generated UUIDs and a legacy API endpoint to a workflow-based session system. It introduces bootstrap components for both new and existing chats, requires ChangesWorkflow-based chat session migration
Sequence DiagramsequenceDiagram
participant User
participant HomePage
participant NewBoot as NewChatBootstrap
participant useAutoLogin
participant useNewBoot as useNewChatBootstrap
participant Chat
participant Privy
participant WorkflowAPI as /api/chat/workflow
User->>HomePage: Visit home /
HomePage->>NewBoot: render(initialMessages)
NewBoot->>useAutoLogin: call useAutoLogin()
useAutoLogin->>Privy: ensure authentication
Privy-->>useAutoLogin: authenticated or redirected
NewBoot->>useNewBoot: call useNewChatBootstrap()
useNewBoot->>Privy: getAccessToken()
Privy-->>useNewBoot: accessToken
useNewBoot-->>NewBoot: { sessionId, chatId }
NewBoot->>Chat: render with sessionId + chatId
Chat->>WorkflowAPI: POST /api/chat/workflow
WorkflowAPI-->>Chat: response
sequenceDiagram
participant User
participant ChatRoom as /chat/[roomId]
participant ExistBoot as ExistingChatBootstrap
participant useExistBoot as useExistingChatBootstrap
participant getChat
participant Privy
participant ChatAPI as /api/chats/{roomId}
participant Chat
participant WorkflowAPI as /api/chat/workflow
User->>ChatRoom: Visit /chat/roomId
ChatRoom->>ExistBoot: render(roomId)
ExistBoot->>useExistBoot: call useExistingChatBootstrap(roomId)
useExistBoot->>Privy: ensure authenticated
useExistBoot->>Privy: getAccessToken()
Privy-->>useExistBoot: accessToken
useExistBoot->>getChat: fetch getChat(roomId, token)
getChat->>ChatAPI: GET /api/chats/{roomId}
ChatAPI-->>getChat: { sessionId, chatId, ... }
getChat-->>useExistBoot: ApiChat
useExistBoot-->>ExistBoot: { status: ready, sessionId, chatId }
ExistBoot->>Chat: render with sessionId + chatId
Chat->>WorkflowAPI: POST /api/chat/workflow
WorkflowAPI-->>Chat: response
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 694fc72324
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const startedRef = useRef(false); | ||
|
|
||
| useEffect(() => { | ||
| if (!authenticated) return; |
There was a problem hiding this comment.
Trigger login before waiting for existing-chat auth
When a logged-out user opens /chat/[roomId] directly, this effect returns before fetching and ExistingChatBootstrap keeps rendering the skeleton; because <ChatContent> is only mounted after this hook reaches ready, its useAutoLogin() call never runs. This regresses the previous /chat/[roomId] behavior where <Chat> mounted immediately and prompted sign-in, so direct existing-chat links can hang indefinitely for unauthenticated non-mini-app users.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
components/VercelChat/ExistingChatBootstrap.tsx (1)
24-38: ⚡ Quick winAnnounce the
not_found/errorstates to assistive tech.Both branches render their message as a plain
<div>, so screen-reader users navigating into a freshly-resolved chat get no indication that it's unavailable or errored. Adding arole(and anaria-livefor the async error) makes these status messages perceivable.♿ Add status roles
if (state.status === "not_found") { return ( <div className="flex items-center justify-center h-dvh"> - <div className="text-grey-dark-1">This chat isn’t available.</div> + <div role="status" className="text-grey-dark-1">This chat isn’t available.</div> </div> ); } if (state.status === "error") { return ( <div className="flex items-center justify-center h-dvh"> - <div className="text-red-500 dark:text-red-400">{state.message}</div> + <div role="alert" className="text-red-500 dark:text-red-400">{state.message}</div> </div> ); }As per coding guidelines: "Provide proper ARIA roles/states and test with screen readers."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/VercelChat/ExistingChatBootstrap.tsx` around lines 24 - 38, The "not_found" and "error" branches in ExistingChatBootstrap render plain <div>s so screen-readers don't announce them; update the JSX in the ExistingChatBootstrap component to add appropriate ARIA roles and live regions—e.g., mark the not_found container as a status (role="status" and/or aria-live="polite") and mark the error message container as an assertive live region (role="alert" and/or aria-live="assertive") so state.status and state.message are exposed to assistive tech; ensure the element containing {state.message} uses the live/alert attributes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@hooks/useExistingChatBootstrap.ts`:
- Around line 36-56: The catch block in the useExistingChatBootstrap hook
currently resets startedRef.current = false which allows implicit retries when
deps like getAccessToken change; instead, make the error terminal by removing
the reset of startedRef.current in the catch so startedRef continues to guard
against re-invocation (leave setState({ status: "error", message }) intact),
keeping retry behavior tied to route remounts (key={roomId}) as documented.
---
Nitpick comments:
In `@components/VercelChat/ExistingChatBootstrap.tsx`:
- Around line 24-38: The "not_found" and "error" branches in
ExistingChatBootstrap render plain <div>s so screen-readers don't announce them;
update the JSX in the ExistingChatBootstrap component to add appropriate ARIA
roles and live regions—e.g., mark the not_found container as a status
(role="status" and/or aria-live="polite") and mark the error message container
as an assertive live region (role="alert" and/or aria-live="assertive") so
state.status and state.message are exposed to assistive tech; ensure the element
containing {state.message} uses the live/alert attributes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 82f473c0-12d8-4e9a-886c-a00ef31d2820
⛔ Files ignored due to path filters (2)
lib/chats/__tests__/getChat.test.tsis excluded by!**/*.test.*and included bylib/**lib/messages/__tests__/getChatMessages.test.tsis excluded by!**/*.test.*and included bylib/**
📒 Files selected for processing (13)
app/chat/[roomId]/page.tsxapp/page.tsxcomponents/Home/HomePage.tsxcomponents/VercelChat/ExistingChatBootstrap.tsxcomponents/VercelChat/NewChatBootstrap.tsxcomponents/VercelChat/chat.tsxhooks/useChatTransport.tshooks/useExistingChatBootstrap.tshooks/useMessageLoader.tshooks/useVercelChat.tslib/chats/getChat.tslib/messages/getChatMessages.tsproviders/VercelChatProvider.tsx
| useEffect(() => { | ||
| if (!authenticated) return; | ||
| if (startedRef.current) return; | ||
| startedRef.current = true; | ||
|
|
||
| void (async () => { | ||
| try { | ||
| const accessToken = await getAccessToken(); | ||
| const chat = await getChat(chatId, accessToken); | ||
| if (!chat || !chat.sessionId) { | ||
| setState({ status: "not_found" }); | ||
| return; | ||
| } | ||
| setState({ status: "ready", sessionId: chat.sessionId, chatId: chat.id }); | ||
| } catch (error) { | ||
| startedRef.current = false; | ||
| const message = error instanceof Error ? error.message : "Failed to load chat."; | ||
| setState({ status: "error", message }); | ||
| } | ||
| })(); | ||
| }, [authenticated, chatId, getAccessToken]); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Is the getAccessToken function returned by usePrivy() in @privy-io/react-auth referentially stable / memoized across renders?
💡 Result:
Answer: No definitive public guarantee was found that getAccessToken returned by usePrivy() is referentially stable/memoized across renders. What Privy docs confirm: - Privy documents that you can retrieve the token via const { getAccessToken } = usePrivy(); and then call await getAccessToken(); it also notes that getAccessToken “will also automatically refresh” the token if it’s near expiry/expired. [1] What Privy docs do not confirm: - The docs do not state that getAccessToken’s function identity is memoized (stable) across component re-renders, nor do they mention useCallback/memoization behavior for that specific method. [1] Evidence from official package pages: - The publicly visible package/registry/README content similarly shows the usePrivy() API surface but does not document referential-stability guarantees for getAccessToken. [2] Practical implication: - Because no source explicitly guarantees stability, you should assume the function reference could change when usePrivy() re-runs due to context/state updates, unless you verify empirically in your app (e.g., log the reference across renders) or inspect the exact installed package source for your version. If you want, tell me your installed @privy-io/react-auth version (e.g., 3.24.0-beta…) and whether you’re using React strict mode; then I can try to locate and cite the exact implementation for that version to answer definitively.
Citations:
- 1: https://docs.privy.io/authentication/user-authentication/access-tokens
- 2: https://registry.npmjs.org/@privy-io/react-auth
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show privy version(s)
ls -la
rg -n "\"`@privy-io/react-auth`\"" package.json package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null || true
# Inspect the hook file around the cited lines and any surrounding logic (startedRef, roomId key, retries)
HOOK_FILE="hooks/useExistingChatBootstrap.ts"
if [ -f "$HOOK_FILE" ]; then
wc -l "$HOOK_FILE"
sed -n '1,120p' "$HOOK_FILE" | nl -ba
else
echo "Missing $HOOK_FILE"
fiRepository: recoupable/chat
Length of output: 2236
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the hook file fully with line numbers
HOOK_FILE="hooks/useExistingChatBootstrap.ts"
if [ -f "$HOOK_FILE" ]; then
echo "=== $HOOK_FILE ==="
cat -n "$HOOK_FILE"
else
echo "Missing $HOOK_FILE"
fi
# Search for React keys involving roomId (e.g., key={roomId})
rg -n --hidden --no-ignore-vcs "key=\{[^}]*roomId[^}]*\}" . || true
rg -n --hidden --no-ignore-vcs "roomId[^\n]{0,80}key=" . || trueRepository: recoupable/chat
Length of output: 3010
Align the error behavior with the hook’s “StrictMode-only” startedRef guard
useExistingChatBootstrap documents that startedRef only guards StrictMode’s double-invoke and that re-resolution happens via key={roomId} route remounts. However, the catch block resets startedRef.current = false, which implicitly allows additional fetch attempts later if the useEffect deps change (notably getAccessToken—Privy does not document referential stability for that function). Since remounting on route change already covers “retry”, keeping the error state terminal avoids this unintended coupling.
♻️ Make the error state terminal (drop the implicit retry)
} catch (error) {
- startedRef.current = false;
const message = error instanceof Error ? error.message : "Failed to load chat.";
setState({ status: "error", message });
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| useEffect(() => { | |
| if (!authenticated) return; | |
| if (startedRef.current) return; | |
| startedRef.current = true; | |
| void (async () => { | |
| try { | |
| const accessToken = await getAccessToken(); | |
| const chat = await getChat(chatId, accessToken); | |
| if (!chat || !chat.sessionId) { | |
| setState({ status: "not_found" }); | |
| return; | |
| } | |
| setState({ status: "ready", sessionId: chat.sessionId, chatId: chat.id }); | |
| } catch (error) { | |
| startedRef.current = false; | |
| const message = error instanceof Error ? error.message : "Failed to load chat."; | |
| setState({ status: "error", message }); | |
| } | |
| })(); | |
| }, [authenticated, chatId, getAccessToken]); | |
| useEffect(() => { | |
| if (!authenticated) return; | |
| if (startedRef.current) return; | |
| startedRef.current = true; | |
| void (async () => { | |
| try { | |
| const accessToken = await getAccessToken(); | |
| const chat = await getChat(chatId, accessToken); | |
| if (!chat || !chat.sessionId) { | |
| setState({ status: "not_found" }); | |
| return; | |
| } | |
| setState({ status: "ready", sessionId: chat.sessionId, chatId: chat.id }); | |
| } catch (error) { | |
| const message = error instanceof Error ? error.message : "Failed to load chat."; | |
| setState({ status: "error", message }); | |
| } | |
| })(); | |
| }, [authenticated, chatId, getAccessToken]); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@hooks/useExistingChatBootstrap.ts` around lines 36 - 56, The catch block in
the useExistingChatBootstrap hook currently resets startedRef.current = false
which allows implicit retries when deps like getAccessToken change; instead,
make the error terminal by removing the reset of startedRef.current in the catch
so startedRef continues to guard against re-invocation (leave setState({ status:
"error", message }) intact), keeping retry behavior tied to route remounts
(key={roomId}) as documented.
There was a problem hiding this comment.
4 issues found across 15 files
Confidence score: 3/5
- There is a concrete user-facing regression risk: unauthenticated visitors can be left in a permanent loading state due to early-return/bootstrap flow changes in
hooks/useExistingChatBootstrap.tsandcomponents/VercelChat/ExistingChatBootstrap.tsx. - The auth/bootstrap sequencing appears fragile right now; when auth prompting no longer runs before bootstrap resolution on
/chat/[roomId], users may not reach sign-in or a terminal state. - There is additional medium risk from repeated bootstrap attempts if
getAccessTokenidentity changes and from duplicateuseAutoLoginhook instances triggering overlappinglogin()calls incomponents/VercelChat/NewChatBootstrap.tsx. - Pay close attention to
hooks/useExistingChatBootstrap.ts,components/VercelChat/ExistingChatBootstrap.tsx,components/VercelChat/NewChatBootstrap.tsx- prevent stuck loading and duplicate auth/bootstrap retries.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="hooks/useExistingChatBootstrap.ts">
<violation number="1" location="hooks/useExistingChatBootstrap.ts:37">
P2: This early return leaves unauthenticated visitors stuck in the loading state instead of progressing to sign-in or a terminal state.</violation>
<violation number="2" location="hooks/useExistingChatBootstrap.ts:51">
P2: The `startedRef.current = false` reset in the `catch` block allows unintended re-fetches if `getAccessToken` (which is in the dependency array and not guaranteed to be referentially stable by Privy) changes identity. Since `key={roomId}` already handles remounting for route changes, the error state should be terminal — remove this reset to prevent the hook from silently retrying on unrelated re-renders.</violation>
</file>
<file name="components/VercelChat/NewChatBootstrap.tsx">
<violation number="1" location="components/VercelChat/NewChatBootstrap.tsx:27">
P2: `useAutoLogin` is now called in both bootstrap and chat content, which can trigger duplicate `login()` attempts from separate hook instances.</violation>
</file>
<file name="components/VercelChat/ExistingChatBootstrap.tsx">
<violation number="1" location="components/VercelChat/ExistingChatBootstrap.tsx:40">
P2: Anonymous visits to `/chat/[roomId]` can get stuck on a permanent loading skeleton because auth prompting no longer runs before bootstrap resolution.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| } | ||
| setState({ status: "ready", sessionId: chat.sessionId, chatId: chat.id }); | ||
| } catch (error) { | ||
| startedRef.current = false; |
There was a problem hiding this comment.
P2: The startedRef.current = false reset in the catch block allows unintended re-fetches if getAccessToken (which is in the dependency array and not guaranteed to be referentially stable by Privy) changes identity. Since key={roomId} already handles remounting for route changes, the error state should be terminal — remove this reset to prevent the hook from silently retrying on unrelated re-renders.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At hooks/useExistingChatBootstrap.ts, line 51:
<comment>The `startedRef.current = false` reset in the `catch` block allows unintended re-fetches if `getAccessToken` (which is in the dependency array and not guaranteed to be referentially stable by Privy) changes identity. Since `key={roomId}` already handles remounting for route changes, the error state should be terminal — remove this reset to prevent the hook from silently retrying on unrelated re-renders.</comment>
<file context>
@@ -0,0 +1,59 @@
+ }
+ setState({ status: "ready", sessionId: chat.sessionId, chatId: chat.id });
+ } catch (error) {
+ startedRef.current = false;
+ const message = error instanceof Error ? error.message : "Failed to load chat.";
+ setState({ status: "error", message });
</file context>
| const startedRef = useRef(false); | ||
|
|
||
| useEffect(() => { | ||
| if (!authenticated) return; |
There was a problem hiding this comment.
P2: This early return leaves unauthenticated visitors stuck in the loading state instead of progressing to sign-in or a terminal state.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At hooks/useExistingChatBootstrap.ts, line 37:
<comment>This early return leaves unauthenticated visitors stuck in the loading state instead of progressing to sign-in or a terminal state.</comment>
<file context>
@@ -0,0 +1,59 @@
+ const startedRef = useRef(false);
+
+ useEffect(() => {
+ if (!authenticated) return;
+ if (startedRef.current) return;
+ startedRef.current = true;
</file context>
| export default function NewChatBootstrap({ | ||
| initialMessages, | ||
| }: NewChatBootstrapProps) { | ||
| useAutoLogin(); |
There was a problem hiding this comment.
P2: useAutoLogin is now called in both bootstrap and chat content, which can trigger duplicate login() attempts from separate hook instances.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/VercelChat/NewChatBootstrap.tsx, line 27:
<comment>`useAutoLogin` is now called in both bootstrap and chat content, which can trigger duplicate `login()` attempts from separate hook instances.</comment>
<file context>
@@ -3,22 +3,28 @@
export default function NewChatBootstrap({
initialMessages,
}: NewChatBootstrapProps) {
+ useAutoLogin();
const state = useNewChatBootstrap();
</file context>
| ); | ||
| } | ||
|
|
||
| return <ChatSkeleton />; |
There was a problem hiding this comment.
P2: Anonymous visits to /chat/[roomId] can get stuck on a permanent loading skeleton because auth prompting no longer runs before bootstrap resolution.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/VercelChat/ExistingChatBootstrap.tsx, line 40:
<comment>Anonymous visits to `/chat/[roomId]` can get stuck on a permanent loading skeleton because auth prompting no longer runs before bootstrap resolution.</comment>
<file context>
@@ -0,0 +1,41 @@
+ );
+ }
+
+ return <ChatSkeleton />;
+}
</file context>
|
Closing — superseded by the incremental cutover shipped via chat#1747 on 2026-06-01. This was the Approach A monolithic cutover that bundled (a) Patterns from this PR (the |
Summary
Completes the chat.recoupable.com cutover (#1747) by removing the legacy-vs-new conditional now that the Phase 2 backfill is in place. Every chat — new, existing-from-history, and the home landing — routes through recoup-api's
/api/chat/workflowand reads history fromchat_messages.Three coordinated changes:
sessionIdfor existing chats./chat/[roomId]now rendersExistingChatBootstrap(useExistingChatBootstrap), which fetchesGET /api/chats/[id]to recover the chat'ssessionIdbefore mounting<Chat>. Backfilled rooms carry asession_id(chats.id == rooms.id); a chat with no session / no access renders a graceful "isn't available" state.chat_messages.getChatMessagesnow callsGET /api/sessions/{sessionId}/chats/{chatId}(returnsmessagesfromselectChatMessages) instead of the memories-backedGET /api/chats/[id]/messages.useMessageLoaderthreadssessionIdthrough./api/chatbranch. Removed theif (!sessionId)fork inuseChatTransport;sessionIdis now required throughuseVercelChat→VercelChatProvider→Chat(compiler-enforced — no sessionless mount). Migrated the home route/to bootstrap a session like/chat(NewChatBootstrap), and moveduseAutoLoginintoNewChatBootstrapso anonymous landings still prompt sign-in.Dependencies (api, against
test)GET /api/chats/[id](chatId → chat row incl.sessionId). Required at runtime.partsshape fix; the ~46k migrated rows were already reconciled on prod so migrated history deserializes asUIMessage.Merge gating
Do not merge to
mainahead of the cutover bundle. Production still writesrooms/memories(cutover is ontest), so rooms created after the backfill have no session/chat rows yet. This lands onmainonly with the rest of the bundle, after the final idempotent backfill re-run.Behavior notes / risks
/now creates a session + sandbox on load (post-login landing, logo, SideMenu "New Chat" all hit/) — matches/chattoday; accepted tradeoff.send_email, Telegram) and inherited gaps (Stop, multi-tool traces, sandbox persistence).Test plan
getChat(200/404/403/500) andgetChatMessages(success / empty / not-ok / throw). 8 tests green.tsc --noEmitclean on changed files;eslintclean./chat/[roomId]→ history renders fromchat_messages, follow-up message streams via workflow./and/chat→ session provisioned, streams, URL →/chat/<id>./→ sign-in prompt (not a hung spinner).🤖 Generated with Claude Code
Summary by cubic
Routes all chats (new and historical) through the workflow path and removes the legacy
/api/chatbranch. History now loads fromchat_messages, completing the cutover.New Features
/chat/[roomId]resolvessessionIdviaGET /api/chats/{id}usingExistingChatBootstrap; missing/no-access chats show “isn’t available”.chat_messagesviaGET /api/sessions/{sessionId}/chats/{chatId};useMessageLoaderthreadssessionId.sessionIdis required throughChat→VercelChatProvider→useVercelChat→ transport. Home/now bootstraps a session viaNewChatBootstrap;useAutoLoginmoved there to prompt sign-in for anonymous users.Migration
rooms/memories. Requiresrecoupable/api#625(runtime) andrecoupable/api#627.Written for commit 694fc72. Summary will update on new commits.
Summary by CodeRabbit
New Features
Refactor