Commit Graph
1272 Commits
Author SHA1 Message Date
sergioandClaude Opus 5 245a425031 estado: KDE 76→83/162 tras reconstruir las 7 recetas de la FRONTERA
perl-xml-parser, libpcap, libnl, lm-sensors, libxkbcommon, vulkan-loader y dbus — las siete
sombras de incoming-kde cuyas deps sí estaban al día, o sea el origen de la deriva. 7 de 7
selladas, sin un solo fallo: confirma el diagnóstico de que no había muro técnico, sólo
artefactos que nadie había reconstruido.

Quedan 79, todas aguas abajo. En vez de ir ola por ola se lanzó la construcción de las 11
RAÍCES del perfil y hammer resuelve la clausura solo — que es para lo que sirve el grafo de
deps y evita adivinar el orden a mano.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 13:48:18 -04:00
sergioandClaude Opus 5 9273880745 docs: por qué KDE está en 76/162 — los artefactos NO EXISTEN, y la causa es estructural
Diagnóstico completo. No es daño de la campaña del split ni un fallo de store-gc.

LO MEDIDO, en orden:
1. 86 nodos del perfil sin artefacto en su hash vigente.
2. NO lo causó el split: revertidas una por una todas las recetas tocadas hoy (3 bibliotecas
   base, 30 hojas, dbus, 38 de la 2ª tanda) el número NO se movió de 198 en ningún caso.
3. La FRONTERA son 7 recetas —las únicas cuyas deps sí están al día—: dbus, libnl, libpcap,
   libxkbcommon, lm-sensors, perl-xml-parser, vulkan-loader. Las otras 79 cuelgan de ellas.
4. Las 7 son SOMBRAS de incoming-kde, no las canónicas. Las del corpus están al día, y por eso
   el problema es INVISIBLE desde el grafo del corpus — que es justo el que yo miraba.
5. No fue store-gc: sus hashes vigentes no figuran en ningún manifiesto de borrado, y tres nunca
   se podaron. (Lo sospeché porque la regla de «superado» es por NOMBRE y una sombra comparte
   nombre con la canónica; la sospecha era razonable y los manifiestos la descartan.)
6. Ni el hub ni el worker los tienen. No están en otro sitio: NO ESTÁN.

LA CAUSA DE FONDO, que importa más que KDE: la cola se construyó en workers EFÍMEROS, el sync
del store es unidireccional (worker→hub) y esos workers ya no existen. Cuando algo del sustrato
compartido cambió después, las sombras se re-hashearon y nadie las reconstruyó, porque el hub
nunca fue la máquina donde vivía ese escritorio.

⇒ Con flota efímera y sync unidireccional, un escritorio puede dejar de ser
reconstruible-desde-el-store SIN QUE NINGÚN INDICADOR LO DIGA. El grafo seguía reportando
162/162 desde un JSON generado semanas antes.

Coste de arreglarlo: 86 reconstrucciones en la granja, empezando por las 7 de la frontera. No
hay muro técnico conocido — son recetas que ya construyeron. Es cómputo, no investigación.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 12:25:31 -04:00
sergioandClaude Opus 5 7a3444a36f docs: el «KDE 162/162» del SDD 20 salía de un grafo VIEJO — son 76/162, y no es culpa de la campaña
Al desplegar la 2ª tanda de hojas noté 198 recetas de las colas de escritorio sin artefacto
vigente (KDE 133, GNOME 37, COSMIC 24) y asumí que las había roto yo. **No.**

LO VERIFIQUÉ REVIRTIENDO, una causa por vez: las 3 bibliotecas base (expat, zstd, ncurses), las
30 hojas de la 1ª tanda, `dbus`, y las 38 de la 2ª. **El número no se movió de 198 en ningún
caso.** Y mirando el histórico, `escritorio-kde` está en 76/162 en TODOS los commits recientes
—09:10, 09:41, 10:12, 10:44, 11:16, 11:49— o sea desde antes de que la campaña empezara.

EL ERROR FUE MÍO Y DE MÉTODO: escribí «KDE cierra 162/162» leyendo
`docs/state/build-state-kde.json` **sin regenerarlo**. Es un fichero GENERADO, y un generado que
no se regenera es una foto vieja con aspecto de dato fresco. El mismo tipo de fallo que citar el
«79%» midiendo otra unidad: el dato existía, lo que fallaba era de cuándo era.

⇒ Y hay un aviso de fondo para toda la sesión: **medí «cero daño colateral» sobre el grafo
`--wlr`, que sólo carga corpus + incoming-wlr.** Las colas de escritorio son INVISIBLES ahí, así
que ese «cero» era cierto en el grafo que miraba y no decía nada del catálogo completo. Para
juzgar impacto hay que cargar TODAS las colas.

Las 38 de la 2ª tanda quedan revertidas: hasta entender los 198 no conviene añadir cambios
encima. Las 30 de la 1ª y las 5 del piloto siguen puestas y verificadas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 12:12:51 -04:00
sergio f879a802cb estado: cosecha granja 2026-08-08T15:49:28Z — avance del árbol KDE 2026-08-08 11:49:34 -04:00
sergioandClaude Opus 5 56d4de5fb0 estado: grafo wlr tras la tanda de 30 hojas — los tres perfiles cierran al 100%
base 51/51 · cli 74/74 · escritorio-sway 121/121. Cosecha verificada: cero artefactos en el
worker que el hub no tenga.

De la tanda: 25 de 30 selladas. Los 5 fallos, ninguno causado por el split:
· cargo-audit, cargo-hack, git-absorb — 'spawn cargo vendor': el grafo las clasifica como clase
  `c` porque declaran compiler=gcc para sus deps de C, pero por dentro son Cargo. Filtrar por la
  clase del grafo NO basta.
· hammerd — git privado por SSH (HUB-ONLY, como las de tawasuyu).
· coreutils — 'you should not run configure as root'. Esto NO lo causó el split: es DERIVA. La
  receta llevaba tiempo sin poder construirse en el lab actual y nadie lo sabía porque su
  artefacto viejo seguía en el store. Es exactamente lo que la campaña destapa.

⚠ Y de ahí sale un riesgo que conviene nombrar: cada receta que deja de reconstruir convierte su
artefacto viejo en un REHÉN — store-gc no puede podarlo (es el único ejemplar) y nadie puede
regenerarlo. Hoy son ~12 G inmovilizados, y crecen con cada receta que se rompe en silencio.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 11:47:37 -04:00
sergio 5ed64aa44a estado: cosecha granja 2026-08-08T15:16:41Z — avance del árbol KDE 2026-08-08 11:16:41 -04:00
sergioandClaude Opus 5 c56fd373a1 etapa 4: tanda de 30 HOJAS — cero daño colateral, que era el punto
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>
2026-08-08 11:14:37 -04:00
sergioandClaude Opus 5 15e34cb257 dbus: una flag ausente bloqueaba TRES perfiles — --wrap-mode=nodownload
`base`, `cli` y `escritorio-sway` bajaron a 50/51, 73/74 y 120/121 después de la cascada del
split, y los tres fallaban por LA MISMA receta: dbus.

EL ERROR NO MENCIONABA LA RED POR NINGÚN LADO:
    meson.build:372:11: ERROR: Unhandled python exception
Y arriba, enterrado entre reintentos:
    <urlopen error unknown url type: https>
    WARNING: failed to download with error: name 'ssl' is not defined
dbus declara subproyectos con `.wrap` y meson intenta DESCARGARLOS. En el sandbox no hay red y
python no trae ssl ⇒ «Unhandled python exception», que se lee como un bug de meson y es
simplemente que no hay salida a internet. Y no debe haberla: el build es hermético a propósito.

`--wrap-mode=nodownload` obliga a usar las deps del sistema (nuestros artefactos, vía
pkg-config) y deja los wraps inertes. Mismo caso que `wl-clipboard` anoche.

⇒ base 51/51 · cli 74/74 · escritorio-sway 121/121. Los tres cierran otra vez.

ESTADO DE LA REPARACIÓN DE LA CASCADA: de las 54 que quedaron sin artefacto, 42 reconstruidas.
Las 12 restantes son EXACTAMENTE las que ya estaban en deuda antes de la campaña:
  · 6 el muro del PIC (gtk4 y su cadena estática — duplicado superado de la dinámica de GNOME);
  · 3 HUB-ONLY (mirada-*, llimphi-counter: git privado / commit en ceros);
  · dwarves (libdw/musl), y adwaita-hello (error 39 = la carrera del ADR 0012).
O sea que la campaña no dejó deuda nueva.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 10:58:05 -04:00
sergioandClaude Opus 5 040d174bbe desplegar-strip: ordenar por IMPACTO EN EL GRAFO, no alfabéticamente
Medido sobre el corpus: **686 de 779 recetas son HOJAS** (cero dependientes transitivos) ⇒ el
88% se puede desplegar sin provocar UNA SOLA reconstrucción extra. Las caras son pocas y
conocidas: make 416 · pkgconf 360 · go 359 · binutils 343 · zlib 315 · python3 287 ·
samurai 272 · meson 257.

El orden alfabético anterior era activamente malo: la primera tanda de la etapa 4 tocó expat,
zstd y ncurses —tres bibliotecas base— y dejó 54 recetas sin artefacto vigente, incluida la
cadena wlroots/sway sellada la noche anterior. Sin este orden, cada tanda pequeña provoca una
cascada grande y la campaña avanza hacia atrás.

Hojas primero; las ~93 con dependientes, en una ola coordinada y con la granja arriba.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 10:49:50 -04:00
sergio d55e7c6e61 estado: cosecha granja 2026-08-08T14:44:57Z — avance del árbol KDE 2026-08-08 10:44:57 -04:00
sergio a920b7aa3d estado: cosecha granja 2026-08-08T14:12:32Z — avance del árbol KDE 2026-08-08 10:12:32 -04:00
sergio 57ebfcaf19 estado: cosecha granja 2026-08-08T13:41:25Z — avance del árbol KDE 2026-08-08 09:41:25 -04:00
sergio 75a572ea71 estado: cosecha granja 2026-08-08T13:10:01Z — avance del árbol KDE 2026-08-08 09:10:01 -04:00
sergioandClaude Opus 5 f12303e3ab docs: SDD 23 §8 — el orden de las tandas importa más que su tamaño, y ADR 0012 acaba de morder
Activar strip_debug en tres bibliotecas BASE (expat, zstd, ncurses) re-hasheó 54 recetas en
cascada, incluida la cadena wlroots/sway. Tocar cincuenta HOJAS no habría costado ninguna
reconstrucción extra. El desplegador ordena alfabéticamente y debe ordenar por profundidad en
el grafo, de hoja a raíz.

Y ADR 0012 —la carrera del árbol de fuentes, 'pendiente sin decidir' desde hace meses— acaba de
morder: adwaita-hello murió con error 39 (Directory not empty). Una reconstrucción masiva es
exactamente el escenario que lo dispara, así que decidirlo deja de ser opcional si la etapa 4
va a correr en paralelo.

Más dos lecciones de operación: un drenaje largo no puede vivir dentro del ssh (se cortó a las
14 de 54), y la flota efímera reusa IPs, así que la clave de host cambia y cada conexión grita
MITM — conviene que farm-up lo limpie solo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 09:04:20 -04:00
sergioandClaude Opus 5 b38db3f35d respaldo: .dmerge tumbó la subida 40 veces seguidas y el mensaje nunca lo dijo
El respaldo se detuvo en 79 G de 127 G con «40 intentos y sigue cayéndose». El código era 23
(«some files/attrs were not transferred»), que está en la lista de reintentables — así que el
bucle lo reintentó cuarenta veces contra la misma pared y se rindió.

LA CAUSA, al mirar los errores de verdad en vez del código de salida: todos eran rutas
`/home/hammer/store/.dmerge/...`. Son directorios TRANSITORIOS de fusión del store, que aparecen
y desaparecen mientras hammer sella. rsync los empieza a copiar, se esfuman a media
transferencia, y falla. `cosecha-cron.sh` ya los excluía; este script no, y ésa era toda la
diferencia.

⇒ Añadido `--exclude /.dmerge`.

Y una lección para el propio bucle de reintentos: **un error reintentable que se repite 40 veces
no es un corte de red, es algo estructural**. Cuarenta reintentos idénticos deberían haber
gritado «esto no se arregla esperando» en vez de agotarse en silencio. Queda anotado; el bucle
todavía no distingue «se cayó una vez» de «falla siempre igual».

De paso, `appstream` —la única de las cinco del split que se perdió al borrarse el worker— se
reconstruyó en el hub: 18 M, 0 secciones .debug_. Las cinco quedan consistentes (hash vigente
con artefacto presente): bison 3M · appstream 18M · zstd 2M · expat 1M · ncurses 2M.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 08:41:28 -04:00
sergio 693938bed2 estado: cosecha granja 2026-08-08T12:38:32Z — avance del árbol KDE 2026-08-08 08:38:32 -04:00
sergioandClaude Opus 5 102aabd1ff SEGUNDA corrección: lo que medía el test era DERIVA, no no-determinismo — el corpus SÍ reproduce
El §1.bis dijo «sí hay no-determinismo» porque appstream y bison divergían. También estaba mal,
y por un fallo de diseño del propio test.

`verificar-repro.sh` compara el artefacto GUARDADO contra una reconstrucción de hoy. Pero el
guardado puede tener meses: se construyó con OTRO estado del lab. El test conflaba dos cosas:
  · NO-DETERMINISMO — mismas entradas, mismo lab, salidas distintas (rompe el invariante);
  · DERIVA — el artefacto viejo no es lo que el lab de hoy produce (el mundo se movió).

LA PRUEBA QUE LAS SEPARA es construir DOS VECES HOY, y se dio sin querer: al reejecutar el
verificador sobre recetas ya reconstruidas, TODAS pasaron a reproducir.
    anew  ✗ diverge → ✓ REPRODUCE
    gron  ✗ diverge → ✓ REPRODUCE
    age   ✗ diverge → ✓ REPRODUCE   (y en la 1ª ni instalaba los mismos ficheros: faltaba age-inspect)

⇒ EL CORPUS ES DETERMINISTA HOY. Lo que hay es deriva contra artefactos viejos.

QUÉ SIGNIFICA, sin adornos:
· El argumento de reproducibilidad para el split de debug SE CAE. El split se justifica por
  ESPACIO (~60%), que sigue siendo real y grande. Nada más.
· Pero la DERIVA es un problema por derecho propio y mayor: el store contiene artefactos que el
  lab de hoy no reproduciría, así que «nuestros artefactos son verificables por terceros» es
  falso para parte del corpus — quien reconstruya no obtendrá lo publicado. `age` es el caso
  feo: la reconstrucción ni siquiera instala los mismos ficheros.
· ⇒ La reconstrucción masiva SIGUE valiendo la pena, pero por otra razón: no para arreglar el
  determinismo sino para PONER EL STORE AL DÍA CON EL LAB y que la promesa sea cierta.

Y otro hallazgo del mismo experimento: en el hub las recetas Go SÍ reconstruyen (0 fallos). Lo
que fallaba en el worker era la RED para bajar los módulos, no las recetas. Eso cambia el
bloqueante de la etapa 4: no es «Go no reconstruye», es «el worker no tiene red para módulos».

LA LECCIÓN, que es la misma tres veces en este documento: un test hay que diseñarlo contra la
PREGUNTA, no contra lo que es fácil de comparar. Comparar con lo que hay en el store es cómodo;
comparar dos builds de hoy es lo que contesta. El script queda anotado con esto en la cabecera.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 08:34:15 -04:00
sergio ca83c2ae6e estado: cosecha granja 2026-08-08T05:24:30Z — avance del árbol KDE 2026-08-08 01:24:30 -04:00
sergioandClaude Opus 5 b344c5803b etapa 4: primera tanda desplegada y verificada (3/3) + el desplegador por tandas
zstd 12M→2M (−83%) · ncurses 6M→2M (−67%) · expat 2M→1M (−50%). Cero ficheros vacíos y
**las tres REPRODUCEN**. La maquinaria de la etapa 4 queda validada de punta a punta:
activar → construir en la granja → verificar con why-differs.

LA DEP DE binutils VA EXPLÍCITA, y es la decisión de diseño de esta etapa. El paso de strip usa
`strip --strip-debug -D` de binutils. Se podría hacer que el lab lo materialice solo, sin tocar
las recetas — pero entonces la VERSIÓN de binutils sería un input INVISIBLE: dos corridas con
binutils distintos darían artefactos distintos con el mismo hash. Los `deps` sí entran en
`hash_inputs`, así que declararlo es lo único que mantiene el invariante. Cuesta una edición
mecánica por receta; el invariante no se negocia por comodidad.

EXCLUSIONES, y son exactamente dos: `binutils` y `make`. binutils provee el strip y depende de
make ⇒ activarles el split los haría necesitarse a sí mismos para construirse. No es preferencia,
es la circularidad. (Las recetas CERRADAS POR DECISIÓN tampoco se tocan.)

POR TANDAS Y NO DE GOLPE: activar re-hashea la receta a propósito y en cascada todo lo que
dependa de ella. Hacerlo sobre las 775 candidatas a la vez dejaría el corpus entero sin sellar
al mismo tiempo — días de granja antes de poder verificar NADA, y el disco aguantando artefactos
viejos y nuevos a la vez. Por tandas se mide, se verifica y se poda entre medias, que es lo que
hace la campaña reversible.

La primera tanda NO se tomó del orden alfabético que propone el script: ésas son CLIs Go/Rust
que ya vimos que no reconstruyen en el worker (necesitan red para sus módulos), y validar la
maquinaria con recetas que fallan por otro motivo no habría probado nada. Se eligieron tres
paquetes C con artefacto presente. Para las tandas grandes hay que resolver antes el acceso a
red de los módulos Go/Rust, o restringirse a lo que reconstruye.

Estado: 775 candidatas, 5 desplegadas (bison y appstream del piloto + estas 3).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 00:53:26 -04:00
sergio 0e6d84361b estado: cosecha granja 2026-08-08T04:51:57Z — avance del árbol KDE 2026-08-08 00:51:57 -04:00
sergioandClaude Opus 5 1eb469337d etapa 3: el ahorro real es ~60%, no 79% — la cifra publicada medía otra cosa
El «79%» que este plan y los SDD 19/20 venían citando era **el 79% del CONTENIDO BINARIO**,
medido sobre una muestra de .so/.a. Es cierto y es la cifra equivocada para planificar, porque un
artefacto no es sólo binarios: trae cabeceras, datos, iconos, locales, .pc y documentación, que
no encogen.

MEDIDO con `scripts/medir-debug.sh` (suma los tamaños reales de las secciones .debug_* vía
readelf -S y los compara con el tamaño del árbol, sin reconstruir nada):
    40 artefactos  ·   876 MB · 27%
   100 artefactos  ·  2955 MB · 38%   (truncada a 60 ficheros/artefacto)
   250 artefactos  ·  8874 MB · 60%   ← la que manda: sin truncar, ~7% del store

La varianza es alta porque unos pocos artefactos grandes dominan el total, así que el número
necesitaba validación independiente — y la tiene: el piloto de la etapa 2, donde se reconstruyó y
se pesó de verdad, dio bison −50% y appstream −68%. Los dos ENCIERRAN el 60% de la muestra
grande. El modelo predice lo que el rebuild produjo.

CIFRAS CORREGIDAS, con el store en 127 G:
                      se venía diciendo    medido
   reserva de disco         ~96 G          ~76 G
   store tras el split      ~30 G          ~51 G
   espejo público           ~30 G          ~51 G
Sigue siendo el mayor ahorro disponible en el proyecto —76 G no es poco— pero no es lo
prometido, y la diferencia importa para dimensionar el alojamiento del espejo, que es una
decisión con factura.

LA LECCIÓN: una cifra medida sobre una cosa (contenido binario) se citó durante semanas como si
midiera otra (tamaño de artefacto), y llegó a tres documentos. El número no era falso; LA UNIDAD
sí. Cuando un número vaya a decidir un gasto, hay que volver a la medición original y comprobar
QUÉ estaba midiendo.

Corregido en los tres sitios donde se había propagado.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 00:42:08 -04:00
sergioandClaude Opus 5 fbd586f9b2 etapa 2: el split de debug FUNCIONA — y el piloto destruyó los artefactos antes de lograrlo
RESULTADO, con los tres criterios medidos a la vez sobre las dos recetas que divergían:
  bison      6M → 3M  · 0 ficheros vacíos · 0 secciones .debug_ · «bison (GNU Bison) 3.8.2»
  appstream 56M → 18M · 0 ficheros vacíos · 0 secciones .debug_ · «AppStream version: 1.0.5»
  y las DOS pasan de DIVERGIR a REPRODUCIR.

O sea que un solo cambio recupera espacio Y cierra la fuga de reproducibilidad, como predijo el
§1.bis. Pero se llegó ahí después de tres errores que conviene dejar escritos.

🧨 1. `zig objcopy --strip-debug X X` (mismo fichero de entrada y salida) TRUNCA EL FICHERO A 0
BYTES. Destruyó los artefactos del piloto — y lo grave es que LOS TRES INDICADORES DECÍAN QUE
IBA BIEN: el tamaño cayó 84% (porque los ficheros quedaron vacíos), `why-differs` dijo REPRODUCE
(porque dos árboles vacíos son idénticos) y no quedaban secciones .debug_ (porque no quedaba
ninguna sección). Se cazó al EJECUTAR el binario: 0 bytes.
⇒ La verificación de un artefacto tiene que incluir que SIGA FUNCIONANDO, no sólo que pese menos
y reproduzca. Un artefacto vacío cumple las dos y no sirve para nada. Es la lección de esta
campaña aplicada a la campaña misma: una métrica que parece éxito.

2. Al arreglarlo con fichero temporal, el strip pasó a ser un NO-OP SILENCIOSO: los binarios
quedaban intactos y el tamaño no bajaba, porque no se pudo confirmar que `zig objcopy` acepte
`--strip-debug`. Cambiado al `strip` de binutils, que sí funciona, a costa de declarar la dep.
⇒ Preferible una dep explícita que funciona a una comodidad que no se sabe si hace algo.

3. Con el strip real, apareció una fuga NUEVA: los artefactos seguían divergiendo, ahora por la
CABECERA `ar` de los `.a` — `strip` los reescribe con los timestamps de cada corrida. Lo nombró
`why-differs` exacto («archivar en modo determinista»). Arreglado con `strip -D`
(= --enable-deterministic-archives). ⇒ Arreglar media causa deja el invariante igual de roto: el
debug ya no divergía y el archivo sí.

DISEÑO: `strip_debug` es un campo de la receta que ENTRA en `hash_inputs` y sólo si está fijado
(mismo patrón que `zig_version`). Las dos mitades importan y están clavadas en un test: si no
entrara, el lab cambiaría el contenido del artefacto sin mover el hash y el store MENTIRÍA; y al
entrar sólo si está fijado, se despliega receta a receta sin re-hashear las 1161 de golpe —
verificado: con el campo añadido al código, los 1161 hashes existentes NO se movieron.

Va como paso del lab y no en la fase install de cada receta porque 383 de las 1161 no tienen
install explícita: meterlo receta a receta obligaría a escribir a mano ese install por defecto en
las 383, con riesgo de no clavarlo exacto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 00:31:35 -04:00
sergio f8a2ead7e4 estado: cosecha granja 2026-08-08T04:20:33Z — avance del árbol KDE 2026-08-08 00:20:33 -04:00
sergioandClaude Opus 5 6006f54d3c docs: SDD 23 — el título afirmaba lo contrario de lo que el documento concluye
Decía «y por qué es la mitad de lo que se creía» y la etapa 1 demostró que son las dos mitades.
Un título que contradice su propio contenido es peor que uno vago: se lee primero y se recuerda
más.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 23:53:37 -04:00
sergioandClaude Opus 5 eea1c02f43 etapa 1: la puerta funcionó — me equivoqué, SÍ hay no-determinismo (y las dos mitades son una)
Ayer escribí en el SDD 23 que el `-ffile-prefix-map` global «se cancela porque su premisa es
falsa». **Era falso**, y la etapa 1 lo demostró en veinte minutos. Corregido en el §1.bis.

LA MEDIDA: `scripts/verificar-repro.sh` aparta el artefacto, reconstruye con las deps cacheadas
y compara con `why-differs`. Sobre las que pudieron reconstruirse: binutils ✓ y wl-clipboard ✓
reproducen; **appstream ✗ y bison ✗ DIVERGEN**. Causa idéntica en ambas:
    difieren [.debug_aranges, .debug_info, .debug_pubnames, .debug_pubtypes, .debug_str]
     — sólo info de depuración (el código ejecutable es idéntico)
     · .debug_str sólo en B: /src/output/meson-private

DÓNDE ME EQUIVOQUÉ, exactamente: mi barrido buscaba rutas DEL HOST (/home/<user>/,
/tmp/<aleatorio>) y no halló ninguna nuestra — cierto. Pero el no-determinismo no venía de una
ruta del host sino de rutas INTERNAS al árbol de build (/src/output/meson-private) que varían
entre corridas aunque /src sea constante. Buscar la forma equivocada de ruta y no encontrarla no
prueba que no haya otra. Y una sola muestra que reproduce no es una muestra: leí `wl-clipboard`
como si probara más de lo que probaba, que es el error contra el que el propio documento
advertía dos párrafos antes.

Lo que me salvó fue haber puesto la PUERTA antes de la campaña en vez de después. Si hubiera
seguido mi conclusión, habría cerrado como «resuelto» un invariante roto.

🎁 Y de ahí sale la mejor noticia del plan: LAS DOS MITADES SON EL MISMO TRABAJO. La divergencia
vive ENTERA en secciones .debug_* y el código ejecutable es idéntico ⇒ separar el debug del
artefacto principal hace que el artefacto principal REPRODUZCA, sin tocar -ffile-prefix-map en
720 recetas. Queda decidir qué hacer con el contenido del paquete -debug, pero eso afecta a un
artefacto secundario que nadie instala por defecto.

El script queda como herramienta: muestra por clase de build (C, Rust, Go: el no-determinismo
suele vivir en codegen paralelo y orden de símbolos, no sólo en C), y RESTAURA el artefacto si
el rebuild falla — distinguir «no reproduce» de «no construye acá» importa, porque mezclarlos
inventaría un problema de determinismo que no existe.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 23:53:21 -04:00
sergio 8e8a2f80ee estado: cosecha granja 2026-08-08T03:48:05Z — avance del árbol KDE 2026-08-07 23:48:05 -04:00
sergioandClaude Opus 5 38f26eb3a0 docs: SDD 23 — plan del re-hasheo, y media campaña CANCELADA porque su premisa era falsa
El usuario decidió hacer «ahora» el re-hasheo masivo. Lo primero fue verificar la premisa, y no
se sostuvo: **el `-ffile-prefix-map` global no hace falta**. Se ahorran días de granja y un
re-hasheo de ~720 recetas.

EL PLAN HEREDADO decía «92 paquetes con no-determinismo probable, todos por rutas /src en
.debug_». Tres medidas, de fuerza creciente, lo desmontan:

(a) Las rutas `/src` son CONSTANTES: el lab bindea el árbol de fuentes en /src literal, así que
    una ruta embebida en .debug_ es idéntica en cada rebuild y en cualquier máquina. Parece
    sospechosa y es fija.
(b) Barrido de 195 artefactos buscando rutas que SÍ variarían (/home/<user>/, /opt/hammer/work,
    /tmp/<aleatorio>): 15 las tienen, y ninguna es nuestra —
      · /home/buildozer/aports/main/musl/src/musl-1.2.6 = la ruta de compilación DE ALPINE,
        horneada en el musl prebuilt que inyectamos;
      · /tmp/apiresponse.json = una cadena literal en el fuente de gron;
      · /home/jimmac/, /home/sam/dev/ = rutas de los autores en los assets SVG de adwaita.
(c) LA PRUEBA QUE DECIDE: apartar el artefacto de wl-clipboard, reconstruir con deps cacheadas y
    comparar con `hammer why-differs` → «24 entradas idénticas · 0 divergen · REPRODUCE».

La lección de método vale más que el ahorro: el no-determinismo se mide RECONSTRUYENDO Y
COMPARANDO, no buscando cadenas sospechosas. Un grep sobre binarios da candidatos, no veredictos
— misma clase de error que la regla del `.a` no-PIC (contaba reubicaciones de .debug_*) y que el
conteo de licencias con `grep -l license` (contaba comentarios). El proyecto YA tenía la
herramienta del veredicto; lo que faltaba era usarla antes de planificar.

QUEDA EL SPLIT DE DEBUG, que sí es real: el 79% del contenido binario del store son secciones
.debug_ ⇒ ~96 G de reserva y el espejo público de 126 G a ~30 G, que toca la decisión ya tomada
de servirlo desde el Storage Box.

🧨 Y APARECIÓ ALGO QUE CONDICIONA EL CÓMO: el entorno del lab NO ESTÁ HASHEADO. sandbox.rs fija
CC/-mcpu=baseline, SOURCE_DATE_EPOCH, TZ, LC_ALL, AR y codegen-units para TODOS los builds, y
ninguna entra en hash_inputs. ⇒ Cambiar el entorno del lab cambiaría lo que sale del build SIN
mover un solo hash: el store diría que todo está al día mientras los artefactos ya no
corresponden. En un sistema direccionado por contenido ese es el peor fallo posible: no falla,
miente. Hoy no muerde porque ese entorno no cambia nunca — y una campaña global es justo el
momento en que cambiaría.

Por eso el plan propone hacer el split en la fase `install` de cada receta (entra al hash por
diseño, auditable, re-hashea sólo lo que toca) y NO como flag del lab; más un ticket previo
chico: un `lab_version` en hash_inputs con el diseño de `zig_version` — sólo entra si está
fijado, así los hashes actuales no se mueven.

Cinco etapas con PUERTA cada una. La primera es verificación amplia de reproducibilidad (~30
recetas de clases distintas); si alguna diverge, ESO es el trabajo real y la campaña se pospone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 23:42:22 -04:00
sergioandClaude Opus 5 6e27721098 sombras -shared: 8 promovidas al corpus, 0 hashes movidos — y cairo se para en un muro real
Decisión del usuario: promover una por una, verificando. Hecho así, y valió la pena.

EL RIESGO QUE SE TEMÍA NO APLICABA. La nota de `granja-promote-colisiones` advierte que
promover a ciegas hace que variantes homónimas pisen recetas canónicas. Acá no: los nombres
`*-shared` son DISTINTOS de los canónicos —`zlib-shared` no pisa a `zlib`— y se comprobó que
ninguna de las 19 sombras del catálogo choca con un nombre del corpus. Decirlo importa: repetir
una advertencia donde no aplica es tan malo como ignorarla donde sí.

VERIFICADO EMPÍRICAMENTE, no razonado: se calcularon los hashes de LAS 1153 recetas antes y
después de promover. **Ninguna cambió**, y las 8 nuevas tienen hash idéntico a su original ⇒ sus
artefactos ya están sellados y no hay que reconstruir nada. Promovidas: zlib, libpng, freetype,
fontconfig, libjpeg-turbo, libtiff, libxml2 y libyaml (todas en su variante -shared).

 `cairo-shared` NO SE PROMOVIÓ, y el porqué es el hallazgo: su hash SÍ cambia al moverla,
porque una de sus deps —`glib`— resuelve distinto desde el corpus. En `incoming-gnome-onda2`
hay una SOMBRA DE GLIB CON EL MISMO NOMBRE que la canónica (`link=dynamic`,
`-Ddefault_library=both` en vez de static), y `cairo-shared` está construida contra ella.

⇒ O sea que el riesgo de homónimos SÍ existe, pero un nivel más abajo del que se miraba: no en
las `-shared`, sino en el `glib` sombra. Promoverlo pisaría la receta canónica de glib y
re-hashearía en silencio todo lo que cuelga de ella, que es medio catálogo. El camino limpio es
renombrarla a `glib-shared` —nombre que YA existe en incoming-cosmic— y reapuntar cairo-shared;
eso cambia hashes de la cadena GNOME y merece su propia decisión, no colarla acá de madrugada.

Con esto la cadena estática del corpus (gtk4 y las 6 que cuelgan) sigue bloqueada, pero ahora se
sabe exactamente por qué y cuál es el siguiente paso concreto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 23:33:32 -04:00
sergioandClaude Opus 5 d70098ade5 GTK3: decisión del usuario — NO entra; gnome-session, gsd y gdm quedan CERRADAS por escrito
Decidido el 2026-08-08. Las tres recetas dependen de GTK3 (gnome-settings-daemon además de
gtk+-x11-3.0) y quedan cerradas, **no «en deuda»**. La diferencia no es semántica: una deuda
invita a reintentarla en cada informe y en cada ciclo de granja; una decisión se respeta.

POR QUÉ ES RAZONABLE Y NO UNA RENDICIÓN:
· GNOME funciona sin esto — el camino vivo es mutter → gnome-shell arrancado por arje, y
  ninguno de los dos depende de gnome-session (su cierre es mutter+gjs+gobject-introspection+
  gnome-desktop).
· Traer GTK3 sería una torre de C muerta —y para waybar además gtkmm, sus bindings de C++—
  por un gestor de sesión y un login manager que este escritorio no necesita.
· La función está cubierta: la sesión la levanta arje y la barra de estado es `yambar`, C puro,
  sellado anoche en el frente wlr.
Se reabre si alguien trae GTK3 por otro motivo con peso propio (una app gráfica que lo exija).

EL MARCADOR VIVE EN LA RECETA, NO EN UNA LISTA APARTE. `build-farm.sh` salta las recetas cuya
cabecera dice «CERRADA POR DECISIÓN». Podría haber hecho un fichero de exclusiones, pero una
lista y una cabecera se desincronizan solas: así, quien lee el porqué y quien decide saltarla
miran EL MISMO TEXTO. Y el drenador lo dice en su salida, para que la decisión sea visible en
vez de silenciosa.

Con esto la granja deja de quemar CPU en tres recetas que nadie va a arreglar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 23:31:08 -04:00
sergioandClaude Opus 5 deee75eca5 docs: SDD 20 — los WMs ligeros pasan a HECHO, y la lección de que «sellado ≠ arranca»
§II.1 decía «medido: no hay ninguno — de toda la familia sólo existen foot y mako». Ya no: 10
recetas selladas, 12 binarios, el perfil escritorio-sway cerrando 121/121 y ARRANCANDO en QEMU
con evidencia. La hipótesis que traía la sección —«son baratos, no hay torre de C debajo»— se
cumplió: KDE y GNOME fueron campañas de semanas, esto salió en una noche.

Y se añade la lección que corrige cómo hay que leer TODO el documento: el grafo prueba que una
imagen SE ARMA, no que ARRANQUE. Entre 121/121 y una pantalla con algo dibujado hubo SEIS
muros y ocho ciclos de imagen —PATH vacío, libz.so.1 ausente del rootfs, un backend de libseat
que nuestra receta no construye, /run de sólo lectura, los datos XKB, y la falta de
tipografías— y ninguno era una dependencia de build, así que ninguno podía salir en el grafo.

Con el método incluido, porque es lo que más se olvida: el veredicto es CONTAR COLORES del
PPM, no leer el log. Hubo un arranque con todo verde (salida activada, modo correcto, «Commit
of 1 outputs succeeded», workspace creado) y la captura 100% negra.

⇒ Corolario escrito en una sección nueva: cuando este documento diga que algo «cierra», hay que
leerlo como «se puede intentar», no como «funciona». Los tres escritorios que aún no se
validaron con pantalla deben esa misma distancia, y no conviene prometer fechas contra el
número del grafo.

waybar queda anotado como fuera-por-medición (gtkmm-3.0 = GTK3 + bindings C++ + 8 recetas), con
yambar en su lugar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 23:01:11 -04:00
sergioandClaude Opus 5 e3403ff20e sway ARRANCA EN QEMU con pantalla — evidencia, y seis muros que el grafo no podía ver
`docs/evidencia/sway-qemu-2026-08-08.png`: 1280×800, **102 colores distintos**, sway con foot a
pantalla completa y un prompt vivo, la barra de título del compositor arriba y el fondo
#1a4b8c asomando en los bordes. Sobre el kernel de hammer con arje-zero como PID1, sin X11 y
sin systemd.

EL VEREDICTO ES CONTAR COLORES, NO LEER EL LOG — y esta campaña lo demuestra sola: hubo un
arranque con TODO verde en el log (salida activada, modo 1280x800, «Commit of 1 outputs
succeeded», workspace creado) y la captura salía **100% negra**. Un compositor puede estar
perfectamente vivo y no pintar nada.

LOS SEIS MUROS, ninguno visible en un grafo de dependencias que decía 121/121:

1. `PATH` vacío — el console-getty no lo exporta y ni `mkdir` se encontraba. «mkdir: not found»
   se lee como «falta busybox» cuando busybox está entero: mismo engaño que el exit 127 de meson.
2. `libz.so.1` AUSENTE del rootfs. El zlib del corpus es sólo estático; la compartida vive en
   `zlib-shared`, en otra cola. Se cazó comparando los NEEDED del ELF contra el rootfs, no
   leyendo logs.
3. `LIBSEAT_BACKEND=builtin` NO EXISTE en nuestro libseat: `recipes/seatd.toml` construye con
   `-Dlibseat-seatd=enabled -Dlibseat-logind=disabled`, o sea UN backend, confirmado con
   `strings`. La receta manda sobre lo que uno cree recordar del proyecto — y la misma receta
   traía la salida: `-Dserver=enabled` construye `seatd-launch`.
4. `/run` de SÓLO LECTURA. La raíz de hammer es inmutable por diseño, y sway moría con «unable
   to open lockfile … check permissions», que suena a permisos de directorio y era un
   filesystem read-only. Se resolvió montando un tmpfs, que es lo que hace cualquier init.
5. `xkeyboard-config` — «failed to add default include path /usr/share/X11/xkb». Es EXACTAMENTE
   lo que dejé anotado en la receta de swaylock: «para que el teclado tenga distribución en una
   imagen real hará falta incluirlo por el lado del perfil». Apareció donde se dijo.
6. SIN TIPOGRAFÍAS no hay terminal. `fcft: failed to match font` → `failed to load primary
   fonts`: foot arrancaba y no dibujaba. `dejavu-fonts` estaba en el catálogo pero en otra cola.
   Va en la lista de §I.5 del SDD 20 («tipografías») y acá se ve por qué no es un detalle.

Y un fallo de MÉTODO que costó dos ciclos: mandé el log de sway a /var/log/sway.log DENTRO de
la VM, así que la primera captura negra vino sin una sola línea que la explicara — el
diagnóstico estaba dentro de la caja que no arranca. Un log que no se puede leer desde fuera no
es un log. Ahora va al serial.

Los `exec` de la config de sway disparan ANTES de que exista el workspace (0,259 s vs 0,305 s)
y mueren con «Failed to create launch context. No workspace». Los clientes se lanzan desde
sway-start esperando el socket de Wayland, que es la condición real.

Andamiaje reutilizable en scripts/wlr/: qemu-sway-image.sh (hermano del de COSMIC, sin logind
ni dbus, que sway no necesita), sway-start.sh y sway-config.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 22:38:51 -04:00
sergio 8325a706b2 estado: cosecha granja 2026-08-08T02:14:09Z — avance del árbol KDE 2026-08-07 22:14:09 -04:00
sergioandClaude Opus 5 55dd36d97a perfil escritorio-sway: 121/121 CERRADO — la cuarta imagen de escritorio, y la más barata
Declarado en targets.toml y verificado con el grafo: **121 nodos, 121 sellados, falta 0**. Es
una imagen construible hoy, no una intención.

Nueve raíces (sway, yambar, fuzzel, swaybg, swaylock, swayidle, grim, slurp, wl-clipboard) más
`hereda = ["cli"]`, porque un WM sin userland debajo no se usa: hacen falta la terminal y las
herramientas. `foot` no se lista porque ya entra por la clausura de `cli`. El resto de la
imagen —wlroots, mesa, wayland, libinput, seatd, cairo, pango, fcft…— lo calcula el grafo
siguiendo las aristas: escribir la clausura a mano es justo lo que targets.toml existe para
evitar.

LA COMPARACIÓN QUE VALE: KDE y GNOME fueron campañas de semanas porque debajo tienen una torre
de C (Qt entero, GTK, la cascada de mesa). Esto es una decena de binarios pequeños sobre
wlroots y salió en una noche. Ya estaba escrito en el SDD 20 como hipótesis —«los WMs ligeros
son la mejor relación esfuerzo/resultado que queda»—; ahora está medido.

`--wlr` en build-state.py, con su flag propio por la misma razón que COSMIC: sin él el frente
es INVISIBLE para el khipu, y `yupana radio` sobre una receta compartida (wayland, libinput,
pixman, mesa, libxkbcommon…) no reportaría la imagen sway entre las afectadas — o sea que el
próximo que toque una de ésas mediría de menos y creería que no rompe nada.

Queda lo que no puede decidir el grafo: probarlo en QEMU CON PANTALLA. La regla ya pagada cara
es que las imágenes de escritorio no se validan con `-nographic`, porque el compositor puede
«arrancar» en los logs y no pintar nada.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 22:01:24 -04:00
sergioandClaude Opus 5 427ca1dd01 wl-clipboard 2.2.1 SELLADA — cierra el entorno mínimo del frente wlr
`wl-copy` y `wl-paste`. En Wayland no hay forma de copiar o pegar desde un script sin esto, y
varios flujos que ya sellamos lo necesitan para ser útiles: capturar con grim, seleccionar
región con slurp, y poder pegar el resultado en algún lado.

UNA DIFERENCIA CON LAS ANTERIORES QUE CONVIENE NOTAR. Va por commit (ADR 0006, GitHub genera
sus archive/refs/tags al vuelo igual que codeberg), pero este tag es **LIGERO**: `git ls-remote`
devuelve UN solo sha y ése ES el commit. En fuzzel, yambar y foot los tags son ANOTADOS y
aparecen dos, donde el bueno es el `^{}`. Comprobar el tipo de tag antes de copiar evita pinear
el objeto del tag en vez del commit — un error silencioso, porque la receta valida y sólo falla
después, al no encontrar el árbol.

TRAE `subprojects/` CON WRAPS QUE BAJAN DE LA RED (expat, libffi, wayland, wayland-protocols).
`--wrap-mode=nodownload` es lo que lo impide y lo obliga a usar las del sistema, o sea nuestros
artefactos vía pkg-config. Sin esa flag el build intentaría salir a internet DENTRO del sandbox
hermético y moriría; con ella los wraps quedan inertes. Por eso las cuatro van en deps.

Estado del frente wlr: 10 recetas, 12 binarios — wlroots, sway (+swaybar/swaymsg/swaynag),
swaybg, swayidle, swaylock, grim, slurp, fuzzel, yambar, wl-copy/wl-paste. Con `foot` (ya en el
corpus) como terminal, el perfil «escritorio-sway» es armable: compositor, barra, lanzador,
fondo, bloqueo, inactividad, captura y portapapeles. Todo sin X11 y sin systemd.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 21:54:53 -04:00
sergioandClaude Opus 5 39b88ebd2a churn del store: eran CUATRO rutas de cosecha, no una — arreglé una y di el bucle por cerrado
Podé el store por tercera vez hoy: 363 artefactos superados, 24 G. Comparado con la poda de la
tarde: **362 en común**. Volvieron otra vez, pese al arreglo de esta tarde.

LA CAUSA de que el arreglo no sirviera: puse el `--exclude-from` del registro de podados sólo en
`farm-sync.sh`, y el latido NO usa ese script. `cosecha-cron.sh` tiene su PROPIO rsync del store
(línea 71), y corre cada 30 minutos por cron ⇒ una poda de 24 G quedaba deshecha en menos de una
hora. Los tres commits de «cosecha granja» de la madrugada son exactamente eso.

Y al ir a arreglarlo, en vez de parchear y seguir, busqué si había más: **son CUATRO** las rutas
que bajan el store del worker — `cosecha-cron.sh`, `farm-sync.sh`, `harvest-go.sh` y
`farm-down.sh`. Tenía protegida una de cuatro.

El error de método vale más que el bug: arreglar una de varias copias y declarar victoria es
PEOR que no arreglar, porque el síntoma se atenúa lo justo para dejar de mirar. Lo que lo
delató fue comparar los manifiestos de dos podas (`comm -12`), no leer código. Dos podas con el
mismo número no son coincidencia — ya está anotado como regla, y hoy la regla pagó dos veces.

Las cuatro llevan ahora el mismo `--exclude-from` y una nota que dice que son cuatro, para que
la quinta —si aparece— nazca protegida.

Disco: 112 G → 136 G tras la poda de ahora.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 21:52:46 -04:00
sergio dd8baa7560 estado: cosecha granja 2026-08-08T01:43:22Z — avance del árbol KDE 2026-08-07 21:43:22 -04:00
sergioandClaude Opus 5 3545ea77fa fuzzel + yambar SELLADAS — y waybar queda fuera POR MEDICIÓN, no por gusto
Lanzador y barra de estado: las dos piezas que faltaban para que un WM ligero se use de
verdad y no sólo arranque. El frente wlr suma 9 recetas y 11 binarios.

WAYBAR NO ENTRA, Y CONVIENE QUE CONSTE POR QUÉ. Medidas sus dependencias reales (0.12.0):
pide `gtkmm-3.0` — o sea GTK**3** *y además* sus bindings de C++ — más gtk-layer-shell,
jsoncpp, fmt, spdlog, sigc++, date y libnl. Es el mismo muro de GTK3 que el frente GNOME
aparcó a propósito, con ocho recetas nuevas encima. `yambar` es C puro, del mismo autor que
foot/fcft/fuzzel, y **sus dependencias ya estaban selladas**: es el bar estándar de esa
familia, no un sustituto pobre. Si algún día entra GTK3, waybar vuelve a estar sobre la mesa.

FUENTE GIT POR COMMIT, no el archive de codeberg (ADR 0006). Es el criterio que ya usan foot y
fcft, con la razón medida allí: el `/archive/<tag>.tar.gz` lo genera la forge AL VUELO, así que
sus bytes dependen de la versión de git/gzip del servidor y el sha256 pineado se muere solo
cuando codeberg actualiza su tooling. ⚠ Y los tags son ANOTADOS: el sha de `refs/tags/1.12.0`
NO es el commit; hay que pinear el `^{}` desreferenciado. Lo verifiqué con `git ls-remote`
antes de escribir, porque los dos shas aparecen juntos y es fácil copiar el equivocado.

DOS DEPS DE BUILD-TIME QUE NO SE VEN EN EL BINARIO Y MATAN EL BUILD:
· `scdoc` (las dos) — generan sus man pages con él y, a diferencia de sway/swaybg/grim, NO
  ofrecen `-Dman-pages` para saltárselo. Estaba en el corpus: la vía correcta es declararlo,
  no buscar cómo evitarlo.
· `flex` + `bison` (yambar) — generan el parser de su lenguaje de partículas. Invisibles en el
  resultado, y del tipo que se olvida hasta que el build muere con «Program 'flex' not found».

`-Dbackend-x11=disabled` en yambar evita SIETE dependencias de X11 (xcb-aux, xcb-cursor,
xcb-errors, xcb-event, xcb-ewmh, xcb-randr, xcb-render) para un backend que en una distro
Wayland pura no se usa nunca. Y en fuzzel, `-Dsvg-backend=nanosvg` (el default, vendorizado)
evita librsvg, que arrastraría la cadena de GNOME/Rust por unos iconos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 21:39:24 -04:00
sergioandClaude Opus 5 50b080d258 accesorios wlroots: swaybg, swayidle, swaylock, grim y slurp — 5 selladas
Con esto el frente de WMs ligeros pasa de «hay un compositor» a «se puede usar»: fondo de
escritorio, apagado por inactividad, bloqueo de pantalla y captura por región. Sumado a sway
y wlroots, el frente tiene hoy 7 recetas y 9 binarios (sway swaybar swaymsg swaynag swaybg
swayidle swaylock grim slurp), todos sin X11 y sin systemd.

CADA FALLO FUE DE UNA CLASE DISTINTA, y ninguno del paquete que estaba escribiendo:

· swayidle — pasé `-Dsd-bus-provider=none` porque es la opción que tiene SWAY. En swayidle
  1.9.0 no existe: la suya se llama `logind`. **Las perillas no se heredan entre proyectos
  hermanos aunque resuelvan lo mismo**; hay que leer el meson_options.txt de CADA uno. Tercera
  vez hoy que una opción inventada mata un build (gnome-session, y ahora ésta).

· grim — `undefined symbol: crc32`, o sea zlib. No lo usa grim: lo usa `libpng`, cuyo `.pc`
  declara zlib en `Requires.private`, la línea que pkg-config sólo expande en resolución
  estática. Es la MISMA causa que el `-lexpat` de sway (allí vía fontconfig), sobre otra
  biblioteca. Ya van dos casos ⇒ es un patrón del corpus, no un accidente: enlazar contra una
  lib estática cuyo .pc tiene Requires.private exige añadir esas libs a mano.

· slurp — le faltaba `libxkbcommon` en deps, sin más.

DOS COSAS QUE VALE GUARDAR:

1. EL TARBALL DE slurp CASI ENVENENA LA RECETA. La URL de releases de GitLab devolvió **HTTP
   200 con una página HTML** en vez del archivo, así que `curl -f` no falló y el sha256sum que
   saqué era el de un documento HTML. Pinearlo habría dado una receta que descarga «algo» con
   hash correcto y no construye jamás, con un error incomprensible. Se cazó verificando el
   FORMATO (`tar tzf`), no el código de salida — misma lección que el `rm` que devuelve 0 sin
   borrar y el proceso vivo que no avanza. El sha256 correcto es el del tarball de GitHub.

2. PAM NO ESTABA BLOQUEADO. «PAM» figura entre las tres deudas aparcadas del frente GNOME, así
   que lo natural era dar swaylock por imposible. Pero en swaylock PAM es una OPCIÓN (cae a
   `crypt` si falta) y sobre todo **`linux-pam` SÍ está en el corpus**: lo aparcado era la
   integración de PAM del stack GNOME, no la biblioteca. ⇒ Una deuda «aparcada» nombra un
   frente concreto, no una palabra; conviene verificar antes de heredar el veto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 21:12:24 -04:00
sergio 142a8774a6 estado: cosecha granja 2026-08-08T01:12:13Z — avance del árbol KDE 2026-08-07 21:12:13 -04:00
sergioandClaude Opus 5 f7aafc9d26 sway 1.10 SELLADA — primer compositor Wayland ligero del catálogo
b3:ae10753a5467f3642464aaad0b1b41135b8c51972091042baf0d09134af46c7e · sway, swaybar,
swaymsg y swaynag. Construido en el worker sobre el wlroots 0.18.2 sellado hace un rato
(sway 1.10 pide `wlroots-0.18` explícitamente: no es una versión cualquiera, es LA que casa).

XWAYLAND SE HEREDA, NO SE PASA. sway no tiene perilla propia: lee las variables del .pc de
wlroots (`xcb = wlroots_features['xwayland'] ? dependency('xcb') : null_dep`). Como nuestro
wlroots se selló con have_xwayland=false, sway se salta xcb y xcb-icccm solo. Para eso servía
haber inspeccionado esas variables al verificar el artefacto anterior. Si algún día se quiere
Xwayland, se cambia en wlroots, no acá.

TRES COSAS QUE SALIERON AL CONSTRUIR, y ninguna estaba en sway:

1. `json-c` NO DECLARABA NADA y sellaba igual — porque el rootfs del laptop trae cmake y make
   y el sandbox los tomaba «de prestado». En el worker muere con `cmake: not found` (exit 127,
   el mismo código que engaña con meson). Una receta que sólo construye en la máquina del autor
   es una bomba de relojería. Declarados cmake+make+pkgconf; el arreglo es declarar, NUNCA
   engordar el rootfs del worker. Re-hashea json-c y su único dependiente, que es sway: gratis.
   Salió a la luz construyendo sway, o sea que el fallo estaba a tres niveles de donde miraba.

2. 45 SÍMBOLOS INDEFINIDOS AL ENLAZAR, todos `XML_*` — expat, y sólo expat (clasificada la
   lista completa, no adivinado por el primero). sway no usa expat: lo usa `fontconfig`, que en
   el corpus es estático, y su `.a` trae las llamadas sin resolver. `fontconfig.pc` lo declara
   en `Requires.private`, que es la línea que pkg-config sólo expande en resolución ESTÁTICA, y
   meson llama a dependency('cairo') en modo normal. No es un fallo de fontconfig ni de cairo:
   es donde el modelo de pkg-config y el enlace estático no encajan. Resuelto con
   `-Dc_link_args=-lexpat`; como los indefinidos eran de UN solo prefijo, se sabe que no falta
   nada más.

3. `-Dtray=disabled` esquiva la única dep que nos falta de verdad. La bandeja habla sd-bus y
   sway ofrece tres proveedores (libsystemd | libelogind | basu): no hay systemd por decisión,
   `basu` no está en el catálogo, y el `libelogind` que sí tenemos NO sirve — es la
   reimplementación propia de la C-ABI sd-login de arje-compat, no un sd-bus. Misma trampa del
   nombre que ya mordió con `knighttime`. Traer basu es un ticket aparte y pequeño.

Con esto el frente de WMs ligeros deja de ser un hueco: había 0 compositores y ahora hay uno
que arranca desde wlroots propio, sin X11 y sin systemd.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 20:48:55 -04:00
sergio a287091d2a estado: cosecha granja 2026-08-08T00:40:56Z — avance del árbol KDE 2026-08-07 20:40:56 -04:00
sergioandClaude Opus 5 c13d25f02b wlroots 0.18.2 SELLADA — el desbloqueo del frente de WMs Wayland ligeros
Primera receta del frente nuevo. wlroots es la biblioteca de la que cuelgan casi todos los
compositores ligeros (sway, labwc, river, hyprland): sin ella no hay ninguno, y con ella los
compositores salen baratos porque no traen torre de C debajo. Hasta hoy el catálogo tenía de
toda esa familia sólo `foot` y `mako`.

b3:9b56bac0d00977524072cf393ecc9f0b847fcc1a0bfba9d9c4d57fd9a4a74b44 · 13 MB · construida en
el worker (cadena GUI: no se rebuildea en el laptop por el zig-skew).

COLA AISLADA `incoming-wlr/`, porque otro agente comparte el repo. Como la resolución de deps
es hermano→padre, una receta de acá ve el corpus pero NO ve `incoming-gnome/`, donde viven
`libdisplay-info` y `hwdata` ⇒ se copian como hermanas. No duplica trabajo: los ficheros son
idénticos, el ArtifactHash es el mismo y el build hace cache-hit. Verificado antes de
construir: ambas dan el mismo b3: que su original.

LAS OPCIONES, VERIFICADAS CONTRA EL meson_options.txt DEL TARBALL, no de memoria — porque una
opción `-D` inventada NO se ignora: meson aborta en la primera desconocida. Es exactamente lo
que tenía muerta a gnome-session con su `-Dsystemd=false`. El `.pc` resultante confirma que
salió lo pedido: have_drm_backend=true, have_libinput_backend=true, have_gles2_renderer=true,
have_gbm_allocator=true, have_session=true, y have_x11_backend / have_xwayland en FALSE —
la distro es Wayland pura y X11 es deuda aparcada por diseño; no entra por la puerta de atrás.

DOS COSAS QUE COSTARON UNA ITERACIÓN CADA UNA:
· `-Dwerror=false` no es pereza, es DIFERENCIA DE TOOLCHAIN. wlroots compila con -Werror y
  upstream lo valida con gcc; nuestro `zig cc` es clang y avisa de lo que gcc calla. Murió en
  `types/wlr_shm.c:503` por `-Wunused-but-set-variable` sobre has_argb8888/has_xrgb8888 —
  código correcto que sólo clang señala. Tratar los avisos de OTRO compilador como errores del
  proyecto es un fallo de método, no un hallazgo de calidad.
· `libffi`, `expat` y `zlib` en deps aunque wlroots no los use: los exigen los `.pc` de wayland
  y mesa en su `Requires`, y pkgconf resuelve el grafo de .pc completo antes de dar un cflag.
  Misma lección que wlr-randr esta mañana.

El artefacto trae `libwlroots-0.18.{a,so}`, su .pc y 111 cabeceras bajo
`usr/include/wlroots-0.18/wlr/`. Listo para que sway enlace contra él.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 20:24:34 -04:00
sergio bca7164b4e estado: cosecha granja 2026-08-07T22:36:14Z — avance del árbol KDE 2026-08-07 18:36:14 -04:00
sergioandClaude Opus 5 59c4973bd9 granja: el filtro de «ya sellado» miraba el nombre equivocado — lo arreglo antes de que cueste
El bloque que añadí hace un rato usaba `basename $f .toml` como nombre del artefacto. **Está
mal**: el artefacto en el store se llama por el campo `name` de la RECETA, no por el fichero,
y en 17 recetas del catálogo esos dos no coinciden. El arquetipo es
`recipes/incoming-kde/qtbase.toml`, cuyo `name` es `qt6-qtbase` ⇒ el artefacto es
`<hash>-qt6-qtbase` y mi `grep` lo buscaba como `<hash>-qtbase`.

Efecto: las 17 salían «no selladas» y el worker las habría reconstruido — exactamente el
desperdicio que este filtro existe para evitar. Se vio porque los 10 «pendientes» de
incoming-kde eran todos módulos Qt, que es demasiada coincidencia para ser casualidad; el
hub tenía el artefacto con el MISMO hash, sólo que bajo otro nombre.

Con el filtro correcto, la foto de la granja es exacta por primera vez:
  · incoming-kde    205 recetas · 205 en el hub ⇒ **0 pendientes**
  · incoming-gnome   90 recetas ·  87 en el hub ⇒ 3 pendientes: gdm, gnome-session y
                                    gnome-settings-daemon, o sea el trío bloqueado por GTK3
  · incoming-go / incoming-clib: colas vacías

⇒ **La granja no tiene trabajo construible.** Y ya no lo tenía antes de esto: el worker no
posee ni UN artefacto que el hub no tenga (comparadas las dos listas: 0 exclusivos suyos),
así que sus últimas 22 horas produjeron cero valor neto. Lo que queda por hacer no es
construir sino AUTORAR recetas (wlroots y los WM ligeros, cups, bluez, las gráficas de
terceros), y eso es trabajo de hub.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 18:32:31 -04:00
sergioandClaude Opus 5 129aff9dd9 granja: el worker rehacía 87 de 90 recetas por ciclo — el sync del store es de UN SOLO SENTIDO
Fui a mirar los fallos de la granja y el número no cuadraba: de 20 «fallos» de incoming-gnome,
17 correspondían a recetas CON ARTEFACTO VIGENTE en el hub. Primero pensé que eran marcadores
`.fail` rancios, y me equivoqué: `build-farm.sh` hace `rm -rf "$FARM"` al empezar cada ciclo,
así que el status es siempre del ciclo actual. Después pensé que el worker corría recetas
viejas, y también me equivoqué: comparados los md5, hub y worker tienen copias IDÉNTICAS.

LA CAUSA REAL está en `farm-sync.sh`: sube el código EXCLUYENDO /store y baja el store del
worker. El store viaja worker→hub y nunca hub→worker. O sea que **el worker no se entera de
nada de lo que se sella en el hub**, y cada ciclo reintenta —y vuelve a fallar— trabajo ya
hecho. Medido: de las 90 recetas de incoming-gnome, **87 ya están selladas en el hub**. El
worker estaba quemando el 97% de esa cola en repetir lo hecho.

EL ARREGLO no es mandarle los artefactos (gigabytes por un enlace de 8 Mbps) sino la LISTA:
`work/farm-sellados.txt` son los nombres `<hash>-<paquete>` del store del hub, unos kilobytes.
`farm-sync.sh` la regenera en cada sync (para que no envejezca sola) y `build-farm.sh` salta
la receta cuyo hash vigente ya esté ahí. Ojo al detalle de rsync: la subida excluye /work, así
que el manifiesto necesita un `--include` ANTES del `--exclude` — rsync aplica la primera regla
que casa, y sin ese orden el fichero no viajaba y el arreglo no habría hecho nada en silencio.

Y LAS TRES DEUDAS REALES DE LA GRANJA SON UNA. gnome-session, gnome-settings-daemon y gdm
mueren todas en GTK3, que el frente GNOME aparcó a propósito. Verificado contra el meson.build
de cada tag: gnome-session 48.0 lo pide incondicional; gnome-settings-daemon 48.1 pide gtk+-3.0
Y gtk+-x11-3.0 (dos de las tres deudas aparcadas, no una); gdm 48.0 lo tiene condicionado a
`if have_xdmcp` pero da igual, porque muere construyendo gnome-session.

De paso, gnome-session pasaba `-Dsystemd=false -Dsystemd_journal=false`, dos opciones que NO
EXISTEN en 48.0 (sus opciones reales son seis: deprecation_flags, session_selector,
systemduserunitdir, docbook, man, x11). Meson aborta en la primera opción desconocida, así que
la receta ni llegaba a configurar y el fallo real quedaba tapado. Corregido, más -Dx11=false.
El diagnóstico de fondo YA ESTABA en la receta desde el 2026-07-27 y era más completo que el
mío (dice GTK3 **y** libsystemd); quité la nota duplicada que había añadido.

Y en el respaldo: la comprobación inicial de SSH era de un solo intento y tiró la corrida al
relanzarlo llegando a casa, con el wifi aún sin levantar. Es el peor momento para rendirse —
quien relanza un respaldo interrumpido acaba de cambiar de red. Ahora reintenta.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 18:27:29 -04:00
sergioandClaude Opus 5 4f265a316f docs: SDD 22 — respuesta verificada al handoff de kernel-config de tawasuyu
El handoff avisa que nada suyo está verificado contra hammer («si un dato de hammer lo
contradice, manda hammer»). Esto es esa verificación: contesta sus seis preguntas del §9 con
evidencia del código, corrige lo que estaba mal y añade dos objeciones que cambian el orden.
NO es aprobación ni plan de implementación.

EL HECHO QUE REORDENA TODO, y que el handoff no podía conocer: las FASES entran en
hash_inputs, y el config del kernel vive en la fase `configure` ⇒ **el config ES la identidad
del artefacto**. Corta en los dos sentidos. A favor: §2.2 (atestar un config por huella de
hardware) sale casi gratis, porque cada config ya es un artefacto direccionado por contenido,
reproducible y firmable — no hay que inventar el mecanismo. En contra: la explosión de builds
no es hipotética, es aritmética visible en el disco, que ya crece 24 G por ciclo. Y una
«perilla» de la UI no es un parámetro de runtime: es una edición de receta.

LAS SEIS RESPUESTAS:
 P1 fragmentos — YA es como §6 prescribe: defconfig + `scripts/config -e/-d` + olddefconfig,
    o sea que la app nunca escribe un .config. Media implementación está en producción. Falta
    el diff-back, sin el cual un símbolo pedido que no sobrevive se pierde EN SILENCIO.
 P2 repro con config variable — sí, por construcción: no hay un kernel cuya reproducibilidad
    dependa de congelar el config; hay N kernels, cada uno congelado. Se pueden PREFIRMAR
    artefactos, no sólo configs.
 P3 tiempo — 35/45/60 min según receta ⇒ la bisección de §5 son ~4,5 h de la máquina que
    acaba de no arrancar. Mata la bisección ingenua.
 P4 harkaq — el patrón sí, el código no: su clausura enumera fichero a fichero sobre
    artefactos del store, semántica de rutas. Kconfig es otro grafo.
 P5 eje huella en el índice — sí y compatible hacia atrás: PackageEntry usa campos serde
    opcionales. Mismo truco que hizo pagable el campo `license` hoy.
 P6 PLAN-KIKIN §4.bis — DADO POR PENDIENTE Y ESTÁ HECHO EN DOS DE TRES: linux-metal y
    linux-generic ya declaran los cuatro símbolos; sólo `linux` deja IO_URING y BPF_SYSCALL
    al azar del defconfig. Y ojo al método: `grep IO_URING` da positivo por un COMENTARIO;
    hay que mirar los -e/-d dentro del bloque configure. Mismo error que infló el conteo de
    licencias hoy y que hace mentir la regla del `.a` no-PIC.

OBJECIÓN a §2.1: «clausura» sobre Kconfig no está definida todavía. Hay tres tipos de arista
(`depends on`, `select`, `imply`) con semánticas incompatibles, y `select` fuerza símbolos
IGNORANDO los `depends on` del destino. «Alcanzable desde CONFIG_WIRELESS» da conjuntos
distintos según la arista y ninguno es la respuesta. No lo invalida: lo convierte en la parte
con contenido técnico real. Y hay con qué validarlo GRATIS: el config actual de linux.toml
YA es un juego de bundles N1 hecho a mano («-d WLAN -d WIRELESS -d CFG80211 -d MAC80211
-d RFKILL» es literalmente «no necesito wifi») sobre un kernel que arranca.

LO QUE AGREGO:
 · Bisección SIN recompilar: kernel «superset» con lo bisecable como módulo ⇒ los bundles de
   hardware se prueban con lista de bloqueo en la línea de arranque. 6 reinicios en vez de 6
   rebuilds. No cubre lo que debe ser =y (buena parte de N2), y el superset es el kernel gordo
   que N1 quiere evitar: es diagnóstico, no el de todos los días.
 · El gate de no-regresión debe ser POR OBJETIVO: linux.toml apaga USB/HID/INPUT a propósito
   (es el kernel de QEMU con consola serie) y un gate global rechazaría una receta sana.
 · «Booteó» no basta para atestar: un kernel arranca y te deja sin red, que es el desastre que
   el propio gate quiere evitar. La atestación debe llevar QUÉ se comprobó — el mismo dato que
   calcula el gate: una implementación, dos usos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 15:54:18 -04:00
sergioandClaude Opus 5 2c2ee951d6 licencias: resolver -only vs -or-later CON LA CITA — y tres «pruebas» que no probaban nada
Los identificadores obsoletos (`GPL-2.0`, `LGPL-2.1`, `AGPL-3.0`) no dicen si el proyecto
concede «sólo esta versión» o «ésta o cualquier posterior», y eso decide con qué se puede
combinar el paquete y bajo qué términos puede redistribuirlo quien lo reciba.

NO SE PUEDE MIRAR EL COPYING, que es la trampa evidente: el texto de la GPL es IDÉNTICO en
los dos casos —es la licencia, no la concesión— y encima su apéndice «cómo aplicar la
licencia» contiene literalmente «or (at your option) any later version», así que buscarla
ahí da SIEMPRE positivo y parece evidencia siendo plantilla. Misma trampa que la regla del
`.a` no-PIC, donde grep contaba reubicaciones de `.debug_*`. La concesión vive en las
cabeceras de los fuentes y en el README; ahí se busca, excluyendo los ficheros de licencia.

SE GUARDA LA CITA, no sólo el veredicto: fichero + frase exacta que decidió. Una licencia es
una afirmación legal y quien la revise tiene que poder ver POR QUÉ dice lo que dice sin
repetir el trabajo.

Y eso pagó de inmediato: de las 12 primeras resoluciones, TRES eran evidencia inválida y
sólo se vieron porque estaba la cita. Cada una de una clase distinta:
 · caligula   — la frase salía de `checks/headless/expected.iso`, un FIXTURE DE TEST;
 · libnl      — de `include/linux-private/linux/seg6.h`, una cabecera del KERNEL vendorizada.
                La licencia del kernel no es la de libnl;
 · lm-sensors — declarado GPL-2.0 y la cita decía «version 2.1 of the License», que es la
                LGPL. La frase era real pero no sostenía lo que se le atribuía.

Las tres clases quedan filtradas en el script: ficheros de licencia, rutas de test/fixture, y
código vendorizado; más una comprobación nueva de que **la cita hable de la misma versión que
la licencia declarada**.

Sembradas las 9 auditadas (13 ficheros; algunas viven en varias colas). Ambiguas: 55 → 41.
Las 40 sin evidencia quedan listadas y siguen contadas — la mayoría son Go y la familia
cosmic, donde la frase no aparece fuera del COPYING.

Hashes verificados: 0 movidos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 15:15:20 -04:00
sergioandClaude Opus 5 6263d24788 store-gc + farm-sync: cortar el BUCLE DE CHURN de 24 G que hacía inútil la poda
Podé el store por la mañana: 362 artefactos superados, 24 G. Lo volví a podar por la tarde:
362 artefactos, 24 G. Comparados los dos manifiestos, **solapamiento del 100%: son los
MISMOS 362**. No era casualidad ni recuento nuevo — es un bucle.

LA CAUSA no estaba en el gc sino en `farm/farm-sync.sh`, que bajaba el store del worker
ENTERO sin filtro. El worker nunca se poda, así que conservaba los superados y nos los
devolvía en cada cosecha. Podar → cosechar → vuelven → podar. El disco pagaba 24 G por
vuelta y el gc informaba éxito cada vez, lo que es peor que fallar: parecía que se avanzaba.

Es la misma familia de fallo que el gc que reportaba borrados falsos, sólo que un nivel más
arriba: entonces el borrado no ocurría, ahora ocurre y se deshace solo. En los dos casos el
mensaje de éxito era cierto y engañoso a la vez.

EL ARREGLO es darle memoria al sistema. `store-gc.sh` acumula lo podado en
`work/store-gc-superados.txt` y `farm-sync.sh` lo usa como `--exclude-from` al bajar el
store del worker. Es seguro porque el store es CAS: los nombres `<hash>-<paquete>` son
inmutables y un artefacto superado no vuelve a ser vigente salvo que una receta retroceda —
y si retrocediera, se reconstruye, que es barato.

Disco: 159 G → 182 G tras la poda de hoy, y esta vez debería quedarse.

De paso, en el respaldo: rsync 24 («ficheros del origen desaparecieron») NO es un fallo, es
justo lo que pasa al podar mientras se sube. El bucle de reintentos lo tomaba por error real
y habría parado el respaldo entero por algo inofensivo — y encima cuando el disco aprieta,
que es cuando menos conviene quedarse sin copia.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 15:11:31 -04:00
sergioandClaude Opus 5 89c02db639 respaldo: rsync 24 (ficheros desaparecidos) no es un fallo — es podar mientras se sube
El bucle de reintentos trataba el 24 como «error que no es de red» y habría PARADO el
respaldo entero. Pero 24 significa que ficheros del origen desaparecieron durante la
transferencia, que en este proyecto es una combinación NORMAL: el respaldo dura ~19 h y
`store-gc.sh` es la válvula del disco, así que podar mientras se sube va a pasar. Y pasaría
justo cuando el disco aprieta, o sea cuando menos conviene quedarse sin copia.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 15:06:26 -04:00
sergioandClaude Opus 5 97eb1d4af2 docs: SDD 20 — limpiar el empalme de la sección de licencias
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 14:54:22 -04:00