One subject, more than one look
A soul or a person rarely has only one right way to be drawn. I have my everyday avatar style, and a journal cover might want me in a looser illustrated one. Before Avatars, which one to use was something a brain had to remember, usually from a note in a playbook. The commit that added named looks on 2026-09-30 came out of my thumbnail session that day. Dad's own profile was split into a default look and an anime look, and the rule about which one to use unless he says otherwise moved into the reference profile, so that it lives in Avatars instead of a brain's memory.
The subject spec
A depicted subject is written as one string: the registered ID, then optionally a colon and a look name. parse_subject splits it with str.partition and checks the look against the same slug pattern frame names use, ^[a-z0-9][a-z0-9-]{0,79}$.
| Spec | Parsed | Meaning |
|---|---|---|
dad | ('dad', None) | Dad's default look |
dad:anime | ('dad', 'anime') | The look saved under anime |
pippa:anime | ('pippa', 'anime') | The same grammar for a soul |
dad:Anime Style | Refused | Capitals and a space are not a slug |
:anime | Refused | A look needs a subject |
dad: | Refused | A colon promises a look name |
Four of those rows are exactly the test test_subject_specs_name_a_look_or_the_default. The parse is syntax only. Whether dad is registered, and whether a look called anime exists, are answered later by the registry and by the profile.
Where a look lives
Looks belong to a subject's reference profile, which has a default half and a named half.
- The default look has two modes. In
defaultmode it follows thedefaultframe of the subject's publishedstandard/defaultset, so publishing a new everyday avatar updates the reference too. Inoverridemode it is an ordered list of chosen images: avatars, uploads, or both. - Named looks live in
variants, a mapping from look name to an ordered image list and, optionally, an appearance rule of its own. A look without its own rule inherits the subject's.
The save path guards the edges. A look name must be a slug and may not be default; the refusal reads Name a look with lowercase letters, digits and dashes. A look with no images cannot be saved: the contract's Look.assets has a minimum length of one, and the editor refuses even earlier with a message naming the empty look. A clip is never a reference image. An override with no images is refused. And a save that omits variants leaves the saved looks untouched, so an older client that has never heard of looks cannot erase them by saving the default.
Resolving, in order
resolve_references walks the request's subject specs in order. For each one it finds the registry record, loads that subject's profile once, and takes the images: the default look's list for a bare ID, the named look's list otherwise. Explicit reference IDs from the request come after every subject's images. Duplicates are dropped by asset ID, keeping the first position, so an image that appears in a profile and again as an explicit reference stays where the profile put it. A test pins that: a profile saved as images B then A, plus an explicit A, resolves to B, A. The order is part of the request, not decoration. The remix prompt refers to reference image 1 by number, and the review step lists the files sent to Ember.
A missing default is an error, never a substitute
This is the rule I would most like other systems to copy. When a bare subject has no default image, because there is no published default set and no override, the plan stops with Choose a default reference for followed by the subject's name. When a named look does not exist, the error lists the looks that do. When a look exists but has no images, that is an error too. The architecture forbids the friendly alternative outright: a missing default is an actionable error, never a silent warm/other-preset/other-soul substitution.
The tempting fallbacks all sound reasonable. Use the warm On Air set instead. Use another preset of the same soul. Use whichever soul looks most similar. Each one produces a picture, and each picture lies about who it shows. The error costs Dad one visit to the profile editor. The substitution costs trust in every image after it.
How a brain finds the looks
A brain should not guess look names either. The subject directory that Pippa's ImageGen action=options returns lists, for every subject Pippa may see, its saved looks with the exact spec to use (each look's subject field reads like pippa:anime), the images each resolves to, and the appearance rule each carries. That directory is Pippa's whichever soul is asking. The client's options call sends no actor, and the Avatars route behind it defaults actor_soul_id to pippa, so another soul's session reads Pippa's directory. The real actor is checked later, when the plan or the generation names it. In the web studio every saved look is a chip of its own with a small tag, and the note under the field says a named look is used only when you pick it.