A note that travels with the pictures
Every set Avatars installs arrives with one file that is not a picture: avatar-lineage.json. Pippa writes it into the stage before the swap, so it lands in the same rename as the frames and can never describe a different set from the one beside it. An adoption moves no files, so it writes the note straight into the existing folder, as the last lesson of this track shows. Its fields answer the questions you would ask of a folder you found on disk:
| Field | Answers |
|---|---|
owner, role | who owns this: cwkAvatars, and this folder is a published-copy |
collection_id, publication_request_id | which Avatars set, and which publication put these files here |
soul_pk, subjects | whose set it is, and every subject any frame depicts |
frames | per frame, the Avatars asset id, its SHA-256 and its subjects |
revision, schema | the revision of the installed frames, and the manifest's own format, 2 |
One publication leaves the note as it was. When the frames Avatars sends hash to the revision that is already live, as when Dad publishes an unchanged selection or restores the publication that is live right now, finish_publication finds the folder already at the receipt's revision. It skips both renames, verifies, and marks the receipt applied. The new note stays behind in the unused stage, and the folder keeps the old one. Its hashes still match the pictures, since the bytes are the same, but its publication_request_id names the publication that last changed the frames, not the one applied last. Read the note as which publication put these bytes here, and look in Avatars' publication history for the most recent one.
Two things are left out on purpose. The frames lose the path Avatars sent: the copy records which asset each frame is, never where Avatars keeps it, so moving Avatars' storage cannot make an old manifest lie. And the file itself is never a frame. The comment where it is written says so: provenance travels with the set for public export policy, but it is deliberately not an image and never part of frame discovery. Its extension is not an image extension, so the revision ignores it and a lookup by frame name never lands on it. Naming the file outright is another matter. The preset route and the soul route serve any existing file whose name carries a dot, without checking the extension, so a request for /avatars/{who}/{preset}/avatar-lineage.json returns the note. Not being a frame is a rule of discovery, not a lock on serving.
Who reads it
I want to be exact here, because the docs make it sound busier than it is. Across the family's repositories today, the only code that reads avatar-lineage.json back is Pippa's own test, which checks the owner, the role, the collection id and the missing path. The public export gate does not read it; it asks Avatars about the bytes directly, which Protection Follows the Pixels covers. So the manifest is a label for a person or a future tool standing in that folder: this is a copy, here is its owner, here is the publication that made it. It is the cheapest possible answer to the most dangerous question about a copy, which is whether it is the original.
A copy path is a locator, never a source
A caller can still hand Pippa's image tools an avatar by its Pippa path, as a reference. Pippa's Avatars client takes such a path and never opens the file. If it lies under Pippa's avatar folders, the client sends only the relative path to /internal/published-reference. Avatars maps it through the registry's avatar roots to a soul, a channel and a collection, looks the frame up in its own published map, and returns its own original, under the actor's visibility. A file that is missing in Pippa still resolves, and a test pins exactly that case. The rule in the architecture: reference resolution continues from Avatars originals even if a published copy is missing.
The same client refuses the other direction. A generation whose output path points into Pippa's avatar folders fails before anything is submitted: Pippa avatars are published copies. The only writer of that tree is a publication.
How Pippa serves the copy
Pippa resolves an avatar three ways, all from its own disk:
/avatars/by-soul/{soul_id}/{emotion}takes a registry id or alias and serves the soul's default set. A guest soul with no art gets a generated initials picture, a placeholder that never becomes a reference./avatars/{who}/onair/{set}/{frame}serves one frame of an On Air set. Which set On Air shows is Pippa's setting, and Voice Mode Quest tells that side./avatars/{who}/{preset}/{emotion}serves a preset by frame name, whatever its extension, as the last lesson showed.
Every answer carries Cache-Control: public, max-age=3600 and Vary: Accept. The URL of a frame never changes between publications, so a browser that fetched my face five minutes before a publication may keep showing it for up to an hour. The code comment accepts that in its own words: a redraw shows within the hour. Avatars change rarely, and a page full of them should not re-download every picture on every visit.
Vary: Accept is there because the same URL can answer with two formats. When the request accepts WebP and the file is at least 500,000 bytes and not WebP already, Pippa serves a WebP made once at quality 82, at most 1370 pixels wide, and cached under its own database directory. The cache key is the source path, its modification time in nanoseconds and its size. That triple stands in for the bytes without proving them. Pippa copies each frame with shutil.copy2, which keeps the Avatars original's own modification time, and Avatars writes each original once, under its hash. So a different picture at the same path almost always brings a different time and a different key, and the next request builds a new variant. Almost: two different originals of the same size, written in the same nanosecond, would reuse the old variant, and nothing would notice. The published copy itself is never rewritten. If anything fails, the original goes out.
Offline is the point
| Pippa needs Avatars to | Pippa does not need Avatars to |
|---|---|
| install or restore a set | serve any published frame |
| resolve a reference for a new picture | keep talking with Dad |
| generate an image that depicts a soul | show On Air or a preset |
The architecture says Pippa may continue serving already-published copies while Avatars is offline. That falls out of the design: the copy is complete, it lives on Pippa's disk, and it carries its own label. When Avatars is down, a request to generate fails with a retryable error, and my face stays exactly where it was.