User Profile¶
UserProfile is the central domain model for all data collected during onboarding. It is stored as a JSON-serialized string in DataStore Preferences and used by the simulation engine, the AI context builder, and the Home screen.
Data Class¶
@Serializable
data class UserProfile(
// Identity
val displayName: String? = null,
val ageYears: Int? = null,
val biologicalSex: BiologicalSex? = null,
val heightCm: Int? = null,
val weightKg: Float? = null,
// Manual lifestyle baselines
val averageSleepHours: Float? = null,
val averageDailySteps: Int? = null,
val perceivedStressLevel: Int? = null, // 1..10
// Current-day overrides (Home screen)
val currentSleepHoursOverride: Float? = null,
val currentStepsOverride: Int? = null,
val currentHeartRateOverride: Int? = null,
val currentStressLevelOverride: Int? = null,
// Habits
val smokingStatus: SmokingStatus? = null,
val alcoholDrinksPerWeek: Int? = null,
val dietQuality: DietQuality? = null,
// Bookkeeping
val onboardingCompletedAtEpochMs: Long? = null,
)
All fields are nullable. null means "not yet provided" — never conflated with 0 or any other sentinel.
Field Groups¶
Identity¶
Basic demographic data. Used in the simulation engine (age contributes to long-term risk) and in the AI context prompt (age, sex, height, weight).
displayName is shown in the Home screen greeting only — it is not included in the AI context to preserve the "speaking as yourself" persona.
Manual Lifestyle Baselines¶
Collected during onboarding Step 3. Used as fallbacks when no wearable data is available:
averageSleepHours→ fallback forSimulationInput.sleepHoursAvg7daverageDailySteps→ fallback forSimulationInput.stepsAvg7dperceivedStressLevel→ fallback forSimulationInput.stressAvg7d
Current-Day Overrides¶
Set from the Home screen's manual override dialogs. Take precedence over wearable data for the currently displayed day. Historical wearable records are never touched.
Override priority: currentOverride > wearable > baseline fallback > null
Habits¶
Always manual — no wearable measures smoking or diet. Fed directly into the simulation risk scores.
| Enum | Values |
|---|---|
SmokingStatus |
NEVER, FORMER, OCCASIONAL, REGULAR |
DietQuality |
POOR, AVERAGE, GOOD, EXCELLENT |
BiologicalSex |
MALE, FEMALE, OTHER, PREFER_NOT_TO_SAY |
Persistence¶
UserProfileDataSource serializes the entire UserProfile to a JSON string using kotlinx.serialization and stores it as a single DataStore Preferences key.
// Simplified read/write pattern
val profileFlow: Flow<UserProfile?> = dataStore.data
.map { prefs -> prefs[KEY_PROFILE]?.let { Json.decodeFromString(it) } }
suspend fun save(profile: UserProfile) {
dataStore.edit { prefs ->
prefs[KEY_PROFILE] = Json.encodeToString(profile)
}
}
Using JSON rather than Proto DataStore avoids the schema-compilation step, which matters at hackathon speed. The stored JSON is human-readable and inspectable with adb shell.