No onboarding detour.
The existing plus action opens a two-choice sheet. Group thread gets the visual priority; solo capture remains available without competing for attention.
Product note: the user sees the value phrase before any contact permission or setup form.
Make the invite worth opening.
Setup stays utilitarian until the kickoff answer. The designed moment is the preview of what family will actually receive.
Context before commitment.
The recipient sees who invited them, who else is there, and a real voice note before any account friction.
Progress should cause a reply.
Do not expose a mastery dashboard. Surface a timely reason to celebrate someone or practise together, then make the next social action one tap away.
Borrow mechanics, not styling.
The useful references are specific interaction contracts. Generic incentivized referral screens are deliberately excluded.
GroupMe · Telegram · WhatsApp
Contact selection is a searchable list with obvious selected state and progress. It does not need to sell the feature.
Open Mobbin results ↗Partiful
The sender previews a designed artifact before sharing. The payload carries context instead of relying on accompanying copy.
Open Partiful ↗Discord invites
The recipient sees the specific group identity before accepting. Group threads makes that context smaller, private, and family-based.
Open join flows ↗Apple Invites
The invite is a destination with recognizable people and activity—not an anonymous utility link.
Open Apple Invites ↗The flow stays honest.
The onboarding earns the social ask, preserves privacy, and reaches a real conversation as quickly as possible.
Keep
- Value firstExplain Group thread before asking for contacts.
- Real payloadThe creator's voice note travels with the invite.
- Two-sidedDesign the recipient funnel as carefully as creation.
- Shared participationEveryone can answer, react, and offer support without choosing a role.
- Actionable progressEvery shared milestone offers a next action: celebrate, challenge, or practise together.
Avoid
- Referral UINo codes, rewards, or generic “invite friends” pitch.
- Empty groupNever land the first invitee in a blank thread.
- Auth wallShow family context before account friction.
- Public socialNo discovery, follower counts, or stranger graph.
- Passive mastery dashboardsNo rankings or exposed weakness; progress is shared only when it helps someone act.
Keep the magic. Change the ownership model.
Today, a Lexifari Thread belongs to one user_id, and its participants are private Person records inside that learner’s world. A real family thread needs shared ownership, authenticated membership, and message-level authorization. Build that multiplayer domain deliberately; do not stretch the capture model until its tenancy stops being legible.
A small library beats a loading spinner.
The name should make the group feel personal immediately. Curated topic marks give us consistency and zero-latency creation; bespoke AI artwork stays an optional later action.
Suggest three. Let people choose.
Match the title and selected topic against a versioned taxonomy, then offer three coherent marks. Unknown groups receive a monogram and a deterministic colour. Store the chosen avatar_asset_key; never block creation on generation.
Reuse the rails. Add the multiplayer core.
Lexifari already has most of the expensive primitives. The new work is authorization, invitations, shared state, and a thin synchronization contract.
SwiftUI client
Local contact selection, the existing M4A recorder, thread UI, and system share sheet.
OpenAPI
Firebase bearer identity and additive generated Swift operations. No hand-rolled HTTP.
FastAPI
Create draft, post message, publish invite, accept role, react, and correct.
Postgres
Groups, memberships, opaque invites, prompts, messages, reactions, and participation.
Cloudflare R2
S3-compatible storage with group-scoped keys and short-lived, membership-checked playback URLs.
DBOS + APNs
Daily prompts, respectful nudges, recap generation, and push-driven refresh.
Online truth. Instant local memory.
The API owns multiplayer truth; SwiftData owns the last useful view of it. A social product may need connectivity to send, but it should not forget the conversation every time it launches.
API + Postgres
Membership, ordering, reactions, corrections, edits, and deletions remain authoritative on the server.
SwiftData cache
Groups and recent message metadata render immediately across launches, then reconcile in the background.
File cache
Voice files and unfinished recordings live on disk, not as database blobs. Signed URLs refresh when needed.
Cursor + client ID
Monotonic group sequence numbers and optimistic client IDs make refreshes incremental and retries safe.
The invite is a state machine.
The backend enforces the product promise: nothing becomes shareable until the creator’s kickoff answer exists.
Draft
Create the private group shell. Selected contact intent remains local.
POST /groups
Idempotency-Key
Seed
Upload the creator’s text or short voice answer with a client message ID.
POST /messages
client_message_id
Publish
Only a seeded group can mint an expiring, revocable opaque invite.
POST /invites
token shown once
Preview
The web landing reveals minimal context: inviter, group, prompt, and seeded note.
GET /invites/{token}
no transcript access
Accept
After Firebase sign-in, the recipient joins once and lands directly in the conversation.
POST /accept
invite token
Converse
Members fetch after a cursor; retries never duplicate a voice note or reaction.
GET ?after_seq=
membership required
Small schema. Strong invariants.
Model the social facts directly. Avoid burying membership, corrections, or invite security inside an untyped metadata blob.
groupsPrivate group identity, language, lifecycle state, creator, chosen topic mark, and last activity.
group_membersOne row per authenticated person with membership status and join time. No participation role required.
group_invitesHashed token, expiry, revocation, use limit, creator, and accepted membership. Never store the raw token.
group_promptsThe shared daily Swahili question and English gloss, keyed by local group day.
group_messagesText or audio key, author, prompt, reply target, correction target, sequence, and client idempotency key.
group_reactionsOne lightweight emoji reaction per member/message/emoji tuple.
group_milestonesA positive, consented projection such as “getting comfortable.” Raw review evidence and weak-word history remain learner-private.
group_challengesA short cooperative objective, local deadline, and qualifying contribution rule. Completion is derived per member, never ranked.
daily_participationFirst qualifying contribution per member/day. The streak is derived from this evidence, not mutated as a counter.
The bucket changes. The access model changes more.
R2 speaks the same S3 protocol the Railway-hosted API already uses. The real migration is replacing permanent public URLs with durable object keys and explicit authorization.
R2 stores bytes. Lexifari grants access.
Keep the provider S3-compatible, configure the R2 endpoint and auto region, and persist a storage reference instead of a public URL. The API verifies group membership before producing a short-lived playback URL.
One picker, not two.
The contact screen earns its place only if those selections become meaningful pending invites. Opening a second recipient picker inside WhatsApp would repeat the job.
Local contact intent. Explicit sharing.
Use iOS limited contact access to choose individuals. Keep the address book on-device, create a unique invite packet only for each selected person, and show the exact outgoing message before the user initiates sharing.
Make product thinking phone-native.
These one-off boards are faster and more reviewable than folding every exploration into Studio. Product artifacts deserve a tiny publishing path of their own.
Any static folder
HTML, CSS, images, and lightweight interaction stay portable and framework-free.
/tmp/group-threads/
One CLI command
A reusable command validates the entry point, uploads the directory, and records a manifest.
stu artifact publish
Worker + R2
A small Worker resolves clean slugs, content types, cache headers, and optional Access policy.
artifacts.lexifari.com/{slug}
Open anywhere
Phone-ready URLs can be shared in Linear, WhatsApp, Telegram, or a founder review without a local server.
versioned · reversible · indexable
Prove the two-person loop first.
The valuable proof is not a complete family-plan platform. It is one seeded invite turning into one real reply between two people who know each other.
Reuse now
Build deliberately
Seed → invite → first reply
Two authenticated people complete the entire loop with real text and voice.
- Draft group and kickoff message
- Expiring invite + web preview
- One-tap membership acceptance
- Member-only thread and playback
Invite roster + support
Expand from one invitee to a small known circle without adding public-social machinery.
- Limited contact selection
- Pending/joined invite states
- Voice/text corrections and reactions
- Positive celebration events
- Cursor refresh + targeted APNs
Daily reason to return
Only after conversation works, add the compounding ritual and external artifact.
- Daily shared prompt
- Derived group streak
- Cooperative word quests
- Supportive opt-in nudges
- Weekly WhatsApp recap
Engineer the k-factor.
Internal codename for the virality workstream (a contagion homage). The Bier review verdict is accepted: family circles retain, they do not spread. Spread needs interlocking graphs and a cheaper first taste. Every idea here carries one number it must move.
Closed graph
5-8 people who mostly share the same "other family." k < 1 by design. Measure D30 retention and family-plan conversion, never virality.
Interlocking graphs
A university Swahili section is 15-40 students who meet weekly and share other classes, clubs, and dorms. The only segment where k > 1 is plausible.
Bought distribution
Universities and East-Africa NGOs pay for cohorts outright. No k-factor required; measure seats, activation, and renewals instead.
Moves later
Broaden the app's existing anonymous usage: real inference before any sign-in. Every segment converts better when the first taste is free.
Take the loop where graphs interlock.
University Swahili courses are the closest thing this product has to Bier's school graph: ages near the invite sweet spot, physical contact several times a week, and sections that overlap through majors, clubs, and housing.
The cohort thread
"Swahili 201 crew." One student seeds with a real voice answer; classmates and the instructor join the same conversation without special roles.
The graph math
Invite decay favors 18-22. Each member sits in four other classes and two clubs, so invites can jump graphs. A family invite dies at the family boundary; a campus invite does not.
The wedge
Every course already has a WhatsApp group. The invite payload (prompt + a classmate's voice) drops into it, and the weekly recap keeps pulling from it all semester.
The renewable node
One instructor or student organizer re-seeds a fresh 30-student cohort every semester. Churn at semester's end is matched by re-acquisition at the next enrollment.
Inference before identity.
The app already supports some anonymous usage. Broaden it so the invite page lets a recipient speak before the sign-in wall, and let creators seed without performing.
Reply from the browser. Claim it after.
On the web invite, the recipient records an answer and hears one round of feedback (transcription plus a gentle correction) with no account. Sign-in exists to post the reply into the thread, not to try the product.
Sell cohorts. Keep the app clean.
Universities and development organizations working in East Africa already pay for Swahili training. That is bought distribution and real revenue, and none of its sales machinery may leak into consumer onboarding.