Connections and outputs
Slot identity is:
{ node: NodeId, out: number }Output name is convenience. Output index is identity. Unnamed, duplicated, and renamed outputs all round-trip because the compiler never keys on the name.
Handles
Generated specs expose named handles:
ckpt.MODEL // first MODEL-typed output
ckpt.CLIP
ckpt.VAEPositional forms always work:
ks.slots[0]
ks.out(0)Custom nodes with duplicate output names, empty names, or "weird" /object_info still compile if you use index.
Type mismatch
Wiring a MODEL into a CLIP input fails in the type checker when both specs are generated. At compile time the same mistake is E_TYPE_MISMATCH with expected / got / nodeId / input.
{ "error": { "code": "E_TYPE_MISMATCH", "nodeId": "n5", "input": "clip", "expected": "CLIP", "got": "MODEL" } }unsafe
import { unsafe } from "@stepupgaming/comfy-workflows";
g.add(someSpec, { clip: unsafe(ckpt.MODEL) });That is a deliberate type-system bypass. It does not disable validation of everything else. Escape hatches.
Graph outputs vs node outputs
g.output(handle, { name }) declares what the runtime should return as artifacts. Node output handles are how you wire the graph. Do not confuse the two.
List-valued inputs
Some nodes take arrays of connections. Pass an array of handles. IR stores SlotRef[].