Files
fn_registry/docs/capabilities/comfyui-skill.md
T
egutierrez 10dbc510b7 feat(ml): LoRAs con prefijo de arquitectura (SD15_/SDXL_/FLUX_) + refs actualizadas
Mueve el indicador de arquitectura del SUFIJO al PREFIJO del nombre de cada
LoRA para que el dropdown del LoraLoader muestre de inmediato que LoRA casa con
que checkpoint (evita el shape mismatch SD1.5 vs SDXL que crashea ComfyUI).

- 20 LoRAs renombradas en disco (15 SD15/SDXL en /mnt/2tb, 5 FLUX en ~/ComfyUI),
  mapa de reversion en ~/ComfyUI/models/loras/_rename_map.json.
- Refs actualizadas en builders gamedev-2d, style presets, pipelines, tests y
  docs/capabilities. Defaults hardcodeados (pixel-art, lcm-lora, etc.) apuntan a
  los nombres con prefijo.
- Ejemplos genericos en docstrings normalizados a la convencion de prefijo.
- comfyui_replicate_civitai_oneshot::_norm ignora el token de arquitectura al
  comparar, robusto al reordenado (sufijo civitai vs prefijo instalado).

Refs a repos HuggingFace (nerijs/pixel-art-xl) y checkpoints (juggernaut_xl_v11)
preservados. Verificado: dropdown LoraLoader con prefijos + generacion real
pixel-art OK + tests comfyui verdes (481 ml + 26 pipelines).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 16:33:03 +02:00

25 KiB

ComfyUI Skill — Recetas versionadas de generación reutilizables

Tag: comfyui-skill. Grupo para tratar una configuración de generación de ComfyUI como una skill: una receta versionada en disco (checkpoint + LoRAs + params + scaffold de prompt + bloques de post-proceso) que se guarda una vez y se reproduce a un workflow concreto cambiando solo el subject. Es la doctrina del issue 0087 aplicada a la generación de imágenes: el registry crece promoviendo configuraciones que funcionan a recetas reutilizables, no reescribiendo el grafo de nodos cada vez.

Construye sobre el grupo comfyui (los builders puros de workflow y el ciclo submit/wait). Una skill no es un workflow: es la receta que compila a uno.

Filtro MCP: mcp__registry__fn_search query="" tag="comfyui-skill".

Qué es una skill

Una receta vive en ~/ComfyUI/skills_library/<slug>/ y la manipulan las funciones de este grupo:

~/ComfyUI/skills_library/
  INDEX.md                      # índice regenerado de todas las skills
  <slug>/
    recipe.json                 # la receta actual
    versions/vN.json            # snapshot inmutable de cada save (N incremental)
    growth_log.jsonl            # bitácora append-only de cada save
    exports/                    # plantillas de workflow exportadas
    samples/                    # imágenes de muestra

Schema de recipe.json (canónico)

{
  "schema_version": 1,
  "slug": "portrait_cinematic_sdxl",
  "version": "1.0.0",
  "title": "Retrato cinematográfico SDXL",
  "base_workflow": "txt2img",
  "checkpoint": "juggernaut_xl_v11.safetensors",
  "loras": [{"name": "add_detail.safetensors", "strength_model": 0.6, "strength_clip": 0.6}],
  "params": {"steps": 30, "cfg": 5.5, "sampler_name": "dpmpp_2m",
             "scheduler": "karras", "width": 832, "height": 1216, "denoise": 1.0},
  "prompt_scaffold": {"positive": "cinematic portrait of {subject}, sharp focus",
                      "negative": "blurry, lowres", "trigger_words": []},
  "blocks": [{"type": "facedetailer", "params": {"denoise": 0.45}},
             {"type": "hires_fix", "params": {"upscale_by": 1.5, "denoise": 0.4}}],
  "score_mean": 0.0, "score_n": 0,
  "provenance": {"source": "manual", "nsfw": false},
  "export_template_path": "exports/portrait_cinematic_sdxl.template.json"
}

base_workflow ∈ {txt2img, flux, sdxl_refiner} (las bases que se generan desde un subject de texto). blocks[].type ∈ {facedetailer, hires_fix}.

Funciones del grupo

ID Firma corta Qué hace Purity
comfyui_build_skill_workflow_py_ml build_skill_workflow(recipe, subject, *, seed=0) -> dict Compila una receta a un workflow en API format: despacha al builder base, sustituye {subject} + trigger_words, encadena LoRAs y aplica los blocks en orden. SkillWorkflowError si la base es desconocida o requiere imagen. pura
comfyui_export_skill_template_py_ml export_skill_template(slug, *, ui_graph=False, port=9222, ...) -> dict Exporta una skill a artefactos cargables como GRAFO: template API en exports/<slug>.template.json y, con ui_graph=True, el UI graph posicionado (vía load_workflow_ui+export_workflow_ui por CDP) en la carpeta nativa ~/ComfyUI/user/default/workflows/<slug>.json (menú Workflows del navegador). Sin navegador, deja el template API y reporta el fallback. impura
comfyui_inject_hires_fix_py_ml comfyui_inject_hires_fix(workflow, *, upscale_by=1.5, denoise=0.4, steps=20, ...) -> dict Inyecta una 2ª pasada hires-fix (UpscaleModelLoader + UltimateSDUpscale) sobre un workflow ya construido, repuntando el SaveImage. Versión encadenable-sobre-dict del builder hermano. pura
comfyui_inject_multi_lora_py_ml comfyui_inject_multi_lora(workflow, loras) -> dict Encadena N LoraLoader sobre un workflow ya construido reusando comfyui_inject_lora por LoRA. Cada lora = {name, strength_model, strength_clip}; respeta el orden (primero cerca del checkpoint, último cerca del KSampler). Apila estilo + detalle en una sola llamada. pura
comfyui_build_ipadapter_workflow_py_ml comfyui_build_ipadapter_workflow(prompt, ref_image, *, base_checkpoint, mode='style'|'faceid', weight=0.8, ...) -> dict txt2img + IPAdapter (custom node cubiq). mode='style' transfiere estilo/composición de una imagen de referencia (IPAdapterUnifiedLoader+IPAdapter); mode='faceid' impone un rostro consistente vía insightface + .bin FaceID + su LoRA (IPAdapterUnifiedLoaderFaceID+IPAdapterFaceID). Repunta el KSampler a la rama IPAdapter. pura
comfyui_save_skill_py_ml comfyui_save_skill(recipe, *, library_dir=None) -> dict Valida el schema mínimo y escribe recipe.json + snapshot versions/vN.json + growth_log + INDEX.md. No muta la receta (round-trip con load). impura
comfyui_load_skill_py_ml comfyui_load_skill(slug, *, version=None, library_dir=None) -> dict Lee recipe.json (actual) o un snapshot versions/vN.json. Slug/versión inexistente → {ok:False} sin lanzar. impura
comfyui_list_skills_py_ml comfyui_list_skills(*, library_dir=None, include_nsfw=False) -> dict Lista las skills con slug/title/base_workflow/version/score/nsfw/n_versions. Oculta NSFW por defecto. impura
ask_llm_vision_py_core ask_llm_vision(prompt, image_path='', *, image_b64='', media_type='', model='claude-opus-4-8', ...) -> dict Pregunta multimodal (imagen + texto) al modelo via API directa de Anthropic (grupo claude-direct). Útil para puntuar el PNG de una skill y alimentar score_mean. impura
comfyui_generate_with_skill_oneshot_py_pipelines generate_with_skill_oneshot(slug, subject, *, server='127.0.0.1:8188', dest=None, seed=0, judge=True, recipe_patch=None, ...) -> dict One-shot del bucle: carga la skill, la compila para el subject, encola, espera, descarga el PNG y (si judge) lo puntúa con el panel comfyui-judge, acumulando el score en la media. recipe_patch prueba una variante en memoria sin guardar. pipeline (impura)
comfyui_update_skill_score_py_ml comfyui_update_skill_score(slug, new_score, *, library_dir=None) -> dict Acumula el score de un juicio en score_mean/score_n por media incremental, reescribiendo recipe.json en sitio (sin snapshot ni growth_log). impura
comfyui_bump_skill_version_py_ml comfyui_bump_skill_version(slug, change, *, score_before, score_after, judge_run_id=None, recipe_patch=None, force=False, ...) -> dict Promueve una versión nueva solo si el score sube (gate objetivo): snapshot versions/vN.json + aplica recipe_patch + sube el semver + línea en growth_log. Gate bloquea si no mejora. impura
comfyui_extract_recipe_from_png_py_ml comfyui_extract_recipe_from_png(png_path, *, slug=None, civitai_meta=None, image_url='', nsfw=False) -> dict Destila un PNG cosechado de Civitai en una receta de skill candidata (score_n=0, provenance.source='civitai'). Compone comfyui_import_workflow_png (workflow API embebido) + comfyui_read_png_metadata (params del KSampler); fallback a la meta de Civitai. Degradación honesta: ok=False sin inventar si no hay ni workflow embebido ni meta utilizable. impura
comfyui_harvest_civitai_skill_oneshot_py_pipelines comfyui_harvest_civitai_skill_oneshot(*, query=None, model_version_id=None, nsfw='None', dest_dir, library_dir='~/ComfyUI/skills_library', ...) -> dict One-shot Civitai → skill candidata: searchfetch (segrega NSFW) → extract_recipesave_skill. Itera los items hasta hallar uno con receta destilable (preferentemente workflow embebido), descartando los PNG sin receta; 2º pase al feed global si filtró por modelo. No baja modelos a ciegas: los ausentes van a missing_models. pipeline (impura)

build_skill_workflow compone los builders del grupo comfyui: comfyui_build_txt2img_workflow, comfyui_build_flux_workflow, comfyui_build_sdxl_refiner_workflow, comfyui_inject_lora, comfyui_build_facedetailer_workflow y comfyui_inject_hires_fix.

Ejemplo canónico end-to-end (receta → workflow → PNG → score)

Guardar una skill, cargarla, compilarla a un workflow para un sujeto, encolarla y puntuar el resultado con visión. Requiere el server ComfyUI en 127.0.0.1:8188 y los modelos de la receta instalados.

import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from ml.comfyui_save_skill import comfyui_save_skill
from ml.comfyui_load_skill import comfyui_load_skill
from ml.comfyui_build_skill_workflow import build_skill_workflow
from ml.comfyui_submit_workflow import comfyui_submit_workflow
from ml.comfyui_wait_result import comfyui_wait_result
from ml.comfyui_fetch_output_image import comfyui_fetch_output_image
from core.ask_llm_vision import ask_llm_vision

# 1. Definir y guardar la skill (una vez).
recipe = {
    "schema_version": 1, "slug": "portrait_cinematic_sdxl", "version": "1.0.0",
    "title": "Retrato cinematográfico SDXL", "base_workflow": "txt2img",
    "checkpoint": "dreamshaper_8.safetensors",
    "loras": [{"name": "add_detail.safetensors", "strength_model": 0.6, "strength_clip": 0.6}],
    "params": {"steps": 28, "cfg": 6.0, "sampler_name": "dpmpp_2m", "scheduler": "karras"},
    "prompt_scaffold": {"positive": "cinematic portrait of {subject}, sharp focus",
                        "negative": "blurry, lowres", "trigger_words": []},
    "blocks": [{"type": "facedetailer", "params": {"denoise": 0.45}}],
    "score_mean": 0.0, "score_n": 0, "provenance": {"source": "manual", "nsfw": False},
}
comfyui_save_skill(recipe)                                 # ~/ComfyUI/skills_library/portrait_cinematic_sdxl/

# 2. Cargar + compilar a un workflow para un sujeto concreto.
recipe = comfyui_load_skill("portrait_cinematic_sdxl")["recipe"]
wf = build_skill_workflow(recipe, "a woman with red hair", seed=42)

# 3. Encolar y esperar el PNG (camino headless del grupo comfyui).
pid = comfyui_submit_workflow(wf)["prompt_id"]
outputs = comfyui_wait_result(pid)["outputs"]
img = comfyui_fetch_output_image(outputs[0]["filename"], dest_dir="/tmp")["path"]

# 4. Puntuar el resultado con visión (alimenta el bucle de scoring de la skill).
verdict = ask_llm_vision(
    "Puntúa de 0 a 10 el realismo de este retrato. Responde solo el número.",
    image_path=img, model="claude-opus-4-8",
)
print(verdict["text"])

El paso "guardar la receta" se hace una sola vez; a partir de ahí cada generación es load → build → submit, cambiando solo el subject y la seed.

Bucle de mejora (skill → genera → juzga → bump)

La doctrina del issue 0087 cerrada en un lazo: una skill no crece inflando la receta a ciegas, crece registrando mejoras medibles. El juez (no el humano) decide qué se promueve.

   ┌─────────────────────────────────────────────────────────────────┐
   │  generate_with_skill_oneshot(slug, subject, judge=True)          │
   │     load → build → submit → wait → fetch → judge → score_mean    │  ← canónica
   └─────────────────────────────────────────────────────────────────┘
            │  score_before = score de la receta vigente
            ▼
   ┌─────────────────────────────────────────────────────────────────┐
   │  generate_with_skill_oneshot(..., recipe_patch={params:{...}})   │  ← variante (no guarda score)
   │     misma seed, un cambio plausible → judge.score = score_after  │
   └─────────────────────────────────────────────────────────────────┘
            │
            ▼  GATE objetivo
   comfyui_bump_skill_version(slug, change, score_before, score_after, judge_run_id=...)
            │
      score_after > score_before ?
        ├── sí → promueve: versions/vN.json (snapshot) + recipe_patch + semver↑ + growth_log
        └── no → {ok:False} — NO se promueve (la variante se descarta)

Pasos concretos:

  1. Genera la canónica con judge=True. El panel comfyui-judge emite un score y el pipeline lo acumula en score_mean/score_n de la skill (vía comfyui_update_skill_score). Ese score es el score_before.
  2. Genera una variante con recipe_patch (p.ej. {"params": {"steps": 32}}) y la misma seed. El patch se aplica en memoria, NO se guarda, y su score NO contamina la media. Su judge.score es el score_after, y su judge_run_id es la evidencia.
  3. Promueve con el gate: comfyui_bump_skill_version aplica el patch a recipe.json, sube el semver y deja una línea en growth_log.jsonl solo si score_after > score_before. Si no mejora, devuelve {ok:False} y la receta se queda como estaba. El gate es objetivo: lo decide el número del juez, no quien lanza la generación.

Así versions/ y growth_log reflejan versiones de receta con mejora demostrada, mientras score_mean es la telemetría de calidad media de la versión vigente.

Skills como grafos en el navegador

Una skill no vive solo como receta JSON: se exporta a un grafo de ComfyUI cargable como tal en el navegador. comfyui_export_skill_template cierra ese hueco (receta → grafo):

from ml.comfyui_export_skill_template import export_skill_template

# Headless (sin navegador): congela el template API junto a la skill.
export_skill_template("portrait_cinematic_sd15")
# -> exports/portrait_cinematic_sd15.template.json  (API format, node-template reproducible)

# Con navegador (pestaña ComfyUI abierta en CDP 9222): además el grafo visual posicionado.
out = export_skill_template("portrait_cinematic_sd15", ui_graph=True, port=9222)
# -> ~/ComfyUI/user/default/workflows/portrait_cinematic_sd15.json  (aparece en el menú Workflows)

Dos formatos, dos usos:

  • API format (exports/<slug>.template.json) — el dict {node_id:{class_type,inputs}}. Se carga con comfyui_load_workflow_ui (app.loadApiJson, litegraph lo auto-posiciona) o va directo a comfyui_submit_workflow. Es el node-template versionable de la skill.
  • UI graph (~/ComfyUI/user/default/workflows/<slug>.json + copia en exports/<slug>.ui.json) — nodes/links/pos (app.graph.serialize()). La carpeta nativa de la UI solo acepta este formato; por eso solo se escribe con ui_graph=True (se genera vía CDP cargando el API en la UI y serializando el grafo posicionado). Es el que se abre como grafo visual desde el menú Workflows.

Fotos ↔ grafo. Cada PNG de ComfyUI lleva su workflow embebido (chunk prompt, API format). comfyui_import_workflow_png lo recupera, de modo que toda muestra de una skill queda asociada a su grafo reproducible 1:1 (ver INDEX.md de la librería: samples/<base>.png + samples/<base>.graph.json).

No destructivo en el navegador: ui_graph=True reemplaza el grafo in-memory de la pestaña. Si hay trabajo sin guardar (título con *), respalda antes con comfyui_export_workflow_ui(api_format=True, save_path=...) y restáuralo después con comfyui_load_workflow_ui.

Cosecha Civitai → skill candidata

El registry crece también captando recetas que ya existen en internet, no solo escribiéndolas a mano: doctrina del issue 0087 aplicada a la captación de assets. Cada imagen publicada en Civitai suele llevar su workflow de ComfyUI embebido en el PNG (chunk prompt, API format); cosecharla destila la receta entera (checkpoint + LoRAs + params + prompt) en una skill candidata lista para juzgar.

import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions", "pipelines"))
from comfyui_harvest_civitai_skill_oneshot import comfyui_harvest_civitai_skill_oneshot

res = comfyui_harvest_civitai_skill_oneshot(
    query="cinematic portrait", nsfw="None",
    dest_dir=os.path.expanduser("~/ComfyUI/civitai_harvest"),
)
print(res["ok"], res["slug"], "workflow_embebido=", res["has_workflow"])
print("guardada en:", res["skill_path"])           # ~/ComfyUI/skills_library/<slug>/recipe.json
print("modelos ausentes:", res["missing_models"])  # checkpoints/LoRAs a bajar (NO bajados)

Dos piezas, una composición:

  • comfyui_extract_recipe_from_png (paso puro de destilación, impura solo por leer disco) — toma un PNG ya descargado y produce la receta candidata. Compone comfyui_import_workflow_png (workflow embebido) + comfyui_read_png_metadata (params del KSampler), con fallback a la meta de generación de Civitai y, para samplers no-KSampler (flux/SamplerCustomAdvanced), heurística sobre los nodos CLIPTextEncode. Degradación honesta: si no hay ni workflow embebido ni meta utilizable devuelve ok=False sin inventar la receta.
  • comfyui_harvest_civitai_skill_oneshot (pipeline) — encadena search_civitai_images → fetch_civitai_image → extract_recipe_from_png → save_skill en una sola llamada. Itera los resultados del search hasta encontrar el primero con receta destilable (descartando los PNG sin workflow), y si filtró por un modelo concreto y ninguno trae grafo, hace un 2º pase al feed global "Most Reactions" (donde abundan los workflows ComfyUI de usuarios flux).

Notas de uso:

  • La skill nace CANDIDATA (score_n=0, provenance.source='civitai'): no está validada. El prompt cosechado es concreto, no un scaffold con {subject} — sustitúyelo a mano si quieres reutilizar la skill para otros sujetos. La validación la da el bucle generate_with_skill_oneshot (juzga) + comfyui_bump_skill_version (promueve si mejora).
  • No baja modelos a ciegas: si la receta referencia un checkpoint o LoRA que no está en <comfyui_dir>/models/, lo lista en missing_models y no descarga nada. Bajarlos (comfyui_search_civitai_models + comfyui_download_model) es una decisión aparte del caller.
  • NSFW segregado: el PNG se descarga a <dest_dir>/nsfw/ si el item es NSFW (permitido pero siempre separado). El dest_dir vive fuera del repo (~/ComfyUI/) y se trata como datos: no se commitea ni se indexa.
  • El token Civitai es secreto: viene de pass civitai/api-token, nunca hardcodeado.

Mezclar capacidades (mixer)

Una skill fija una receta. El mixer resuelve el otro eje: combinar a la carta todas las capacidades de generación sobre un mismo workflow base y activar/desactivar cada una para iterar. Misma doctrina del issue 0087 (componer piezas probadas, no reescribir el grafo), pero aplicada a mezclar capacidades en vez de a guardar una receta.

Dos funciones:

ID firma corta qué hace
comfyui_compose_capabilities_py_ml compose_capabilities(base, *, loras, controlnet, ipadapter, hires, facedetailer) -> dict PURA. Aplica EN ORDEN las capacidades activadas (cada arg None = desactivada) sobre un dict base, componiendo los inyectores/builders encadenables. Reconecta MODEL/CLIP/positive/IMAGE. Sin ninguna = base intacto.
comfyui_generate_mixed_oneshot_py_pipelines generate_mixed_oneshot(base, subject, *, capabilities, server, judge, ...) -> dict Pipeline. base (skill slug / 'txt2img' / dict) → compose → submit → wait → fetch → (si judge) juzga. Devuelve {ok, prompt_id, image_path, capabilities_active, judge, error}.

El mixer se apoya en los inyectores encadenables-sobre-dict (cada uno la versión componible de su builder-desde-cero hermano):

Capacidad Inyector Reconecta
LoRAs (N) comfyui_inject_multi_lora_py_ml cadena MODEL/CLIP tras el checkpoint
ControlNet comfyui_inject_controlnet_py_ml KSampler.positiveControlNetApply
IPAdapter (style/faceid) comfyui_inject_ipadapter_py_ml KSampler.model ← IPAdapter (tras las LoRAs)
hires/upscale comfyui_inject_hires_fix_py_ml UltimateSDUpscale tras el VAEDecode
FaceDetailer comfyui_build_facedetailer_workflow_py_ml regenera caras del VAEDecode

Orden fijo: loras → controlnet → ipadapter → facedetailer → hires. El IPAdapter se aplica sobre el MODEL ya modificado por los LoRAs (orden correcto). Tras FaceDetailer el mixer deja un único SaveImage (el del detailer).

Ejemplo canónico (≥3 capacidades, juzgado)

import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from pipelines.comfyui_generate_mixed_oneshot import comfyui_generate_mixed_oneshot

# txt2img dreamshaper + 2 LoRAs + FaceDetailer (3 capacidades). Activar/desactivar = cambiar args.
res = comfyui_generate_mixed_oneshot(
    "txt2img",
    "a heroic knight portrait, 3d render style, dramatic lighting, detailed face",
    checkpoint="dreamshaper_8.safetensors",
    capabilities={
        "loras": [
            {"name": "SD15_3d_render_redmond.safetensors", "strength_model": 0.9},
            {"name": "SD15_detail_tweaker.safetensors", "strength_model": 0.5, "strength_clip": 0.5},
        ],
        "facedetailer": {"denoise": 0.45},
        # "ipadapter": {"ref_image": "face.png", "mode": "faceid"},  # se activa con solo añadirla
        # "hires": {"upscale_by": 1.5},
    },
    dest="/tmp/comfy_mixed", seed=42, judge=True,
)
print(res["ok"], res["prompt_id"], res["capabilities_active"], res["judge"])

Límite conocido (8GB / piezas actuales)

  • hires + facedetailer no encadenan: ambos toman su imagen del VAEDecode del render base, así que combinarlos deja a uno sin efecto sobre la salida final (con los dos activos, hires "gana" y facedetailer queda sin consumidor). Usa uno U otro por workflow. El resto de combinaciones (LoRAs + ControlNet + IPAdapter + uno de los dos post-procesos) encadenan limpio.
  • VRAM: en 8GB lowvram con SD1.5 entran ~2-3 capacidades modestas (p.ej. 2 LoRAs + FaceDetailer a 512px). Apilar IPAdapter FaceID + ControlNet + hires + facedetailer a la vez puede dar OOM — baja resolución o reduce capacidades. mixer no valida VRAM; el OOM aflora en wait.
  • Incompatibilidad explícita, no silenciosa: ControlNet sin control_image o IPAdapter sin ref_image lanzan ValueError del inyector (no petan a medias). Las imágenes de control/referencia deben estar en el input/ del servidor antes de encolar.

Fronteras

  • No genera ni descarga modelos: una skill referencia checkpoints/LoRAs por nombre; deben estar ya instalados en ComfyUI (comfyui_download_model, otro flujo). build_skill_workflow es puro y no valida contra el servidor — usa comfyui_validate_workflow antes de encolar si dudas.
  • base_workflow solo de texto: txt2img, flux, sdxl_refiner. Las bases que parten de una imagen (img2img, inpaint, controlnet) lanzan SkillWorkflowError; para esas, monta el workflow con los builders del grupo comfyui directamente.
  • blocks soportados: facedetailer y hires_fix. Otros post-procesos (IPAdapter, multi-ControlNet) se añaden creando su función-inyector hermana y registrándola en el dispatcher de build_skill_workflow.
  • El juicio (comfyui-judge) vive en su grupo: este grupo lo consume (vía generate_with_skill_oneshot con judge=True), pero el panel multi-juez —estético + CLIP + LLM-vision— se documenta en comfyui-judge. Aquí solo se acumula su score en score_mean (comfyui_update_skill_score) y se usa como gate del bump.
  • El bump solo sube versiones, no genera ni juzga: comfyui_bump_skill_version aplica el patch y registra la mejora; generar la imagen y puntuarla es trabajo del pipeline + el panel-juez. Una variante que no supera a la vigente se descarta sola (el gate la rechaza).
  • La librería es metadata local: vive bajo ~/ComfyUI/skills_library (no toca el venv ni los modelos en disco). No tiene repo propio ni se indexa — es estado vivo, como un operations.db.
  • Las funciones impuras del grupo (save/load/list, ask_llm_vision) no llevan unit tests por diseño (I/O de disco / red); build_skill_workflow e inject_hires_fix son puras y sí tienen tests de estructura offline (python/functions/ml/tests/test_comfyui_build_skill_workflow.py, test_comfyui_inject_hires_fix.py).