Skip to content

Release v0.13.0-alpha: LoRA support, Depth ControlNet, and two broken presets fixed - #84

Merged
MehranMarxian merged 13 commits into
mainfrom
release/v0.13.0
Aug 6, 2026
Merged

Release v0.13.0-alpha: LoRA support, Depth ControlNet, and two broken presets fixed#84
MehranMarxian merged 13 commits into
mainfrom
release/v0.13.0

Conversation

@MehranMarxian

Copy link
Copy Markdown
Owner

Ships optional LoRA support, a Depth ControlNet sketch preset, and fixes for two presets that were quietly broken in v0.12.0.

What's in it

Optional LoRA on eleven presets — every preset that loads a model and a text encoder, across Text to Image, Image to Image and Sketch to Image. Choosing nothing leaves the shipped workflow byte-identical, because ComfyUI's LoraLoader has no "none" entry: the node is spliced into the graph only when a LoRA is actually picked, and the model and text-encode inputs downstream are rewired to it. This is the first time building a workflow changes its shape rather than its values, so each preset declares its own wiring rather than having it inferred. Three genuinely different shapes turned up among the eleven, including Z-Image, where the LoRA must be applied before ModelSamplingAuraFlow rather than at the sampler — rewiring the sampler there would have bypassed the wrapper and changed how the model is sampled, with no error.

A Depth ControlNet sketch preset, holding scene perspective where LineArt and Scribble hold the drawn stroke. Needs one new ControlNet weight; no new node package, since the depth estimator comes from comfyui_controlnet_aux which the sketch presets already required.

Two fixes for presets that failed silently. Sketch to Image fed ControlNet a blank control image for light-on-dark art, so the sketch was ignored with nothing reported. The Flux.2 dev (GGUF) preset that headlined v0.12.0 could not be run at all: its Model dropdown asked core UNETLoader for the file list, and that loader does not enumerate .gguf files, and pressing Generate then failed because the builder required a negative-prompt target on a preset that deliberately has none.

Verification

  • 669 tests, typecheck and build pass.
  • All eleven LoRA-spliced graphs were submitted to a live ComfyUI and confirmed valid, with a real uploaded source image so the six image-input presets exercised their LoadImage node rather than a placeholder.
  • Manually smoke-tested in Photoshop against ComfyUI 0.30.0 on an RTX 4070 Ti: LoRA on and off per tool, per-tool independence, strength changes, the Z-Image wrapper case, and the depth preset.

Notes for review

  • 1105811 (the sketch preprocessor fix and the Scribble preset) predates this branch's release work but had never been released, so it is included in the v0.13.0 notes rather than assumed shipped.
  • The "None" LoRA defect was introduced and fixed within this branch, so it is deliberately absent from the changelog — no released build ever had it.
  • docs/BATCH_GENERATION.md is a design note only. No batch code is in this PR.

🤖 Generated with Claude Code

MehranMarxian and others added 13 commits August 6, 2026 16:34
Both sketch-to-image presets fed ControlNet a pure-black control image for
light-on-dark art and solid filled shapes: LineartStandardPreprocessor and
ScribblePreprocessor each returned max=0, 0% ink on such a source. ControlNet
had no signal, so the preset degraded to plain text-to-image and the user just
saw their sketch ignored -- with no error anywhere. Measured live against
ComfyUI on a 1024px filled silhouette:

  Scribble_PiDiNet_Preprocessor  3.18% ink
  AnyLineArtPreprocessor_aux     1.14% ink
  CannyEdgePreprocessor          0.24% ink
  ScribblePreprocessor           0.00% ink  (dead)
  LineartStandardPreprocessor    0.00% ink  (dead)

The learned edge detectors are polarity-robust; the naive ones assume dark
strokes on white paper. So:

- sketch2img-linecn-basic switches to AnyLineArtPreprocessor_aux, keeping its
  existing LineArt ControlNet. This is a bug fix to a preset that shipped
  "stable" while being broken for that whole class of input.
- New sketch2img-scribble-basic preset on Scribble_PiDiNet_Preprocessor and the
  Scribble ControlNet, for loose gestural strokes where LineArt holds the drawn
  line too tightly. Its ControlNet model was already installed; no new custom
  node package.

Both preprocessors now run at 1024 rather than the hardcoded 512, which was
discarding line detail before ControlNet ever saw it.

Both new node classes are registered in CUSTOM_NODE_PACKAGES, without which
Setup and Workflow Health would stop telling users they need controlnet_aux.
LineartStandardPreprocessor stays mapped there for users' own custom workflows.

Verified end-to-end against ComfyUI: both presets submitted with the reporting
user's seed and prompt now compose to the sketch silhouette.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two defects kept txt2img-flux2-dev-gguf from working at all.

The Model dropdown was empty of .gguf files because the preset shared
DIFFUSION_MODEL_SOURCE, which asks UNETLoader for its file list. Core
UNETLoader does not enumerate .gguf at all -- comfyClient already
documented this for the inventory buckets, but the preset dropdown asks
preset.modelSource directly and never got the same treatment. A preset
built on UnetLoaderGGUF now points at that loader instead, so the model
sitting in models/diffusion_models/ is visible where it belongs.

Generation then failed on the preset that has no negative conditioning
node. The txt2img/img2img/sketch builders injected negativePrompt with
required=true, which throws when a preset has no such target -- and
Flux.2 deliberately has none, being guidance-distilled. buildInpaint
already passed it non-required for exactly this reason (Flux Fill); the
other three now match, so a preset without a negative prompt no longer
crashes on Generate.

Verified against the live ComfyUI: /object_info/UnetLoaderGGUF lists
flux2-dev-Q4_K_M.gguf, and the preset generates end to end.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LineArt and Scribble both hold the drawn stroke. Neither carries depth,
so nothing in the panel could match a generated element to the camera
angle of an existing composite -- the thing artists actually reach for
ControlNet to do when compositing into a scene.

sketch2img-depth-basic is the third variant of a graph shape the
registry already documents as deliberately shared: the node ids match
LineArt and Scribble exactly, and only the preprocessor and the
ControlNet it feeds differ. controlStrength was already an injection
name, so no new injection type was needed.

DepthAnythingV2Preprocessor comes from comfyui_controlnet_aux, which was
already the sole entry in CUSTOM_NODE_PACKAGES for the existing sketch
presets -- the frozen custom-node inventory test passes unchanged, so
this adds no new node dependency. The workflow pins ckpt_name and
resolution explicitly rather than relying on the node pack's defaults,
so a run stays reproducible across pack updates.

The ControlNet weight is new: control_v11f1p_sd15_depth_fp16 from the
same comfyanonymous repo the lineart and scribble weights come from, its
size verified by HEAD request at 722601100 bytes -- byte-identical to
its three siblings already in the registry.

Verified against the live ComfyUI: all ten node classes resolve, and the
source/API equivalence check covers the new pair rather than skipping it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The verified-machine spec said ComfyUI 0.27.1; the running instance
reports 0.30.0. A research agent judging what will run on this hardware
was reasoning from a version two minors stale.

Python 3.10.11 and PyTorch 2.6.0+cu124 were re-checked against
/system_stats and are unchanged, so only the one number moves.

The "ver": "0.27.1" strings in src/workflows/source/*.json are litegraph
provenance stamps recording which version authored each node, not a
statement about this machine, and are deliberately left alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eleven LoRAs sat on the reference machine that the panel could not use.
getLoraNames() has been in comfyClient since before this change and was
never called by anything.

A LoRA cannot be injected the way every other control is. Core
LoraLoader has no "none" entry -- lora_name is a combo of files that
exist -- so a permanently wired loader would force everyone to own and
load one. The node is therefore absent from the shipped workflow and
spliced in only when an artist picks a LoRA, rewiring the sampler's
model input and both text encodes' clip inputs. This is the first time
building a workflow changes its topology rather than its values, so the
wiring is declared per preset rather than inferred: guessing which MODEL
and CLIP edges are the main ones would, when wrong, produce an image
with the LoRA applied to nothing and no error to explain it. Choosing
nothing leaves the graph byte-identical to what ships.

Both text encodes are rewired, not just the positive one. At CFG 1 the
negative contributes nothing, but leaving it on the bare CLIP would put
two different text encoders in one graph -- harmless now and a confusing
bug the first time this preset runs above CFG 1.

The dropdown is labelled, not filtered. ComfyUI serves only a LoRA's
name, size and timestamps, so the panel cannot inspect a file it offers;
the metadata that would answer the question is not served and lies when
read anyway -- illustration.safetensors and meat_v1.safetensors both
declare ss_base_model_version sd_1.5 while their tensor keys are Flux.
Filename hints therefore label entries and sort likely matches first,
but never hide one: a strict filter keyed on any available signal would
hide working LoRAs and still admit broken ones. The hint never claims a
match it cannot support, which is the asymmetry the tests pin down --
over-claiming sends the artist hunting a bug in the wrong place, while a
missed mismatch only costs a generation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Selecting None on the Krea-2 preset failed the whole generation with
HTTP 400 prompt_outputs_failed_validation, while selecting an actual
LoRA worked -- the opposite of what a broken splice would do, which is
what pointed at the UI rather than the builder. Both built workflows
were confirmed valid by submitting them to a live ComfyUI directly.

readSelectValue falls back to an option's label when its value is empty,
because UXP selects do not always report `.value`. Every other dropdown
in the panel is unaffected: their values and labels are the same string.
The LoRA list introduced the first option where they differ, so an
empty-valued "None" came back as the literal text "None", which read as
a real choice -- the strength field appeared under a None selection,
visible in the screenshot that reported this, and the splice then asked
ComfyUI to load a file called None.

The None entry now carries a sentinel that is neither empty nor a
plausible filename, so the label fallback has nothing to do. Selection
handling moves into a pure resolveLoraSelection so the case can be
pinned by tests rather than only in the panel: None, the sentinel, the
bare label, empty and whitespace all resolve to no LoRA, and a strength
of 0 stays 0 rather than being swallowed as falsy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LoRA support shipped on Krea-2 Turbo alone, which left the feature half
present: the control appeared for one entry in the Workflow list and
silently vanished for the rest.

The four remaining text-to-image presets now declare an insertion point.
The wiring was derived from each shipped workflow rather than assumed,
and the three shapes differ in ways that matter:

  - txt2img-basic and flux1-dev-fp8 load from a checkpoint, which is its
    own CLIP source -- MODEL on slot 0, CLIP on slot 1 of one node,
    where a diffusion-model stack has two separate loaders. The
    insertion already carried a slot, so both shapes splice unchanged.
  - z-image-turbo's model consumer is ModelSamplingAuraFlow, not the
    sampler. A LoRA has to apply before the sampling-mode wrapper;
    rewiring the sampler instead would have bypassed the wrapper and
    quietly changed how the model is sampled.
  - flux2-dev-gguf has one clip consumer, not two, having no negative
    conditioning node, and its model consumer is BasicGuider.

Each insertion is now checked by a test that runs per preset: the node
id is unused by that workflow, every declared source and consumer
exists, and nothing downstream is left reading the bare model or CLIP --
the silent failure where a LoRA loads and applies to nothing.

All five spliced graphs were confirmed against the live ComfyUI by
submitting them to /prompt, not only by unit test.

Also bundles the flux2-dev-gguf workflow into WORKFLOW_TEMPLATES. Every
other runnable preset was already imported there; that one was left to a
runtime fetch, which worked in the panel but made the preset unbuildable
under test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Completes the rollout: all eleven presets that load a model and a text
encoder now take an optional LoRA, leaving only inpaint, outpaint and
upscale without one, which is deliberate.

The three sketch presets share a graph shape the registry already treats
as deliberately identical -- only the preprocessor and ControlNet differ
between them, and neither touches the model or CLIP path -- so they
share one insertion built by a helper rather than three copies that
could drift apart.

The panel's LoRA logic now takes a control group instead of reading
fixed element names, because the same row exists on three tool cards and
the alternative was the same six-element signature repeated at every
call site.

One ordering bug was caught and fixed while writing this: the Krea-2
img2img insertion was declared above the node map it reads, which is a
temporal dead zone error at module load that tsc does not report. It now
sits below its map, and the per-preset tests would have caught it on the
next run regardless.

All eleven spliced graphs were confirmed valid by submitting them to the
live ComfyUI, with a real uploaded source image so the six image-input
presets exercised their LoadImage node rather than failing on a
placeholder filename.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Written before any code, because the feature touches result handling and
the preview panel's URL ownership, and a mistake there affects every
tool rather than only this feature.

Two findings changed the shape of it. batch_size is already present on
the empty-latent node of eight shipped workflows, so batching those is
an ordinary value injection with no graph surgery -- only the three
img2img presets need a RepeatLatentBatch splice, because they build
their latent from the captured layer with VAEEncode.

And the generation controller's one-run invariant is not threatened by
this at all, which had been assumed. A batch is one prompt, one prompt
id, one run; the controller gates on which run is current and never
inspects the result's shape, and runPipeline is already generic in it.
What actually breaks is narrower: the single-result type, the preview
panel's two owned URL slots, and what Import means when there are four
images. The last of those is a product question and is left open.

Four decisions are called out for Mehran rather than guessed, and the
work is staged so the retrieval half can be built before they land.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bumped early so the panel footer reads the new version during the
release smoke test rather than after it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Written from git log against the v0.12.0 tag and the code, not from a
task list, per the lesson from v0.8.0: a draft written from tasks
described a fix a later commit had replaced.

Two entries are deliberately absent. The "None" LoRA defect was
introduced and fixed inside this release, so it never reached a user and
is not a fix worth reporting; and the sketch preprocessor fix predates
this session's work on the branch but was never released, so it belongs
here rather than being assumed shipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the alpha checkpoint line, the "New in" list, the tester focus
and the version-stamped zip and plugin names. The one remaining v0.12.0
mention is deliberate: it names the release the Flux.2 defects shipped
in, which is the point of that line.

The tester focus leads with the LoRA paths because they are the release's
new surface, and includes the mismatched-LoRA case explicitly -- a
generation that succeeds with a visibly unchanged image is the expected
behaviour there, and a tester who is not told that will file it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nine version references, plus three claims that stopped being true.

"LoRA browser" was listed as future work; LoRAs ship in this release, so
the page would have been advertising a shipped feature as unbuilt. The
Sketch to Image line named LINECN and one ControlNet model as the only
option, where there are now three presets each needing their own. And a
new boundary is added for the one real limitation of the LoRA work: the
list cannot be filtered by compatibility, because ComfyUI does not report
it, so a mismatched LoRA fails silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@MehranMarxian
MehranMarxian merged commit f3ca2ab into main Aug 6, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant