plan: con qué conviene hacer lo de busybox — la imagen lleva el toolchain adentro
Medido sobre los 950 nodos, las aristas de yupana y los bytes del store: el cierre que define una imagen es build ∪ runtime, así que las 8 imágenes llevan llvm21, go, python3, binutils, cmake y perl adentro. servidor 4,56 → 2,09 GiB; KDE 13,05 → 5,76. Es la misma forma del caso busybox (llegaba por 24 build-deps, no por targets.toml) y cuesta CERO reconstrucciones porque no toca ninguna receta. Con el mismo instrumento: sudo y doas viajan los dos y NINGUNO tiene el bit setuid (nadie en crates/ lo pone; dos recetas afirman que la hidratación lo hace); los 6 applets de kmod son clase BORRAR porque los 4 kernels que arman imágenes llevan -d MODULES; el GRUB de la rama BIOS sale de /usr/lib/grub del constructor y no de una receta. Y las campañas que sí cuestan: meson→muon (107) y zlib→zlib-rs (122).
Semántico sayk'uy
Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.
1 qillqakuna · +347 −0
diff --git a/docs/plan-que-sigue-tras-busybox.md b/docs/plan-que-sigue-tras-busybox.md
new file mode 100644
index 00000000..2c9d9648
--- /dev/null
+++ b/docs/plan-que-sigue-tras-busybox.md
@@ -0,0 +1,347 @@
+# Plan — con qué conviene hacer lo de busybox, MEDIDO
+
+**Fecha:** 2026-09-22 · **Pregunta:** «botado busybox, ¿con qué conviene hacer lo mismo? ¿Hay que
+rusterizar o soberanizar algo vital, hay opciones modernas a cosas tradicionales, o hay una
+arquitectura mejor?»
+
+**Instrumento** (todo reproducible, nada escrito a mano):
+
+```sh
+# grafo y clases: los 950 nodos con su clase, hash, perfiles y dependientes
+python3 -c 'import json;json.load(open("docs/state/build-state.json"))'
+# aristas y membresía: la MISMA función que usan el grafo, la hidratación y el repo firmado
+python3 -c 'import sys;sys.path.insert(0,"scripts");import yupana;yupana.cargar_colas()'
+# bytes: tamaño real de cada artefacto sellado
+cd /store && du -sk --apparent-size * # 1.435 artefactos, 59,8 GiB
+# runtime real: quién pide qué SONAME
+readelf -d <artefacto>/usr/bin/<bin>
+```
+
+## El hallazgo que ordena todo, y no es una pieza de userland
+
+> **La clausura que define una imagen es `build ∪ runtime`. Entonces cada imagen de takana lleva
+> adentro la cadena de compilación: `llvm21`, `go`, `python3`, `binutils`, `cmake`, `perl`, `nasm`,
+> `bison`, `flex`, `m4`, `meson`, `wasi-sdk`, `nodejs`.**
+
+Es **exactamente la forma del caso busybox**, y por eso vale la pena verlo dos veces: `busybox`
+nunca estuvo declarado en `targets.toml` —llegaba a los 7 perfiles por la clausura, porque 24
+recetas lo pedían en `[deps] build`—, y sacarlo no costó escribir una línea de userland (paso 6 de
+`plan-botar-busybox.md`). Lo que ese paso arregló **para un nombre**, esto lo arregla **para la
+clase entera**.
+
+La definición está en `scripts/yupana.py:98` (`_deps`) y en `scripts/build-state.py:128`, y en las
+dos hay un comentario que explica por qué es la unión: `firefox` declaraba `runtime = ["gcc-libs"]`,
+nadie leía ese campo, y la imagen se armaba sin `libstdc++.so.6`. **El arreglo fue correcto y la
+unión es lo que hay que afinar, no lo que hay que deshacer** — ver el riesgo más abajo.
+
+### Lo que cuesta, en nodos y en bytes
+
+Clausura de hoy contra «raíces + aristas de runtime», medido sobre los artefactos sellados:
+
+| perfil | nodos hoy | sólo-runtime | GiB hoy | GiB sólo-runtime | grasa |
+|---|---|---|---|---|---|
+| `base` | 85 | 48 | **1,35** | 0,71 | **47 %** |
+| `cli` | 152 | 103 | **2,57** | 1,56 | 39 % |
+| `servidor` | 185 | 132 | **4,56** | 2,09 | **54 %** |
+| `escritorio-kde` | 444 | 163 | **13,05** | 5,76 | **56 %** |
+| `escritorio-gnome` | 344 | 158 | 9,63 | 3,86 | 60 % |
+| `escritorio-cosmic` | 295 | 157 | 8,48 | 4,34 | 49 % |
+| `escritorio-sway` | 281 | 143 | 7,90 | 4,11 | 48 % |
+| `escritorio-mirada` | 41 | 17 | 0,79 | 0,20 | 75 % |
+
+Y los caminos, que es lo que lo hace concreto:
+
+| en la imagen | pesa | entra por | qué es |
+|---|---|---|---|
+| `llvm21` | **1.527 MiB** | `servidor` → `rust` → `llvm21` | libs estáticas + headers de LLVM |
+| `python3` | 209 MiB | **`base` → `iputils` → `python3`** | `ping` se construye con meson |
+| `go` | 222 MiB | `cli` → `fzf` → `go` | el toolchain de Go |
+| `binutils` | 127 MiB | `base` → `bwrap` → `binutils` | ensamblador y linker |
+| `perl` | 84 MiB | `base` → `git` → `perl` | los `configure` de OpenSSL/curl |
+| `cmake` | 94 MiB | `servidor` → `rust` → `cmake` | generador de builds |
+| `nodejs` | 86 MiB | `escritorio-kde` → `atuq` → `firefox` → `nodejs` | el JS del build de Gecko |
+
+`llvm21` es el caso de libro y se verificó **en el binario, no en la receta**:
+
+```
+readelf -d /store/…-rust/usr/bin/rustc ⇒ NEEDED librustc_driver-*.so, libc.so (NO libLLVM)
+readelf -d /store/…-lld21/usr/bin/lld ⇒ NEEDED libz.so.1, libgcc_s.so.1, libc.so
+recipes/llvm21.toml:75-76 ⇒ -DLLVM_BUILD_LLVM_DYLIB=OFF -DLLVM_LINK_LLVM_DYLIB=OFF
+```
+
+O sea: 1,5 G de LLVM viajan en la imagen del servidor para que **nadie** los abra. Y `servidor` es
+el perfil donde compilar **sí** es deliberado (`targets.toml:1725-1745`, «la caja que sirve el repo
+y construye, sí»): incluso ahí, el compilador es `rust` + `lld21`, no `llvm21`.
+
+### Por qué es barato: **CERO reconstrucciones**
+
+Esto no toca ninguna receta ⇒ no toca ningún `ArtifactHash` ⇒ no invalida nada. Se cambia **cómo se
+proyecta**, no qué se construye. Es lo que lo pone primero: todo lo demás de este documento cuesta
+builds.
+
+### Por qué es seguro: la ambigüedad es ENUMERABLE
+
+El miedo legítimo es la cicatriz de `gcc-libs`: si una lib dinámica se cae de la imagen, el binario
+sella, reproduce y no arranca. Medido sobre el cierre de `servidor`:
+
+> **de 185 nodos, sólo 28 contienen algún `.so`.** 144 no traen ni uno (todo el corpus es
+> `link = static`), y 13 no están en el store de este hub.
+
+Los 28: `alsa-lib binutils bzip2-shared cmus dbus elfutils-libdw ffmpeg gcc-libs gmp go
+libffi-shared libnl libudev-zero llvm21 mold mpc mpfr musl-fts musl-shared ncurses-shared perl
+python3 readline-shared sqlite-shared util-linux xz-shared zlib-shared zstd`.
+
+Entonces la regla correcta **no** es «usá sólo las aristas de runtime» (eso es lo que rompió
+firefox). Es:
+
+```
+cierre de imagen = raíces ∪ deps de runtime declaradas ∪ proveedores de los SONAME OBSERVADOS
+```
+
+y el instrumento para la tercera parte **ya existe y ya se corre**: `scripts/vigia-sonames.py`
+contesta «¿qué `.so` pide esta imagen que ningún artefacto de su cierre provee?». Hoy es un guardián
+*a posteriori*; acá pasa a ser la **definición**.
+
+### Dónde se toca, y el criterio de terminado
+
+- `scripts/yupana.py` — una tercera vista además de `cargar_colas()` y `cargar_colas(solo_build=True)`:
+ la de **imagen**. Las otras dos no se tocan: `radio` necesita la de build (re-hasheo) y el grafo de
+ estado necesita la unión (cota de «qué hay que construir»).
+- `scripts/hydrate-profile.py` — hoy «proyecta al FHS el cierre ENTERO».
+- `scripts/repo-perfil.sh` y `scripts/build-state.py` — consumen la misma membresía.
+- ⚠ **Hay DOS linajes de imagen y este es el que no está curado.** El otro —`takana bootstrap
+ product`— se arma de tres listas a mano (`STAGE1_COMPONENTS` 4, `USERLAND_COMPONENTS` 5,
+ `SERVICE_COMPONENTS` 2) y por eso `product-image.sh` puede anunciarse «lean, SIN toolchain». La
+ imagen del servidor es **producto + hidratación del perfil encima** (SDD 28 §«Receta del
+ ensamblado»), y la segunda mitad es la que trae los 2,46 GiB. Unificar los linajes es su propia
+ unidad de trabajo; **no hace falta para cobrar esto.**
+
+**Terminado cuando:** `hydrate-profile.py servidor` no proyecta `llvm21 python3 perl binutils cmake
+go`, `vigia-sonames.py` da **cero** huecos en los 8 perfiles, y `product-boot-test.sh` arranca. La
+cifra a batir: **4,56 → 2,09 GiB** en `servidor` y **13,05 → 5,76** en KDE.
+
+⚠ **Un detalle que sale de paso y no es de este frente:** el **kernel no está en ningún perfil**
+(`linux*` → `perfiles: []`); lo pasa un `KERNEL=` de script. Lo que sí viaja en los 7 es
+`linux-headers`. La imagen declara los headers del kernel y no el kernel.
+
+---
+
+## Los tres grupos, porque mezclarlos es lo que cuesta el trabajo
+
+Igual que los tres frentes de busybox, acá la diferencia que importa es **cuántas reconstrucciones
+paga cada cosa** — `[deps] build` entra en `hash_inputs`, `[deps] runtime` no:
+
+| grupo | qué es | rebuilds |
+|---|---|---|
+| **1 · proyección** | cómo se arma la imagen; ninguna receta cambia | **0** |
+| **2 · hojas** | recetas con **0 consumidores**: nadie las declara | **0** (sólo ellas) |
+| **3 · campañas** | tocar una herramienta de build que N recetas declaran | **N** |
+
+---
+
+## Grupo 2 · Las hojas: cambio moderno, coste cero
+
+Todas verificadas con `yupana`: **0 consumidores**, o sea que tocarlas no invalida a nadie.
+
+### 2.1 · `sudo` **y** `doas` en los 7 perfiles — y ninguno de los dos puede funcionar
+
+Dos herramientas para lo mismo, declaradas juntas en `base` (`targets.toml:58`) y heredadas por los
+otros 6 perfiles. `sudo` = 12,1 MiB, `doas` = 0,1 MiB. Pero el hallazgo es otro:
+
+```
+/store/…-sudo/usr/bin/sudo → -r-xr-xr-x (sin setuid)
+/store/…-doas/usr/bin/doas → -r-xr-xr-x (sin setuid)
+grep -rn "S_ISUID\|4755" crates/ → 0 hits que pongan el bit
+```
+
+Y las recetas dicen tres cosas distintas:
+
+| receta | qué afirma | ¿cierto? |
+|---|---|---|
+| `recipes/sudo.toml:57` | «el paquete lo re-aplica al hidratar» | **no** |
+| `recipes/doas.toml:38` | «el binario setuid es cosa de la hidratación» | **no** |
+| `recipes/shadow.toml:15` | «el modelo de artefactos no puede ni expresar ni garantizar un setuid» | **sí** |
+
+Es la familia de [[needed-colgante-libstdcxx]] y [[subcomando-sin-driver]]: sella, reproduce y no
+corre. **Hoy es LATENTE**, y por eso nadie lo vio: el instalador sólo pone contraseña de root
+(`takana-live-install.sh`) y los `[[user]]` de las 7 recetas que los declaran son cuentas de servicio, que no
+escalan. Se vuelve real el día que la imagen tenga un usuario humano no-root — y entonces `sudo`,
+`doas`, `su` y `passwd` fallan los cuatro.
+
+**Orden correcto, y no es «poner sudo-rs»:**
+1. **Decidir quién pone el bit** (el modelo de artefactos, la hidratación o el `.swm`). Es la
+ conversación que `shadow.toml:18` ya pidió y que sigue abierta.
+2. **Elegir UNA.** Si la respuesta es «la chica», `doas` ya ganó. Si es «la compatible con los
+ runbooks», el reemplazo moderno es **`sudo-rs`** (Rust, auditado, drop-in de `sudo`) y de paso
+ saca un setuid en C del producto.
+3. Corregir las dos recetas que afirman algo falso, gane quien gane.
+
+### 2.2 · `chrony` → `ntpd-rs`
+
+La hora es vital y el modo de fallo es traicionero: sin NTP, TLS y las firmas fallan con errores que
+dicen «firma inválida» y no «la hora» — está escrito en el propio `targets.toml`. `chrony` es C, 0
+consumidores, sólo en `servidor`. `ntpd-rs` es el reemplazo Rust hecho exactamente para este nicho
+(cliente + servidor, NTS). Receta Cargo con commit pineado, el patrón de `uutils`.
+
+⚠ Verificar antes de prometer: que cubra el modo *cliente* con el mismo contrato de arranque, y que
+su `[[service]]` encaje en arje (SDD 30). El criterio es `arjectl status` + reloj convergido en
+QEMU, no que compile.
+
+### 2.3 · `gnupg` — mirar si hay que reemplazarlo o **borrarlo**
+
+`gnupg` es raíz de `base` (`targets.toml:60`) ⇒ viaja en los 7 perfiles, 39,1 MiB, **0 consumidores**, y en todo el repo lo invoca **un
+solo script de prueba** (`scripts/product-base-system-boot-test.sh`). La firma de takana **no lo
+usa**: es ed25519 propio (`takana keygen release`, `trust/`). Entonces son dos preguntas distintas:
+
+- Como **herramienta del usuario**: el reemplazo moderno es `sequoia` (`sq`, Rust). ⚠ No confundir
+ con `recipes/sq.toml`, que es otro `sq` (neilotoole, Go, consultas a datos).
+- Como **infraestructura**: no lo es. Si nadie lo pide, es la clase «105 BORRAR» del censo de
+ busybox, no la clase «reemplazar».
+
+⚠ `libgcrypt` (18 consumidores) y `libgpg-error` (20) **se quedan** con o sin gnupg.
+
+---
+
+## Grupo 3 · Las campañas: buen cambio, factura grande
+
+### 3.1 · `meson` → `muon`: matar el intérprete de Python del sandbox
+
+| dato | valor |
+|---|---|
+| recetas que declaran `meson` | **107** |
+| de esas, que declaran también `python3` | **107 (todas)** |
+| consumidores de `python3` en total | 302 |
+| `python3` en el store | **209,0 MiB** |
+| `meson` | 3,9 MiB |
+
+El precedente ya está sentado y es del propio repo: **`samurai`** (C, 0,6 MiB, 256 consumidores)
+reemplazó a ninja. `muon` es su hermano exacto: implementación en C de `meson.build`, sin Python,
+pensada para correr con samurai. Es el cambio que convierte «`ping` arrastra 209 MiB de intérprete»
+en un no-problema del lado del build, además del lado de la imagen.
+
+⚠ **Cuesta 107 recetas re-selladas** (y sus consumidores): `deps.build` está en `hash_inputs`.
+`yupana radio meson` antes de tocar nada. Conviene **sólo pegado a otra invalidación grande**, no
+como cambio suelto.
+⚠ Y se mide primero, no se adopta: muon **no cubre el 100 %** de meson. El criterio de decisión es
+cuántas de las 107 configuran con muon sin tocar el `meson.build`; con una muestra de 10 ya se sabe
+si el frente vale.
+
+### 3.2 · `zlib` → `zlib-rs`: la lib C más consumida del corpus
+
+`zlib` tiene **122 consumidores** (+83 de `zlib-shared`) — más que ninguna otra librería del
+catálogo, y es un clásico de CVEs de memoria. `zlib-rs` publica la **misma ABI C** (`libz.a` /
+`libz.so.1`), así que en teoría es un swap sin migrar llamadores, que es justo lo que no pasa con
+`ripgrep`/`sd`/`fd`.
+
+⚠ 122 recetas re-selladas: es **la campaña más cara de este documento**. Y ⚠ hay que verificar su
+estado upstream antes de prometerlo — la misma cautela que el plan de busybox aplicó a
+`uutils/util-linux`.
+
+**Cómo se decide sin pagar la campaña:** receta `zlib-rs` publicando el mismo SONAME, swap en **un**
+consumidor dinámico (`lld21` pide `libz.so.1`), y comparar. Si ahí anda, la campaña es una decisión
+de calendario; si no, costó una receta.
+
+### 3.3 · Lo que NO es campaña aunque se parezca
+
+`openssl`: 55 consumidores, 44,1 MiB. **9 son recetas Rust** (`cargo-generate`, `cargo-outdated`,
+`cargo-udeps`, `cargo-cache`, `cosmic-settings-daemon`, `hurl`, `monolith`, `nu`, `rink`) que podrían
+usar `rustls` por *feature*. Pero los otros 34 son C y openssl seguiría viajando igual ⇒ **el
+esfuerzo no compra bytes ni superficie**. Es trabajo del día que openssl se caiga sola.
+
+---
+
+## Correcciones al plan de busybox que salen de esta medición
+
+`plan-botar-busybox.md` cifró **23 applets de trabajo real**. Dos de las ocho piezas ya están
+hechas y una **no hace falta**:
+
+| pieza | applets | estado hoy |
+|---|---|---|
+| `psmisc` | `killall killall5 pstree fuser` | ✅ `sealed`, en 7 perfiles (commit `4357b29c`) |
+| `bc` | `bc dc` | ✅ `sealed`, en 7 perfiles (commit `4357b29c`) |
+| `awk` | `awk` | ✅ resuelto **escribiendo**: `akllay`, Rust, 3.531 líneas (`86915f26`) |
+| **`kmod`** | `insmod rmmod lsmod modprobe depmod modinfo` | **no hace falta: no hay módulos** |
+
+El detalle de `kmod`, que es el que cambia la cuenta: **los cuatro kernels que arman imágenes llevan
+`-d MODULES`** — `linux-generic.toml:69`, `linux-metal`, `linux-metal-dual`, `linux-metal-kexec`. El
+único que conserva los módulos del `defconfig` es `recipes/linux.toml`, y ése **no arma ninguna
+imagen** (el SDD 28 ya lo descartó: apaga SCSI y no ve el disco de hcloud). ADR 0018 lo dice del
+otro lado: «un único bzImage, sin módulos ⇒ una sola firma».
+
+⇒ **los 6 applets de `kmod` son clase BORRAR, no clase escribir.** Vuelven el día que exista un
+módulo fuera del árbol (el tester NVIDIA es el candidato), y ese día `kmod` es una receta, no seis
+applets. Con eso el trabajo restante del censo baja de 23 a **11** (`grep` 3, `sed` 1, gestor de
+`/dev` 3, `getty` 2, `nslookup` 1), y de esos `getty` **ya se sabe que es ADR** y no escritura.
+
+---
+
+## Lo único del producto que no sale de una receta: el cargador de arranque
+
+```
+recipes/grub*.toml ⇒ no existe
+scripts/iso-image.sh:37 ⇒ GRUB_LIB="${GRUB_LIB:-/usr/lib/grub/i386-pc}"
+scripts/install-image.sh:49 ⇒ idem · exige grub-mkimage, grub-bios-setup
+```
+
+O sea que **el GRUB que arranca una imagen de takana es el de la caja que la construyó**, y su
+versión no entra en ningún hash. Es la rama BIOS/El-Torito; la rama UEFI **ya se escapó** por
+EFI-stub (`EFI_STUB=1`, `metal-iso.sh:104`, y el ADR 0018 con vendor path propio + reconciliador de
+NVRAM, implementado y verificado en metal).
+
+La decisión es chica y está sin tomar: **o `grub` entra al corpus como receta, o la rama BIOS se
+retira** y todo el arranque queda EFI-stub, que es además la arquitectura moderna (UKI: un PE
+firmable, sin gestor intermedio). Lo que no conviene es lo de hoy, que es depender de un binario
+ajeno sin decirlo. ⚠ Y el ADR 0018 deja abierta la de Secure Boot, que es la que lo ata.
+
+---
+
+## Arquitectura: los tres pendientes, por lo que cuestan HOY
+
+1. **ADR 0012 · el árbol de fuentes compartido — PENDIENTE desde 2026-07-22, y es el que más paga.**
+ Es lo que obliga a `JOBS=1` y a envolver todo en `flock`: la granja compila **de a uno** en una
+ caja de 6 cores. Medido en su día: al invalidarse `libdrm`, ~93 de 205 recetas KDE murieron con el
+ wrapper de zig barrido por el fetch de otra. Resolverlo (caché inmutable direccionada por
+ contenido + copia privada por build, con reflink/overlay) es **la única de estas tres que
+ multiplica la velocidad del frente entero**. Todo lo demás de este documento se construye más
+ rápido después de esto.
+2. **Las 28 recetas `*-shared`.** `zlib`/`zlib-shared`, `pcre2`/`pcre2-shared`, `glib`/`glib-shared`…
+ 168,4 MiB de artefactos y, sobre todo, **28 builds y 28 puntos de mantenimiento duplicados** para
+ emitir dos formas del mismo código. La arquitectura correcta es una receta con **varias salidas**
+ (estática / dinámica / headers), que además es lo que haría al vigía de sonames innecesario en su
+ caso más común. ⚠ Re-hashea; va después del §1, que es el que hace que la distinción importe.
+3. **Los dos linajes de imagen** (tres listas a mano vs. clausura del perfil). Hoy conviven y
+ divergen en silencio: es la misma forma que los `hydrate-*.sh` contra `targets.toml`
+ ([[hidratar-desde-el-perfil-no-el-script]]) y que el `sshd` que entra por
+ `product-userland-from-repo.sh` y no por el manifiesto — lo dice el propio `targets.toml`.
+
+---
+
+## Lo que NO conviene tocar, y por qué (para que no se proponga en tres semanas)
+
+| candidato | por qué no |
+|---|---|
+| `openssh` | no hay reemplazo Rust drop-in de un servidor SSH. Es el acceso a la caja: un experimento acá se paga con una máquina inalcanzable |
+| `linux-pam` | 5 consumidores, todos greeters de escritorio. No hay reemplazo; la deuda está contenida y nombrada |
+| `cups` / `bluez` | 291,8 y 35,5 MiB en los 4 escritorios, **ya decidido a conciencia** (`targets.toml`: viajan como paquete y NO se arrancan como servicio). Es el punto 8 de PUBLICABLE, no un olvido |
+| `NetworkManager` | 50,3 MiB, pero los applets de los 4 escritorios le hablan **por D-Bus**. Es migración de llamadores, no borrado — la lección de `ifconfig` del censo de busybox |
+| el demonio de `dbus` | 124 consumidores son **clientes** de libdbus; cambiar el demonio (busd/dbus-broker) es posible y no urge |
+| forkear `ripgrep`/`sd`/`fd` | ya cerrado en `plan-botar-busybox.md`: se usan como **librerías**, no se forkean |
+| deduplicar `work/sources` | el árbol por receta ES el aislamiento del ADR 0012. Deduplicar *de paso* re-crea la carrera |
+
+---
+
+## Orden de ataque
+
+| # | unidad | rebuilds | gana |
+|---|---|---|---|
+| 1 | cierre de imagen `build` vs `runtime` (+ el vigía como definición) | **0** | −2,5 GiB en servidor, −7,3 en KDE, y el toolchain fuera del producto |
+| 2 | decidir el setuid y quedarse con **una** de `sudo`/`doas` | 0 | cierra un «sella y no corre» antes de que sea visible |
+| 3 | `kmod` a BORRAR en el censo de busybox (23 → 11 applets) | 0 | no se escribe lo que nadie necesita |
+| 4 | la rama BIOS: receta de `grub` o retirarla | 0 | ningún binario ajeno sin hash en el arranque |
+| 5 | `chrony` → `ntpd-rs` | 0 | una hoja C vital menos, con drop-in Rust |
+| 6 | **ADR 0012** | — | la granja deja de compilar de a uno |
+| 7 | `meson` → `muon` (medir 10 antes) | 107 | un intérprete menos en el sandbox |
+| 8 | `zlib-rs` en UN consumidor, para decidir | 1 | la lib C más consumida del corpus, con la misma ABI |
+
+Los cinco primeros **no cuestan una sola reconstrucción**. Eso es lo que los pone antes: la lección
+de `uutils`/`brush`/`foot` —trabajo hecho y no cobrado— aplicada al calendario.