El plan de botar busybox decía de sí mismo que el reparto applet→dueño era
«juicio, no medida». Medido contra los 1407 artefactos del store: de los 277
applets vivos, 167 los provee el dueño que el TSV nombra, 31 los provee OTRO
paquete, y 79 no los provee NADIE salvo busybox — 56 más de los 23 previstos.
El hallazgo transversal es que una receta de LIBRERÍA no es un paquete de
HERRAMIENTAS, y build-state no distingue: `xz` figura en SEIS perfiles con el
`usr/bin` VACÍO (su receta pasa --disable-xz --disable-xzdec --disable-scripts,
o sea liblzma y nada más), y `ncurses` instala sólo install.libs/install.includes
⇒ ni clear, ni reset, ni tput. Las dos viajan, las dos dicen `sealed`, y el único
binario que hay adentro de la imagen es el applet de busybox.
Y al revés: `switch_root` y `sulogin` los trae util-linux, `partprobe` lo trae
parted, y `getty` tiene sustituto — `agetty` de util-linux, en los 7 perfiles ⇒
deja de ser ESCRIBIR y pasa a ser migrar el exec de la card. Seis de los 18
huecos de iproute2 (ipaddr iplink ipneigh iproute iprule iptunnel) no son
comandos de nadie: son nombres internos de busybox y se borran del defconfig.
uutils traía 78 applets, no los 90 que el plan le atribuía: el multicall
contestaba `coreutils: unknown program 'chmod'`. Construía con las default
features (feat_common_core) y el resto vive detrás de feat_os_unix_musl, que
cubre exactamente los 25 que faltaban. Construido y verificado contra un store
tirable: 79 → 106 applets, 27 nuevos y CERO perdidos, chmod/uname/id ejecutan, y
`stdbuf` correctamente ausente (por eso la variante _musl y no feat_os_unix:
upstream lo excluye en musl porque necesita un cdylib).
Importaba más de lo que parece: USERLAND_COMPONENTS hidrata uutils DESPUÉS de
busybox para ensombrecerlo, y lo que no existe no ensombrece nada — chmod, chown,
id, uname y stat los seguía dando busybox con el userland Rust instalado encima.
⚠ Dicho en la receta y en el documento: who/users/uptime/pinky compilan contra
los stubs de utmpx de musl y contestan vacío. No es regresión, tampoco es
sustitución.
El censo queda como script (scripts/busybox-censo-proveedores.py) y como dos
columnas del TSV, no como una tabla escrita a mano que vuelva a envejecer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Esta mañana eran 0. Tras la tabla curada (228) y este paso, 936. Ninguna receta se
re-hasheó: verificado en 30 de las 708 tocadas, 30 hashes idénticos, 0 cambiados.
DE DÓNDE SALE EL DATO, Y POR QUÉ NO ES ADIVINAR. `scripts/licencias-detectar.sh` consulta
la API /repos/{o}/{r}/license de GitHub para las 673 recetas cuya fuente vive allí. Eso
devuelve el resultado de DETECTAR el fichero LICENSE que el repo tiene de verdad
(licensee), no una etiqueta escrita a mano en una web: es la misma evidencia que veríamos
abriendo el tarball, obtenida sin bajar 673 tarballs por un enlace de 8 Mbps. 610 con SPDX
definido; las 63 que GitHub marca NOASSERTION/other se DESCARTAN — un «no sé» de la fuente
se propaga como hueco, no se redondea a una licencia plausible.
Las familias no-GitHub van curadas por la URL DE FUENTE, que es la evidencia que el nombre
no da. Y ahí cometí el error simétrico al que este mismo fichero advertía: había puesto
`knighttime` entre las «herramientas Go» POR SU NOMBRE, y su tarball sale de
download.kde.org — es un Framework de KDE, LGPL. Descartar por nombre falla igual que
aceptar por nombre. Corregido en la cabecera.
LAS DOS IMPRECISIONES, contables con `licencias.sh --revisar` en vez de escondidas:
1. SPDX OBSOLETOS Y AMBIGUOS (71 recetas). GitHub devuelve `GPL-3.0`, `LGPL-2.1`,
`AGPL-3.0`… identificadores que SPDX declaró obsoletos PRECISAMENTE porque no
distinguen `-only` de `-or-later`, y esa diferencia decide con qué se puede combinar el
paquete. NO se normalizan a ciegas: mapear GPL-3.0 → GPL-3.0-or-later sería inventar el
dato que falta. Quedan marcadas para resolver mirando el fuente.
2. LICENCIAS DOBLES COLAPSADAS. La API devuelve UNA sola licencia y muchos proyectos Rust
son «MIT OR Apache-2.0» (p.ej. `fd` quedó como Apache-2.0). No es falso —cumplir una de
las opciones concedidas basta— pero es incompleto. Lo resuelve el cierre estructural:
leer el campo `license` del Cargo.toml en la fase de fetch.
Y un fallo que habría escrito basura en silencio: `licencias-detectadas.tsv` tiene TRES
columnas (añade el owner/repo consultado, para poder auditar) y el sembrador leía dos, así
que la licencia se habría llevado pegado el slug — `license = "MIT<TAB>owner/repo"`, sin
que nada lo validara. Arreglado antes de sembrar.
Quedan 205 sin licencia y 71 por desambiguar.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Primera pieza del userland Rust-nativo (Etapa C): adopta uutils/coreutils 0.9.0
(MIT) como receta Cargo — el patrón de hammerd/arje-zero, no recompila C ni se
escribe de cero. Multicall static-musl (12 MB) + 79 symlinks por applet
(ls/cp/cat…), drop-in de busybox/GNU coreutils.
- Esquiva el miscompile de zig por construcción: codegen rustc/LLVM, zig cc sólo
como linker.
- `--bin coreutils` (no `-p`): el paquete tiene lib+bin y `cargo rustc -- flags`
exige un único target (gotcha vs arje-zero).
- Install custom (sólo esa fase; configure/compile siguen auto-Cargo) crea los
symlinks con `coreutils --list`.
Construido + sellado + verificado funcional (ls/cat/echo vía dispatch argv0).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>