# Student nicknames **Created:** 2026-09-11 **Last updated:** 2026-09-11 **Description:** Optional nickname used as the display first name on informal surfaces. ## Behavior - **First Name** and **Last Name** stay the official name (`Name`, `FirstNameLastName`, `LastNameFirstName`). - Optional **Nickname** on `Student`. Blank or whitespace is stored as null. - `DisplayFirstName` is the trimmed nickname when set, otherwise first name. - Informal UI (teams, event rankings, calendars, meeting lists, rank badges, team member print tokens) uses `DisplayFirstName`. - `{{FirstName}}` stays legal. New tokens: `{{Nickname}}` (empty when unset) and `{{DisplayFirstName}}`. ## Data Migration `AddStudentNickname` adds nullable `Students.Nickname` (max 100). Applied on next app start via `MigrateAsync`. ## Testing Unit tests stay in the Core `Tests` project (no Blazor/bUnit). They lock the two-Josiahs contract: | Layer | What we assert | | --- | --- | | Entity | `DisplayFirstName` / `ToString` / `NormalizeNickname`; legal `Name` / `FirstNameLastName` / `LastNameFirstName` ignore nickname | | Display | Formatters and alphabetical team lists use nickname; suffix markers still attach; no nickname falls back to first name | | Matching | Fuzzy matcher, year-rollover paste, assignment CSV columns, team import, notes import, and ranking import distinguish `Jo` vs `Josiah B` | | Collision | Assignment header `Josiah` still binds the first Josiah (requirement files must use display names) | | Print | Catalog includes `Nickname` and `DisplayFirstName`; merge keeps `{{FirstName}}` legal | Not covered by unit tests: Blazor create/edit/index captions, `NotePrintService` token map (same keys as the catalog/merger contract), and rewriting stored team identifiers. ## Follow-up After setting nicknames on students who share a first name, rename any team identifiers that still collide (identifiers are stored, not recomputed).