pixelkiln 0.46.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/AGENTS.md CHANGED
@@ -67,7 +67,7 @@ not a replacement:
67
67
  | PixelLab MCP | Agent-facing access to PixelLab generation capabilities. |
68
68
  | PixelKiln skill | Agent guidance for safe project-level operations. |
69
69
  | PixelKiln library/CLI | Budgets, state, provenance, review, recovery, audit, and packaging. |
70
- | PixelLab adapter | The current production and live-tested generation backend. |
70
+ | PixelLab adapter | The current production and live-tested generation backend: stills, tiles, and the character family (four engines, states, loops, mirrors, adoption). |
71
71
  | Retro Diffusion adapter | Experimental backend; authenticated paid single-still lifecycle plus mocked advanced-workflow tests. |
72
72
  | ComfyUI adapter | Experimental self-hosted still, controlled-revision, and atomic frame-set backend; PixelKiln automates lineage and mechanical quality checks while a person retains visual approval. |
73
73
  | Scenario adapter | Experimental hosted still-image backend; BFL Flux 2 Dev quote, paid single/two-output generation, human review, and durable recovery live-tested. |
@@ -55,19 +55,30 @@ named approval. Planning and doctor inspect the record without changing it.
55
55
  Pack and mount require current approval and include the record downstream.
56
56
  Tiles and provider-native animation remain outside this contract.
57
57
 
58
- ## Revision dependency graph
59
-
60
- An asset revision adds an immutable edge from a child spec to a parent spec in
61
- the same style. Manifest loading rejects missing parents, self-references, and
62
- cycles. Resolution includes transitive parents even when `--only` selects just
63
- the child, chooses the parent's approved quality output when one exists, and
64
- hashes the parent and optional mask bytes into the child identity.
58
+ ## Dependency graph
59
+
60
+ Four kinds of asset draw from another asset's generated bytes, and all four
61
+ are edges from a child spec to a parent spec in the same style: a
62
+ `revision` (image-to-image or inpaint from a still), a character `state` or
63
+ `animation` (drawn by PixelLab from the character it holds, so the edge is
64
+ to the parent's south-facing file), a `mirror` (a local left-to-right flip
65
+ of the parent's files), and a pro base's `styleCharacter` (the look it
66
+ follows). Manifest loading rejects missing parents, self-references, and
67
+ cycles. Resolution includes transitive parents even when `--only` selects
68
+ just the child, chooses the parent's approved quality output when one
69
+ exists, and hashes the parent's bytes (and a revision's optional mask) into
70
+ the child identity, so a regenerated parent makes its children stale rather
71
+ than silently mismatched.
65
72
 
66
73
  Planning walks the graph from the child and reports `blocked` unless each
67
74
  parent is committed source, an intact current download, or a current approved
68
- quality output. Blocked work is not actionable and has zero planned spend. The
69
- submit boundary repeats the walk after queue and rate-limit waits, closing the
70
- gap between a printed plan and provider work.
75
+ quality output. Blocked work is not actionable and has zero planned spend. A
76
+ child whose parent is actionable in the same plan is deferred, and `gen`
77
+ runs the plan in waves until nothing new becomes actionable. The submit
78
+ boundary repeats the walk after queue and rate-limit waits, closing the gap
79
+ between a printed plan and provider work. A mirror is the one edge with no
80
+ provider on the far side: submit flips the files itself and records the
81
+ parent's output hashes, and the plan compares those directly.
71
82
 
72
83
  Providers opt into revision modes through `supportsRevision`. The first
73
84
  implementation is ComfyUI: it uploads content-addressed inputs, binds only the
@@ -0,0 +1,309 @@
1
+ # Characters
2
+
3
+ A `character` style holds one PixelLab character as three kinds of asset: a
4
+ base drawn facing 4 or 8 directions, states that apply a pose or an outfit
5
+ to every direction at once, and loops, one direction each. Each is an asset
6
+ with its own lockfile record and files, so `plan` prices the cast, one `gen`
7
+ runs it in waves, and `pack` writes every direction and loop for the engine.
8
+ This page is the reference for the manifest shapes; the [manifest
9
+ reference](./MANIFEST.md) covers the fields every asset shares.
10
+
11
+ ## The three shapes
12
+
13
+ A cast in full, with a base, a state, and two loops. This is the robot on
14
+ the [site](https://pixelkiln.griffen.codes/#characters), drawn the way the
15
+ example says:
16
+
17
+ ![The robot facing south, south-west, west, north-west, north, north-east, east, and south-east](../website/public/sprites/characters/robot-8dir.png)
18
+
19
+ ```json
20
+ {
21
+ "styles": {
22
+ "cast": {
23
+ "generator": "character",
24
+ "outDir": "art/characters",
25
+ "view": "side",
26
+ "size": 96,
27
+ "mode": "pro-flash",
28
+ "template": "custom"
29
+ }
30
+ },
31
+ "assets": {
32
+ "bot": {
33
+ "prompt": "small round orange robot with one blue eye, short jointed legs"
34
+ },
35
+ "bot.dented": {
36
+ "prompt": "shell dented and scuffed, one arm hanging loose, eye dim",
37
+ "state": { "of": "bot", "paletteFromReference": true }
38
+ },
39
+ "bot.walk": {
40
+ "prompt": "walking forward in place, short legs stepping, body bobbing slightly",
41
+ "animation": { "of": "bot", "direction": "south", "frames": 12, "fps": 12 }
42
+ },
43
+ "bot.jump": {
44
+ "prompt": "crouching down, springing up into the air with legs tucked, landing with a small bounce",
45
+ "animation": { "of": "bot", "direction": "south", "frames": 8, "fps": 12 }
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ![The robot's resting pose and twelve frames of it walking](../website/public/sprites/characters/walk-loop.png)
52
+
53
+ The robot is round, so its style names the `custom` skeleton template and
54
+ its loops come from a sentence each on the v3 engine; a biped on the
55
+ `mannequin` template could take a template loop instead
56
+ (`"animation": { "of": "bot", "template": "walk" }`), one generation per
57
+ direction. See [Animations](#animations).
58
+
59
+ ### The base
60
+
61
+ A base is a prompt, wrapped in the style's prefix and suffix like any other
62
+ asset, that PixelLab draws facing 4 or 8 directions. `mode` picks the
63
+ engine: `standard` (1 generation, the skeleton template, `outline`,
64
+ `shading`, and `detail` as soft guidance, and `palette` sent as a colour
65
+ reference), `v3` (2 to 9 by size, the highest quality, up to 256px),
66
+ `pro` (20 to 40 by size), or `pro-flash` (6 to 17 by size: PixelLab's
67
+ newest image model draws the south sprite and v3 rotates it; sizes are
68
+ multiples of 4 up to 256, `template` may be `custom`, and a `reference`
69
+ pays for the rotations only, 1 at 64px). `size` is the character's size;
70
+ `view` is `low top-down` (the default), `high top-down`, or `side`;
71
+ `template` picks the body. Each direction lands as
72
+ `<asset>-<direction>.png`, south first.
73
+ Standard mode draws on a canvas 28px larger than `size`, 14px of room on
74
+ each side for animation (a 64px character comes back as 92px files, a
75
+ 104px one as 132px); the lock records the size asked for, and the files
76
+ are what PixelLab drew.
77
+
78
+ ### Proportions, guidance, and isometric view
79
+
80
+ Three more knobs shape a `standard` base. `proportions` is a preset
81
+ (`chibi`, `heroic`, and so on) or multipliers on the mannequin's head,
82
+ arms, legs, shoulders, and hips; it lives on the style and any base may
83
+ override it, and only the mannequin template has proportions to set.
84
+ `textGuidanceScale` (1 to 20, PixelLab's default 8) is how closely the text
85
+ is followed, and applies to template loops as well. `isometric` draws the
86
+ base and every loop in isometric view. The v3 and pro engines take none of
87
+ the three for a base; a v3 style may still set the last two for its loops.
88
+ A `v3` base takes `enhancePrompt` instead, which has PixelLab expand the
89
+ prompt into a fuller one before drawing.
90
+
91
+ ### Starting from your own sprite
92
+
93
+ A base can also start from your own sprite. `reference` names a
94
+ manifest-relative PNG or JPEG of the character facing south, and PixelLab
95
+ draws the other directions from it, with the prompt as guidance:
96
+
97
+ ```json
98
+ "bot": { "prompt": "small round orange robot with one blue eye", "reference": "refs/bot-south.png" }
99
+ ```
100
+
101
+ That is how the robot above was made the second time: its own south
102
+ sprite handed back to pro-flash, which rotated it for 2 generations at 96px
103
+ and left the south file pixel for pixel as it was.
104
+
105
+ `standard` uses each image as it is, centred on its larger canvas, and
106
+ generates the rest, so the image must be the style's `size` exactly; it
107
+ accepts one image per direction
108
+ (`{ "south": ..., "east": ... }`), and a quadruped template needs south and
109
+ east. `v3` rotates one south image of up to 256px (its `template` must
110
+ match the body in the image). `pro` rotates one south image of up to 168px
111
+ through its `rotate_character` method. The reference's bytes are part of
112
+ the base's identity, so a redrawn file makes the base stale, and the file
113
+ is read again at submit time and refused if it changed since `plan`.
114
+ States, animations, and mirrors take their look from their parent and
115
+ cannot carry a reference.
116
+
117
+ ### Concept images and style anchors (pro)
118
+
119
+ The pro engine has two more ways in. `concept` on a base names a concept
120
+ image (a painting, a photo, a sketch, up to 1024px) that seeds the design
121
+ in place of the prompt alone (PixelLab's `create_from_concept`); the
122
+ prompt still guides it. `styleCharacter`, on the style or a base, names a
123
+ generated 8-direction character in the same style whose look the base
124
+ follows (`style_character_id`); its south sprite becomes the style image
125
+ unless a style image is also given. The anchor is a dependency like a
126
+ loop's character: the base is `blocked` until the anchor is downloaded and
127
+ current, `gen` runs it in the wave after the anchor lands, and a
128
+ regenerated anchor makes every base drawn in its style `stale`. The anchor
129
+ itself ignores the setting, so a style can name its own first character.
130
+ PixelLab wants the base at least as large as the anchor's visible sprite
131
+ and fails the job fast otherwise. A base rotating its own `reference`
132
+ takes neither: the rotate method has one image slot and it is the
133
+ character.
134
+
135
+ ### Style images (pro and pro-flash)
136
+
137
+ Style images on a `character` style are a pro input: one image of up to
138
+ 168px that anchors the look of a `pro` base drawn from text or a concept
139
+ (`create_with_style` and `create_from_concept` both take it), or one of
140
+ up to 256px that a `pro-flash` base copies its look from, no larger than
141
+ the character's `size` on either side (crop it to its subject), with
142
+ `styleTraits` (`palette`, `outline`, `detail`, `shading`, each on by
143
+ default) choosing which traits it lends. `standard` and `v3` have no such
144
+ slot and refuse them; a base with a `reference` refuses them too, in
145
+ either engine.
146
+
147
+ ### States
148
+
149
+ A state is a text edit of an existing character, `state.of`, applied to
150
+ every direction at once: a pose, an outfit, a held object. The prompt is
151
+ the edit and goes to PixelLab as written, without the style's prefix and
152
+ suffix, since the character already carries the look.
153
+ `paletteFromReference` snaps the result to the parent's colours; `canvas`
154
+ asks for a larger frame when the edit adds something big. A state costs 20
155
+ to 40 by canvas and keeps the parent's directions. A state may be a state
156
+ of another state.
157
+
158
+ ### Animations
159
+
160
+ A template loop moves a skeleton, so it wants a body the skeleton fits: a
161
+ biped on `mannequin`, or one of the quadruped templates. A character that
162
+ is neither (a round robot, a slime) gets a walk from text on the v3
163
+ engine instead; a template on it either fails upstream (`custom`
164
+ template) or redraws the character as the biped it expected.
165
+
166
+ An animation is one loop of one character (`animation.of`, a base or a
167
+ state) in one `direction`. With a `template` (PixelLab's `walk`,
168
+ `breathing-idle`, `running-8-frames`, and so on) it costs 1 and the
169
+ template decides the frame count; without one the prompt is the motion and
170
+ PixelLab's v3 engine draws `frames` frames (4 to 16, even, default 8) for
171
+ `ceil(size² × frames / 65536)` generations, one at 64px. `keepFirstFrame`
172
+ (on by default) stores the resting pose as frame 0, so 8 frames land as 9
173
+ files, `<asset>-frame-00.png` onwards. `fps` is recorded with the frames
174
+ for the gallery, `pack`, and the Godot and Aseprite formats; PixelLab does
175
+ not keep one. An animation lands in review as an ordered set: `pick` shows
176
+ the loop and accepts or rejects it whole. One asset per direction; declare
177
+ another asset for another direction, or a [mirror](#mirrors) of this one
178
+ for the direction that faces the other way.
179
+
180
+ ### Pose frames and other loop controls
181
+
182
+ A v3 loop can start and end where you say. `startFrame` is a
183
+ manifest-relative image of the pose to begin from instead of the
184
+ character's rotation; `endFrame` is a pose to reach, and with it the loop
185
+ interpolates from the start frame to that image (both up to 256px, and the
186
+ end frame the same size as the start frame or, without one, as the
187
+ character's rotation, which `plan` checks once the parent is on disk; the
188
+ frames can still come back on a taller canvas when the motion needs it, as
189
+ a 92px crouch did at 92×104). `subject` replaces the character's own description for this loop when it
190
+ would mislead the model (a state that took the armour off), and
191
+ `enhancePrompt` lets PixelLab expand the action into a fuller motion
192
+ description first. A template loop takes none of those; it takes
193
+ `outline`, `shading`, and `detail` overrides instead, over the character's
194
+ own. The style's `palette` goes to every loop as a colour reference, as it
195
+ does to a `standard` base, and `enforcePalette` still snaps the frames
196
+ afterwards. Pose images hash into the loop's identity and are read again
197
+ at submit time, like a base's `reference`.
198
+
199
+ ```json
200
+ "bot.crouch": {
201
+ "prompt": "folding down onto its legs until it rests on the ground",
202
+ "animation": { "of": "bot", "direction": "south", "frames": 6, "endFrame": "poses/bot-crouched.png" }
203
+ }
204
+ ```
205
+
206
+ ### How the family depends on the base
207
+
208
+ States and animations depend on their parent the way a revision does. The
209
+ parent must be downloaded and current before the child is actionable;
210
+ `plan` reports the child as `blocked` and names the parent until then, and
211
+ one `gen` runs the waves in order: bases, then states, then animations,
212
+ under one budget. The
213
+ child's identity includes the parent's generated south-facing file, so
214
+ regenerating the parent makes every state and animation of it `stale`. A
215
+ hand edit of the parent does not, because PixelLab draws the child from the
216
+ character it holds, not from local bytes.
217
+
218
+ Regenerating an animation clears PixelKiln's own earlier take of that
219
+ direction on the character first, since PixelLab skips a direction that
220
+ already exists. The lock records the animation and group ids PixelLab
221
+ assigned, and the delete goes by those (PixelLab keeps the name PixelKiln
222
+ gives a loop only for text animations, not template ones). Nothing else on
223
+ the character is touched, and a base or state is never deleted by
224
+ PixelKiln.
225
+
226
+ ### Palette and packing
227
+
228
+ `enforcePalette` snaps every direction and every frame. `pack --style cast
229
+ --format godot` writes a `SpriteFrames` with each direction of a base or
230
+ state as a still and each animation as a looping set at its fps;
231
+ `--format aseprite` does the same with `frameTags`.
232
+
233
+ ### Adopting a character from the account
234
+
235
+ Characters that already exist on the account come under the manifest with
236
+ `adopt`. Declare the asset with `remoteId` (the character id, or
237
+ `<character id>#<animation group id>` for a loop; `get_character` in
238
+ PixelLab's own tools shows both) and run `pixelkiln adopt`: it records the
239
+ character, writes every direction and frame that is not on disk, and costs
240
+ nothing. A base can also be matched by the bytes of its south-facing file.
241
+ See [`adopt`](CLI.md#adopt).
242
+
243
+ ## Mirrors
244
+
245
+ Each direction of a loop is its own generation, and a sprite walking east
246
+ is the sprite walking west flipped. A `mirror` asset is that flip, made
247
+ locally from the source asset's downloaded files:
248
+
249
+ ```json
250
+ "bot.walk.west": {
251
+ "prompt": "walking forward in place, short legs stepping, body bobbing slightly",
252
+ "animation": { "of": "bot", "direction": "west", "frames": 12, "fps": 12 }
253
+ },
254
+ "bot.walk.east": { "mirror": "bot.walk.west" }
255
+ ```
256
+
257
+ A full 8-direction set of one loop is then 5 generations (south, north,
258
+ west, south-west, north-west) and 3 mirrors; a 4-direction set is 3 and 1.
259
+ The mirror takes its shape from the source (a loop of the same character,
260
+ facing the other way, at the same fps), needs no prompt, and costs
261
+ nothing: `plan` lists it under the provider with a cost of 0 and `gen`
262
+ flips it in the wave after the source lands. It has its own lock entry and
263
+ files (`bot.walk.east-frame-00.png` onwards), so `pack`, the gallery, and
264
+ an engine see an ordinary loop. Nothing is sent to the provider, and the
265
+ account holds no east animation.
266
+
267
+ The lock records a hash over the source's output hashes when the flip was
268
+ made. A source that is regenerated, restored, or re-snapped makes its
269
+ mirrors `stale`, and the next `gen` flips them again for free. A mirror
270
+ whose files went missing is `stale` too; one that was hand-edited is
271
+ `orphaned`, like generated art. A source that is not downloaded, current,
272
+ and untouched blocks its mirrors.
273
+
274
+ A loop facing south or north cannot be mirrored: the flip would be the same
275
+ direction with its asymmetries swapped, not a new one. A single image or a
276
+ frame set from any generator can be mirrored, and so can a base or state
277
+ (every direction flipped and relabelled, so west becomes east). A tile set
278
+ cannot; its edges carry meaning. Mirroring swaps handedness, so a character
279
+ who holds a sword in the right hand holds it in the left when facing the
280
+ mirrored way. Most games accept that; if yours does not, generate both
281
+ sides. `adopt` skips mirrors, since there is nothing upstream to adopt.
282
+
283
+ Engines that flip sprites at draw time (Godot's `flip_h`, Unity's
284
+ `flipX`) do not need mirrored files at all. Declare only the directions
285
+ you generate and flip in the engine; mirrors are for pipelines that want
286
+ every direction on disk.
287
+
288
+ ## Working with a cast
289
+
290
+ - `pixelkiln plan` shows a state or loop as `blocked` until its parent is
291
+ downloaded and current, and one `pixelkiln gen` runs the waves in order
292
+ (bases, then states, then loops and mirrors) under one budget; see
293
+ [`gen`](./CLI.md#gen).
294
+ - Loops land in review as ordered sets; `pixelkiln pick` accepts or rejects
295
+ a set whole.
296
+ - Characters that already exist on the account come under the manifest with
297
+ [`adopt`](./CLI.md#adopt), by `remoteId` or by the bytes of a south file.
298
+ - A character edited in PixelLab's own editor comes back with
299
+ [`fetch --refresh`](./CLI.md#fetch), which re-resolves the character's
300
+ current URLs first.
301
+ - `pixelkiln gallery` shows the family: states under their base, loops with
302
+ their direction, mirrors with their source; see
303
+ [`gallery`](./CLI.md#gallery).
304
+ - `pack --format godot` and `--format aseprite` write each direction as a
305
+ still and each loop as a looping set at its fps; see
306
+ [engine formats](./ARTIFACTS.md).
307
+ - Costs per engine are in the [generator table](./GENERATORS.md#character),
308
+ and what the live runs measured is in [Measured
309
+ endpoints](./ENDPOINTS.md#characters-measured).
package/docs/CLI.md CHANGED
@@ -17,7 +17,7 @@ work. The experimental `comfyui` adapter runs committed API-format `map` and
17
17
  ordered still-frame workflows on a self-hosted server. The experimental `scenario` adapter runs
18
18
  hosted still models with Compute Unit preflight and durable asset recovery.
19
19
 
20
- ## Which command
20
+ Which command:
21
21
 
22
22
  | I want to | Run |
23
23
  |---|---|
@@ -25,6 +25,8 @@ hosted still models with Compute Unit preflight and durable asset recovery.
25
25
  | generate everything the manifest still needs | `pixelkiln gen --budget 40` |
26
26
  | hold every generated file to the style's palette | set `"enforcePalette": true` on the style, then `pixelkiln fetch` |
27
27
  | draw a character in 8 directions, then its poses and loops | a `character` style with `state` and `animation` assets; one `pixelkiln gen` runs the waves |
28
+ | rotate your own sprite into 8 directions | `"reference": "refs/bot-south.png"` on the base; `mode: pro-flash` does it for 1 generation at 64px |
29
+ | get the east-facing loop without paying for it | `"bot.walk.east": { "mirror": "bot.walk.west" }` |
28
30
  | generate one asset, or one style | `pixelkiln gen --only anvil --budget 2`, `pixelkiln gen --style neon --budget 20` |
29
31
  | finish a run that was interrupted | `pixelkiln plan`, then the `next:` command it prints (`poll`, `pick`, or `fetch`) |
30
32
  | choose among candidates the provider returned | `pixelkiln pick` |
@@ -113,8 +115,9 @@ PixelKiln validates the complete budget set before submitting the first group.
113
115
 
114
116
  `gen` runs in waves. A wave is the whole lifecycle for everything the plan
115
117
  can act on now. Once its downloads land, assets that were `blocked` on a
116
- parent (a character's states, then their animations; a revision of a still
117
- that was just made) become actionable, and the next wave takes them under
118
+ parent (a character's states, then their animations and the mirrors of
119
+ those; a pro base drawn in another character's style; a revision of a
120
+ still that was just made) become actionable, and the next wave takes them under
118
121
  what is left of the same budget, asking the same "Spend ...?" question each
119
122
  time unless `--yes`. It stops when a wave finds nothing new, when a wave
120
123
  submits nothing, or when the remaining budget cannot cover the next wave; in
@@ -416,6 +419,8 @@ pixelkiln gallery --style environment --port 4180 --no-open
416
419
  pixelkiln gallery --json > generations.json
417
420
  ```
418
421
 
422
+ #### What it shows
423
+
419
424
  The gallery is lock-first. A lock entry the manifest no longer declares still
420
425
  appears, marked `undeclared`, so paid work is never hidden; a declared asset
421
426
  with no entry appears as a placeholder so the page also shows what has not
@@ -431,10 +436,14 @@ provider, and never writes anything. Stop it with Ctrl+C. See the
431
436
  [Getting started guide](GETTING_STARTED.md#start-a-new-project) for a
432
437
  screenshot.
433
438
 
439
+ #### `--json`: the snapshot as data
440
+
434
441
  `--json` prints the same snapshot to stdout without starting a server. It is
435
442
  the offline, machine-readable answer to "what has this project generated?" for
436
443
  scripts and agents; `--style` and `--only` narrow it the same way.
437
444
 
445
+ #### `--edit`: change prompts and style fields
446
+
438
447
  `--edit` lets the page change *intent*: an asset's prompt (for every style or
439
448
  only the one being viewed), width, height, size, category, and tags; a style's
440
449
  prompt prefix, prompt suffix, palette, whether downloaded art is snapped to
@@ -462,6 +471,8 @@ pixelkiln gallery --edit
462
471
  pixelkiln gallery --edit --workspace pixelkiln.workspace.json
463
472
  ```
464
473
 
474
+ #### Restore and regenerate from a record
475
+
465
476
  An `orphaned` record, one whose file is gone or changed after download, offers
466
477
  **Restore** (the recorded bytes back from the cache or the provider, no cost)
467
478
  and, for a changed file, **Regenerate**; both replacements say what they
@@ -476,6 +487,8 @@ no cost, the page's form of [`restore --generation`](#restore). Any `--budget`
476
487
  enables it, `0` included, and the current generation moves into the list so
477
488
  the restore can be undone.
478
489
 
490
+ #### `--budget`: generate from the page
491
+
479
492
  `--budget <n|provider=n>` enables generation from the page under that session
480
493
  ceiling. It is the same `--budget` `gen` takes: one unkeyed amount when a run
481
494
  involves a single provider, or one keyed amount per provider for a mixed run,
@@ -494,6 +507,8 @@ out over the gallery, **Apply selections** writes the lockfile and downloads
494
507
  the chosen art, and unchosen rows stay in review exactly as with `pick`. A
495
508
  regeneration's sheet shows the current art beside the candidates.
496
509
 
510
+ #### Compare records side by side
511
+
497
512
  **Compare** puts two to four records side by side at one shared zoom with
498
513
  their fields in rows (provider, generator, candidates, size, cost, prompt,
499
514
  dates, quality, hashes) and tints every row whose values differ. Shift-click
@@ -519,6 +534,8 @@ says how many. PixelLab's count follows the generator and size (`map` and
519
534
  `pixflux` return one image; a `1dir` style returns 4–64), so the header
520
535
  explains that instead of offering a number.
521
536
 
537
+ #### Hand edits, in your editor or the browser
538
+
522
539
  With `--edit`, a record also gains **Edit by hand**, the page's form of
523
540
  [`pixelkiln edit`](#edit): it creates the edit file, declares it, and opens
524
541
  it in `PIXELKILN_EDITOR` or the OS default. The record then shows the generated
@@ -556,6 +573,8 @@ on the same file. The editor page runs same-origin under its own
556
573
  content-security policy and never sees the gallery's session token; the page
557
574
  does the write. `--no-editor` hides all of it and serves none of its routes.
558
575
 
576
+ #### Pull changes made in PixelLab's editor
577
+
559
578
  A PixelLab `map` or `1dir` record also links to its account object (**Open in
560
579
  pixellab ↗**), where PixelLab's own editor can change it; with `--budget`
561
580
  (any amount; `--budget 0` allows provider contact and no spend) the record
@@ -568,6 +587,8 @@ pixelkiln gallery --edit --budget 80
568
587
  pixelkiln gallery --budget pixellab=40 --budget retrodiffusion=1.25 --workspace pixelkiln.workspace.json
569
588
  ```
570
589
 
590
+ #### `--workspace`: every project in one gallery
591
+
571
592
  `--workspace <catalog>` shows every project the catalog registers in one
572
593
  gallery, the way `workspace status` reads them: no manifest is needed in the
573
594
  current directory, each project gets its own section and filter chip, and a
package/docs/COMFYUI.md CHANGED
@@ -619,6 +619,74 @@ PixelKiln refuses missing nodes and inputs during the offline plan. It also
619
619
  clones the workflow before applying bindings, so one asset cannot mutate the
620
620
  next asset's request.
621
621
 
622
+ ## Manifest fields
623
+
624
+ ComfyUI runs a committed API-format workflow on a self-hosted server. The
625
+ workflow file is resolved relative to the manifest and its parsed content is
626
+ part of the spec hash. Adapter success proves transport and output structure,
627
+ not pixel-art quality. Use provider-neutral `pixelkiln refine` for native-grid,
628
+ final-palette, and recorded audit checks. Prompt coverage and human 1× approval
629
+ still require a person.
630
+
631
+ ```jsonc
632
+ {
633
+ "name": "my-game",
634
+ "provider": "comfyui",
635
+ "styles": {
636
+ "local": {
637
+ "generator": "map",
638
+ "outDir": "assets/generated/local",
639
+ "seed": 31415,
640
+ "providerOptions": {
641
+ "comfyui": {
642
+ "workflowFile": "workflows/pixel-api.json",
643
+ "outputNodeId": "9",
644
+ "numImages": 4,
645
+ "bindings": {
646
+ "prompt": { "nodeId": "6", "input": "text" },
647
+ "width": { "nodeId": "5", "input": "width" },
648
+ "height": { "nodeId": "5", "input": "height" },
649
+ "batchSize": { "nodeId": "5", "input": "batch_size" },
650
+ "seed": { "nodeId": "3", "input": "seed" },
651
+ "composition": { "nodeId": "19", "input": "image" },
652
+ "controlStrength": { "nodeId": "20", "input": "strength" }
653
+ }
654
+ }
655
+ }
656
+ }
657
+ },
658
+ "assets": {
659
+ "mountain": {
660
+ "prompt": "a snowbound mountain pass",
661
+ "width": 768,
662
+ "height": 512,
663
+ "providerInputs": {
664
+ "composition": "controls/mountain-layout.png",
665
+ "controlStrength": 0.7
666
+ }
667
+ }
668
+ }
669
+ }
670
+ ```
671
+
672
+ Node IDs come from the exported workflow; they are not stable across unrelated
673
+ workflows. Binding names beyond PixelKiln's built-ins are project-defined and
674
+ an asset overrides one with a matching `providerInputs` value. A custom
675
+ binding aimed at `LoadImage.image` or `LoadImageMask.image` treats its string as
676
+ a manifest-relative PNG/JPEG, hashes it, and uploads it at submission. Other
677
+ custom inputs accept a string, number, or boolean matching the workflow's
678
+ placeholder type. Local paths never enter stable provenance.
679
+
680
+ The current adapter supports `map` and ordered still-image `frames`, PNG output
681
+ from one node, 1–16 `map` candidates, and dimensions from 16–4096px. A frame
682
+ style uses `numImages: 1`; the varying input supplies 2–64 renders. It rejects
683
+ manifest `styleImages` and `palette`;
684
+ use custom image bindings for per-asset ControlNet or reference images, and keep
685
+ shared model/LoRA/palette controls inside the workflow. See
686
+ [Set up ComfyUI](COMFYUI.md) for the complete procedure, safe workflow, and
687
+ quality limits. The 4096px adapter ceiling is not a recommended generation or
688
+ native-art size.
689
+
622
690
  ## Plan, generate, and review
623
691
 
624
692
  ```bash
package/docs/ENDPOINTS.md CHANGED
@@ -325,8 +325,39 @@ Listed so the gaps are known rather than assumed away:
325
325
  - the tileset family, with schema documented above and costs unmeasured
326
326
  - `create-isometric-tile`, `create-ui-asset`, `generate-font-pro`, all job-based,
327
327
  with response shapes not in the simple `{usage, image}` form
328
- - the character family beyond what the `character` generator uses (portraits,
329
- outfit transfer, lip-sync, skeleton animation, Pro Flash)
328
+ - the character family beyond what the `character` generator uses: portraits
329
+ (`portrait-character-pro` both ways, `/characters/{id}/portrait`), outfit
330
+ transfer (`transfer-outfit-v2`), lip-sync, skeleton animation, and
331
+ `source_image_id` on Pro Flash. The four creation engines, states, and
332
+ `animate-character` are covered in full; see the September 2026 measurements
333
+ below.
334
+
335
+ ## Characters, measured
336
+
337
+ - Standard bases draw on a canvas 28px larger than `image_size` (64 → 92,
338
+ 104 → 132). A `directions` reference is placed on it as is, centred, pixel
339
+ for pixel.
340
+ - Pro Flash returns exactly the requested canvas, and refuses a `style_image`
341
+ larger than it ("Style image must fit the native canvas"). Its
342
+ `/pro-flash/cost` answer for `operation=character` was `image` 5 up to 96px,
343
+ 6 from 100 to 208px, 9 at 224px and above, plus `rotations` equal to v3's
344
+ `ceil(side² × 8 / 65536)` on the padded square canvas; two live jobs (7 and
345
+ 2) billed exactly the estimate. `first_frame` bills the rotations only.
346
+ - A 64px `create-character-state` moved the balance by about 22 against the
347
+ 20 to 40 tier documented; other jobs were running on the account, so it is a
348
+ rough number.
349
+ - `animate-character` with `custom_start_frame` and `end_frame` may return
350
+ frames taller than the rotation (92 × 104 for a crouch). `enhance_prompt`
351
+ writes the expanded text into the animation's `animation_type`. A v3 text
352
+ loop on a 96px pro-flash base came back at 108 × 108.
353
+ - Template loops need a skeleton template. On a pro-flash base with
354
+ `template_id: custom` a `walking-8-frames` job failed upstream at once; on
355
+ a round robot fitted to `mannequin` the same template returned a different,
356
+ humanoid character standing nearly still. A v3 text loop ("walking forward
357
+ in place, short legs stepping") kept the robot and walked. For anything
358
+ that is not a biped or one of the four quadrupeds, animate from text.
359
+ - Request validation is strict: an unknown body field is a 422 listing every
360
+ problem at once, which is a free way to check a body's shape.
330
361
 
331
362
  ---
332
363
 
@@ -189,13 +189,13 @@ onwards, with the resting pose as frame 0 unless `keepFirstFrame` is off.
189
189
  `enforcePalette` snaps all of them. Since each direction of a loop is its
190
190
  own generation, declare the east-facing loop as a `mirror` of the west one
191
191
  (and each diagonal as a mirror of the other) and PixelKiln flips it locally
192
- for nothing; see [Mirrors](./MANIFEST.md#mirrors). `pack --format godot`
192
+ for nothing; see [Mirrors](./CHARACTERS.md#mirrors). `pack --format godot`
193
193
  writes each base direction as a still and each animation as a looping set.
194
194
  The gallery
195
- labels a state or loop with its parent ("state of hero", "loop of
196
- hero.sit, east"), links parent and children in the record, and counts the
195
+ labels a state or loop with its parent ("state of bot", "loop of
196
+ bot.dented, east"), links parent and children in the record, and counts the
197
197
  family in the style header ("3 characters, 4 states, 6 loops"). See
198
- [Characters](./MANIFEST.md#characters).
198
+ [Characters](./CHARACTERS.md).
199
199
 
200
200
  ## Style variants
201
201
 
@@ -208,8 +208,8 @@ separate output directory and separate lock keys:
208
208
  "neon": {
209
209
  "generator": "1dir",
210
210
  "size": 64,
211
- "promptPrefix": "Neon-noir game icon: ",
212
- "promptSuffix": ", magenta/cyan rim light, transparent background",
211
+ "promptPrefix": "neon-noir game icon",
212
+ "promptSuffix": "magenta/cyan rim light, transparent background",
213
213
  "styleImages": [{ "path": "art/style-refs/neon.png" }],
214
214
  "outDir": "art/neon"
215
215
  }
@@ -69,7 +69,7 @@ implemented. Authenticated single-candidate RD Fast and RD Plus stills have
69
69
  passed from quote through validated download, provenance, and cache.
70
70
  Multi-candidate, tileset, GIF, and spritesheet live runs remain, so PixelLab
71
71
  remains the production adapter. See
72
- [Manifest reference](MANIFEST.md#experimental-retro-diffusion) for
72
+ [Manifest reference](RETRO_DIFFUSION.md#manifest-fields) for
73
73
  provider options and current limits, or
74
74
  [provider comparison](../PROVIDERS.md) for selection guidance.
75
75
 
@@ -260,6 +260,30 @@ Repeated `--style`, `--only`, `--claims`, and `--output-role` flags accumulate;
260
260
  comma-separated values also work. Unknown flags are errors, so a misspelled
261
261
  filter cannot accidentally widen a paid run.
262
262
 
263
+ ## A character and its loops
264
+
265
+ A `character` style holds a base drawn facing 4 or 8 directions, states (a
266
+ pose, an outfit) applied to every direction at once, and loops, one
267
+ direction each. Each is an asset with its own record and files:
268
+
269
+ ```json
270
+ {
271
+ "styles": { "cast": { "generator": "character", "outDir": "art/cast", "size": 64, "mode": "pro-flash", "template": "custom" } },
272
+ "assets": {
273
+ "bot": { "prompt": "small round orange robot with one blue eye", "reference": "refs/bot-south.png" },
274
+ "bot.walk.west": { "prompt": "walking forward in place, short legs stepping", "animation": { "of": "bot", "direction": "west", "frames": 12, "fps": 12 } },
275
+ "bot.walk.east": { "mirror": "bot.walk.west" }
276
+ }
277
+ }
278
+ ```
279
+
280
+ `pixelkiln plan` prices it (1 generation to rotate the sprite, 1 for the
281
+ loop, 0 for the mirror) and one `pixelkiln gen` runs the waves: the base,
282
+ then the loop, then the flip. `pixelkiln pack --style cast --format godot`
283
+ writes a `SpriteFrames` with every direction as a still and both loops.
284
+ Regenerate the base and everything under it goes `stale`. See
285
+ [Characters](./CHARACTERS.md) and [Mirrors](./CHARACTERS.md#mirrors).
286
+
263
287
  ## Automation
264
288
 
265
289
  Use machine-readable planning and auditing as build gates: