tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

catálogo objetivo P5: tandas a archivo, y el triaje que cierra el lazo

tandas/README.md declara los 55 ficheros ARCHIVO de campañas pasadas (no se
borran: su git log es la crónica de cada frente) y documenta de dónde sale el
trabajo ahora. import-batch.sh -f sigue vivo como vía de escape, no como camino.

Lo que faltaba para que la cola fuera derivada de verdad era el paso humano, y
estaba sin forma: 137 candidatos clasificables sólo en la cabeza de alguien.
triaje.py les da forma durable — docs/state/frontera-triaje.toml, 170 candidatos
unificados de los 3 perfiles. Cada veredicto cierra un lazo distinto:

  hueco    → raíz en targets.toml ⇒ nace un nodo `wanted`, el sembrador lo
             siembra y el drenaje lo ordena (= trabajo nuevo para el worker)
  opcional → registrado, deja de reaparecer como pregunta
  nix-ismo → vuelve a seed-graph.py como descarte ⇒ el sembrador APRENDE de su
             propio triaje y cada corrida sale más limpia

Sincronizar NUNCA pisa un juicio humano: agrega los nuevos y marca ausente=true
los que dejaron de aparecer (progreso, o cambio de nixpkgs — se ve en el diff).
`sugerencia` propone con evidencia mecánica pero `veredicto` arranca en
`pendiente`: la heurística abarata el juicio, no lo cierra.

Lazo verificado punta a punta: python3-3.14.6-env marcado nix-ismo → --aplicar
lo escribe al descarte → seed-graph.normalizar() lo devuelve clasificado y deja
de contarlo como hueco. Probado y revertido; los 170 siguen pendientes.

Cadena cerrada y corriendo: targets.toml → build-state.json → drenaje.json →
worker, regenerada por el latido cada 30min. El único paso que pide una persona
es el triaje.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

ec28a9b394a0e27da44ea26fc0eff6b15acf57e6 · by sergio · (3 months ago) · parent 25e387833 · browse the tree at this commit

Semantic delta · 7

scripts/triaje.py

6 files · +1463 −0

diff --git a/docs/plan-catalogo-objetivo.md b/docs/plan-catalogo-objetivo.md
index a62c1188..e396eaac 100644
--- a/docs/plan-catalogo-objetivo.md
+++ b/docs/plan-catalogo-objetivo.md
@@ -241,6 +241,36 @@ alimenta de ahí en vez de listas escritas a mano.
 **Eso es lo que de verdad mata la sensación de caos: la cola del worker deja de ser autoría y pasa a
 ser derivada.**
 
+#### P5 — RESULTADO (2026-07-22)
+
+`tandas/README.md` declara el directorio **archivo de campañas pasadas** (55 ficheros, no se borran:
+su `git log` es la crónica de cada frente) y documenta de dónde sale el trabajo ahora. La vía de
+escape (`import-batch.sh -f`) sigue viva; deja de ser el camino principal.
+
+Lo que faltaba para que la cola fuera de verdad derivada era el paso humano, y estaba sin forma:
+137 candidatos de frontera clasificables sólo en la cabeza de alguien. `scripts/triaje.py` le da
+forma durable — `docs/state/frontera-triaje.toml`, **170 candidatos** unificados de los tres
+perfiles. Cada veredicto CIERRA un lazo distinto, que es lo que lo hace algo más que una lista:
+
+| veredicto | destino | efecto |
+|---|---|---|
+| `hueco` | raíz en `targets.toml` | nace un nodo `wanted` ⇒ el sembrador lo siembra y el drenaje lo ordena |
+| `opcional` | queda registrado | deja de reaparecer como pregunta |
+| `nix-ismo` | `nixismos-triaje.txt` | vuelve a `seed-graph.py` como descarte ⇒ **el sembrador aprende de su propio triaje** |
+
+Sincronizar **nunca pisa un juicio humano**: agrega candidatos nuevos y marca `ausente = true` los
+que dejaron de aparecer (que puede ser progreso —se escribió la receta— o un cambio de nixpkgs, y
+conviene verlo en el `git diff`). El campo `sugerencia` propone con evidencia mecánica (cuántas
+recetas lo piden, si el nombre tiene forma de env/wrapper de nix) pero `veredicto` arranca en
+`pendiente`: la heurística **abarata** el juicio, no lo cierra.
+
+Lazo verificado de punta a punta: marcar `python3-3.14.6-env` como `nix-ismo` → `--aplicar` lo
+escribe al descarte → `seed-graph.normalizar()` lo devuelve clasificado y deja de contarlo como
+hueco. (Probado y revertido; los 170 siguen en `pendiente` — clasificar es decisión del humano.)
+
+**Estado de la cadena**: cerrada y corriendo. `targets.toml` → `build-state.json` → `drenaje.json` →
+worker, regenerada por el latido cada 30 min. El único paso que requiere una persona es el triaje.
+
 ## 5. Secuencia (commits chicos, valor en el primero)
 
 | paso | qué | riesgo | esfuerzo |
diff --git a/docs/state/frontera-triaje.toml b/docs/state/frontera-triaje.toml
new file mode 100644
index 00000000..76e20306
--- /dev/null
+++ b/docs/state/frontera-triaje.toml
@@ -0,0 +1,1177 @@
+# frontera-triaje.toml — VEREDICTOS sobre los candidatos a hueco de la frontera.
+#
+# Lo genera y sincroniza `scripts/triaje.py` desde `seed-frontera.json`; los veredictos los
+# pone un HUMANO editando este fichero. Sincronizar NO pisa lo ya decidido: sólo agrega
+# candidatos nuevos y marca `[ausente]` los que dejaron de aparecer.
+#
+# veredicto ∈ pendiente | hueco | opcional | nix-ismo
+#   hueco    — falta de verdad. Ponelo como raíz en docs/state/targets.toml ⇒ nace un nodo
+#              `wanted` en el grafo y entra al drenaje. (`triaje.py --aplicar` te da la línea.)
+#   opcional — dep que nixpkgs habilita y nosotros no queremos. Queda registrada para que no
+#              vuelva a preguntarse.
+#   nix-ismo — andamiaje de nixpkgs sin contraparte acá. `--aplicar` lo manda al descarte del
+#              sembrador ⇒ la próxima corrida sale más limpia.
+#
+# `sugerencia` es una heurística con evidencia, NO un veredicto: abarata el juicio, no lo cierra.
+schema = "hammer-frontera-triaje/1"
+
+[candidato."SPIRV-LLVM-Translator"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mesa); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."acl"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["cli", "escritorio-kde"]
+piden = ["kio", "rsync"]
+
+[candidato."adns"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (gnupg); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["gnupg"]
+
+[candidato."appstream-qt"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["frameworkintegration", "plasma-workspace"]
+
+[candidato."aspell"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (sonnet); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["sonnet"]
+
+[candidato."at-spi2-core"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."attr"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["kfilemetadata", "kio"]
+
+[candidato."audit"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["cli", "escritorio-kde", "escritorio-mirada"]
+piden = ["dbus", "linux-pam"]
+
+[candidato."aurorae"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kwin); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kwin"]
+
+[candidato."autoconf"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (util-linux); mirá si es una feature opcional"
+perfiles = ["cli", "escritorio-kde"]
+piden = ["util-linux"]
+
+[candidato."autoconf-archive"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (python3); mirá si es una feature opcional"
+perfiles = ["cli", "escritorio-kde", "escritorio-mirada"]
+piden = ["python3"]
+
+[candidato."automake"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (util-linux); mirá si es una feature opcional"
+perfiles = ["cli", "escritorio-kde"]
+piden = ["util-linux"]
+
+[candidato."avahi"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kdnssd); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kdnssd"]
+
+[candidato."baloo"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (plasma-workspace); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["plasma-workspace"]
+
+[candidato."bluez-headers"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (python3); mirá si es una feature opcional"
+perfiles = ["cli", "escritorio-kde", "escritorio-mirada"]
+piden = ["python3"]
+
+[candidato."check"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (parted); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["parted"]
+
+[candidato."clang"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mesa); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."cmocka"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (shadow); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["shadow"]
+
+[candidato."cups"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."db"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["cli", "escritorio-kde", "escritorio-mirada"]
+piden = ["iproute2", "linux-pam"]
+
+[candidato."dejagnu"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (libffi); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["libffi"]
+
+[candidato."directx-headers"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mesa); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."docbook5"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (linux-pam); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["linux-pam"]
+
+[candidato."docutils"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (libdrm); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["libdrm"]
+
+[candidato."double-conversion"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."doxygen"]
+veredicto = "pendiente"
+porque = "lo piden 3 recetas"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["libqalculate", "libxkbcommon", "wayland"]
+
+[candidato."ebook-tools"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kfilemetadata); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kfilemetadata"]
+
+[candidato."ed"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (lsof); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["lsof"]
+
+[candidato."editorconfig-core-c"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (ktexteditor); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["ktexteditor"]
+
+[candidato."flatpak"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (plasma-workspace); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["plasma-workspace"]
+
+[candidato."flite"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtspeech); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtspeech"]
+
+[candidato."fuse"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (e2fsprogs); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["e2fsprogs"]
+
+[candidato."gdbm"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (python3); mirá si es una feature opcional"
+perfiles = ["cli", "escritorio-kde", "escritorio-mirada"]
+piden = ["python3"]
+
+[candidato."git-minimal"]
+veredicto = "pendiente"
+sugerencia = "nix-ismo?"
+porque = "el nombre tiene forma de andamiaje de nixpkgs (env/minimal/wrapper)"
+perfiles = ["cli"]
+piden = ["starship"]
+
+[candidato."glibc"]
+veredicto = "pendiente"
+sugerencia = "nix-ismo?"
+porque = "el nombre tiene forma de andamiaje de nixpkgs (env/minimal/wrapper)"
+perfiles = ["cli"]
+piden = ["go"]
+
+[candidato."glslang"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mesa); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."gmp-with-cxx"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mpfr); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["mpfr"]
+
+[candidato."gnutls"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (gnupg); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["gnupg"]
+
+[candidato."gpgmepp"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kwallet); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kwallet"]
+
+[candidato."gpsd"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (plasma-workspace); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["plasma-workspace"]
+
+[candidato."graphviz"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (wayland); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["wayland"]
+
+[candidato."groff"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (sudo); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["sudo"]
+
+[candidato."gst-libav"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtmultimedia); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtmultimedia"]
+
+[candidato."gst-plugins-bad"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtmultimedia); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtmultimedia"]
+
+[candidato."gst-plugins-base"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["libcanberra", "qtmultimedia"]
+
+[candidato."gst-plugins-good"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtmultimedia); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtmultimedia"]
+
+[candidato."gstreamer"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["libcanberra", "qtmultimedia"]
+
+[candidato."gtk+3"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."help2man"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (flex); mirá si es una feature opcional"
+perfiles = ["cli", "escritorio-kde", "escritorio-mirada"]
+piden = ["flex"]
+
+[candidato."hunspell"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (sonnet); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["sonnet"]
+
+[candidato."iptables"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (iproute2); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["iproute2"]
+
+[candidato."jasper"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtsvg); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtsvg"]
+
+[candidato."jemalloc"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (fd); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["fd"]
+
+[candidato."kdegraphics-mobipocket"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kfilemetadata); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kfilemetadata"]
+
+[candidato."kdoctools"]
+veredicto = "pendiente"
+sugerencia = "hueco?"
+porque = "lo piden 6 recetas de la imagen — difícil que sea accesorio"
+perfiles = ["escritorio-kde"]
+piden = ["kded", "kio", "kpackage", "kservice", "kwallet", "kwin"]
+
+[candidato."kio-extras"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (plasma-workspace); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["plasma-workspace"]
+
+[candidato."kmod"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (pciutils); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["pciutils"]
+
+[candidato."krb5"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (curl); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["curl"]
+
+[candidato."kuserfeedback"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (plasma-workspace); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["plasma-workspace"]
+
+[candidato."libapparmor"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (dbus); mirá si es una feature opcional"
+perfiles = ["cli", "escritorio-kde"]
+piden = ["dbus"]
+
+[candidato."libappimage"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kfilemetadata); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kfilemetadata"]
+
+[candidato."libb2"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."libbpf"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (iproute2); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["iproute2"]
+
+[candidato."libbsd"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (shadow); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["shadow"]
+
+[candidato."libcap-ng"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["cli", "escritorio-kde"]
+piden = ["dbus", "util-linux"]
+
+[candidato."libclc"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mesa); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."libdatrie"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."libdmtx"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (prison); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["prison"]
+
+[candidato."libei"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kwin); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kwin"]
+
+[candidato."libglvnd"]
+veredicto = "pendiente"
+porque = "lo piden 3 recetas"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["libepoxy", "mesa", "qtbase"]
+
+[candidato."libidn2"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["cli"]
+piden = ["curl", "iputils"]
+
+[candidato."libimobiledevice"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (solid); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["solid"]
+
+[candidato."libkexiv2"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (plasma-workspace); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["plasma-workspace"]
+
+[candidato."libmng"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtsvg); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtsvg"]
+
+[candidato."libmnl"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (iproute2); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["iproute2"]
+
+[candidato."libpciaccess"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (libdrm); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["libdrm"]
+
+[candidato."libpq"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."libproxy"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."libpsl"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (curl); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["curl"]
+
+[candidato."libpulseaudio"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["libcanberra", "qtmultimedia"]
+
+[candidato."libqaccessibilityclient"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kwin); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kwin"]
+
+[candidato."libsecret"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kwallet); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kwallet"]
+
+[candidato."libselinux"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."libsepol"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."libthai"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."libunwind"]
+veredicto = "pendiente"
+porque = "lo piden 3 recetas"
+perfiles = ["cli", "escritorio-kde", "escritorio-mirada"]
+piden = ["mesa", "qtmultimedia", "strace"]
+
+[candidato."libva-minimal"]
+veredicto = "pendiente"
+sugerencia = "nix-ismo?"
+porque = "el nombre tiene forma de andamiaje de nixpkgs (env/minimal/wrapper)"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."libwacom"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (libinput); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["libinput"]
+
+[candidato."libxcb-cursor"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."libxcb-image"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase", "xcb-util-cursor"]
+
+[candidato."libxcb-keysyms"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa", "qtbase"]
+
+[candidato."libxcb-render-util"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase", "xcb-util-cursor"]
+
+[candidato."libxcb-util"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."libxcb-wm"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."libxcomposite"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."libxcrypt"]
+veredicto = "pendiente"
+sugerencia = "hueco?"
+porque = "lo piden 4 recetas de la imagen — difícil que sea accesorio"
+perfiles = ["cli", "escritorio-kde", "escritorio-mirada"]
+piden = ["linux-pam", "perl", "shadow", "util-linux"]
+
+[candidato."libxft"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (plasma-workspace); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["plasma-workspace"]
+
+[candidato."libxrandr"]
+veredicto = "pendiente"
+porque = "lo piden 3 recetas"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa", "qtmultimedia", "vulkan-loader"]
+
+[candidato."libxscrnsaver"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kidletime); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kidletime"]
+
+[candidato."libxshmfence"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mesa); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."libxxf86vm"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mesa); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."lksctp-tools"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."llvm"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mesa); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."lndir"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."lttng-ust"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."lua"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (libinput); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["libinput"]
+
+[candidato."lvm2"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (parted); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["parted"]
+
+[candidato."mariadb-connector-c"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."mariadb-connector-odbc"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."md4c"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."mesa-libgbm"]
+veredicto = "pendiente"
+porque = "lo piden 3 recetas"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["kwin", "mesa", "qtbase"]
+
+[candidato."milou"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["kwin", "plasma-workspace"]
+
+[candidato."mpdecimal"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (python3); mirá si es una feature opcional"
+perfiles = ["cli", "escritorio-kde", "escritorio-mirada"]
+piden = ["python3"]
+
+[candidato."nghttp2"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (curl); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["curl"]
+
+[candidato."nghttp3"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (curl); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["curl"]
+
+[candidato."ngtcp2"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (curl); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["curl"]
+
+[candidato."openldap"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (gnupg); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["gnupg"]
+
+[candidato."orc"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtmultimedia); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtmultimedia"]
+
+[candidato."oxygen-icons"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (frameworkintegration); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["frameworkintegration"]
+
+[candidato."packagekit-qt"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["frameworkintegration", "plasma-workspace"]
+
+[candidato."pandoc-cli"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (eza); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["eza"]
+
+[candidato."plasma-nano"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (plasma-workspace); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["plasma-workspace"]
+
+[candidato."ply"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mesa); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."po4a"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (util-linux); mirá si es una feature opcional"
+perfiles = ["cli", "escritorio-kde"]
+piden = ["util-linux"]
+
+[candidato."polkit-qt-1"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["kauth", "plasma-workspace"]
+
+[candidato."poppler-qt6"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kfilemetadata); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kfilemetadata"]
+
+[candidato."popt"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (rsync); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["rsync"]
+
+[candidato."psqlodbc"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."pycparser"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mesa); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."pyside6"]
+veredicto = "pendiente"
+sugerencia = "hueco?"
+porque = "lo piden 6 recetas de la imagen — difícil que sea accesorio"
+perfiles = ["escritorio-kde"]
+piden = ["kcoreaddons", "kguiaddons", "knotifications", "kstatusnotifieritem", "kwidgetsaddons", "kxmlgui"]
+
+[candidato."python3-3.14.6-env"]
+veredicto = "pendiente"
+sugerencia = "nix-ismo?"
+porque = "el nombre tiene forma de andamiaje de nixpkgs (env/minimal/wrapper)"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["kcoreaddons", "kguiaddons", "knotifications", "kstatusnotifieritem", "kwidgetsaddons", "kxmlgui", "libinput"]
+
+[candidato."pyyaml"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mesa); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."qca"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kwallet); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kwallet"]
+
+[candidato."qqc2-breeze-style"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["kscreenlocker", "plasma-workspace"]
+
+[candidato."qtquick3d"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["kwin", "qtmultimedia"]
+
+[candidato."qtsensors"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kwin); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kwin"]
+
+[candidato."qtserialport"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtpositioning); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtpositioning"]
+
+[candidato."qtvirtualkeyboard"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["kwin", "plasma-workspace"]
+
+[candidato."qtwebsockets"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qcoro); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qcoro"]
+
+[candidato."remove-references-to"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (jq); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["jq"]
+
+[candidato."rhash"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (cmake); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["cmake"]
+
+[candidato."rust-bindgen"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mesa); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."rust-cbindgen"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (mesa); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["mesa"]
+
+[candidato."setxkbmap"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (libxkbcommon); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["libxkbcommon"]
+
+[candidato."shared-mime-info"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kcoreaddons); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kcoreaddons"]
+
+[candidato."shiboken6"]
+veredicto = "pendiente"
+sugerencia = "hueco?"
+porque = "lo piden 6 recetas de la imagen — difícil que sea accesorio"
+perfiles = ["escritorio-kde"]
+piden = ["kcoreaddons", "kguiaddons", "knotifications", "kstatusnotifieritem", "kwidgetsaddons", "kxmlgui"]
+
+[candidato."speech-dispatcher"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtspeech); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtspeech"]
+
+[candidato."spirv-tools"]
+veredicto = "pendiente"
+porque = "lo piden 3 recetas"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["kdeclarative", "mesa", "plasma-workspace"]
+
+[candidato."sqlite-connector-odbc"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."syndication"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (knewstuff); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["knewstuff"]
+
+[candidato."systemd"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde"]
+piden = ["libcanberra", "qtbase"]
+
+[candidato."systemd-minimal"]
+veredicto = "pendiente"
+sugerencia = "nix-ismo?"
+porque = "el nombre tiene forma de andamiaje de nixpkgs (env/minimal/wrapper)"
+perfiles = ["cli", "escritorio-kde", "escritorio-mirada"]
+piden = ["dbus", "mako"]
+
+[candidato."systemd-minimal-libs"]
+veredicto = "pendiente"
+sugerencia = "nix-ismo?"
+porque = "el nombre tiene forma de andamiaje de nixpkgs (env/minimal/wrapper)"
+perfiles = ["cli", "escritorio-kde", "escritorio-mirada"]
+piden = ["dhcpcd", "htop", "libinput", "linux-pam", "mesa", "qtbase", "seatd", "util-linux"]
+
+[candidato."taglib"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kfilemetadata); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kfilemetadata"]
+
+[candidato."tcb"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (shadow); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["shadow"]
+
+[candidato."texinfo"]
+veredicto = "pendiente"
+porque = "lo piden 3 recetas"
+perfiles = ["cli"]
+piden = ["e2fsprogs", "git", "gnupg"]
+
+[candidato."tinyxxd"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (dosfstools); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["dosfstools"]
+
+[candidato."tpm2-tss"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (gnupg); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["gnupg"]
+
+[candidato."uname"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (git); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["git"]
+
+[candidato."unixodbc"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (qtbase); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["qtbase"]
+
+[candidato."v4l-utils"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (libdisplay-info); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["libdisplay-info"]
+
+[candidato."valgrind"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["libdrm", "mesa"]
+
+[candidato."w3m"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (linux-pam); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["linux-pam"]
+
+[candidato."wayland-scanner"]
+veredicto = "pendiente"
+sugerencia = "hueco?"
+porque = "lo piden 7 recetas de la imagen — difícil que sea accesorio"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["kscreenlocker", "libxkbcommon", "mako", "mesa", "qtbase", "wayland", "wayland-protocols"]
+
+[candidato."xclip"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (gitui); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["gitui"]
+
+[candidato."xkbcomp"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (libxkbcommon); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["libxkbcommon"]
+
+[candidato."xmlto"]
+veredicto = "pendiente"
+porque = "lo piden 2 recetas"
+perfiles = ["cli", "escritorio-kde", "escritorio-mirada"]
+piden = ["git", "wayland"]
+
+[candidato."xvfb"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (libxkbcommon); mirá si es una feature opcional"
+perfiles = ["escritorio-kde", "escritorio-mirada"]
+piden = ["libxkbcommon"]
+
+[candidato."xwayland"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (kwin); mirá si es una feature opcional"
+perfiles = ["escritorio-kde"]
+piden = ["kwin"]
+
+[candidato."xxhash"]
+veredicto = "pendiente"
+sugerencia = "opcional?"
+porque = "lo pide una sola receta (rsync); mirá si es una feature opcional"
+perfiles = ["cli"]
+piden = ["rsync"]
diff --git a/docs/state/nixismos-triaje.txt b/docs/state/nixismos-triaje.txt
new file mode 100644
index 00000000..b2e726cc
--- /dev/null
+++ b/docs/state/nixismos-triaje.txt
@@ -0,0 +1,2 @@
+# nixismos-triaje.txt — DERIVADO de frontera-triaje.toml por scripts/triaje.py --aplicar.
+# Lo lee seed-graph.py y lo suma a su lista de descarte: el sembrador aprende de su triaje.
diff --git a/scripts/seed-graph.py b/scripts/seed-graph.py
index f2d1c931..2b480f2f 100755
--- a/scripts/seed-graph.py
+++ b/scripts/seed-graph.py
@@ -69,6 +69,13 @@ NIXISMOS_EXACTOS = {
     "glibc-locales", "cmake-minimal",
 }
 NIXISMOS_SUFIJOS = ("-hook", "Hook", "-wrapper", "-setup-hook", ".sh")
+# Lazo cerrado: los candidatos que un humano marcó `nix-ismo` en el triaje (P5) vuelven acá como
+# descarte, así cada corrida del sembrador sale más limpia que la anterior. Si el fichero no está,
+# no pasa nada — el sembrador funciona igual, sólo con más ruido.
+_TRIAJE = ROOT / "docs/state/nixismos-triaje.txt"
+if _TRIAJE.exists():
+    NIXISMOS_EXACTOS |= {l.strip() for l in _TRIAJE.read_text().splitlines()
+                         if l.strip() and not l.startswith("#")}
 NIXISMOS_INFIJOS = ("-hook",)   # `cargo-build-hook.sh` no termina en `-hook`, lo contiene
 # Toolchains que provee el LAB (el rootfs del sandbox), no una receta: ninguna receta Rust declara
 # `rust` ni `cargo` en `[deps].build` — están vacías y el toolchain viene de abajo. Que nixpkgs los
diff --git a/scripts/triaje.py b/scripts/triaje.py
new file mode 100755
index 00000000..1720ed9b
--- /dev/null
+++ b/scripts/triaje.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python3
+# triaje.py — convierte los CANDIDATOS de la frontera en VEREDICTOS durables. (P5 del plan
+# docs/plan-catalogo-objetivo.md.)
+#
+# POR QUÉ. `seed-graph.py --frontera` produce hipótesis: 137 nombres que nixpkgs pide y hammer no
+# puede construir. Clasificarlos es decisión HUMANA (la regla de la granja: el worker MIDE, el hub
+# CLASIFICA). Pero si ese juicio se queda en la cabeza o en un mensaje, se repite entero cada vez que
+# alguien vuelve a correr el sembrador. Este fichero lo hace durable y consumible por máquina.
+#
+# TRES VEREDICTOS, tres destinos distintos — y ahí está el valor, porque cada uno CIERRA un lazo:
+#   hueco     → falta de verdad ⇒ va como raíz a `targets.toml` y nace un nodo `wanted` en el grafo,
+#               que el sembrador siembra y el drenaje ordena. Es lo que da trabajo nuevo al worker.
+#   opcional  → dep que nixpkgs habilita y hammer no quiere (gstreamer en un escritorio sin audio).
+#               Queda registrada para que deje de reaparecer como pregunta.
+#   nix-ismo  → andamiaje de nixpkgs sin contraparte acá ⇒ vuelve a `seed-graph.py` como descarte,
+#               así la próxima corrida es más limpia. El sembrador aprende de su propio triaje.
+#
+# NO decide nada solo. `sugerencia` propone con evidencia mecánica (cuántas recetas lo piden, si el
+# nombre tiene forma de env/wrapper de nix), pero `veredicto` arranca en `pendiente` y sólo lo
+# cambia un humano editando el TOML.
+#
+# Uso:  scripts/triaje.py              # sincroniza candidatos → docs/state/frontera-triaje.toml
+#       scripts/triaje.py --resumen    # cuántos pendientes/clasificados hay
+#       scripts/triaje.py --aplicar    # veredictos → descarte del sembrador + líneas para targets.toml
+import json, re, sys, tomllib
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parent.parent
+FRONTERA = ROOT / "docs/state/seed-frontera.json"
+TRIAJE = ROOT / "docs/state/frontera-triaje.toml"
+DESCARTE = ROOT / "docs/state/nixismos-triaje.txt"
+VEREDICTOS = ("pendiente", "hueco", "opcional", "nix-ismo")
+
+# Formas de nombre que en nixpkgs son andamiaje casi seguro. Sólo alimenta `sugerencia`, nunca
+# `veredicto`: una heurística no cierra un juicio, lo abarata.
+FORMA_NIXISMO = re.compile(r"(-env$|^python3-[\d.]+|-minimal(-libs)?$|^glibc|-wrapper$|\.sh$)")
+
+
+def cargar_triaje():
+    if not TRIAJE.exists():
+        return {}
+    d = tomllib.load(open(TRIAJE, "rb"))
+    return d.get("candidato", {})
+
+
+def sugerir(dep, piden):
+    if FORMA_NIXISMO.search(dep):
+        return "nix-ismo?", "el nombre tiene forma de andamiaje de nixpkgs (env/minimal/wrapper)"
+    if len(piden) >= 4:
+        return "hueco?", f"lo piden {len(piden)} recetas de la imagen — difícil que sea accesorio"
+    if len(piden) == 1:
+        return "opcional?", f"lo pide una sola receta ({piden[0]}); mirá si es una feature opcional"
+    return "", f"lo piden {len(piden)} recetas"
+
+
+def escribir(cands):
+    lineas = [
+        "# frontera-triaje.toml — VEREDICTOS sobre los candidatos a hueco de la frontera.",
+        "#",
+        "# Lo genera y sincroniza `scripts/triaje.py` desde `seed-frontera.json`; los veredictos los",
+        "# pone un HUMANO editando este fichero. Sincronizar NO pisa lo ya decidido: sólo agrega",
+        "# candidatos nuevos y marca `[ausente]` los que dejaron de aparecer.",
+        "#",
+        "# veredicto ∈ pendiente | hueco | opcional | nix-ismo",
+        "#   hueco    — falta de verdad. Ponelo como raíz en docs/state/targets.toml ⇒ nace un nodo",
+        "#              `wanted` en el grafo y entra al drenaje. (`triaje.py --aplicar` te da la línea.)",
+        "#   opcional — dep que nixpkgs habilita y nosotros no queremos. Queda registrada para que no",
+        "#              vuelva a preguntarse.",
+        "#   nix-ismo — andamiaje de nixpkgs sin contraparte acá. `--aplicar` lo manda al descarte del",
+        "#              sembrador ⇒ la próxima corrida sale más limpia.",
+        "#",
+        "# `sugerencia` es una heurística con evidencia, NO un veredicto: abarata el juicio, no lo cierra.",
+        'schema = "hammer-frontera-triaje/1"',
+        "",
+    ]
+    for dep in sorted(cands):
+        c = cands[dep]
+        lineas.append(f'[candidato."{dep}"]')
+        lineas.append(f'veredicto = "{c.get("veredicto", "pendiente")}"')
+        if c.get("sugerencia"):
+            lineas.append(f'sugerencia = "{c["sugerencia"]}"')
+        if c.get("porque"):
+            lineas.append(f'porque = "{c["porque"]}"')
+        lineas.append(f'perfiles = {json.dumps(sorted(c.get("perfiles", [])))}')
+        piden = sorted(c.get("piden", []))
+        lineas.append(f'piden = {json.dumps(piden[:12])}'
+                      + (f"  # …y {len(piden)-12} más" if len(piden) > 12 else ""))
+        if c.get("ausente"):
+            lineas.append("ausente = true  # ya no aparece en el último barrido de la frontera")
+        lineas.append("")
+    TRIAJE.write_text("\n".join(lineas))
+
+
+def sincronizar():
+    if not FRONTERA.exists():
+        print(f"falta {FRONTERA.relative_to(ROOT)} — corré scripts/seed-graph.py --frontera <perfil>",
+              file=sys.stderr)
+        return 1
+    fr = json.loads(FRONTERA.read_text())
+    previo = cargar_triaje()
+
+    vistos = {}
+    for perfil, d in (fr.get("perfiles") or {}).items():
+        for dep, quienes in (d.get("candidatos") or {}).items():
+            e = vistos.setdefault(dep, {"perfiles": set(), "piden": set()})
+            e["perfiles"].add(perfil)
+            e["piden"].update(quienes)
+
+    cands, nuevos = {}, 0
+    for dep, e in vistos.items():
+        sug, porque = sugerir(dep, sorted(e["piden"]))
+        anterior = previo.get(dep, {})
+        if not anterior:
+            nuevos += 1
+        cands[dep] = dict(
+            veredicto=anterior.get("veredicto", "pendiente"),  # NUNCA se pisa un juicio humano
+            sugerencia=sug, porque=porque,
+            perfiles=e["perfiles"], piden=e["piden"])
+    # Los que ya no aparecen no se borran: se marcan. Un candidato que desaparece puede ser progreso
+    # (se escribió la receta) o un cambio de nixpkgs, y conviene verlo en el `git diff`.
+    ausentes = 0
+    for dep, c in previo.items():
+        if dep not in cands:
+            cands[dep] = dict(c, ausente=True,
+                              perfiles=set(c.get("perfiles", [])), piden=set(c.get("piden", [])))
+            ausentes += 1
+
+    escribir(cands)
+    print(f"== triaje sincronizado → {TRIAJE.relative_to(ROOT)}")
+    print(f"   {len(cands)} candidatos ({nuevos} nuevos, {ausentes} ya ausentes)")
+    return resumen()
+
+
+def resumen():
+    cands = cargar_triaje()
+    if not cands:
+        print("   (sin triaje todavía)")
+        return 0
+    cuenta = {}
+    for c in cands.values():
+        cuenta[c.get("veredicto", "pendiente")] = cuenta.get(c.get("veredicto", "pendiente"), 0) + 1
+    print("\n== veredictos")
+    for v in VEREDICTOS:
+        if cuenta.get(v):
+            print(f"   {v:10} {cuenta[v]:4}")
+    pend = [d for d, c in cands.items() if c.get("veredicto", "pendiente") == "pendiente"]
+    if pend:
+        print(f"\n== los pendientes que MÁS recetas piden (empezá por acá)")
+        for d in sorted(pend, key=lambda d: -len(cands[d].get("piden", [])))[:12]:
+            c = cands[d]
+            s = f" [{c['sugerencia']}]" if c.get("sugerencia") else ""
+            print(f"   {len(c.get('piden', [])):3}×  {d:28}{s}")
+    return 0
+
+
+def aplicar():
+    cands = cargar_triaje()
+    if not cands:
+        print("no hay triaje que aplicar (corré scripts/triaje.py primero)", file=sys.stderr)
+        return 1
+    nixismos = sorted(d for d, c in cands.items() if c.get("veredicto") == "nix-ismo")
+    DESCARTE.write_text(
+        "# nixismos-triaje.txt — DERIVADO de frontera-triaje.toml por scripts/triaje.py --aplicar.\n"
+        "# Lo lee seed-graph.py y lo suma a su lista de descarte: el sembrador aprende de su triaje.\n"
+        + "\n".join(nixismos) + ("\n" if nixismos else ""))
+    print(f"== {len(nixismos)} nix-ismos → {DESCARTE.relative_to(ROOT)} (los lee seed-graph.py)")
+
+    huecos = {d: c for d, c in cands.items() if c.get("veredicto") == "hueco"}
+    if not huecos:
+        print("\n== 0 huecos confirmados: no hay raíces nuevas que agregar a targets.toml")
+        return 0
+    print(f"\n== {len(huecos)} huecos confirmados — pegá estas raíces en docs/state/targets.toml")
+    por_perfil = {}
+    for d, c in huecos.items():
+        for p in c.get("perfiles", []):
+            por_perfil.setdefault(p, []).append(d)
+    for p, ds in sorted(por_perfil.items()):
+        print(f"\n   [perfil.{p}] — agregar a `paquetes`:")
+        print("     " + ", ".join(f'"{d}"' for d in sorted(ds)))
+    print("\n   Después:  scripts/build-state.py  (nacen los nodos `wanted`)")
+    print("             scripts/seed-graph.py --perfil <p>   (siembra sus aristas)")
+    print("             scripts/drenar.py --todos            (entran al orden de masticado)")
+    return 0
+
+
+def main():
+    args = sys.argv[1:]
+    if "--resumen" in args:
+        return resumen()
+    if "--aplicar" in args:
+        return aplicar()
+    return sincronizar()
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/tandas/README.md b/tandas/README.md
new file mode 100644
index 00000000..1e383902
--- /dev/null
+++ b/tandas/README.md
@@ -0,0 +1,51 @@
+# tandas/ — ARCHIVO de campañas pasadas
+
+**Estos 55 ficheros son historia, no la cola de trabajo.** Se conservan porque documentan qué se
+importó y por qué en cada campaña (el `git log` de cada uno es la crónica del frente Go, del
+base-system, del stack GUI). No se borran. Pero **el trabajo nuevo ya no sale de acá**.
+
+## Por qué dejaron de ser la cola
+
+Una tanda era una lista plana escrita a mano. Eso tenía tres problemas que no se veían mientras
+funcionaba:
+
+1. **No decía el orden.** 40 nombres sin aristas: cuál primero, qué destraba qué, qué se puede
+   paralelizar — todo quedaba en la cabeza del que construía.
+2. **No decía cuánto faltaba.** Una tanda terminada no significaba una imagen completa; nadie sabía
+   la distancia al objetivo porque el objetivo no estaba escrito en ningún lado.
+3. **Había que autorarla.** El worker quedaba idle esperando que un humano escribiera la próxima
+   lista — el problema que [[vps-nunca-idle]] intentaba parchear a mano.
+
+## De dónde sale el trabajo ahora
+
+Todo se deriva del grafo de estado. El flujo completo, y qué comando corre cada paso:
+
+```
+docs/state/targets.toml          ← QUÉ debe tener la distro (perfiles = imágenes, sólo RAÍCES)
+        │  scripts/build-state.py
+        ▼
+docs/state/build-state.json      ← QUÉ hay, qué falta (`wanted`), qué depende de qué
+        │  scripts/drenar.py --todos
+        ▼
+docs/state/drenaje.json          ← EN QUÉ ORDEN: ondas topológicas por imagen
+        │
+        ▼  el worker muele la onda 1:  scripts/farm/campana-deuda.sh <recetas>
+```
+
+Y para descubrir lo que todavía no está declarado:
+
+```
+scripts/seed-graph.py --frontera <perfil>   → docs/state/seed-frontera.json   (candidatos a hueco)
+scripts/triaje.py                           → docs/state/frontera-triaje.toml (veredicto humano)
+scripts/triaje.py --aplicar                 → raíces nuevas para targets.toml + descarte del sembrador
+```
+
+El triaje es el único paso que **requiere un humano**: decidir si un candidato es hueco real, dep
+opcional que no queremos, o andamiaje de nixpkgs. Todo lo demás se deriva.
+
+## Qué sigue vivo acá
+
+- `import-batch.sh -f tandas/<x>.txt` sigue funcionando para importar una lista puntual a mano. Es
+  la vía de escape, no el camino principal.
+- Los subdirectorios `needs-review-*` y `staged-fallidos*` son **resultados**, no colas: recetas que
+  fallaron un smoke-test y esperan diagnóstico.