Skip to content

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 maps student→learner and teacher→trainer (and accepts canonical values as-is) when parsing the ?as= param and when reading signup_intent back. New signups persist the canonical value.
  • Backend (apps/backend): capabilities.py normalizes legacy to canonical before bucketing; the b2c_auth validator accepts both canonical and legacy values. signup_intent is a plain str column, 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_PERSONA fixed so the teacher→learner artifact mismatch is gone. ?as= now carries canonical values.
  • apps/web: SIGNUP_INTENTS/schema gain canonical values; PERSONA_TO_SIGNUP_INTENT and intentToPersona become identity over canonical with legacy normalization; ARTIFACT_KEY_BY_INTENT re-keyed to canonical plus parent; default persona for bare /signup becomes learner.
  • apps/backend: parent accepted and bucketed (learner-like B2C caps); validator widened; capabilities.py normalizes 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).