The bug (production).SubmitArtAssetValidator required SourceImageGridFsId to parse as an ObjectId. That was correct only while artifacts lived in GridFS. Since the MinIO cutover UploadAsync returns {assetId}__{guid:N}, and Storage__UseS3=true on the backend stack — so the API minted a key and then rejected its own submission with SourceImageGridFsId must be a valid ObjectId. Every image-upload art submission was failing. It stayed invisible because every test fixture used a GridFS-shaped id, so nothing ever fed the validator a key the production store actually produces.
The feature.ArtAssetSourceType.TextAndImage, which KEEPS the concept stage — the references condition a generated concept rather than replacing it (a pure ImageUpload still skips it, since there the image IS the concept). Methods declare an ImageAction; one without it is rejected by name rather than silently dispatched as text_to_image and ignoring the uploaded image.
References are a LIST end to end (ReferenceImageKeys), exposed as image_paths with image_path still the first — that is the FLUX.2 shape (up to ten references, conditioned by instruction, no denoise strength at all).
Two things.
**The bug (production).** `SubmitArtAssetValidator` required `SourceImageGridFsId` to parse as an ObjectId. That was correct only while artifacts lived in GridFS. Since the MinIO cutover `UploadAsync` returns `{assetId}__{guid:N}`, and `Storage__UseS3=true` on the backend stack — so the API minted a key and then rejected its own submission with *SourceImageGridFsId must be a valid ObjectId*. **Every image-upload art submission was failing.** It stayed invisible because every test fixture used a GridFS-shaped id, so nothing ever fed the validator a key the production store actually produces.
**The feature.** `ArtAssetSourceType.TextAndImage`, which KEEPS the concept stage — the references condition a generated concept rather than replacing it (a pure ImageUpload still skips it, since there the image IS the concept). Methods declare an `ImageAction`; one without it is rejected by name rather than silently dispatched as text_to_image and ignoring the uploaded image.
References are a LIST end to end (`ReferenceImageKeys`), exposed as `image_paths` with `image_path` still the first — that is the FLUX.2 shape (up to ten references, conditioned by instruction, no denoise `strength` at all).
Audited against origin/master — the production bug is fixed, but one silent regression path remains. Staying open.
Fixed — the image-upload submission bug:SubmitArtAssetValidator.cs:43-47 now validates with IsValidStorageKey, applied to the source key at :81-89 and the list at :94-103. The remaining ObjectId.TryParse at :115 is for PhotoStack photograph ids and is correct there.
Also landed:ArtAssetSourceType.TextAndImage (ArtAsset.cs:614); ReferenceImageKeys as a list end-to-end (ArtAsset.cs:91-96, RequestArtAssetStageCommand.cs:294-295) surfaced as image_paths with image_path first for back-compat (ArtPipeStageOrchestrator.cs:1036-1043, :1138-1141) and consumed by the worker at sdxl_lightning.py:98-113; ImageAction declared per method (ArtStudioStageMethodOptions.cs:486-496) with rejection-by-name listing allowed keys (SubmitArtAssetCommandHandler.cs:391-397); TextAndImage keeps the concept stage while pure ImageUpload skips it (ArtAssetStagePlans.cs:52-62, test ArtAssetStagePlansTests.cs:94-103).
Remaining — restart silently reverts text+image to text. This is the most consequential of the epic's small gaps, because it's exactly the silent behaviour this ticket set out to eliminate:
Submit threads the source type: SubmitArtAssetCommandHandler.cs:399 — ToStageMethod(option, request.SourceType).
Restart does not: RestartFromStageCommandHandler.cs:390 — ToStageMethod(option), no second argument.
ActionFor (ArtStudioStageMethodOptions.cs:489-492) returns ImageActiononly when sourceType == TextAndImage. So restarting a TextAndImage asset dispatches the text action and quietly ignores the reference images.
Also remaining — the SupportsSourceType guard has narrower reach than it appears.git grep -n "SupportsSourceType" origin/master -- SpikerSoft.Business SpikerSoft.Api returns exactly two hits, both inside the explicit selection loop (:391/:396). The preset-merge, failover and darkroom paths all use the source-type-unaware overload — SubmitArtAssetCommandHandler.cs:583, ArtStudioStageFailoverOptions.cs:173, DarkroomCommands.cs:224 — so a method that can't honour a reference image can still be selected through those routes.
Fix is small: pass the source type at RestartFromStageCommandHandler.cs:390, and decide whether the three unguarded paths should use the source-type-aware overload.
Audited against `origin/master` — **the production bug is fixed, but one silent regression path remains.** Staying open.
**Fixed — the image-upload submission bug:** `SubmitArtAssetValidator.cs:43-47` now validates with `IsValidStorageKey`, applied to the source key at `:81-89` and the list at `:94-103`. The remaining `ObjectId.TryParse` at `:115` is for PhotoStack photograph ids and is correct there.
**Also landed:** `ArtAssetSourceType.TextAndImage` (`ArtAsset.cs:614`); `ReferenceImageKeys` as a list end-to-end (`ArtAsset.cs:91-96`, `RequestArtAssetStageCommand.cs:294-295`) surfaced as `image_paths` with `image_path` first for back-compat (`ArtPipeStageOrchestrator.cs:1036-1043`, `:1138-1141`) and consumed by the worker at `sdxl_lightning.py:98-113`; `ImageAction` declared per method (`ArtStudioStageMethodOptions.cs:486-496`) with rejection-by-name listing allowed keys (`SubmitArtAssetCommandHandler.cs:391-397`); TextAndImage keeps the concept stage while pure ImageUpload skips it (`ArtAssetStagePlans.cs:52-62`, test `ArtAssetStagePlansTests.cs:94-103`).
**Remaining — restart silently reverts text+image to text.** This is the most consequential of the epic's small gaps, because it's exactly the silent behaviour this ticket set out to eliminate:
- Submit threads the source type: `SubmitArtAssetCommandHandler.cs:399` — `ToStageMethod(option, request.SourceType)`.
- Restart does not: `RestartFromStageCommandHandler.cs:390` — `ToStageMethod(option)`, no second argument.
- `ActionFor` (`ArtStudioStageMethodOptions.cs:489-492`) returns `ImageAction` **only** when `sourceType == TextAndImage`. So restarting a TextAndImage asset dispatches the *text* action and quietly ignores the reference images.
**Also remaining — the `SupportsSourceType` guard has narrower reach than it appears.** `git grep -n "SupportsSourceType" origin/master -- SpikerSoft.Business SpikerSoft.Api` returns exactly two hits, both inside the explicit selection loop (`:391`/`:396`). The preset-merge, failover and darkroom paths all use the source-type-unaware overload — `SubmitArtAssetCommandHandler.cs:583`, `ArtStudioStageFailoverOptions.cs:173`, `DarkroomCommands.cs:224` — so a method that can't honour a reference image can still be selected through those routes.
Fix is small: pass the source type at `RestartFromStageCommandHandler.cs:390`, and decide whether the three unguarded paths should use the source-type-aware overload.
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.
Two things.
The bug (production).
SubmitArtAssetValidatorrequiredSourceImageGridFsIdto parse as an ObjectId. That was correct only while artifacts lived in GridFS. Since the MinIO cutoverUploadAsyncreturns{assetId}__{guid:N}, andStorage__UseS3=trueon the backend stack — so the API minted a key and then rejected its own submission with SourceImageGridFsId must be a valid ObjectId. Every image-upload art submission was failing. It stayed invisible because every test fixture used a GridFS-shaped id, so nothing ever fed the validator a key the production store actually produces.The feature.
ArtAssetSourceType.TextAndImage, which KEEPS the concept stage — the references condition a generated concept rather than replacing it (a pure ImageUpload still skips it, since there the image IS the concept). Methods declare anImageAction; one without it is rejected by name rather than silently dispatched as text_to_image and ignoring the uploaded image.References are a LIST end to end (
ReferenceImageKeys), exposed asimage_pathswithimage_pathstill the first — that is the FLUX.2 shape (up to ten references, conditioned by instruction, no denoisestrengthat all).Audited against
origin/master— the production bug is fixed, but one silent regression path remains. Staying open.Fixed — the image-upload submission bug:
SubmitArtAssetValidator.cs:43-47now validates withIsValidStorageKey, applied to the source key at:81-89and the list at:94-103. The remainingObjectId.TryParseat:115is for PhotoStack photograph ids and is correct there.Also landed:
ArtAssetSourceType.TextAndImage(ArtAsset.cs:614);ReferenceImageKeysas a list end-to-end (ArtAsset.cs:91-96,RequestArtAssetStageCommand.cs:294-295) surfaced asimage_pathswithimage_pathfirst for back-compat (ArtPipeStageOrchestrator.cs:1036-1043,:1138-1141) and consumed by the worker atsdxl_lightning.py:98-113;ImageActiondeclared per method (ArtStudioStageMethodOptions.cs:486-496) with rejection-by-name listing allowed keys (SubmitArtAssetCommandHandler.cs:391-397); TextAndImage keeps the concept stage while pure ImageUpload skips it (ArtAssetStagePlans.cs:52-62, testArtAssetStagePlansTests.cs:94-103).Remaining — restart silently reverts text+image to text. This is the most consequential of the epic's small gaps, because it's exactly the silent behaviour this ticket set out to eliminate:
SubmitArtAssetCommandHandler.cs:399—ToStageMethod(option, request.SourceType).RestartFromStageCommandHandler.cs:390—ToStageMethod(option), no second argument.ActionFor(ArtStudioStageMethodOptions.cs:489-492) returnsImageActiononly whensourceType == TextAndImage. So restarting a TextAndImage asset dispatches the text action and quietly ignores the reference images.Also remaining — the
SupportsSourceTypeguard has narrower reach than it appears.git grep -n "SupportsSourceType" origin/master -- SpikerSoft.Business SpikerSoft.Apireturns exactly two hits, both inside the explicit selection loop (:391/:396). The preset-merge, failover and darkroom paths all use the source-type-unaware overload —SubmitArtAssetCommandHandler.cs:583,ArtStudioStageFailoverOptions.cs:173,DarkroomCommands.cs:224— so a method that can't honour a reference image can still be selected through those routes.Fix is small: pass the source type at
RestartFromStageCommandHandler.cs:390, and decide whether the three unguarded paths should use the source-type-aware overload.