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.
ArtAssetStageRun records the worker's generation_params per attempt (#843) — on the run, not the asset, because StageMethodProvenance is upsert-by-stage and would destroy earlier attempts
RestartFromStageCommand.GenerationParamOverrides carried to the worker as a field separate from static ArtAssetStageMethod.Params, with prompt/negative_promptrejected rather than clamped so the restart path can't launder a moderated prompt (#845)
ArtAssetSourceType.TextAndImage + the SubmitArtAssetValidator ObjectId bug that was failing every image-upload submission in production post-MinIO (#846)
ReproduceStageRunId / ArtAssetReproduceMode / ReproducedFromStageRunId for exact-reproduce vs. deliberate-vary (#847)
artpipe — generation is always seeded now: sdxl_lightning.py:380,436, sdxl_turbo.py:97, flux_schnell.py, text3d.py all build a torch.Generator(...).manual_seed(seed), with the CUDA-vs-MPS/CPU non-reproducibility caveat documented at sdxl_lightning.py:470.
Angular — libraries/features/art-studio/src/lib/art-generation-details/ (component + dialog) surfaces the recorded parameters when you click a card, with a per-attempt Reproduce action (art-generation-details.component.html:251-256) and isReproducible() in art-studio.models.ts refusing to call a run reproducible without a recorded seed (seed 0 handled correctly).
— verified and closed by Opus 5 Agent during the 2026-08-06 tracker sweep.
**Epic complete** — all six scope items shipped across the three repos.
**Backend** (spikersoft-backend#480, "Closes #843, #845, #846, #847, #849"):
- `ArtAssetStageRun` records the worker's `generation_params` per attempt (#843) — on the *run*, not the asset, because `StageMethodProvenance` is upsert-by-stage and would destroy earlier attempts
- `RestartFromStageCommand.GenerationParamOverrides` carried to the worker as a field separate from static `ArtAssetStageMethod.Params`, with `prompt`/`negative_prompt` **rejected rather than clamped** so the restart path can't launder a moderated prompt (#845)
- `ArtAssetSourceType.TextAndImage` + the `SubmitArtAssetValidator` ObjectId bug that was failing **every** image-upload submission in production post-MinIO (#846)
- `ReproduceStageRunId` / `ArtAssetReproduceMode` / `ReproducedFromStageRunId` for exact-reproduce vs. deliberate-vary (#847)
**artpipe** — generation is always seeded now: `sdxl_lightning.py:380,436`, `sdxl_turbo.py:97`, `flux_schnell.py`, `text3d.py` all build a `torch.Generator(...).manual_seed(seed)`, with the CUDA-vs-MPS/CPU non-reproducibility caveat documented at `sdxl_lightning.py:470`.
**Angular** — `libraries/features/art-studio/src/lib/art-generation-details/` (component + dialog) surfaces the recorded parameters when you click a card, with a per-attempt **Reproduce** action (`art-generation-details.component.html:251-256`) and `isReproducible()` in `art-studio.models.ts` refusing to call a run reproducible without a recorded seed (seed `0` handled correctly).
— verified and closed by Opus 5 Agent during the 2026-08-06 tracker sweep.
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.Epic complete — all six scope items shipped across the three repos.
Backend (spikersoft-backend#480, "Closes #843, #845, #846, #847, #849"):
ArtAssetStageRunrecords the worker'sgeneration_paramsper attempt (#843) — on the run, not the asset, becauseStageMethodProvenanceis upsert-by-stage and would destroy earlier attemptsRestartFromStageCommand.GenerationParamOverridescarried to the worker as a field separate from staticArtAssetStageMethod.Params, withprompt/negative_promptrejected rather than clamped so the restart path can't launder a moderated prompt (#845)ArtAssetSourceType.TextAndImage+ theSubmitArtAssetValidatorObjectId bug that was failing every image-upload submission in production post-MinIO (#846)ReproduceStageRunId/ArtAssetReproduceMode/ReproducedFromStageRunIdfor exact-reproduce vs. deliberate-vary (#847)artpipe — generation is always seeded now:
sdxl_lightning.py:380,436,sdxl_turbo.py:97,flux_schnell.py,text3d.pyall build atorch.Generator(...).manual_seed(seed), with the CUDA-vs-MPS/CPU non-reproducibility caveat documented atsdxl_lightning.py:470.Angular —
libraries/features/art-studio/src/lib/art-generation-details/(component + dialog) surfaces the recorded parameters when you click a card, with a per-attempt Reproduce action (art-generation-details.component.html:251-256) andisReproducible()inart-studio.models.tsrefusing to call a run reproducible without a recorded seed (seed0handled correctly).— verified and closed by Opus 5 Agent during the 2026-08-06 tracker sweep.