Files
olivv-cs-ComfyUI-FunPack/app/shell/reference_wiring.js
T
digital-garbage febb67c1ab fix: reference-wiring silent drops/overwrites, found by adversarial review
wireReferences() (new, extracted from boot.js's queueInputs) now reports an
unwired count instead of silently losing a reference when: the pipeline has
fewer reference slots than the scene has references, two references' roles
resolve to the same slot or downstream destination, or a reference's slot
was already claimed by the scene's source-image assignment. boot.js toasts
whenever unwired > 0, worded to cover both the capacity and collision cases.

None of these are reachable through the one shipped preset (H3's R2V, one
role per slot) -- all are latent contract violations a hand-edited or future
preset could trigger. 10 new tests in reference_wiring.test.js.
2026-09-11 13:13:49 +03:00

50 lines
2.5 KiB
JavaScript

// Turns a scene's picked reference-media ids into slot overrides for whatever
// pipeline is current -- pulled out of boot.js so it can be tested without
// booting the whole app (boot.js runs start() on import).
/**
* wireReferences(mediaIds, slots, reservedSlotIds) -> { overrides, unwired }
*
* `overrides` is keyed by slot id, merge these into the raw input overrides
* sent with a run. `unwired` counts references that did not make it into the
* run -- the pipeline has fewer "assets.reference_N" roles than the scene has
* references (e.g. added under a bigger preset, then the preset was swapped
* for a smaller one), two references' roles resolve to the same destination,
* or a reference's slot is one the caller already assigned to something else
* (`reservedSlotIds` -- e.g. a slot that also carries the scene's
* `assets.source_image` role). Any of these silently overwriting or dropping
* a reference would look like it was used; the caller is the one place that
* knows this happened and must say so.
*/
export function wireReferences(mediaIds, slots, reservedSlotIds) {
const pool = slots || [];
const overrides = {};
const claimed = new Set(reservedSlotIds || []);
let unwired = 0;
mediaIds.forEach((mediaId, i) => {
const at = `assets.reference_${i + 1}`;
const slot = pool.find((s) => (s.roles || []).some((r) => r.at === at));
if (!slot) { unwired += 1; return; }
// The wire's target is the role's own data (`wireTo`), never a slot id
// this file would otherwise have to know: the preset declares where its
// own reference lands, the same way it declares everything else.
const role = slot.roles.find((r) => r.at === at);
// A destination -- the slot's own media_id input, or wherever `wireTo`
// points -- can hold one value. Two references claiming the SAME slot
// (a preset with two reference roles on one slot) or the SAME wireTo
// target (two different slots both wired into one downstream input) are
// the same failure either way: the second silently overwrites the first
// unless it is counted unwired instead.
const dest = role && role.wireTo ? `${role.wireTo.slot}.${role.wireTo.input}` : slot.id;
if (claimed.has(slot.id) || claimed.has(dest)) { unwired += 1; return; }
claimed.add(slot.id);
claimed.add(dest);
overrides[slot.id] = { ...overrides[slot.id], media_id: mediaId };
if (role && role.wireTo) {
overrides[role.wireTo.slot] = { ...overrides[role.wireTo.slot],
[role.wireTo.input]: [slot.id, 0] };
}
});
return { overrides, unwired };
}