# Persona Generation Guide

`pkg/personagen` provides a complete pipeline for generating synthetic persona profiles with
biological DNA, personality psychology, lifestyle preferences, a 20-position image matrix,
and 4-motion-type video specs.

## Overview

A `PersonaSpec` is the top-level output — it contains:
- **CoreIdentity**: name, age, gender, ethnicity, occupation, location
- **DNA**: immutable biological characteristics (face, body, voice)
- **Psychology**: HEXACO personality profile, attachment style, values
- **Lifestyle**: interests (5 categories), fashion sense (15 contexts), vacation style
- **ImageMatrix**: 20-position image generation specs (4 tiers: Identity, Expressions, Angles, Context)
- **Videos**: 4 video specs (smile_reveal, personality_moment, lifestyle, invitation)

## Generation Flow

```
POST /api/{service}/persona/generate
  → "persona_generate" job enqueued → 202 {jobId}
  → Worker picks up job
  → personagen.Service.GenerateSpec()     → 5-stage LLM pipeline
  → personagen.Service.GenerateImages()   → 20 image positions (position 1 = anchor first)
  → personagen.Service.GenerateVideo()    → 4 video motion types
  → SSE events delivered to user:<userId>
```

## 5-Stage Spec Pipeline

| Stage | What it does | LLM calls |
|-------|-------------|-----------|
| 1 | Identity: name, age, ethnicity, occupation, location | 1 |
| 2 | Psychology: HEXACO scores, attachment, values | 1 |
| 3 | Lifestyle: interests, fashion context, vacation style | 1 |
| 4 | Visual DNA: all face/body/voice characteristics | 1 |
| 5 | Populate image matrix from lifestyle (no LLM needed) | 0 |

Total: **4 LLM calls** per persona spec.

## Image Matrix Tiers

| Tier | Positions | Focus |
|------|-----------|-------|
| 1 – Identity | 1–5 | Core look, position 1 is the anchor |
| 2 – Expressions | 6–11 | Personality through facial expressions |
| 3 – Angles | 12–16 | Camera angle variety |
| 4 – Context | 17–20 | Situational lifestyle shots |

**Position 1 is always generated first** and becomes the anchor image for all subsequent
positions (passed as `ReferenceImage` to the mediagen provider).

## HEIA Prompt Structure

Images use the HEIA (High-Engagement Influencer Aesthetic) prompt format:

```
[IDENTITY] 26-year-old Korean woman, 5'4" (163cm), slender build.
[FACE] oval face with high cheekbones, defined jawline, almond-shaped dark brown eyes...
[BODY] slender build, narrow shoulders, 0.72 WHR, upright posture.
[POSE] mid distance, 3/4 angle, standing, soft gaze expression, slight over-shoulder turn.
[CLOTHING] crisp white off-shoulder blouse, minimal gold jewelry — slim, structured.
[SCENE] neutral light background, subtle gradient.
[CONSTRAINTS] WOMAN SUBJECT ONLY. Human body has EXACTLY 2 arms, 2 legs, 10 fingers total...
```

## SSE Events

Subscribe to `user:<userId>` channel before calling the generate endpoint:

```json
{"type": "persona_spec_started",   "jobId": "...", "message": "Generating persona profile..."}
{"type": "persona_spec_complete",  "jobId": "...", "result": {"personaId": "ps_abc123"}}
{"type": "persona_image_started",  "jobId": "...", "result": {"position": 1}}
{"type": "persona_image_progress", "jobId": "...", "progress": 45, "result": {"position": 9, "url": "..."}}
{"type": "persona_image_complete", "jobId": "...", "progress": 100, "result": {"personaId": "..."}}
{"type": "persona_video_started",  "jobId": "...", "result": {"motionType": "smile_reveal"}}
{"type": "persona_video_complete", "jobId": "...", "result": {"motionType": "smile_reveal", "url": "..."}}
{"type": "persona_video_failed",   "jobId": "...", "error": "smile_reveal video failed: ...", "result": {"motionType": "smile_reveal"}}
{"type": "persona_failed",         "jobId": "...", "error": "Spec generation failed: ..."}
```

## Environment Requirements

Both AI providers must be configured for persona generation to work:

| Env Var | Purpose |
|---------|---------|
| `LAOZHANG_API_KEY` | Image and video generation (Flux, Kling) |
| `GEMINI_API_KEY` | Text generation (Gemini) + additional media (Imagen, Veo) |

These are auto-injected by rdev into every deployed service. Locally, source from `.secrets`.

## Using the Service Directly

```go
// Create the service
svc := personagen.New(textgenMgr, mediagenMgr, store, logger.Logger)

// Generate a full persona spec (5-stage LLM pipeline)
spec, err := svc.GenerateSpec(ctx, personagen.SeedParams{
    Description: "confident Latina entrepreneur who loves fashion and travel",
    Gender:      "woman",
    Name:        "Sofia Reyes", // optional
})
if err != nil {
    return err
}

// Generate all 20 images (position 1 first, sets anchor automatically)
if err := svc.GenerateImages(ctx, spec, nil); err != nil {
    return err
}

// Generate a specific video
videoSpec, err := svc.GenerateVideo(ctx, spec, persona.MotionSmileReveal)

// Generate utility images
avatarBytes, err := svc.GenerateAvatar(ctx, spec)
bannerBytes, err := svc.GenerateBanner(ctx, spec, "lifestyle")
```

## Fashion Contexts

15 named fashion contexts are available in `pkg/persona`:

| Context Name | Style |
|-------------|-------|
| `classic_minimalist` | Clean lines, neutral palette |
| `streetwear_chic` | Urban oversized fits, sneakers |
| `bohemian_free_spirit` | Flowing fabrics, earth tones |
| `athleisure_pro` | Performance fabrics, gym-to-street |
| `business_casual` | Polished professional with comfort |
| `romantic_feminine` | Florals, lace, pastels |
| `edgy_alternative` | Dark palette, leather, hardware |
| `coastal_casual` | Relaxed, beachy, linen |
| `urban_professional` | Sharp city-dweller aesthetic |
| `festival_glam` | Bold prints, glitter, maximalist |
| `preppy_classic` | Collegiate, polo shirts, chinos |
| `dark_academia` | Literary, tweed, earth tones |
| `cottagecore` | Prairie dresses, pastoral romance |
| `y2k_revival` | Low-rise, metallics, early 2000s |
| `luxe_loungewear` | Elevated comfort in premium fabrics |

Use `persona.AllFashionContexts()` for the full catalog or `persona.FashionContextFor(name)` for a single context.

## Motion Types

| Motion Type | Description | Duration | Aspect |
|------------|-------------|----------|--------|
| `smile_reveal` | Warm genuine smile moment | 5s | 9:16 |
| `personality_moment` | Expressive personality showcase | 8s | 9:16 |
| `lifestyle` | Contextual lifestyle shot | 8s | 16:9 |
| `invitation` | Direct-address to viewer | 5s | 9:16 |

## Errors

| Error | Cause |
|-------|-------|
| `ErrAnchorNotSet` | `GenerateVideo()` called before position 1 image was generated |
| `"mediagen not configured"` | No AI provider keys set |
| `"stage N X: ..."` | LLM call or JSON parse failure in specgen pipeline |
