CMKFamilyBranchGateSDXL
The Node That Says 'Not My Family, Don't Bother'
- MODEL
- PROCESS
- IMAGE
- LOG
- VISUAL
- diagnostic
- MODEL
- IMAGE
- LOG
- VISUAL
- diagnostic
If you run a CMK workflow that can generate with SDXL, with Z-Image Turbo, or with the hybrid path, you'll see these three nodes parked at the edge of each branch. They look like nothing - a couple of wires in, a couple out. The whole point is that when their family isn't the active one, they cost nothing at all.
What it is
A lazy branch gate. The CMK 2.5 architecture keeps SDXL, Z-Image Turbo and HYBRID as technically separate generation paths that hand off to a family-neutral PostProcess zone, and the gates are what stop the inactive paths from being evaluated while the active one runs.
Why that needs a node: ComfyUI expands a subgraph while resolving one of its outputs. If both family branches are wired into one merge, the engine can start evaluating the branch you didn't ask for - loading a checkpoint, running a refiner, burning VRAM and time on an image nobody sees. The gate breaks that: PROCESS is a cheap signal to read, and everything expensive behind it stays lazy until the signal proves the branch is live.
How it works
PROCESS is the family signal. If it's missing, the gate asks for it and that's all. If it arrives with family_active false, the gate returns an ExecutionBlocker for all three outputs and stops - nothing upstream runs.
When the branch is active, the gate validates rather than just passing through: it reads model_family off the process pipe and raises if it isn't sdxl. Feed an SDXL gate a Z-Image process and you get a clear error instead of a mixed-family image that looks subtly wrong. Then it checks nothing arrived empty and forwards.
There's a second, subtler piece. ComfyUI's lazy resolution has a known awkwardness with subgraph boundaries: if you ask for several lazy outputs from the same boundary at once, the outer output node can end up blocked after the inner task completes. So the gate deliberately resolves MODEL, IMAGE and LOG one at a time instead of in a batch. Timing is also instrumented for the log.
Inputs are MODEL (CMK_MODEL_PIPE), PROCESS (CMK_PROCESS_SDXL), IMAGE and LOG (CMK_LOG_PIPE) - all optional, all lazy. Outputs are MODEL, IMAGE, LOG, same types, so the gate can be dropped inline without changing anything about the wiring around it.
When you'd actually touch one
Mostly you won't. It's already placed in the pack's reference workflows and you interact with it by ignoring it. It matters in three cases.
You're building a flow with your own parallel family branches and want the same "only the active one runs" guarantee - copy the pattern, not the node.
You're debugging a workflow where an inactive branch is loading models it shouldn't. Check whether the gate is actually in the path, and check its PROCESS input is connected to the same process pipe the active branch uses. A disconnected or stale PROCESS is the usual cause.
You're reading the log and want to know which branch ran. The gate's timing line plus the family name in its validation error tell you immediately.
Two siblings complete the set: CMKFamilyBranchGateZImage is the same node for CMK_PROCESS_Z_IMAGE and the z_image_turbo family, and CMKFamilyBranchGateSDXLSampled is the version that also gates the proprietary SAMPLED latent handoff used between SDXL sampler, InstantID and refiner.
Install
Manager: search CMK Flow, install the CMKFlow entry, restart. Manual, from your ComfyUI root:
git clone https://github.com/CMKFlow/cmk_nodes.git custom_nodes/cmk_nodes
python3 custom_nodes/cmk_nodes/scripts/install_cmk_requirements.py
Nothing here runs inference, so no model downloads are needed for the gate itself. The pack as a whole still needs its weights - run the resource check when you hit a node that does:
python3 custom_nodes/cmk_nodes/scripts/check_cmk_resources.py --models-root "/path/to/ComfyUI-Shared"
One pack-level requirement is worth repeating here because it bites hardest on these nodes: CMK expects ComfyUI's Nodes 2.0 / Vue frontend setting to be on, and warns at startup when it isn't. Lazy branch switching is exactly the kind of behaviour that degrades least gracefully on the old canvas.
The gotcha
ExecutionBlocker is not None. A blocked branch doesn't hand downstream a null - it stops the graph from resolving that output at all, which is why a merge node on the far side has to be written to ask for branches in a specific order and pick the live one. If you write your own merge, ask for the branch you think is active first. CMK's own PostProcess Boundary Combined does precisely that, probing ZIT before SDXL. Getting that order wrong produces a workflow that hangs rather than one that errors, and hangs are harder to diagnose.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| MODELopt | CMK_MODEL_PIPE | — | |
| PROCESSopt | CMK_PROCESS_SDXL | — | |
| IMAGEopt | IMAGE | — | |
| LOGopt | CMK_LOG_PIPE | — | |
| VISUALopt | CMK_VISUAL_PIPE | — | |
| diagnosticopt | CMK_DIAGNOSTIC | — |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| MODEL | CMK_MODEL_PIPE | — |
| IMAGE | IMAGE | — |
| LOG | CMK_LOG_PIPE | — |
| VISUAL | CMK_VISUAL_PIPE | — |
| diagnostic | CMK_DIAGNOSTIC | — |