B2C Persona Vocabulary: learner / trainer / parent¶
Kwilo's B2C funnel has three personas: learner, trainer, and parent. This doc defines the canonical vocabulary, how legacy values (student, teacher) normalize to it, and where the normalization happens, so the persona a visitor picks on the marketing site survives all the way to the in-app experience and the pricing-card artifact names match the in-app ones.
The defect this fixes: a trainer or parent on kwilo.ai clicks "Go Pro", lands on /signup, and is silently registered as a learner. The site emits ?as=trainer or ?as=parent, but the web parser only accepted the legacy enum teacher|student|creator, so both fell to null, defaulted to student, then learner. The teacher pricing card even advertised "Study Plan", a learner artifact.
How it works¶
Three canonical B2C personas, used everywhere in the UI and stored for all new signups:
| Canonical persona | Legacy value (old links + old DB rows) |
|---|---|
learner |
student |
trainer |
teacher |
parent |
(new; previously not representable) |
creator is not a B2C pricing persona. It is a separate marketplace funnel (signup_intent='creator' → role=external_educator → creator onboarding) and is left untouched. It already resolves to the same artifact (Lesson Plan) as trainer.
Normalization seam. Legacy values map to canonical at exactly one boundary on each side:
- Web (
apps/web): a single resolver mapsstudent→learnerandteacher→trainer(and accepts canonical values as-is) when parsing the?as=param and when readingsignup_intentback. New signups persist the canonical value. - Backend (
apps/backend):capabilities.pynormalizes legacy to canonical before bucketing; theb2c_authvalidator accepts both canonical and legacy values.signup_intentis a plainstrcolumn, so no DB enum migration is needed.
No data migration: existing ?as=teacher links and existing teacher/student DB rows keep resolving via the read-side normalizer.
Scope of the change:
- apps/site: hero persona switcher becomes Learner / Trainer / Parent (the legacy "Teacher" segment folds into Trainer).
PRO_ARTIFACT_KEY_BY_PERSONAfixed so the teacher→learner artifact mismatch is gone.?as=now carries canonical values. - apps/web:
SIGNUP_INTENTS/schema gain canonical values;PERSONA_TO_SIGNUP_INTENTandintentToPersonabecome identity over canonical with legacy normalization;ARTIFACT_KEY_BY_INTENTre-keyed to canonical plusparent; default persona for bare/signupbecomeslearner. - apps/backend:
parentaccepted and bucketed (learner-like B2C caps); validator widened;capabilities.pynormalizes legacy to canonical.
Not in scope: renaming the persisted signup_intent column or backfilling it, the creator / external-educator funnel, the canonical ROLES enum (instructor | learner, since signup_intent is self-declared input, not the role), and the two-tier pricing tiers themselves (Free / Pro ₹399).