refactor(skin): replace trend with texture field on SkinConditionSnapshot

Remove the derived `trend` field (better computed from history by the MCP
agent) and add `texture: smooth|rough|flaky|bumpy` which LLM can reliably
assess from photos. Updates model, API, system prompt, tests, and frontend.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Piotr Oleszczyk 2026-02-28 13:25:57 +01:00
parent abf9593857
commit 4954d4f449
8 changed files with 30 additions and 31 deletions

View file

@ -55,7 +55,7 @@ export type SkinConcern =
| 'uneven_texture'
| 'hair_growth'
| 'sebum_excess';
export type SkinTrend = 'improving' | 'stable' | 'worsening' | 'fluctuating';
export type SkinTexture = 'smooth' | 'rough' | 'flaky' | 'bumpy';
export type SkinType = 'dry' | 'oily' | 'combination' | 'sensitive' | 'normal' | 'acne_prone';
export type StrengthLevel = 1 | 2 | 3;
export type TextureType = 'watery' | 'gel' | 'emulsion' | 'cream' | 'oil' | 'balm' | 'foam' | 'fluid';
@ -250,8 +250,8 @@ export interface SkinConditionSnapshot {
id: string;
snapshot_date: string;
overall_state?: OverallSkinState;
trend?: SkinTrend;
skin_type?: SkinType;
texture?: SkinTexture;
hydration_level?: number;
sebum_tzone?: number;
sebum_cheeks?: number;

View file

@ -13,7 +13,7 @@ export const actions: Actions = {
const form = await request.formData();
const snapshot_date = form.get('snapshot_date') as string;
const overall_state = form.get('overall_state') as string;
const trend = form.get('trend') as string;
const texture = form.get('texture') as string;
const notes = form.get('notes') as string;
const hydration_level = form.get('hydration_level') as string;
const sensitivity_level = form.get('sensitivity_level') as string;
@ -35,7 +35,7 @@ export const actions: Actions = {
const body: Record<string, unknown> = { snapshot_date, active_concerns };
if (overall_state) body.overall_state = overall_state;
if (trend) body.trend = trend;
if (texture) body.texture = texture;
if (notes) body.notes = notes;
if (hydration_level) body.hydration_level = Number(hydration_level);
if (sensitivity_level) body.sensitivity_level = Number(sensitivity_level);

View file

@ -12,7 +12,7 @@
let { data, form }: { data: PageData; form: ActionData } = $props();
const states = ['excellent', 'good', 'fair', 'poor'];
const trends = ['improving', 'stable', 'worsening', 'fluctuating'];
const skinTextures = ['smooth', 'rough', 'flaky', 'bumpy'];
const barrierStates = ['intact', 'mildly_compromised', 'compromised'];
const skinTypes = ['dry', 'oily', 'combination', 'sensitive', 'normal', 'acne_prone'];
@ -28,7 +28,7 @@
// Form state (bound to inputs so AI can pre-fill)
let snapshotDate = $state(new Date().toISOString().slice(0, 10));
let overallState = $state('');
let trend = $state('');
let texture = $state('');
let barrierState = $state('');
let skinType = $state('');
let hydrationLevel = $state('');
@ -64,7 +64,7 @@
try {
const r = await analyzeSkinPhotos(selectedFiles);
if (r.overall_state) overallState = r.overall_state;
if (r.trend) trend = r.trend;
if (r.texture) texture = r.texture;
if (r.skin_type) skinType = r.skin_type;
if (r.barrier_state) barrierState = r.barrier_state;
if (r.hydration_level != null) hydrationLevel = String(r.hydration_level);
@ -178,12 +178,12 @@
</Select>
</div>
<div class="space-y-1">
<Label>Trend</Label>
<input type="hidden" name="trend" value={trend} />
<Select type="single" value={trend} onValueChange={(v) => (trend = v)}>
<SelectTrigger>{trend || 'Select'}</SelectTrigger>
<Label>Texture</Label>
<input type="hidden" name="texture" value={texture} />
<Select type="single" value={texture} onValueChange={(v) => (texture = v)}>
<SelectTrigger>{texture || 'Select'}</SelectTrigger>
<SelectContent>
{#each trends as t (t)}
{#each skinTextures as t (t)}
<SelectItem value={t}>{t}</SelectItem>
{/each}
</SelectContent>
@ -296,8 +296,8 @@
{snap.overall_state}
</span>
{/if}
{#if snap.trend}
<Badge variant="secondary">{snap.trend}</Badge>
{#if snap.texture}
<Badge variant="secondary">{snap.texture}</Badge>
{/if}
</div>
</div>