`bzcmp`, `bzegrep`, `bzfgrep` y `bzless` son symlinks con **la ruta del SANDBOX horneada dentro**.
El Makefile de bzip2 los crea con `ln -s $(PREFIX)/bin/bzdiff bzcmp` —con `$(PREFIX)` DENTRO del
destino— y como bzip2 **no soporta `DESTDIR`**, la receta pasa `PREFIX=/out/usr` (el mismo truco que
valkey) ⇒ el destino que queda es `/out/usr/bin/bzdiff`. Al hidratar, esos cuatro apuntan a un
directorio que en el sistema real no existe.
⚠ **Cinco indicadores en verde sobre cuatro comandos que no funcionan**: el artefacto tiene
contenido, `bzip2`/`bzgrep`/`bzdiff` corren, `static-audit` pasa, el grafo lo cuenta y la receta
REPRODUCE. Ninguna métrica existente mira a dónde apunta un enlace.
Arreglo: rehacerlos RELATIVOS después del install, que es lo correcto para un artefacto direccionado
por hash — un enlace relativo sigue valiendo esté el árbol montado donde esté. Verificado: los cuatro
resuelven y `bzip2 -c | bzcat` sigue dando `hola`.
## El guardián, en el mismo sitio y por la misma pregunta
`--auditar-raices` ya contestaba «¿esta receta dejó ficheros donde no van?». Ahora contesta también
la otra mitad: «¿dejó ENLACES a una raíz que no es del FHS?». Son la misma familia —un `install` que
se equivocó de destino— y comparten la definición de `FHS_RAIZ`, que es lo que evita dos listas que
se desincronizan.
La regla es independiente del perfil, y por eso vale sobre el store entero: un enlace a OTRO
artefacto es normal (`kinfocenter -> /usr/bin/systemsettings`, los dos en `escritorio-kde`, resuelve
en el rootfs fundido). Lo que nunca puede estar bien es un destino absoluto cuya primera componente
no sea del FHS: `/out`, `/src`, `/tmp` son rutas del lab.
Dos correcciones al propio guardián, las dos aprendidas midiendo:
· **Sólo audita el artefacto VIGENTE de cada receta.** El store guarda todos los sellados históricos,
así que la primera versión acusaba a bzip2 por el artefacto YA SUPERADO — el mismo sobre-reporte
que `static-audit.sh` tuvo que quitarse de encima. El hash vigente sale de los grafos de estado,
no de 1150 llamadas a `takana hash`. ⚠ Y hay que regenerar **los cinco** grafos: con sólo el
principal regenerado, el de KDE seguía apuntando al bzip2 viejo y el filtro lo dejaba pasar.
· **Un barrido que no miró NADA lo dice y sale 2.** Filtrar por hash vigente hace que un `--store`
que no case con los grafos deje cero artefactos auditados, y sin la guarda eso se imprimía como
«✓ 0 ofensores»: una respuesta falsa con forma de respuesta. Probado: `--store /tmp` → exit 2.
El control del hallazgo es el propio arreglo, sobre datos reales y no sintéticos: antes del fix el
barrido nombra los cuatro enlaces de bzip2; después, `✓ ninguno`.
Barrido de TEXTO puro: 1427 líneas de comentario TOML. Cero ArtifactHash
movidos, y eso está MEDIDO, no deducido: hasheé las 741 antes y después y los
ficheros de hashes son idénticos byte a byte (723 con hash real + 18 que ya no
hasheaban de antes).
El guardián valió la pena: el primer barrido, filtrando por 'la línea empieza
con #', movió el hash de helix, lsof y steam-runtime-sniper. La causa es que una
fase se escribe como compile = <triple> ... <triple> y sus comentarios de SHELL
también empiezan con #, pero viven dentro del VALOR — y las fases sí entran en
hash_inputs. El barrido ahora calcula los rangos de las cadenas multilínea de
TOML y no entra ahí.
Quedan intactos a propósito: .hammer-zig-cc y .hammer-cargo-vendor (literales
dentro de fases), hammerd, hammer-recover y toda ruta que empiece por /
Primera tanda con el orden nuevo (por impacto en el grafo, no alfabético). 30 recetas C de
clase HOJA: bash, coreutils, git, gnupg, grep, gzip, jq, e2fsprogs, libarchive, htop…
LA MEDIDA QUE JUSTIFICA EL CAMBIO DE ORDEN: activar estas 30 dejó 41 recetas sin artefacto
vigente = las 12 que ya estaban en deuda + las 30 tocadas (una ya estaba entre las 12). **Cero
colateral.** Contra la tanda anterior, donde tocar TRES bibliotecas base (expat, zstd, ncurses)
dejó 54 sin artefacto y tumbó la cadena wlroots/sway entera.
Mismo esfuerzo de build, diez veces menos destrozo. El orden no era un detalle de comodidad.
Selección: clase `c` + `dependientes_total == 0` + estado sellado, excluyendo las de tawasuyu
(git privado ⇒ no construyen en el worker, que es sin secretos por diseño) y las Go/Rust, que
necesitan sus módulos y fallan ahí. Quedan 73 hojas C elegibles; van 30.
La tanda corre DESASIDA con nohup en el worker — la lección de ayer, cuando un drenaje murió a
las 14 de 54 al caerse la sesión ssh.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
La deuda legal bloqueante del SDD 19 §2.1. Medido hoy: **0 de 1141 recetas** declaraban
licencia, no «5 de 771» como decía el informe anterior. Los dos números estaban mal: los
«5» eran falsos positivos de `grep license` (el paquete `addlicense`, el paquete
`cargo-bundle-licenses`, una línea `install .../share/licenses/` y un comentario), y las
recetas son 1141. Contar con `grep -l <palabra>` sobre TOML cuenta comentarios y nombres,
no campos; `scripts/licencias.sh` cuenta el campo de verdad (clave en la raíz, antes del
primer `[table]`).
LO QUE HACE LA DEUDA PAGABLE: `Recipe::hash_inputs` es una LISTA BLANCA — sólo entran
source, compiler, target, link, patches, flags, phases y deps. `license` no entra, igual
que `evidence` y `slots`. Por eso se puede poblar en las recetas YA SELLADAS sin mover un
solo ArtifactHash. Verificado, no supuesto: en 40 recetas modificadas se comparó el hash
con y sin la línea — 40 idénticos, 0 cambiados. Si el campo entrara al hash, declarar la
licencia costaría reconstruir el corpus entero y no se haría nunca. Clavado con el test
`licencia_round_trip_y_no_afecta_el_hash`.
TRAMPA DE TOML: una clave suelta después de un `[table]` pertenece a esa tabla. Puesta al
final del fichero, `license` acaba dentro de `[deps]` y se pierde EN SILENCIO, porque serde
ignora los campos que no conoce — no hay error, simplemente no está. Va arriba, junto a
`name` y `version`; el sembrador la inserta tras `version`.
NO SE ADIVINA. Declarar mal una licencia es peor que dejarla vacía: convierte un hueco
visible en una afirmación falsa. Sólo se puebla desde una tabla curada entrada por entrada
(`docs/licencias-conocidas.tsv`); lo que no tiene evidencia queda vacío y se CUENTA.
Concretamente se descartó el atajo «k* = KDE ⇒ LGPL»: en este catálogo `kail`, `kind`,
`ko`, `kopia`, `krew`, `kustomize`, `kyverno`, `katana`, `kibi`, `kmon` y toda la familia
`kube*` son herramientas Go sin relación con KDE. El nombre no es evidencia.
Quedan 913, casi todas CLIs Go/Rust importados en masa — y ésas sí son automatizables con
evidencia real: Cargo.toml trae el campo `license` y los módulos Go traen su LICENSE en el
árbol. El cierre estructural es capturarlo en la fase de fetch, que ya descarga y extrae
cada tarball, y inyectar el texto en `hammer pack` (aguas abajo del ArtifactHash) en vez de
en la fase install (que sí re-hashearía).
De paso, respaldo-storagebox.sh reordenado por valor irreemplazable y con zstd: medido en
la oficina, el uplink da 8 Mbps iguales por cable y por wifi ⇒ 128 G no caben en una
sentada, así que sube primero el cerebro (estado + repo) y `--partial-dir` hace que cortar
a mitad de un artefacto no tire lo ya subido.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
El frente contaba `compiler = "gcc"` y no miraba las FASES. bzip2 declaraba
compiler=zig-cc y decía "migrado de gcc, matar-gcc 2026-07-16" en la cabecera,
mientras su compile pasaba `CC=gcc`: migración cosmética, cambió el campo y no
el build. El Makefile de bzip2 además HARDCODEA CC=gcc, así que un `make` a
secas tampoco habría usado zig.
Barrido: 7 recetas invocan gcc en sus fases sin declararlo. 4 son escapes ya
conocidos (los 3 kernels + cmake); las otras 3 son deuda que nadie contaba:
bzip2 (ésta), pigz y cargo-edit.
De paso cumple link=static: el -static que hammer exporta (lib.rs:261) se
pierde si la receta no lo pasa al make de un Makefile custom.
OJO -static y NO -all-static: -all-static es flag de LIBTOOL (patrón de
jq/parted/shadow/procps-ng); bzip2 usa Makefile crudo y el flag llega tal cual
al compilador → "error: Unknown Clang option: '-all-static'".
Verificado: 0 NEEDED, corre en el host, comprime/descomprime, bit-repro
b3:48b91bb1b658 ×2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Primera tanda del rollout aprobado. harkaq nombró 47 recetas compiler=gcc;
migra-zig.sh midió que las C-puras migran a zig (~85%). Estas 11 se verificaron
en el LAPTOP (store completo, fiable): construyen con zig-cc Y —las que tienen
binario— corren limpio.
bzip2 expat json-c gzip htop less libpng libyaml zstd libffi xz
El compiler=gcc era deuda histórica: se marcaron con zig 0.13/0.16 (que
segfaultaban C clásicos), zig mejoró, el gcc quedó por inercia. Verificado
in-place: bzip2→b3:86a33b76, json-c→b3:9cbe76e3.
11 dependencias menos del gcc de Alpine. Quedan ~25 C-puras candidatas del VPS
por verificar en el laptop (el store parcial del VPS daba build-falla espurios
por deps faltantes — la medición fiable es la del laptop) + las 11 Rust
(sys-crate C, otra evaluación).
OJO: re-hashea estas 11 (estaban en el índice firmado 748); el índice necesita
re-firma en el próximo packaging.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cosecha desatendida de la campaña harkaq. Cada dep de acá la denunció el audit de
Landlock en un build real: el kernel vio al build usarla sin declararla, y el
store del hub confirma qué artefacto la provee (deuda DECLARABLE, §4.5).
Recetas:bash binutils busybox bzip2
No se tocó ninguna receta con deuda IRREDUCIBLE: declarar algo que el store no
provee rompería el build en vez de arreglarlo. Esas van a tandas/needs-review-harkaq.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
zstd 1.5.7 (libzstd.a — zstd-sys en montones de crates), xz 5.8.3 (liblzma.a),
bzip2 1.0.8 (libbz2.a), libsodium 1.0.22 (libsodium.a). Compresión/cripto =
deps de muchísima cola C + crates -sys. compiler=gcc, estáticas.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>