Home / Ops / More in this area
Updated Jun 29, 2026 · Affirmology_CrossDevicePersistence_BuildBrief_v1.md
For: the Claude Code session on the Mac (repo + Cloudflare/worker + cloud studio access).
Goal: when someone opens their emailed link to result.html#<runId> on ANY device, the page recognizes them, greets them by name, and shows their finished hub (replayable song + the mission/asks/beta) without re-running the film or ritual. Today it only works on the same device because it reads localStorage.
result.html#<runId>&film (first-time arrival, show the one "Play my audio" ritual).result.html#<runId> (a return visit).&film AND the job is already done -> returning visitor -> show the finished hub directly. This is device-independent because it is driven by the backend status, not localStorage.GET /api/status/<jobId> must return the visitor's name alongside the existing fields. The render request (POST /api/render) already receives name and email; persist them on the job record and include name (first name is enough; full name + email optional) in the status JSON.
- Worker: affirmology-demo-site/worker/src/index.js -> handleStatus() proxies to the cloud studio /status/<jobId>; just pass through whatever the cloud returns.
- Cloud studio (affirmology-studio, the demo bridge /status handler): add name (and optionally email) to the status payload from the stored job.
- Resulting shape (superset of today): { state|status, audio_url|mp3_url, pdf_url, blueprint, name, email? }.
affirmology-site/result.html)Current relevant anchors: runId/filmMode/firstName are parsed near the top (const runId = _rawHash.split('&')[0], const filmMode = _rawHash.indexOf('&film') !== -1, firstName from the localStorage affirmology:<runId> blob); startPolling() already fetches /api/status/<runId>; showReturning() already renders the finished hub; revealAfterListen() sets localStorage 'affirmology:done:'+runId.
Make the status fetch authoritative:
1. In startPolling() (or a small init fetch), when status returns, also read j.name; if present, set the displayed name everywhere firstName is used (update #name-delivered, #audio-title, the email line). Fallback order: backend name -> localStorage intake name -> "Friend".
2. Returning-visitor trigger should be: if ((!filmMode && jobDone) || localStorageDoneFlag) showReturning(); where jobDone = state==='done' || status==='success' || !!audio_url. Since the first status poll may take a moment, run an immediate one-shot fetch on load so a return visit resolves fast (show a quiet "loading your page" placeholder, NOT the old film/ritual, while that first fetch is in flight when !filmMode).
3. Keep localStorage as a same-device fast path (instant), but never REQUIRE it.
4. Make sure the audio src + #download-audio href get set from the status audio_url on return (showReturning already polls; confirm it populates once results.audio_url arrives).
The delivery + drip emails (Affirmology_DemoDripEmails_v1.md, {{page_link}}) must link to https://demo.affirmology.ai/result.html#<runId> (no &film), so every emailed return lands in the hub via the returning path.
result.html#<runId> link on device B (fresh browser, never visited): it greets them by name and shows the finished hub with a replayable song, no film, no ritual, no "tap to begin."&film) is unchanged: the film, then the single "Play my audio" tap.