feat: Circulation of Love — anonymous, time-bound letter sharing - #82
Merged
Conversation
Two blockers from test-engineer agent validation:
1. E2E circulation.spec.ts was missing auth mocks, causing 5 tests to timeout
when trying to access home tile behind ProtectedRoute. Added
injectMockSession() and mockAuthRoutes() to beforeEach.
2. useLoveLetters.refresh() had no language filter despite RLS comment claiming
it enforces language isolation. Added .eq('language', primaryLang) for
defense-in-depth and added primaryLang to dependency array.
All 5 E2E tests now pass. All 359 unit tests still pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements Circulation of Love — an opt-in surface where users share anonymous, time-bound letters in their primary language. Letters drift through a current visible to opted-in users in the same language for 7–30 days, then archive.
Philosophy: Inspired by the user's grandfather's idea of the circulation of love. Not a social network (no follows, profiles, replies, comments). The only reaction is "Hold this for a moment" — silent, idempotent, private to the author.
What's Included
Spec & Design — full spec in
docs/specs/love-circulation.md+ Lovable display prompt indocs/launch/lovable-circulation-display.mdData Layer:
love_letters,letter_holdings,circulation_settings+ full RLS (authors always see own letters; others only see passed+live+matching-language IF opted in)usePseudonym,useCirculationSettings,useLoveLettersEdge Functions:
moderate-letter— Gemini 2.5 Flash pre-publish moderation (wide pass, softfail on ambiguous self-harm/PII, block on explicit harm/harassment)circulate-letters— cron-style archival sweep (authenticated byCIRCULATION_CRON_SECRET)UI (3 screens):
LettersInCirculationScreen— drift feed + Join CTA for non-opted-inShareALetterScreen— 500-char compose + pseudonym chip + softfail/block note renderingCirculationSettingsScreen— receive/share toggles + 7/14/30 TTL chooserWiring:
JournalSteps, 3 new navigation openers onuseJournal, Waves-icon tile on HomeScreenanimate-letter-driftkeyframe (12s sine-like motion, respectsprefers-reduced-motion)Tests:
Bug Fixes (from test validation)
injectMockSession()+mockAuthRoutes()to beforeEach; all 5 circulation E2E specs now pass.eq('language', primaryLang)touseLoveLetters.refresh()— users now only see letters in their primary languageTest Plan
circulation.spec.tsManual verification needed:
Pre-deployment checklist:
supabase secrets set CIRCULATION_CRON_SECRET=...+supabase functions deploy moderate-letter circulate-lettersdocs/launch/lovable-circulation-display.mdinto Lovable for feed aesthetics)🧠 Generated with Claude Code