Commit Graph
1222 Commits
Author SHA1 Message Date
sergioandClaude Opus 5 78ad2e8201 docs: SDD 20 — corregido dónde va el texto de licencia (imágenes, no pack) y estado real 1063/1141
El catálogo decía que el texto de la licencia se inyecta en `hammer pack`. El razonamiento
del coste era correcto pero el sitio estaba mal: pack emite un .swm (receta sobre fuente
pública, sin binarios) e install reproduce construyendo, así que el canal de paquetes no
distribuye binarios. El punto de entrega es la IMAGEN, y ahí está hecho, con veto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 14:53:54 -04:00
sergioandClaude Opus 5 be4e91086c licencias: el texto dentro de la imagen, con veto — y el SDD 19/20 se equivocaba de sitio
═══ LA CORRECCIÓN DE FONDO: no es `pack`, son las IMÁGENES ═══
El SDD 19 y el 20 decían «inyectar el texto de la licencia en `hammer pack`, que es aguas
abajo del ArtifactHash y sale gratis». La conclusión sobre el coste era correcta pero el
SITIO estaba mal, y por una razón que sólo se ve leyendo el código: `pack` produce un
`.swm`, que es una RECETA DE TRANSFORMACIÓN sobre fuente pública y por diseño explícito
«NUNCA transporta binarios cocidos»; e `install` REPRODUCE construyendo, con cache-hit del
corpus, en vez de bajar binarios. O sea que **el canal de paquetes de hammer no distribuye
binarios** y la obligación de acompañar-el-binario ahí casi no aplica.

Donde sí aplica, con toda su fuerza, es en la IMAGEN INSTALABLE: su rootfs se puebla con
`hammer install` sobre un prefix y de ahí sale un USB/ISO lleno de binarios que se le da a
alguien. Ése es el punto de entrega. (La obligación del espejo de FUENTES es otra y sigue
entera: las recetas apuntan a URLs de terceros que se caen.)

═══ LO QUE ENTRA ═══
· `scripts/licencias-textos.sh` + `licenses/`: los 44 textos CANÓNICOS de SPDX
  (spdx/license-list-data), uno por identificador realmente en uso — la lista se saca de las
  recetas, no de un fichero escrito a mano que se desincroniza. 764K.
· `scripts/licencias-rootfs.sh`: escribe /usr/share/licenses/<pkg>/ con el texto de CADA
  licencia de la expresión (`Unlicense OR MIT` ⇒ los DOS textos: en un OR el destinatario
  elige, y entregar uno solo le quita una opción que el autor le concedió), más un
  MANIFEST.tsv. Medido: 1,0 MB para el perfil `cli` de 44 paquetes.

═══ Y SOBRE TODO: ES UN VETO ═══
Sale ≠0 si algún paquete del rootfs no declara licencia. Un informe que no puede bloquear no
cambia lo que ocurre; esto corta el paso. Y se ganó el sueldo al primer intento: reveló que
los perfiles `base` y `cli` —los dos más simples, los que primero se publicarían— llevaban
10 y 12 paquetes con binarios y licencia desconocida. Curados los 10 que se podían afirmar
(doas, iputils, less, mandoc, procps-ng, rsync, strace, sudo, tree, usbutils); ambos perfiles
bajan a 2.

Esos 2 NO se rellenan a propósito, y quedan vetando:
 · lsof   — licencia propia del proyecto, sin identificador SPDX. Toca `LicenseRef-lsof` con
            su texto, que ya viaja en el tarball (la receta instala su COPYING).
 · tzdata — dominio público por declaración de sus autores, y SPDX no tiene identificador
            para eso (es una AUSENCIA de licencia, no una licencia). Toca
            `LicenseRef-PublicDomain` documentado, no forzar uno de la lista.

De paso: normalizado `GPL-3.0+` (sufijo antiguo) a `GPL-3.0-or-later`, misma equivalencia
documentada que la barra de Cargo. Y el caso especial que puse para `Linux-syscall-note`
(«las excepciones viven en exceptions/») era una suposición razonable y FALSA: SPDX las
publica en el mismo `text/`; ese directorio no existe.

1063 de 1141 (93%). Hashes verificados: 0 movidos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 14:53:20 -04:00
sergioandClaude Opus 5 487e155f74 licencias: leer el LICENSE de verdad cuando GitHub dice «no sé» — 1053 de 1141 (92%)
Último recurso mecánico, `scripts/licencias-texto.sh`: para las 60 recetas donde la API de
GitHub devuelve NOASSERTION (el repo TIENE un LICENSE pero licensee no lo reconoce: texto
retocado, encabezado propio, dos licencias en un fichero), baja el texto y lo clasifica acá.
34 identificadas — 16 MIT, 10 Apache-2.0, 7 BSD-2-Clause, 1 BSD-3-Clause.

SÓLO PERMISIVAS, Y NO ES PEREZA. MIT, Apache-2.0, BSD, ISC, MPL-2.0 y Unlicense se
reconocen por frases inconfundibles y NO tienen variantes -only/-or-later ⇒ reconocer el
texto da el SPDX completo.

La familia GPL se deja al humano A PROPÓSITO, y la razón es sutil: el COPYING de la GPL es
IDÉNTICO tanto si el proyecto es «sólo v3» como «v3 o posterior» — lo que las distingue vive
en las cabeceras de los fuentes. Y encima el propio COPYING incluye, en su apéndice «cómo
aplicar la licencia», la frase «or (at your option) any later version», así que buscarla da
SIEMPRE positivo y parecería evidencia siendo texto de plantilla. Es exactamente la trampa
de la regla del `.a` no-PIC, donde grep contaba reubicaciones de .debug_* y mentía.

Comprobado a mano un caso que daba mala espina: el LICENSE de `conftest` empieza
«Conftest — Write tests against your config files / Copyright (C) 2019 …», que es el formato
típico de una cabecera GPL. Leído entero, dice «Licensed under the Apache License, Version
2.0». La clasificación era correcta; la sospecha, barata.

Hashes verificados sobre las 36 tocadas: idénticos.

Quedan 88, ya sin vía mecánica: los repos donde ni la API ni el texto deciden, la familia
GPL sin desambiguar, y tarballs de sitios propios (gmplib, xiph, sr.ht, codeberg…).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 14:06:59 -04:00
sergioandClaude Opus 5 0225aa4e08 licencias: el código propio (tawasuyu) queda en MIT — 1017 de 1141 (89%)
Decisión del usuario: «tawasuyu está en MIT». Es la misma licencia que hammer (LICENSE en
la raíz + Cargo.toml), así que todo lo nuestro queda coherente bajo un solo término. Las 20
recetas con fuente en git.tawasuyu.net: arje-* (el init y sus compat), mirada-* (el
compositor, el greeter y mirada-ctl), netup, agora-cli, cosmos-cli, dominium-cli,
tinkuy-sim, llimphi-counter y libelogind.

⚠ ANOTADO EN LA TABLA porque es una trampa que casi pisé: `libelogind` NO es el elogind de
upstream (que sería LGPL-2.1+). Por el nombre lo parece; su cabecera dice que es una
reimplementación PROPIA de la C-ABI sd-login dentro de arje-compat. Declararla LGPL «porque
se llama así» habría sido exactamente el error que este fichero prohíbe — el tercero del
mismo tipo hoy, después de `knighttime` (parecía Go y es KDE) y de la familia `kube*`
(parecen KDE y son Go). El nombre nunca es evidencia; la fuente sí.

Hashes verificados sobre las 20: idénticos.

Quedan 124 sin declarar, ya todas de terceros.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 14:01:15 -04:00
sergioandClaude Opus 5 c8d1357f06 licencias: la declaración del autor manda — y arreglo 5 recetas que ROMPÍ y commiteé
Dos cosas: una mejora de calidad y un fallo mío que hay que contar entero.

═══ EL FALLO: escribí un error JSON dentro de 5 recetas y las commiteé rotas ═══
En `a6a9d59`, cinco recetas quedaron con
    license = "{"message":"Not Found","documentation_url":"...","status":"404"}"
Causa: cuando el repo da 404 (renombrado, borrado, privado), `gh api` escribe el CUERPO DEL
ERROR en stdout y el filtro `--jq` falla, así que la variable queda valiendo el JSON. Mi
`case` sólo descartaba ""/null/NOASSERTION/other ⇒ el JSON pasó como si fuera una licencia.
Las comillas ROMPEN el TOML, y esas 5 recetas dejaron de parsear y de tener ArtifactHash.

No lo vio nadie leyendo: lo cazó la comparación de hashes, al detectar que `cargo-sort`
había cambiado. Afectadas: cargo-sort, cargo-audit, cargo-binstall, waybackurls, usbutils.

La lección no es «se me pasó un caso», es que **validé los «no sé» conocidos en vez de la
FORMA del dato**. Ahora se acepta sólo lo que tiene forma de expresión SPDX, y la barrera
está en los DOS sitios que escriben (detectar y sembrar), más un GUARDIÁN en el informe que
sale ≠0 si alguna licencia tiene forma imposible. Un fallo aguas arriba disfrazado de dato
hay que verlo sin buscarlo.

Y de paso normalizadas 11 licencias con la barra antigua de Cargo (`MIT/Apache-2.0`), que
no es SPDX válido. Traducir la barra a OR no es inferir: Cargo documentó esa equivalencia.

═══ LA MEJORA: `scripts/licencias-cargo.sh` ═══
Lee la licencia que el AUTOR declara en su Cargo.toml, pidiéndola AL TAG QUE LA RECETA
PINEA (`?ref=v<version>`, con HEAD como último recurso y anotando cuál se usó). 176
obtenidas, 167 al tag exacto. Es la fuente más autoritativa de las que usamos y corrige los
dos defectos de la detección por API de una vez:
 · las licencias DOBLES dejan de colapsar: `fd` pasa de `Apache-2.0` a `MIT OR Apache-2.0`,
   `ripgrep` a `Unlicense OR MIT`, `bat` a `MIT OR Apache-2.0`;
 · los SPDX AMBIGUOS caen de 71 a 55, porque el autor sí escribe -only/-or-later.
66 corregidas, 7 nuevas. Y aparecieron cosas que la detección había perdido: `eza` es
EUPL-1.2, una copyleft europea que se habría empaquetado creyendo otra cosa.

Jerarquía de evidencia, escrita en la cabecera del script: nombre (prohibido) < familia/URL
de fuente < API de licencias de GitHub < Cargo.toml del autor.

997 de 1141 (87%). Verificación COMPLETA sobre las 77 recetas tocadas —no una muestra—:
0 hashes cambiados, y 5 que ahora parsean y en HEAD no.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:51:09 -04:00
sergioandClaude Opus 5 5e42d201b1 respaldo: reintentar los cortes de red — el primero murió a los 2,3 G
La primera corrida real subió el cerebro (estado + repo, lo irreemplazable) y 2,3 G de
store, y entonces murió: `Connection reset by peer` / `Broken pipe`, rsync 255. Con ~19 h
de subida sobre un enlace de oficina, que la conexión se corte NO es un accidente: es lo
normal. Y un respaldo que hay que relanzar a mano cada vez que se cae no se completa nunca,
porque nadie está mirando.

Como rsync ya es incremental y `--partial-dir` conserva los trozos a medio subir,
reintentar es barato y seguro: cada intento retoma donde quedó, no reempieza. El bucle
insiste sólo ante fallos de RED (10/12/23/30/35/255) con espera creciente hasta 60 s; un
error de verdad —permisos, destino lleno, ruta inexistente— sale a la primera en vez de
repetirse 40 veces contra la misma pared.

DE PASO, UN ERROR MÍO QUE VALE REGISTRAR: comprobé el respaldo con `pgrep -f
respaldo-storagebox` y dijo que corría, cuando llevaba rato muerto — el pgrep se estaba
matcheando A SÍ MISMO, porque la cadena buscada está en la propia línea de comando del
shell que la ejecuta. Ya me había pasado hoy con el worker. La comprobación buena es mirar
lo que el proceso PRODUCE (bytes en el destino, última línea del log), no si hay un proceso
vivo. Es la misma lección que el guardián de store-gc: un `rm` que devuelve 0 no prueba que
el fichero se fue, y un proceso vivo no prueba que esté avanzando.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:30:58 -04:00
sergioandClaude Opus 5 fe474dab63 licencias: 994 de 1141 (87%) — cerradas las familias Qt, freedesktop, kernel.org y PyPI
Tercera tanda curada, agrupada por la URL DE FUENTE de cada receta (que es la evidencia que
el nombre no da): los once módulos Qt sin prefijo, freedesktop (dbus, libinput, libevdev,
libdisplay-info, poppler, pulseaudio, upower, NetworkManager, ModemManager, polkit),
kernel.org (los tres kernels, linux-headers, git, iproute2, libuuid) y PyPI.

Los kernels llevan `GPL-2.0-only WITH Linux-syscall-note` explícita. No es adorno: sin esa
excepción, todo binario de espacio de usuario que hace un syscall sería obra derivada del
kernel. Es exactamente el tipo de dato que un campo de licencia existe para no perder.

Quedan 147 sin declarar: 69 de GitHub donde la propia API dice NOASSERTION (hay que abrir
el fuente), 20 de tawasuyu —que son nuestras o del otro agente, así que la licencia la
decide el usuario, no yo— y el resto repartido en GNOME, gitlab.freedesktop y sueltos.
Más 71 con SPDX ambiguo pendientes de desambiguar (`licencias.sh --revisar`).

Hashes verificados otra vez sobre 20 recetas tocadas: 20 idénticos, 0 cambiados.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:29:31 -04:00
sergioandClaude Opus 5 a6a9d59e4f licencias: 936 de 1141 (82%) — detección por evidencia, con sus dos imprecisiones declaradas
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>
2026-08-07 13:26:22 -04:00
sergio 6060b9bbcd estado: cosecha granja 2026-08-07T16:49:23Z — avance del árbol KDE 2026-08-07 12:49:23 -04:00
sergioandClaude Opus 5 7b2489a03c deuda del corpus: las 12 no eran un problema, eran cuatro — medido en el worker
Fui a destrabar «las 12 recetas en deuda» con el diagnóstico heredado (fallan por el rootfs
del worker: python OSError en meson, find_package en cmake). Lo probé construyéndolas de
verdad y **el diagnóstico es falso**: cmake y meson corren perfectamente ahí. Son cuatro
situaciones distintas y ninguna es el rootfs.

(a) SEIS ESTÁN SUPERADAS, NO EN DEUDA. `gtk4` del corpus es link=static y enlaza
    libfontconfig.a, que tiene 1122 reubicaciones no-PIC ⇒ 13.032 errores
    `R_X86_64_64 cannot be used against local symbol`. No es una receta rota: es imposible.
    Y mientras tanto recipes/incoming-gnome/gtk4.toml (link=dynamic, deps -shared) YA ESTÁ
    SELLADA, con libadwaita y fontconfig-shared. O sea que la cadena estática del corpus
    —gtk4, libadwaita, gtksourceview y los tres hello/edit que cuelgan— es un DUPLICADO
    superado de la cadena dinámica de GNOME.
    Cerrarla no es construirla: es decidir si se promueven las sombras -shared al corpus,
    porque la resolución de deps es hermano→padre. Es una decisión de arquitectura, y hay
    aviso registrado de que promover a ciegas hace que variantes homónimas pisen recetas
    canónicas. NO la tomo yo.

(b) wlr-randr: cerrada en el commit anterior.

(c) dwarves: frente propio con muro identificado, no deuda. Nuestro elfutils entrega SÓLO
    libelf a propósito (libdw arrastra argp/obstack/fts, lo difícil en musl). No se arregla
    ampliando elfutils: de él cuelga el kernel que ya reproduce bit a bit. El camino es una
    receta aparte `elfutils-libdw`, con el patrón de las sombras -shared. Sin urgencia:
    ningún perfil pide dwarves y el kernel desactiva DEBUG_INFO_BTF justamente por su
    ausencia. Queda escrito en la cabecera de la receta, que es donde se va a leer.

(d) mirada-compositor, mirada-greeter, llimphi-counter: source por SSH a
    git.tawasuyu.net y el worker es SIN SECRETOS por diseño ⇒ HUB-ONLY estructural, no
    fallo. llimphi-counter ni siquiera llega a construir: su commit son ceros con el
    comentario «fijar al commit real». Receta sin terminar, y es de tawasuyu.

EL HALLAZGO DE FONDO: el bucle del worker sólo recorre las colas incoming-*; el corpus
(recipes/) NO está en QUEUES. Las 12 nunca se habían intentado allí. Buena parte de «la
deuda» era de ENCOLADO, no técnica — y por eso el diagnóstico heredado nunca se verificó.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 12:27:03 -04:00
sergioandClaude Opus 5 f24b52df18 wlr-randr: SELLADA — eran dos deps ausentes, no el rootfs del worker
Primera de las 12 recetas del corpus en deuda, cerrada. Y de paso desmiente el diagnóstico
heredado («las 12 fallan por el rootfs del worker: python OSError en meson, find_package en
cmake»). Medido hoy contra el worker, las causas son CUATRO distintas y ninguna es el
rootfs:

 · gtk4 construye ahí SIN TOCAR NADA. Nunca había fallado: el bucle del worker sólo recorre
   las colas `incoming-*` y NO incluye el corpus, así que las 12 jamás se intentaron. La
   deuda no era técnica, era de encolado.
 · wlr-randr (ésta): dos deps que faltaban en la receta.
 · dwarves: FindDWARF no halla las libs ELF/DWARF pese a declarar elfutils. Pendiente.
 · mirada-compositor, mirada-greeter, llimphi-counter: `repo = gitea@git.tawasuyu.net:...`
   por SSH. El worker es SIN SECRETOS por diseño ⇒ son HUB-ONLY estructuralmente, no un
   fallo. Y llimphi-counter ni siquiera es un fallo de build: su commit es literalmente
   ceros, con un comentario «fijar al commit real» — es una receta sin terminar, y es de
   tawasuyu.

LAS DOS DEPS DE ESTA RECETA, que son dos lecciones distintas:

 1. `python3` — porque **meson es un script de Python, no un binario**. Faltando el
    intérprete la fase muere con exit 127, que se lee como «meson no está» cuando meson SÍ
    está y se hidrata bien. gtk4/libadwaita/gtksourceview construyen en el worker
    precisamente porque sí lo declaran. Auditadas las 1141 recetas: wlr-randr era **la
    única** con meson y sin python3. Regla: meson en deps ⇒ python3 al lado.

 2. `libffi` — que wlr-randr no usa. Lo exige `wayland-client.pc` en su línea `Requires`, y
    pkgconf resuelve el grafo de .pc completo antes de emitir un cflag. Patrón ya
    documentado `.pc Requires` → `[deps].build`; el síntoma («Package 'libffi', required by
    'wayland-client', not found») culpa a wayland, que es inocente.

En ambos casos el arreglo es hidratar la dep, NO instalar nada en el rootfs del worker:
engordarlo haría que el build dependa de qué hay en una máquina concreta, que es justo lo
que rompe la reproducibilidad.

Re-hashear sale gratis: la receta estaba en deuda, nunca se había sellado.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 12:23:28 -04:00
sergioandClaude Opus 5 e79b26970a docs: SDD 20 (catálogo hasta publicable y hasta completa) + SDD 21 (manual del piloto)
Pedido del usuario: «el catálogo de todo lo que falta hasta decir la distro está
publicable, y lo de más hasta la distro está completa. Y un tipo manual, todos los pasos
que tengo que ir haciendo en las tres etapas que marcan esos dos codos.»

SDD 20 = el inventario, medido contra el repo (grafo, no recuento a mano). SDD 21 = el
manual de a pie, un comando por vez, marcando qué puede hacer el agente y qué es del
usuario.

LO QUE APARECIÓ AL MEDIR, y que no se sabía:

· La deuda de construcción NO son 12+12+13+12 recetas: es UNA lista de 12 vista desde
  cuatro grafos (la cadena GUI de la cascada de mesa). Un solo arreglo —declarar python3 y
  cmake en `[deps]`, no engordar el rootfs del worker— las destraba todas. `base` y `cli`
  ya están al 100%, KDE cierra 162/162.

· De WMs Wayland ligeros no hay NINGUNO: sólo `foot` y `mako`. Falta wlroots entero, los
  compositores y todos los accesorios (waybar, fuzzel, swaybg, grim, slurp, wl-clipboard).

· De aplicaciones gráficas de terceros hay CERO. Ni visor de imágenes ni lector de PDF.

· Firefox no es «una receta más»: su cadena de `*-sys` con C++ es justo la frontera que el
  techo MSRV del sandbox (1.96) no cruza. Antes de Firefox hay que subir el techo; empezar
  por la receta es empezar por el final.

· El texto de la licencia va inyectado en `hammer pack`, NO en la fase install: las fases
  entran al ArtifactHash, así que hacerlo en la receta re-hashearía las 1141. Pack es aguas
  abajo y sale gratis. Misma lógica que hizo pagable el campo `license`.

Dos correcciones al SDD 19, que escribí yo ayer y tenía mal:
· «5 de 771 recetas declaran licencia» — falso por partida doble. El número real era 0 de
  1141; los «5» eran falsos positivos de `grep license` (nombres de paquete, una línea de
  install, un comentario) y 771 son los nodos del grafo del corpus, no las recetas.
· «El repo no declara licencia» — falso: hay LICENSE (MIT) en la raíz y en Cargo.toml. No
  lo miré antes de escribirlo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:28:37 -04:00
sergioandClaude Opus 5 346cd59706 licencias: campo license en la receta — de 0 a 228 de 1141, sin re-hashear nada
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>
2026-08-07 10:24:11 -04:00
sergioandClaude Opus 5 b12ea3ac9d docs: SDD 19 — qué falta de verdad para lanzar la distro al público
Lo que falta para TERMINAR de construir no es lo mismo que lo que falta para
PUBLICAR. Lo primero es trabajo conocido (16 recetas, WMs Wayland ligeros,
apps de terceros — con el navegador como frente propio). Lo segundo tiene dos
bloqueantes que hoy no existen y no se improvisan el día del anuncio:

1. METADATOS DE LICENCIA: sólo 5 de 771 recetas los declaran. La distro no sabe
   bajo qué términos redistribuye el 99% de lo que empaqueta. Hace falta campo
   obligatorio validado por hammer + el texto dentro del artefacto.
2. ESPEJO DE FUENTES: la GPL no pide que el código exista en internet, pide que
   quien recibe el binario pueda obtener la fuente correspondiente DE VOS. Acá
   estamos bien parados —cada receta pinea tarball+sha256— pero hay que hacerlo.

Más: gestión de claves (firma sin eso es teatro), evidencia de reproducibilidad
publicada como diferenciador comprobable, imagen validada en METAL, y ciclo de
actualización ensayado antes de publicar.

Recomendación de arranque: el campo de licencia obligatorio, ya. A 771 recetas
es un rato; a 2000 es un proyecto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 08:27:14 -04:00
sergio 809b154586 estado: cosecha granja 2026-08-07T12:15:14Z — avance del árbol KDE 2026-08-07 08:15:14 -04:00
sergioandClaude Opus 5 60d8ca9bbf respaldo: Storage Box BX11 en Hetzner — no dependemos más de un laptop
El usuario pidió respaldar en «el volumen, aunque sea montándolo aquí». No se
puede: un HC Volume es un dispositivo de bloque por RED, sólo se adjunta a
servidores de Hetzner del mismo DC, y sólo a uno a la vez. harkaq-cosecha es el
disco del worker efímero y vvv está al 78%.

El producto correcto es un Storage Box: se monta desde el laptop (SSHFS/CIFS) y
habla rsync/borg/restic por SSH. BX11 = 1 TiB, €3,20/mes, sin alta. Creado en
hel1 con protección de borrado y la clave github5.

rsync plano y no borg: el store es CAS, los ficheros son inmutables y se nombran
por hash, así que incremental es exactamente lo correcto y no hay repo ni claves
que mantener. borg comprimiría 4x (79% del store es .debug_) pero 126 G en 1 TiB
no aprieta.

El store va SIN --delete a propósito: un respaldo que replica los borrados no
protege del borrado por error, y store-gc.sh acaba de demostrar que puede
equivocarse en silencio.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 08:10:30 -04:00
sergioandClaude Opus 5 514ae44135 store-gc: guardián — no reportar «borrado» sin comprobarlo
El script informó «364 artefactos borrados · libres: 24G → 48G» y no había
borrado ninguno: los 364 de su propio manifiesto seguían enteros en el store.
`xargs rm -rf` salió con 0 y el echo se lo creyó. Se reprodujo: corriéndolo en
SEGUNDO PLANO el borrado no se materializa; en primer plano sí.

Da igual la causa: un rm que devuelve 0 no es prueba de que el fichero se fue.
La válvula de escape del disco estaba rota EN SILENCIO y encima reportaba
éxito, que es peor que fallar — el disco llegó al 98% mientras yo creía haber
liberado 24G.

Ahora recuenta sobre el filesystem y sale distinto de cero si sobrevivió algo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 07:44:54 -04:00
sergioandClaude Opus 5 85c25619ad recipes: wlr-randr 0.5.0 — el testigo ajeno del protocolo de salidas
La paridad cosmic no se demuestra con un cliente nuestro (eso probaria que nos
entendemos con nosotros mismos), se demuestra con la herramienta que usa el
resto del mundo. wlr-randr habla wlr-output-management-unstable-v1, que mirada
sirve. Entra a base-system-1 con ese rol acotado.

C sobre meson, 28 KB, trae el XML del protocolo adentro: solo wayland-client.
Tarball de release y no el -/archive/ autogenerado de GitLab, que no es estable
byte a byte y rompe el sha256 pinneado con el tiempo.

Queda anotado en la receta lo que hoy NO puede hacer, para no acusar al paquete:
en mirada apply/test de zwlr_output_configuration_v1 contestan failed, asi que
wlr-randr lista bien y no cambia nada. Cuando mirada implemente el apply, esta
misma receta pasa a probar tambien el apply.

Por lo mismo no entran kanshi ni way-displays: no son herramientas sino una
funcion —perfiles de salida por hotplug— y su lugar es adentro de mirada, que ya
tiene el estado. Instalarlos hoy seria ademas inutil.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 07:44:00 -04:00
sergio dd0926df24 estado: cosecha granja 2026-08-07T11:32:45Z — avance del árbol KDE 2026-08-07 07:32:45 -04:00
sergioandClaude Opus 5 aeac4a2ebb gnome: xdg-desktop-portal-gnome SELLA — isla de libadwaita + frontend a 1.19.4
Cae el último muro real de la cola GNOME. Cuatro piezas, ninguna en la receta
que fallaba:

1. SOMBRA de libadwaita en incoming-gnome/. El «relocation ... libfontconfig.a»
   no lo ponía xdp-gnome sino el cierre de la libadwaita del CORPUS, receta de
   la era estática. La del corpus no se toca (3 dependientes ahí).
2. libyaml-shared: la única no-PIC del cierre de libadwaita sin variante.
3. xdg-desktop-portal 1.19.4 en esta cola (COSMIC sigue en 1.18.4). xdp-gnome 48
   exige >=1.19.1, y el corte de la dep dura de gstreamer es 1.19.1 EXACTO
   —1.19.0 no la pide—, así que no hay versión que cumpla ambas. Se saca con dos
   sed: gstreamer alimenta un solo binario auxiliar (validate-sound) que el
   daemon invoca por exec, no linkea. Precio: notificaciones con sonido propio
   quedan rechazadas. Barato vs traer gstreamer+gst-plugins-base al corpus.
4. gettext-tiny + los cierres .pc de libadwaita-1 y gnome-desktop-4.

Y se corrige una regla que estaba MAL escrita en la receta: medir PIC con
`readelf -r x.a | grep R_X86_64_32` cuenta las reubicaciones de .rela.debug_*,
inocuas y presentes en todo objeto. Así medido libepoxy da 39.630 «no-PIC» y
sin embargo está embebida dentro de la libgtk-4.so de la isla, con 3.446
símbolos epoxy_ definidos. Excluyendo debug el control sale limpio (libepoxy=0,
fontconfig=1122), y el barrido dio 7 no-PIC en vez de 13: ahorró cinco builds,
dos de ellos openssl y curl.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 07:18:49 -04:00
sergio 6f215a12dd estado: cosecha granja 2026-08-07T04:14:38Z — avance del árbol KDE 2026-08-07 00:14:38 -04:00
sergioandClaude Opus 5 8aee137e36 gnome: xdp-gnome a la isla dinámica + diagnóstico del muro que queda
Deps canónicas -> variantes -shared, y link=static -> dynamic: la receta era
de la era estática y el link moría con «relocation R_X86_64_64 ... recompile
with -fPIC» sobre libfontconfig.a.

No sella todavía, y el muro queda MEDIDO en la receta para que nadie itere en
falso: el cierre tiene un único camino a la fontconfig estática,
xdp-gnome -> libadwaita -> fontconfig, y esa libadwaita es la del CORPUS, una
receta entera de la era estática (gtk4/glib/cairo/pango canónicas). No le falta
una variante: no pertenece a la isla dinámica.

El paso siguiente es una sombra de libadwaita en incoming-gnome, como se hizo
con glib. `yupana radio libadwaita` = 4 dependientes, 0 sellados cayendo.

El frontend generico xdg-desktop-portal SI sello (b3:db5e5cf8).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 23:59:21 -04:00
sergio cd62c82a0c estado: cosecha granja 2026-08-07T03:30:03Z — avance del árbol KDE 2026-08-06 23:30:03 -04:00
sergio db58515226 estado: cosecha granja 2026-08-07T02:57:00Z — avance del árbol KDE 2026-08-06 22:57:00 -04:00
sergio 494e8fe4bb estado: cosecha granja 2026-08-07T02:24:51Z — avance del árbol KDE 2026-08-06 22:24:51 -04:00
sergio 02cc25542d estado: cosecha granja 2026-08-07T01:52:21Z — avance del árbol KDE 2026-08-06 21:52:21 -04:00
sergio 42d465e490 estado: cosecha granja 2026-08-07T01:19:32Z — avance del árbol KDE 2026-08-06 21:19:32 -04:00
sergio a86eb63244 estado: cosecha granja 2026-08-07T00:46:50Z — avance del árbol KDE 2026-08-06 20:46:50 -04:00
sergio 3605bf63b2 estado: cosecha granja 2026-08-07T00:12:22Z — avance del árbol KDE 2026-08-06 20:12:22 -04:00
sergioandClaude Opus 5 14bed923ad granja: el worker no molía GNOME — faltaban dos colas en QUEUES
El default era fósil de cuando `incoming-gnome-onda1` era el frente entero:
listaba esa cola (6 recetas) pero NO `incoming-gnome` (86, donde viven gtk4,
mutter y gnome-shell) ni `incoming-gnome-onda2` (la isla dinámica, 22). El
worker reportaba moler GNOME y molía 6 de 114.

Se agregan las dos y se mueven las tres ANTES de incoming-kde: con 205 recetas
KDE por delante, un ciclo se consumía sin darles turno aunque estuvieran
listadas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 19:51:41 -04:00
sergioandClaude Opus 5 29d6b32312 gnome: cerrar la frontera de xdg-desktop-portal en la cola GNOME
xdg-desktop-portal-gnome documentaba en prosa que le faltaba el frontend
genérico («la dep dura que falta»). La campaña COSMIC ya lo autoró y selló,
así que se copia a incoming-gnome/ junto con su dep fuse3 y se declara.

NO es cache-hit, y conviene dejarlo escrito: desde incoming-gnome el cierre
resuelve contra la glib de la isla dinámica en vez de la de COSMIC, así que
el ArtifactHash se mueve (f4adb8c9 -> db5e5cf8) y hay que construirlo. Es lo
correcto —backend y frontend tienen que compartir glib— y es el mismo patrón
ya medido con pipewire.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 19:50:12 -04:00
sergio 63c28ae6e6 estado: cosecha granja 2026-08-06T20:11:33Z — avance del árbol KDE 2026-08-06 16:11:33 -04:00
sergio 55ce763e36 estado: cosecha granja 2026-08-06T19:10:27Z — avance del árbol KDE 2026-08-06 15:10:27 -04:00
sergio ba84fecb26 estado: cosecha granja 2026-08-06T18:10:01Z — avance del árbol KDE 2026-08-06 14:10:01 -04:00
sergio c4a8c09022 estado: cosecha granja 2026-08-06T17:09:27Z — avance del árbol KDE 2026-08-06 13:09:27 -04:00
sergioandClaude Opus 5 adbda20e30 🏁 ScreenCast CERRADO en metal — la cadena entera devuelve un stream real
Sube el pin de portal-probe a a5c959b (persist_mode + --wait + flush línea a línea).

MEDIDO en la OptiPlex 3060 (UHD 630, iris por hardware), handshake completo de 4 pasos:

    [3/4] Start → streams: node id 75, position (0,0), size (1920,1080), source_type 1
                  restore_token "d2b5b24f-2f2c-44e2-9a41-014f965b389b"
    [4/4] OpenPipeWireRemote → fd = 5 → socket:[84493]
    == portal-probe screencast: OK ==

Y PipeWire tiene el nodo `cosmic-screencast` como Video/Source con puertos capture_0/1.

El muro no era la GPU. La hipótesis del EGL por software queda falsificada: en metal, con
iris real, el síntoma era idéntico hasta que se mandó `persist_mode`. Era un strlen(NULL)
en xdg-desktop-portal 1.18.4 (ver el commit anterior), que mataba al portal justo antes de
emitir el Response.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 13:07:06 -04:00
sergioandClaude Opus 5 a5c959b618 🎯 portal-probe: omitir persist_mode MATA al portal — por eso Start no contestaba nunca
Causa raíz de que ScreenCast se quedara colgado en Start, medida en metal (OptiPlex 3060).
No era el EGL por software de QEMU: en metal, con iris por hardware, pasaba exactamente
lo mismo. Esa hipótesis queda falsificada.

Lo que pasa de verdad, con cuatro segfaults reproducibles (uno por intento, todos `at 0`
y todos en el MISMO offset de libc, 0x34f64 = `strlen`):

  1. el cliente no manda `persist_mode` ⇒ el portal asume PERSIST_MODE_NONE
  2. el backend de COSMIC devuelve `restore_data` de todos modos
  3. xdg-desktop-portal 1.18.4 entra por la rama NONE de
     xdp_session_persistence_generate_and_save_restore_token, que hace
     `g_clear_pointer(in_out_restore_token, g_free)` — token = NULL
  4. a la vuelta, el llamador hace `g_variant_new_string(*in_out_restore_token)` SIN
     comprobar nada ⇒ glib llama strlen(NULL) ⇒ SIGSEGV

El portal muere JUSTO ANTES de emitir el Response. Desde el cliente eso se ve como «Start
aceptado y nunca contesta», que es indistinguible de un backend que se cuelga — y nos
mandó a buscar el problema en la GPU durante meses.

Con persist_mode >= 1 el token se genera (uuid) y no hay nulo. Es un fallo de aguas
arriba; esto lo esquiva desde el cliente sin tocar el portal. Por defecto 1 (transitorio),
que es lo que quiere cualquiera que sólo va a capturar una vez.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 13:05:34 -04:00
sergioandClaude Opus 5 8182e459b0 🔭 portal-probe: el log quedaba vacío justo mientras medía
Redirigida a fichero, stdout se bufferiza por bloques: mientras la sonda espera un
Response el log muestra sólo la línea de la llamada. Eso se lee como «se colgó al
llamar» cuando en realidad estaba esperando como debe. Nos pasó depurando ScreenCast en
metal — dos minutos mirando un fichero que no avanzaba, con la sonda perfectamente viva.

setvbuf(_IOLBF) incondicional, no sólo cuando la salida es una terminal: el caso que
importa es justamente el redirigido.

Una sonda que no se puede leer MIENTRAS mide es media sonda.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 12:51:46 -04:00
sergio c5e6edc73e estado: cosecha granja 2026-08-06T16:39:08Z — avance del árbol KDE 2026-08-06 12:39:09 -04:00
sergioandClaude Opus 5 5ababec1a5 🔭 portal-probe: el plazo de los pasos que esperan a una PERSONA ahora es ajustable
`Start` de ScreenCast y el `Screenshot` interactivo ABREN UN SELECTOR: no terminan hasta
que alguien elige una pantalla. Tenían 60s clavados, y en metal eso alcanza para que la
sonda se rinda antes de que nadie llegue a hacer clic. El informe entonces dice «el
portal aceptó la llamada y no contestó», que se lee como un fallo del portal cuando la
cadena está simplemente esperando.

Es el mismo error de método que ya nos costó tres diagnósticos falsos en esta campaña
(initramfs 5s, cosmic-diag 50s, wifi-up 6s): medir contra el reloj en vez de contra el
hecho. Acá el hecho depende de un humano, así que el reloj tiene que ser AJUSTABLE, no
adivinado.

Los plazos de CreateSession/SelectSources siguen en 15s a propósito: esos pasos no le
piden nada a nadie, y si tardan es que algo anda mal de verdad.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 12:24:38 -04:00
sergio c18988f1d5 estado: cosecha granja 2026-08-06T16:07:52Z — avance del árbol KDE 2026-08-06 12:07:52 -04:00
sergioandClaude Opus 5 99efe4998b 🖥 mesa: -Dshader-cache=disabled — el escritorio COSMIC ARRANCA en metal
Cierra el segfault mudo de iris. El intento anterior (`-Wl,--build-id=sha1`) NO prendió:
meson lo registra pero `zig cc` 0.13 lo ignora en silencio — el configure lo dice
(«supports link arguments -Wl,--build-id=sha1: NO») y la .so sale sin ninguna nota.
zig 0.16 sí la emite (verificado a mano), pero `zig_version` está pineada a 0.13.0 como
escotilla contra la regresión de zig 0.14: subirlo cambia un muro por otro.

Tampoco había escape por entorno, y esta vez medido sobre el fuente en vez de deducido:
el retorno temprano es `if (INTEL_DEBUG(DEBUG_DISK_CACHE_DISABLE_MASK))` y en 24.0.9 esa
máscara está definida como 0 ⇒ el `if` no dispara nunca. Por eso el desensamblado no
tenía ni un salto condicional: el compilador lo dobló.

`-Dshader-cache=disabled` lo garantiza el preprocesador — el cuerpo entero de
`iris_disk_cache_init` vive en un `#ifdef ENABLE_SHADER_CACHE`. Verificado en el
artefacto: la función es ahora un único `ret`.

MEDIDO EN METAL (OptiPlex 3060 / UHD 630), bibliotecas desplegadas por SSH sobre la
máquina viva, sin re-quemar el USB: sesión COSMIC completa arriba — cosmic-comp, panel
con applets, bg, launcher, term, toplevel, workspaces — y CERO segfaults nuevos.
Confirmado en pantalla por el usuario.

Costo aceptado: sin caché en disco los shaders se recompilan en cada arranque.
Re-sella mesa: b3:6d443d9f… → b3:f43c41d1…

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 12:01:52 -04:00
sergio 6e07646c96 estado: cosecha granja 2026-08-06T15:37:22Z — avance del árbol KDE 2026-08-06 11:37:22 -04:00
sergioandClaude Opus 5 213ebfbb03 📶 metal: el enlace WiFi se caía solo y cortaba la depuración remota a mitad
Depurando la OptiPlex por SSH sobre un hotspot, la máquina desaparecía de la red cada
pocos minutos. No es «la WiFi anda mal»: son dos causas que se suman y ninguna deja
rastro en dmesg.

  - `udhcpc -q` (el que usa wifi-up) pide la dirección UNA vez y termina. Nadie renueva
    el lease.
  - el AP olvida a los clientes ociosos y deja de contestar ARP. Desde fuera se ve «No
    route to host» mientras la máquina se cree perfectamente conectada.

El síntoma engaña justo en la dirección peor: la máquina no reporta nada, así que parece
que se colgó lo que estabas depurando.

`wifi-keep` hace ping al gateway cada 10s, que resuelve las dos a la vez — detecta la
caída para reasociar Y mantiene viva la entrada del cliente en la tabla del AP.

Sin esto cada sesión remota se interrumpe sola y hay que volver físicamente al teclado,
que es exactamente lo que la red venía a evitar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 11:33:30 -04:00
sergioandClaude Opus 5 e2017dcfdc 🎯 mesa: sin build-id, iris desreferenciaba NULL y mataba al compositor en silencio
Causa raíz del escritorio COSMIC que no arrancaba en metal, medida en el OptiPlex 3060
(UHD 630). cosmic-comp moría sin panic y con RUST_BACKTRACE=full; el dmesg tenía la
respuesta que ningún log de usuario podía dar:

    cosmic-comp[1266]: segfault at 10 ip ... error 4 in iris_dri.so[9626c0,...]

El offset resuelve a `_mesa_sha1_format` (bytes del desensamblado idénticos al `Code:`
del kernel), y su llamador `iris_disk_cache_init` es tres llamadas seguidas sin un solo
salto condicional entre medio:

    build_id_find_nhdr_for_addr → NULL      (ninguna .so de mesa traía nota)
    build_id_data               → NULL+0x10 (offsetof del campo)
    _mesa_sha1_format           → lee 0x10  ⇒ SIGSEGV

El `assert(note && ...)` que cazaba esto lo borra -Db_ndebug=true.

Tres decisiones razonables por separado que juntas dan un cuelgue mudo: meson no pide
build-id, lld no lo pone solo (37 de 40 .so del corpus SÍ lo traen — era exclusivo de
mesa) y el assert no existe en release. No lo salva MESA_SHADER_CACHE_DISABLE: la
desreferencia va antes de consultar si la caché está habilitada. Y no se ve en QEMU,
donde el userland es swrast/llvmpipe y esta ruta es de iris.

Fix: -Dc_link_args/-Dcpp_link_args con -Wl,--build-id=sha1.
Re-sella mesa: b3:6d443d9f… → b3:c3c18cde…

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 11:27:35 -04:00
sergioandClaude Opus 5 47d8848e37 🔌 wifi-up: sin ctrl_interface, wpa_cli no tenía puerta a la que llamar
El /tmp/wpa.conf que generaba sólo traía el bloque `network`. Sin
`ctrl_interface=/run/wpa_supplicant`, wpa_supplicant no abre socket de control y
`wpa_cli` no conecta — síntoma que parece un fallo de red y es una puerta que no
existe. Peor: deja ciego el único dato que distingue «clave mal» de «DHCP mudo»,
que es wpa_state. Y la espera de asociación que acabo de añadir dependía de wpa_cli,
o sea que habría fallado siempre.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 18:27:48 -04:00
sergioandClaude Opus 5 89076a5106 📶 wifi-up: esperaba 6s fijos y culpaba a la clave de un problema de tiempo
En el OptiPlex la interfaz wlan SÍ aparece (ath10k cargó y el re-probe PCI funcionó),
pero `udhcpc` salía a preguntar 6s después de lanzar wpa_supplicant — antes de que
hubiera asociación. Sin lease, y el mensaje decía «¿clave correcta?», que manda a
buscar exactamente donde NO está el problema.

Ahora espera al HECHO: wpa_state=COMPLETED (techo 40s), informa el estado real
mientras espera, y distingue los casos en el mensaje de fallo — 4WAY_HANDSHAKE es
clave, SCANNING es que no ve la red. Y DHCP reintenta 3 veces, porque el AP a veces
ignora el primer DISCOVER justo tras asociar.

Tercera vez hoy que un `sleep` fijo produce un diagnóstico falso (initramfs 5s,
cosmic-diag 50s, wifi-up 6s). El patrón: esperar al reloj en vez de al hecho no falla
donde se prueba, falla en la máquina lenta — y miente sobre la causa.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 18:22:23 -04:00
sergio c8b0093aa8 estado: cosecha granja 2026-08-05T22:00:09Z — avance del árbol KDE 2026-08-05 18:00:09 -04:00
sergio 50242906de estado: cosecha granja 2026-08-05T21:18:06Z — avance del árbol KDE 2026-08-05 17:18:07 -04:00
sergioandClaude Opus 5 095b876bba 📶 metal: WiFi por ath10k (QCA9377) + cosmic-diag esperaba con reloj en vez de con hecho
WIFI. El OptiPlex 3060 SÍ tiene WiFi: `pci 0000:02:00.0: [168c:0042]` = Qualcomm
Atheros QCA9377. No aparecía interfaz porque el kernel sólo llevaba IWLWIFI. Añadido
ATH10K/ATH10K_PCI a linux-metal-dual + el firmware QCA9377 a la imagen + `wifi-up`.

El detalle que hace falta: con MODULES desactivado el driver va BUILTIN y prueba el
dispositivo a los ~5s pidiendo firmware, pero el rootfs se monta a los ~13s ⇒
`Direct firmware load failed -2` y la tarjeta queda muda, sin módulo que cargar
después. `wifi-up` fuerza un re-probe PCI (remove+rescan) con /lib/firmware ya
montado. Es la MISMA carrera que la del initramfs con el USB, un piso más abajo.

COSMIC-DIAG. La 1ª versión esperaba `sleep 50` y en esa máquina la sesión tarda ~105s
en llegar al compositor ⇒ el dmesg «después» se capturó ANTES de que cosmic-comp
arrancara y salió byte a byte idéntico al de antes. El viaje se gastó sin dato, y yo
leí ese dmesg vacío como «no hubo segfault», que era una conclusión sin respaldo.
Ahora espera al HECHO: que cosmic-comp aparezca y luego desaparezca (techo de 6 min).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 17:16:54 -04:00