Structured planning context for the pair — the successor to the
planner's PROFILE_KEYS, and since ATH-460 Phase 1 the pair's only
profile (the public user_athlete_profiles table is archived). Every
field is optional: it fills in over time (the settings editor, ATH-460's
intake interview, or opportunistically from chat), and an absent field
is simply not yet known.
The column is written only through the merge_plan_profile RPC
(server-side jsonb ||), never through save_user, so two writers
touching different fields both land.
The value shapes are the editor's: every field is a string.
training_days is the joined label string the editor stores
("Mon, Wed"), deliberately — the athlete's own words, not a parsed
set. Nothing has ever written another shape, so a row that carries one
is a bug to surface, not a legacy to absorb.
The sport is not a field: it is fixed by the athlete the pair is
enrolled with (the harness sport seed), so there is nothing to ask. A
sports key on an older row is ignored on read.
The profile is not a field of :class:UserProfile: Store.save_user
writes the user record from an in-memory copy, and a profile riding on
that copy could clobber a merge. It is read through
Store.get_plan_profile instead.