feat: open the door — no sign-in wall, ever

Point and shoot. SeancePage no longer redirects a stranger to /enter; it
silently provisions a wanderer (POST /auth/guest, a real account) and drops
them straight into the séance. The landing CTA goes to /seance
unconditionally. Registering is now what it should always have been: how
you *keep* a codex and unlock device pairing — not the toll to get in.

Details that matter:
- One auto-attempt only, guarded by a ref: survives StrictMode's double
  effect, and a failure (rate limit, offline) doesn't retry forever.
- The loading veil covers provisioning, so there's no flash of an empty
  séance while the account is created.
- If provisioning genuinely fails, it falls back to /enter rather than a
  blank screen — the manual door still exists.
- A returning visitor already holds a cookie, so this never fires for them.

App.test's "sends anonymous visitors to /enter" assertion was inverted to
assert the open door instead — that test encoded the wall we just removed.

Verified live: POST /auth/guest returns wanderer-a6f1 and that cookie
authenticates on /auth/me. 366 frontend tests pass; i18n parity holds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Indiana
2026-07-29 07:01:27 +00:00
parent 2a67684df4
commit 7d3a6e695d
3 changed files with 33 additions and 5 deletions

View File

@@ -69,10 +69,28 @@ const TOAST_TTL_MS = 6000
const SDR_SAMPLE_RATE_HZ = 2_048_000
export function SeancePage() {
const { user, checking } = useAuth()
const { user, checking, guest } = useAuth()
const { t } = useTranslation()
const [guestFailed, setGuestFailed] = useState(false)
// One auto-attempt only. Guards React StrictMode's double-effect and
// stops a failed attempt (rate limit / offline) from retrying forever.
const guestAttempted = useRef(false)
if (checking) {
// The open door. There is no sign-in wall: if nobody is authenticated
// once the session check settles, silently provision a wanderer — a real
// guest account (POST /auth/guest) — and drop straight into the séance.
// Point and shoot. Registering later is how a seeker *keeps* what they
// find and unlocks device pairing; it was never meant to be the toll to
// get in. A returning visitor already has a cookie, so `user` is set and
// this never fires for them.
useEffect(() => {
if (checking || user || guestAttempted.current) return
guestAttempted.current = true
void guest().catch(() => setGuestFailed(true))
}, [checking, user, guest])
// Still resolving a session, or provisioning the wanderer: hold the veil.
if (checking || (!user && !guestFailed)) {
return (
<div className="seance-loading" role="status">
<div className="loading-sigil" aria-hidden>
@@ -82,6 +100,9 @@ export function SeancePage() {
</div>
)
}
// Auto-provision failed (the veil is genuinely crowded, or offline). Fall
// back to the manual door rather than a blank screen or a crash.
if (!user) return <Navigate to="/enter" replace />
return (