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 ", + 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

(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 +``` + +Y para descubrir lo que todavía no está declarado: + +``` +scripts/seed-graph.py --frontera → 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/.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.