Art Studio has never been able to say how anything was made, and its concept art has never been reproducible.
The root cause
SDXLLightningBackend._text_to_image only builds a seeded torch.Generator when a seed param arrives. The Art Studio path never sends one — ArtPipeStageOrchestrator.BuildJobParamsAsync sends literally {"prompt": <asset.Prompt>} — so generation falls through to the ambient torch RNG and the seed that produced an image is gone the moment it is made. Steps (4), resolution (1024), guidance (1.5) and the empty negative prompt are silent Python defaults that are never written down either.
The worker's result dict is exactly {output_path, output_type, image_path}; images are saved with a bare image.save(path) — no PNG text chunk, no EXIF, no sidecar.
Consequence: POST {id}/restart/{stage} re-runs the same prompt and produces a different picture every time, and clicking an asset card shows name/type/status but nothing about the prompt, model or settings.
Scope
Record what actually produced each stage run, per attempt.
Always seed generation, so a run is reproducible at all.
Show it when you click a card.
Let a user reproduce a run exactly, or vary it deliberately.
Let a user set a seed / negative prompt / steps up front.
Open the text+image concept path (prompt + reference image), which did not exist.
Shaped so FLUX.2 drops in as a concept model without redesigning the contract: references are a list (FLUX.2 takes up to ten), strength is optional and backend-declared (FLUX.2 has none), and steps/guidance bounds come from per-method capability descriptors rather than a hardcoded list.
Sub-issues
See the linked issues.
Art Studio has never been able to say how anything was made, and its concept art has never been reproducible.
## The root cause
`SDXLLightningBackend._text_to_image` only builds a seeded `torch.Generator` when a `seed` param arrives. The Art Studio path never sends one — `ArtPipeStageOrchestrator.BuildJobParamsAsync` sends literally `{"prompt": <asset.Prompt>}` — so generation falls through to the ambient torch RNG and **the seed that produced an image is gone the moment it is made**. Steps (4), resolution (1024), guidance (1.5) and the empty negative prompt are silent Python defaults that are never written down either.
The worker's result dict is exactly `{output_path, output_type, image_path}`; images are saved with a bare `image.save(path)` — no PNG text chunk, no EXIF, no sidecar.
Consequence: `POST {id}/restart/{stage}` re-runs the same prompt and produces a **different picture every time**, and clicking an asset card shows name/type/status but nothing about the prompt, model or settings.
## Scope
1. Record what actually produced each stage run, per attempt.
2. Always seed generation, so a run is reproducible at all.
3. Show it when you click a card.
4. Let a user reproduce a run exactly, or vary it deliberately.
5. Let a user set a seed / negative prompt / steps up front.
6. Open the **text+image** concept path (prompt + reference image), which did not exist.
Shaped so FLUX.2 drops in as a concept model without redesigning the contract: references are a list (FLUX.2 takes up to ten), `strength` is optional and backend-declared (FLUX.2 has none), and steps/guidance bounds come from per-method capability descriptors rather than a hardcoded list.
## Sub-issues
See the linked issues.
Epic status roll-up — audited every child against origin/master / origin/main. All seven shipped and merged (epic PR d0de275a / #480), but three carry real gaps, so the epic stays open until they land.
⚠️ dead Enabled switch; MethodKey accepted and ignored
The three things blocking this epic, in the order I'd fix them:
#846 — restart silently reverts text+image to text.RestartFromStageCommandHandler.cs:390 calls ToStageMethod(option) without the source type, so ActionFor falls back to the text action and the reference images are ignored. One-argument fix, but it's the exact silent-wrong-result behaviour the epic set out to remove.
#847 — ArtStudio:GenerationParams:Enabled is dead code. Declared and documented as "False rejects every override", never read by anything. Overrides are accepted in production today regardless of the flag. Either wire it in or delete the flag and its doc comment — right now the documentation asserts a safety property that doesn't exist.
#845 — the worker's "drop with a warning" is silent. Clamping happens with no log line anywhere, so a clamped override is invisible to both the user and anyone reading worker logs.
Two smaller items also recorded on their children: #847's MethodKey reaches the command and is never read (so "reproduce on a different model" silently uses the same model), and #846's SupportsSourceType guard only covers the explicit selection loop — preset-merge, failover and darkroom bypass it.
Everything else in the epic verified clean, including the cross-repo contract: the backend mapper's KnownKeys match artpipe/src/artpipe/generation_params.py:150-221 key-for-key, pinned from both sides.
**Epic status roll-up** — audited every child against `origin/master` / `origin/main`. All seven shipped and merged (epic PR `d0de275a` / #480), but three carry real gaps, so the epic stays open until they land.
| Child | State |
|---|---|
| #843 record stage-run params | ✅ closed |
| #844 artpipe seed + provenance | ✅ closed |
| #848 UI: show + remake | ✅ closed |
| #849 name storage keys | ✅ closed (superseded by #853) |
| #845 carry overrides to worker | ⚠️ worker clamps silently; `GET options` unfiltered by source type |
| #846 text+image concept | ⚠️ **restart drops the source type** |
| #847 reproduce and vary | ⚠️ dead `Enabled` switch; `MethodKey` accepted and ignored |
**The three things blocking this epic**, in the order I'd fix them:
1. **#846 — restart silently reverts text+image to text.** `RestartFromStageCommandHandler.cs:390` calls `ToStageMethod(option)` without the source type, so `ActionFor` falls back to the text action and the reference images are ignored. One-argument fix, but it's the exact silent-wrong-result behaviour the epic set out to remove.
2. **#847 — `ArtStudio:GenerationParams:Enabled` is dead code.** Declared and documented as "False rejects every override", never read by anything. Overrides are accepted in production today regardless of the flag. Either wire it in or delete the flag and its doc comment — right now the documentation asserts a safety property that doesn't exist.
3. **#845 — the worker's "drop with a warning" is silent.** Clamping happens with no log line anywhere, so a clamped override is invisible to both the user and anyone reading worker logs.
Two smaller items also recorded on their children: #847's `MethodKey` reaches the command and is never read (so "reproduce on a different model" silently uses the same model), and #846's `SupportsSourceType` guard only covers the explicit selection loop — preset-merge, failover and darkroom bypass it.
Everything else in the epic verified clean, including the cross-repo contract: the backend mapper's `KnownKeys` match `artpipe/src/artpipe/generation_params.py:150-221` key-for-key, pinned from both sides.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Art Studio has never been able to say how anything was made, and its concept art has never been reproducible.
The root cause
SDXLLightningBackend._text_to_imageonly builds a seededtorch.Generatorwhen aseedparam arrives. The Art Studio path never sends one —ArtPipeStageOrchestrator.BuildJobParamsAsyncsends literally{"prompt": <asset.Prompt>}— so generation falls through to the ambient torch RNG and the seed that produced an image is gone the moment it is made. Steps (4), resolution (1024), guidance (1.5) and the empty negative prompt are silent Python defaults that are never written down either.The worker's result dict is exactly
{output_path, output_type, image_path}; images are saved with a bareimage.save(path)— no PNG text chunk, no EXIF, no sidecar.Consequence:
POST {id}/restart/{stage}re-runs the same prompt and produces a different picture every time, and clicking an asset card shows name/type/status but nothing about the prompt, model or settings.Scope
Shaped so FLUX.2 drops in as a concept model without redesigning the contract: references are a list (FLUX.2 takes up to ten),
strengthis optional and backend-declared (FLUX.2 has none), and steps/guidance bounds come from per-method capability descriptors rather than a hardcoded list.Sub-issues
See the linked issues.
Epic status roll-up — audited every child against
origin/master/origin/main. All seven shipped and merged (epic PRd0de275a/ #480), but three carry real gaps, so the epic stays open until they land.GET optionsunfiltered by source typeEnabledswitch;MethodKeyaccepted and ignoredThe three things blocking this epic, in the order I'd fix them:
RestartFromStageCommandHandler.cs:390callsToStageMethod(option)without the source type, soActionForfalls back to the text action and the reference images are ignored. One-argument fix, but it's the exact silent-wrong-result behaviour the epic set out to remove.ArtStudio:GenerationParams:Enabledis dead code. Declared and documented as "False rejects every override", never read by anything. Overrides are accepted in production today regardless of the flag. Either wire it in or delete the flag and its doc comment — right now the documentation asserts a safety property that doesn't exist.Two smaller items also recorded on their children: #847's
MethodKeyreaches the command and is never read (so "reproduce on a different model" silently uses the same model), and #846'sSupportsSourceTypeguard only covers the explicit selection loop — preset-merge, failover and darkroom bypass it.Everything else in the epic verified clean, including the cross-repo contract: the backend mapper's
KnownKeysmatchartpipe/src/artpipe/generation_params.py:150-221key-for-key, pinned from both sides.