Commit Graph
2212 Commits
Author SHA1 Message Date
Sergio e5b2d4e07d estado: cosecha granja 2026-09-08T06:01:46Z — avance del árbol KDE 2026-09-08 06:01:47 +00:00
Sergio d28a9313ab estado: cosecha granja 2026-09-08T05:31:26Z — avance del árbol KDE 2026-09-08 05:31:26 +00:00
Sergio c455a14611 estado: cosecha granja 2026-09-08T05:02:10Z — avance del árbol KDE 2026-09-08 05:02:10 +00:00
Sergio dd510d70e7 estado: cosecha granja 2026-09-08T04:31:59Z — avance del árbol KDE 2026-09-08 04:31:59 +00:00
Sergio 2584430a16 estado: cosecha granja 2026-09-08T04:01:28Z — avance del árbol KDE 2026-09-08 04:01:29 +00:00
Sergio 21bec8d02e estado: cosecha granja 2026-09-08T03:32:12Z — avance del árbol KDE 2026-09-08 03:32:12 +00:00
Sergio 92aacc6a5f SDD 26: el jarlog puesto — probado en el artefacto, no medible en el banco
sin PGO   v1        v2        v3 (= v2 + jarlog)
  DOM/maquetación   23.008    20.851    20.527    20.502   (-10,9%)
  SunSpider         16.006    15.859    15.869    15.838   (-1,0%)
  jarlog solo:      -0,1% y -0,2%

Indistinguible de cero, y el argumento no es que los rangos se solapen —que se
solapan— sino que la MISMA variante varía ~1% entre corridas de días distintos
(v1 dio 20.851 hoy y 21.050 ayer). El suelo de ruido del banco es diez veces el
efecto buscado.

No es un fallo del jarlog: el banco corre con la caché de página CALIENTE, y ahí
reordenar omni.ja no ahorra ninguna lectura. Su terreno es el arranque en frío,
que este instrumento no reproduce.

Lo que SÍ está probado es que la función existe, y del lado del artefacto:
libxul IDÉNTICO byte a byte entre v2 y v3, omni.ja distinto en 26 bytes. El
jarlog tocó exactamente lo que debía y nada más. Se conserva porque es lo que
hace upstream y no cuesta nada; lo que no se afirma es una ganancia no medida.

Y no hizo falta la extensión Quitter, que este mismo documento daba por
bloqueante: profileserver.py sólo traduce JARLOG_FILE a MOZ_JAR_LOG_FILE.

Anotado además: los dos atípicos del banco son 120.034 y 120.030 ms, o sea
exactamente el timeout del arnés. No son ruido de carga sino corridas COLGADAS
al arrancar en headless, 2 de 56 (~3,5%).
2026-09-08 03:31:23 +00:00
Sergio 89b296cac9 estado: cosecha granja 2026-09-08T03:01:49Z — avance del árbol KDE 2026-09-08 03:01:49 +00:00
Sergio 91a229a7c4 estado: cosecha granja 2026-09-08T02:32:04Z — avance del árbol KDE 2026-09-08 02:32:04 +00:00
Sergio d9833a581b jarlog: la mitad del PGO que faltaba — el orden de omni.ja para el arranque
El jarlog registra en qué orden se LEEN los ficheros dentro de omni.ja al
arrancar; con él, el empaquetador los reordena y el arranque hace lecturas
secuenciales en vez de saltar por el archivo.

NO hizo falta la extensión Quitter de Mozilla, que era lo que yo daba por
bloqueante: basta MOZ_JAR_LOG_FILE en el entorno del navegador. Su
profileserver.py sólo traduce JARLOG_FILE a esa variable — leerlo costó un grep
y ahorró rehacer el arnés entero.

Y se genera en una corrida APARTE, que está medido y no supuesto. Mozilla lo
emite en la misma sesión del profileserver; nuestro arnés arranca un navegador
POR PÁGINA, así que había que saber si el fichero se acumula o se pisa. Se pisa:
tras rejilla.html y tras tablas.html dio exactamente los mismos 27.652 bytes y
469 líneas. Su contenido lo domina el ARRANQUE, no la página, así que una
corrida dedicada vale igual que una de 46 — rehacer el perfilado sólo para
obtenerlo habría sido gasto sin diferencia. Se conserva el profdata ya medido
(el del -11,4%), que es lo que hace comparables los números.

Guardián propio: un jarlog vacío o que no nombre los archivos reales NO rompe el
build de firefox, sólo deja el omni.ja sin ordenar — o sea que se pierde justo
lo que se vino a buscar, en silencio. Se exige que mencione los DOS archivos que
el navegador abre (omni.ja y browser/omni.ja).
2026-09-08 02:16:53 +00:00
Sergio b00d513fbb estado: cosecha granja 2026-09-08T02:01:48Z — avance del árbol KDE 2026-09-08 02:01:48 +00:00
Sergio 0d03050b2b estado: cosecha granja 2026-09-08T01:32:00Z — avance del árbol KDE 2026-09-08 01:32:00 +00:00
Sergio b5582a2bd9 estado: cosecha granja 2026-09-08T01:02:14Z — avance del árbol KDE 2026-09-08 01:02:14 +00:00
Sergio 84ff534d43 estado: cosecha granja 2026-09-08T00:01:54Z — avance del árbol KDE 2026-09-08 00:01:54 +00:00
Sergio d971413a89 estado: cosecha granja 2026-09-07T23:31:52Z — avance del árbol KDE 2026-09-07 23:31:52 +00:00
Sergio 0a1d087eaa estado: cosecha granja 2026-09-07T23:01:53Z — avance del árbol KDE 2026-09-07 23:01:53 +00:00
Sergio 67269a028a estado: cosecha granja 2026-09-07T22:32:00Z — avance del árbol KDE 2026-09-07 22:32:00 +00:00
Sergio 1dbaf8d85a estado: atuq sobre el firefox con PGO v2 — 860/862 y cero huecos
atuq b3:fab2fbfb, 340 M, libxul de 227.043.776 bytes: el mismo del motor con el
perfil de 46 páginas. Verificado que ARRANCA desde una hidratación limpia de
escritorio-sway, 0 errores de relocación.

El corpus queda en 860/862 sellados —los dos que faltan son `ajeno`, que es
frontera y no deuda— y el vigía de sonames en CERO huecos en los cinco perfiles.
2026-09-07 22:08:38 +00:00
Sergio 54a27b966b estado: cosecha granja 2026-09-07T22:02:23Z — avance del árbol KDE 2026-09-07 22:02:23 +00:00
Sergio 6b469a81d2 SDD 26: el corpus ampliado rinde — de -9,6% a -11,4% en maquetación
sin PGO    v1 (36 pág)      v2 (46 pág)
  DOM/maquetación       23.285 ms  21.050 (-9,6%)   20.640 (-11,4%)
  SunSpider 3d-raytrace 16.003 ms  15.846 (-1,0%)   15.857 (-0,9%)

La mejora en maquetación NO se afirma por las medianas sino por la separación:
SEIS de las siete muestras de v1 son más lentas que TODAS las de v2; sólo una
cae dentro del rango de v2.

En SunSpider v1 y v2 son INDISTINGUIBLES —los valores se entrelazan por
completo—, que es exactamente lo esperable: ese camino lo ejecuta el JIT y el
PGO no lo toca. Un corpus mejor no puede mejorar lo que el PGO no alcanza. El
-0,9% frente al -1,0% no dice que v2 sea peor ahí: dice que no se distingue.

Una de las siete corridas de v2 dio 120.029 ms contra ~20.600 de las otras seis.
La mediana lo ignora por diseño; una media lo habría convertido en «v2 es
catastróficamente peor». Fue la razón de elegir mediana antes de ver un número.

Y lo que el banco no puede afirmar, escrito: las 10 páginas nuevas ejercitan los
mismos subsistemas que la página de medición, así que el parentesco con lo que
ahora se entrena es mayor que antes. El -11,4% es real para ESTA carga;
generalizarlo a «cualquier página» sería el mismo error que cometía el corpus de
Mozilla, en la otra dirección.
2026-09-07 21:52:39 +00:00
Sergio 9769614866 estado: cosecha granja 2026-09-07T21:32:10Z — avance del árbol KDE 2026-09-07 21:32:10 +00:00
SergioandClaude Opus 5 b8865560c0 SDD 26 §8: la unidad 4.e deja de ser una afirmación y pasa a estar medida
`scripts/test-atuq-inicio.py` le pregunta al motor por sus overrides en vez de
mirar el XPI en el disco: moz-extension://…/inicio.html en las dos, contra
about:home / about:newtab cuando se le saca el XPI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 21:03:35 +00:00
SergioandClaude Opus 5 639948f2cd atuq: la página de inicio y la pestaña nueva, verificadas contra el propio motor
La v0.3 las puso por extensión de sistema y quedó como afirmación. Que el XPI
esté en el artefacto no dice nada: el override lo puede rechazar el gestor de
extensiones, lo puede pisar una política, o el navegador puede arrancar con el
chrome viejo cacheado. Ninguna de las tres falla ruidosamente — se ve una pestaña
nueva perfectamente normal, que es de otro.

Se mide preguntándole AL MOTOR, no mirando el disco: una sonda con permiso
`browserSettings` lee `homepageOverride` y `newTabPageOverride`.

    positivo  moz-extension://…/inicio.html  (las dos)
    control   about:home · about:newtab

El control negativo borra el XPI de `inicio` dentro del overlay temporal —el
rootfs real no se toca— y exige el resultado contrario.

Y una escotilla `ATUQ_DIR` en las dos sondas nuevas, con su aviso a gritos: el
corpus es compartido y hoy mismo el perfil PGO v2 de otro frente re-hasheó
`firefox` y con él `atuq`, así que el artefacto VIGENTE no existe en ningún store
hasta que alguien pague un build de horas. Sin escotilla no se puede correr una
sola prueba de atuq en esa ventana; con ella se corre contra un artefacto viejo A
SABIENDAS, y por eso el resultado no se puede citar como «atuq de hoy pasa».

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 21:03:25 +00:00
Sergio 94d7e8626f estado: cosecha granja 2026-09-07T21:02:16Z — avance del árbol KDE 2026-09-07 21:02:16 +00:00
Sergio e74d449655 perfilar.sh: la corrida de entrenamiento PGO, con las tres defensas puestas
Estaba en un scratchpad y se perdía; ahora vive en el repo con el porqué de cada
guarda, porque las tres se pagaron durante la primera corrida:

1. --unshare-pid en el bwrap. Sin él los hijos SOBREVIVEN al sandbox: un
   http.server quedó vivo 19 HORAS, retuvo el puerto y envenenó las corridas
   siguientes con «Address in use».

2. Puerto aleatorio por corrida. Elimina la colisión de raíz en vez de
   detectarla: si otro proceso tiene un puerto, este arranque usa otro.

3. Marcador único por corrida, servido desde una copia ESCRIBIBLE del corpus.
   La versión anterior horneaba el marcador como constante del script, y eso
   rompía justo lo que el chequeo existe para probar: el servidor huérfano
   servía el MISMO fichero de marca y el chequeo lo daba por bueno. Verificaba
   «alguien sirve este contenido», no «este servidor es el mío». Con el marcador
   por corrida, un servidor ajeno devuelve otra cosa y se aborta.

Y queda escrito por qué los sandboxes de Firefox van apagados en el
entrenamiento: con ellos los hijos mueren con signal 11, el proceso que renderiza
no nace, el servidor registra 0 GET y el único .profraw es el del padre
arrancando — un perfil de nada, con todo en verde. Es lo que hace el
profileserver.py de Mozilla. Sólo aplica al entrenamiento.

La espera al servidor va con reintento y no con un sleep fijo: 2 s concluían
«no responde» sobre uno que sí iba a responder.
2026-09-07 20:45:56 +00:00
Sergio a5bcf4fd3e perfil PGO v2: 46 páginas y 6,5× más ejecuciones registradas
El corpus se amplió por una MEDICIÓN, no por corazonada. Con las 36 de Mozilla
la ganancia era -9,4% en DOM/maquetación y -1,0% en 3d-raytrace, y la razón es
que un benchmark JIT-bound es casi ciego al PGO: el bucle caliente lo ejecuta
código que el JIT emite en runtime, y el PGO optimiza el intérprete, el GC y el
propio JIT, no lo que el JIT produce. El corpus de Mozilla está dominado en
número de páginas por SunSpider, o sea que entrenaba justo donde menos rinde.

Con las 10 de scripts/pgo-corpus/ sumadas, el efecto se ve EN EL PROPIO PERFIL:

    ejecuciones registradas   5.885.254.799 -> 38.498.366.335   (6,5x)
    máximo por función          416.219.136 ->  1.552.416.768   (3,7x)

Funciones y bloques totales no cambian (501.521 / 3.734.991) porque son la
estructura estática del binario instrumentado, no lo que se ejecutó.

Blob nuevo publicado y verificado bajándolo del mirror por el mismo camino que
usa hammer: 17.525.708 bytes, sha256 95472411.
2026-09-07 20:43:49 +00:00
Sergio b1f7a1813c estado: cosecha granja 2026-09-07T20:32:51Z — avance del árbol KDE 2026-09-07 20:32:51 +00:00
SergioandClaude Opus 5 1bebeab818 SDD 26 §7.bis: el camino de native messaging existe, con su sonda y su control
Antes de escribir el crate del host en tawasuyu había que saber si el mecanismo
funciona en nuestro build. Funciona, y el cuadro deja las cuatro cosas que hacen
falta para escribirlo: dónde va el manifiesto (/usr/lib/mozilla, no el appdir),
qué instala de verdad la extensión (el escaneo de distribution/extensions, NO el
install_url de la política), que el host tiene que ser un proceso largo, y que el
único error que nombra la causa es el del puerto de connectNative.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 20:27:30 +00:00
SergioandClaude Opus 5 c13be792ab atuq: el camino de native messaging EXISTE — sonda, y tres cosas que no eran obvias
Todo el §6 del SDD 26 —sct, descargas al CAS, archivo con RAG, torrent— pasa por
un solo mecanismo: un proceso Rust hablando native messaging con la extensión.
Antes de escribir ese crate en tawasuyu conviene saber si el camino existe en
NUESTRO build, que es propio, rebrandeado y con MOZ_REQUIRE_SIGNING vacío.

Existe. Una extensión de diez líneas y un host de tres:

    positivo  MENSAJE {"ok":true}
    control   DESCONECTADO No such native application puente_atuq

El control no sólo falla: NOMBRA la causa, que es lo que confirma que la ruta del
manifiesto es la que se probó.

TRES COSAS MEDIDAS QUE NO ERAN OBVIAS:

1. El manifiesto va en `/usr/lib/mozilla/native-messaging-hosts/`, NO en el
   appdir. Gecko lo busca por `XRESysNativeManifests`, que en Linux sale de un
   `/usr/lib/mozilla` compilado, y el rebranding a atuq no lo mueve.

2. Un host que escribe y SALE pierde el mensaje. La primera sonda hacía printf y
   terminaba: el puerto llegaba a onDisconnect «sin error y sin mensaje», o sea
   el peor informe posible — parece que el camino no existe. Con un sleep detrás
   del printf, el mensaje aparece. El host de verdad es un proceso largo, así que
   en producción no se nota; en una prueba, sí.

3. `ExtensionSettings.install_url` con `file://` NO instala nada, y sin una línea
   de log. Probado con normal_installed y con force_installed, y con el XPI dentro
   y fuera del appdir: ninguna instala. Lo que instala las extensiones de atuq es
   el ESCANEO de `distribution/extensions/`. La política sirve para fijarlas y
   configurarlas; leerla como «esto es lo que las instala» es un error fácil,
   porque los dos mecanismos apuntan a los mismos ficheros y se tapan uno al otro.

`sendNativeMessage` no sirve para diagnosticar: devuelve «An unexpected error
occurred» para todo. El error del puerto de `connectNative` es el único que
nombra la causa.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 20:27:07 +00:00
Sergio 8a0496dbb3 corpus de maquetación para el PGO: 10 páginas nuestras
Medida la ganancia del PGO ayer: -9,4% en una página de DOM/maquetación y -1,0%
en 3d-raytrace de SunSpider. La razón de la diferencia es que un benchmark
JIT-bound es casi CIEGO al PGO: el bucle caliente no lo ejecuta el C++ de
SpiderMonkey sino el código máquina que el JIT emite en runtime, y el PGO
optimiza el intérprete, el GC y el propio JIT — no lo que el JIT produce.

El corpus de Mozilla está dominado en número de páginas por SunSpider, o sea que
entrenaba mucho justo donde el PGO menos rinde. Estas diez ejercitan el camino
que sí es C++ de punta a punta:

  flex          flexbox anidado, wrap, alineaciones
  rejilla       CSS Grid: pistas, áreas nombradas, auto-fit
  tablas        tablas grandes con table-layout FIJO y AUTO (dos algoritmos)
  texto         columnas, shaping, reflujo por cambio de ancho
  selectores    DOM profundo contra 600 reglas, muchas que NO casan
  pintado       degradados, sombras, opacidad, mix-blend-mode
  transformar   transforms y contextos de apilamiento
  svg           paths, degradados, clip
  desbordes     overflow anidado, position:sticky, scroll programático
  reflujo       layout thrashing: leer y escribir geometría alternadamente

Reglas que cumplen todas, y no son de estilo:
 · DETERMINISTAS (LCG propio, ni Math.random ni Date ni red) — dos corridas hacen
   lo mismo, así que dos perfiles difieren por el timing de los contadores y no
   por haber visitado caminos distintos.
 · AUTOCONTENIDAS — el perfilado corre sin red.
 · NUESTRAS — nada de páginas ajenas capturadas, que traerían licencia y
   fragilidad.
 · ACOTADAS — 16 a 32 s cada una; el perfilado arranca un navegador POR PÁGINA.

Validadas las diez contra el firefox sellado: todas renderizan y producen
captura. `flex` hubo que acortarla de 40 a 12 cajas raíz: con 40 la página medía
68.241 px de alto y la captura moría con «Failed to allocate a surface due to
invalid size». El layout ocurría igual, pero sin captura no se ejercita el
pintado, que es justo la mitad que se venía a entrenar.
2026-09-07 20:24:52 +00:00
SergioandClaude Opus 5 7d0dc72de0 test-atuq-ruteo: los puertos fijos daban un fallo del producto que no existía
Esta prueba dio «NO concluyente — la pestaña sin contenedor no salió directa»
dos veces seguidas, y la causa no tenía nada que ver con atuq: **otro frente de
esta misma máquina tenía levantado un `python3 -m http.server 8099`** desde hacía
dos horas. La oreja del destino no podía atarse, no llegaba nada, y el informe
publicaba un fallo inexistente. En un repo que comparten varios agentes, un
puerto fijo es estado compartido sin dueño.

Y había un segundo bug que es el que lo hizo dañino: el `bind` vivía DENTRO del
hilo de la oreja, donde `fatal()` no puede matar el proceso — `SystemExit` en un
hilo secundario sólo termina ese hilo. Así que la prueba imprimía «el puerto está
ocupado, la medición no valdría nada» y **seguía adelante hasta publicar un
veredicto**. Un guardián que avisa de que no puede medir y mide igual es peor que
uno que no mide.

Ahora las dos orejas se atan en el hilo principal, con el puerto 0: lo elige el
kernel. Si no hay puertos, no hay prueba.

Con eso, y contra el atuq de hoy (d36ae188, el del arreglo del LD_LIBRARY_PATH):

    positivo  destino 40843 · proxy 38503 → GET /directo al destino, SOCKS5 al proxy
    control   destino 40917 · proxy 37113 → las dos directas, cero al proxy

O sea que el ruteo por contenedor del §6.8 sigue en pie y no lo rompió nada de
hoy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 20:12:20 +00:00
Sergio b12256eb2b estado: cosecha granja 2026-09-07T20:02:22Z — avance del árbol KDE 2026-09-07 20:02:22 +00:00
Sergio 40cf495a14 estado: cosecha granja 2026-09-07T19:33:36Z — avance del árbol KDE 2026-09-07 19:33:36 +00:00
Sergio 79bcbf49fd estado: cosecha granja 2026-09-07T19:01:49Z — avance del árbol KDE 2026-09-07 19:01:49 +00:00
SergioandClaude Opus 5 e31a36d5a4 SDD 26 §6.10: la cadena de notificaciones, probada desde la página web
Con `--via-atuq` el emisor es `new Notification(...)` dentro del navegador, o sea
las cinco piezas: libxul dlopeando libnotify.so.4 (invisible para cualquier
auditor de ELF), D-Bus, la activación y dunst dibujando. Veredicto por el
`onshow` del motor: MOSTRADA #33 con el .service, «ERROR al mostrar» sin él.

Y deja probado de paso que en el proceso de atuq la GLib NO se duplica — usa la
cadena `-shared` que arrastra su GTK3, la misma contra la que enlaza
libnotify.so.4. El que la duplicaba era dunstify.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 18:37:40 +00:00
SergioandClaude Opus 5 ec45c3fa97 dunst-headless: --via-atuq — la cadena entera, empezando en una página web
El modo por defecto prueba la cadena del SISTEMA (`notify-send` → bus → dunst).
Éste prueba la que motivó todo el hilo del §6.10: una página llama a
`new Notification(...)`, `libxul` hace `dlopen("libnotify.so.4")` —que no es
NEEDED de ningún ELF, o sea invisible para cualquier auditor—, eso habla D-Bus,
el bus ACTIVA dunst y dunst dibuja. Cinco piezas, y la única forma de saber que
están las cinco es verlo.

El veredicto acá no puede ser el diff de píxeles solo: el navegador ocupa la
pantalla y repinta por su cuenta. Lo decisivo es el `onshow` del objeto
Notification, que es el motor diciendo que el sistema ACEPTÓ la notificación; el
diff queda como corroboración y la captura «antes» se toma con atuq ya pintado.

    --via-atuq                     MOSTRADA #33 · 151.174 píxeles
    --via-atuq --negative-control  «ERROR al mostrar» · 0 píxeles

El control negativo también se lee distinto según el modo, y no por comodidad:
sin el .service, lo que tiene que faltar en el modo navegador es el onshow.

Captura en docs/evidencia/atuq-notificacion-web-sway-2026-09-07.png: dos globos
de dunst sobre la ventana de atuq, en sway headless, sin que nadie haya lanzado
el daemon a mano.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 18:37:24 +00:00
Sergio ca7a410b53 estado: cosecha granja 2026-09-07T18:31:59Z — avance del árbol KDE 2026-09-07 18:31:59 +00:00
SergioandClaude Opus 5 e98362925a SDD 26 §6.10: la mitad de sway queda cerrada, con la evidencia al lado
`dunst` 1.12.2 atiende `org.freedesktop.Notifications` en el cuarto escritorio.
La prueba no es que la receta selle: el bus ACTIVA al daemon y el daemon DIBUJA
—14.832 píxeles cambiados con el .service puesto, 0 sin él—.

Y queda anotada la media función que apareció de paso: dunstify segfaultea por
las dos GLib (estática del corpus + compartida que arrastra libnotify.so.4), o
sea que en este corpus quién enlaza qué GLib es parte del contrato.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 18:15:50 +00:00
SergioandClaude Opus 5 4f9ee5430f dunst: sway ya atiende las notificaciones, y hay un píxel que lo prueba
Era la única mitad que quedaba abierta del mapa del §6.10: KDE atiende
`org.freedesktop.Notifications` con plasma-workspace, GNOME con gnome-shell y
COSMIC con cosmic-notifications; sway no tenía a NADIE, así que una página que
pedía notificar mandaba el mensaje al bus y se lo comía el silencio.

`dunst` 1.12.2 y no `mako` —que es el de la casa wlroots— por dos razones
medidas: el nombre `mako` YA ESTÁ OCUPADO en el corpus por el motor de plantillas
de mesa, y el cierre del perfil incluye las herramientas de build, así que la
imagen terminaría con dos artefactos homónimos peleando por las mismas rutas; y
mako habla D-Bus por sd-bus, que en musl es una receta nueva (basu), mientras
dunst habla por GDBus, que ya está sellado. Cero dependencias nuevas.

DOS GUARDIANES EN LA PROPIA RECETA, porque las dos cosas que importan no se ven
en que compile:

- `X11=0` es una intención; el hecho se lee en el ELF. dunst compila los dos
  backends por defecto y su config.mk avisa: sin wayland «forzará xwayland». En
  esta distro eso sella un binario que arranca, toma el bus y NO PINTA NUNCA. La
  fase exige `libwayland-client` entre los NEEDED y prohíbe `libX11`.
- El `.service` de D-Bus es la mitad que importa: sin systemd, al daemon lo
  levanta EL BUS. Se comprueba que reclame `org.freedesktop.Notifications` y se
  le saca la línea `SystemdService=`, que acá no puede significar nada.

Y `DUNSTIFY=0`: entró primero en 1 y el binario **segfaultea hasta en
`--help`** (rc=139 en las tres pruebas). La causa es el cuadro que este repo ya
tiene escrito: nuestro `libnotify.so.4` es compartido y arrastra la cadena glib
`.so`, mientras esta cola enlaza la GLib estática ⇒ dos copias de GObject en un
proceso. No se pierde nada: el emisor de la casa es `notify-send`, que viene
DENTRO del artefacto de libnotify y ya está en las cuatro imágenes. Hay un
guardián que frena el día que alguien vuelva a poner DUNSTIFY=1 sin leer.

LA PRUEBA, en `scripts/wlr/dunst-headless.sh`: sway headless + bus de sesión +
`notify-send`, y NADIE lanza dunst a mano — lo activa el bus por el .service,
que es el mismo camino de una página web en atuq. La evidencia es un diff de
píxeles antes/después acotado al cuadrante donde dunst dibuja, porque un log en
verde es compatible con una pantalla vacía y el color no distingue (swaynag pinta
su barra de error en el mismo rojo).

    positivo  14832 píxeles cambiados · servidor: dunst knopwob 1.12.2 1.2
    control   0 píxeles · ServiceUnknown · notify-send rc=1

El control negativo borra el .service dentro del overlay temporal y EXIGE que no
se dibuje nada: probado en los dos sentidos.

Captura en docs/evidencia/dunst-sway-notificacion-2026-09-07.png.
Vigía de sonames: escritorio-sway 203 → 204 nodos, 512 sonames, 0 sin proveedor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 18:15:29 +00:00
Sergio 7d9fc96acb estado: cosecha granja 2026-09-07T18:02:07Z — avance del árbol KDE 2026-09-07 18:02:07 +00:00
SergioandClaude Opus 5 e0d9752e19 firefox: anotada la deuda del lanzador que pierde AV1 (sin mover el hash)
Su `/usr/bin/firefox` tiene el MISMO lanzador que tenía atuq: el appdir una sola
vez en `LD_LIBRARY_PATH` ⇒ Gecko se come ese primer elemento al lanzar el RDD ⇒
el ffvpx bundleado no carga ⇒ AV1 no reproduce, en silencio.

No se aplica todavía a propósito: el lanzador vive dentro de la fase `install`,
así que tocarlo mueve el ArtifactHash y obliga a reconstruir firefox entero
(LTO+PGO, ~4 h) y con él atuq. Ningún perfil declara `firefox` —el navegador que
se shipea es atuq, ya arreglado—, así que hoy es latente. Se paga en el próximo
re-hash por otro motivo, que es cuando cuesta cero.

Comprobado que el comentario NO mueve el hash: b3:1d730334 antes y después.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 17:44:23 +00:00
Sergio 999a13cafc estado: cosecha granja 2026-09-07T17:02:16Z — avance del árbol KDE 2026-09-07 17:02:16 +00:00
Sergio 5171ea9df4 estado: cosecha granja 2026-09-07T16:32:02Z — avance del árbol KDE 2026-09-07 16:32:02 +00:00
Sergio acccbc0a2c SDD 26: la ganancia del PGO, medida — y sale al revés de lo esperable
DOM+layout+strings, FUERA del corpus:  23.540 -> 21.327 ms   -9,4%
  SunSpider 3d-raytrace, DENTRO:         16.034 -> 15.878 ms   -1,0%

Mismo rootfs, mismo script, corridas intercaladas A/B/A/B, mediana de 7,
calentamiento descartado; lo único que cambia entre variantes es un --ro-bind de
/usr/lib/firefox. Los rangos no se solapan en ninguna de las dos, así que ambas
diferencias son reales y no ruido.

Yo esperaba lo contrario: que medir sobre el conjunto de ENTRENAMIENTO inflara
la ganancia. Da el número MÁS BAJO, y la razón es mejor que la predicción:
3d-raytrace es aritmética pura en un bucle caliente, y ese bucle no lo ejecuta
el C++ de SpiderMonkey sino código máquina que el JIT genera en runtime. El PGO
optimiza el intérprete, el GC y el propio JIT — no lo que el JIT emite. Un
benchmark JIT-bound es casi ciego al PGO por construcción.

Donde se ve es en DOM/layout/arranque, que es C++ de punta a punta, y que además
es lo que el usuario percibe: la medición cubre el ciclo completo del proceso
(arrancar, renderizar, capturar, salir), no el régimen de una página cargada.

Corolario anotado: el corpus de entrenamiento está sesgado hacia JS justo donde
el PGO menos rinde. Un corpus con más maquetación probablemente daría más. Es su
propia unidad de trabajo.
2026-09-07 16:20:20 +00:00
Sergio 70026f5730 estado: cosecha granja 2026-09-07T16:02:08Z — avance del árbol KDE 2026-09-07 16:02:08 +00:00
SergioandClaude Opus 5 361a47cfc4 SDD 26: cae la última frase del §6.10 que el §6.11 desmintió, y la unidad 4.f al plan
«El más caro para el usuario es ffmpeg: sin él, un sitio que sirva H.264 no
reproduce» — el hueco más caro del mapa no existía. De los cuatro «sin receta»
quedan tres y ninguno es un códec.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 15:41:37 +00:00
SergioandClaude Opus 5 6a1343d2d2 SDD 26 §6.11: la pregunta era «¿reproduce?», y la respuesta destapó dos errores y un bug
El §6.10 preguntó «¿qué NO puede hacer?» y contestó leyendo cadenas de sonames.
Media pregunta. Al hacer la otra mitad —¿se ve el vídeo?— salieron tres cosas:

DOS ERRORES DEL MAPA, los dos por leer una etiqueta en vez de medir:

- «no hay receta de ffmpeg» era falso: la hay, sellada, y ya viajaba en la
  clausura de los cuatro escritorios por `mpv`. Lo que faltaba era la lista de
  raíces del runner.
- «VA-API sin receta» también: `libva` está y está en las cuatro imágenes. El
  hueco es real pero es el DRIVER — las tres mesa van `-Dgallium-va=disabled`.
  Y queda anotado que ahora el mapa ya no lo puede ver, porque la librería
  presente silencia el aviso sin encender la función.

UN BUG REAL: AV1 no reproducía. Gecko se come el primer elemento de
`LD_LIBRARY_PATH` al lanzar el RDD ⇒ sin appdir ⇒ ffvpx no carga ⇒ se cae al
ffmpeg del sistema, que no trae AV1 por software. Sin error, sin NEEDED
faltante, sin cadena ausente: `readyState=1` para siempre.

Y lo que quedó MEDIDO, por el lanzador de verdad y headless: H.264+AAC, VP9+Opus,
AV1, MP3 y FLAC reproducen, con el tiempo avanzando y no con «se creó el
decodificador» — que en AV1 se creaba igual y no entregaba un cuadro.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 15:40:45 +00:00
SergioandClaude Opus 5 f3aaab75c6 test-atuq-rootfs: el triaje decía «sin receta de ffmpeg» y era falso
La familia `libavcodec.so.*` estaba clasificada como hueco con el motivo «sin
receta de ffmpeg en el corpus». `recipes/ffmpeg.toml` existe, está sellada,
publica `libavcodec.so.61` —uno de los once sonames que sondea libxul— y ya
viajaba en la clausura de los cuatro escritorios arrastrada por `mpv`. Pasa a
ruido: lo que falta son las OTRAS versiones del soname, y Firefox recorre la
lista hasta que una carga.

`libva` igual: la receta está y ahora también en el rootfs del runner. Sigue
siendo hueco, pero por la otra mitad —las tres mesa van con `-Dgallium-va=disabled`
y `-Dvideo-codecs=` vacío, así que no hay un solo `*_drv_video.so` que cargar—,
y el motivo ahora lo dice.

Con eso el mapa pasa de 47 cadenas sin proveedor a 7 huecos reales.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 15:39:36 +00:00
SergioandClaude Opus 5 a44fc4f06a atuq: AV1 no reproducía, y la causa era UN elemento de LD_LIBRARY_PATH
Gecko SE COME EL PRIMER ELEMENTO de `LD_LIBRARY_PATH` al lanzar el proceso RDD
—el que decodifica vídeo—. El lanzador ponía `/usr/lib/atuq` una sola vez, o sea
primero, así que el RDD arrancaba sin él, no encontraba `libmozavcodec.so` /
`libmozavutil.so` (el ffvpx bundleado, donde vive dav1d) y anotaba:

    PlatformDecoderModule  FFVPX: Link result: NoProvidedLib

El navegador NO falla ahí: se cae al ffmpeg del sistema, que cubre
H.264/AAC/VP8/VP9/MP3/FLAC/Opus. Pero AV1 por software NO lo cubre —el
decodificador `av1` de ffmpeg es sólo hwaccel y nuestra receta va sin dav1d—, así
que un vídeo AV1 se quedaba en `readyState=1` para siempre, sin un error, sin un
NEEDED faltante y sin una cadena ausente. Media función apagada en silencio, que
es la forma de fallo de esta casa.

Tres corridas que sólo cambian esa variable, con el mismo artefacto:

    LD=/usr/lib/atuq:/usr/lib:/lib            → RDD NoProvidedLib   AV1 ✗
    LD=/usr/lib/atuq:/usr/lib/atuq:/usr/lib   → RDD Success         AV1 ✓
    LD=/relleno:/usr/lib/atuq:/usr/lib        → RDD Success         AV1 ✓

El arreglo es repetir el appdir. Feo y correcto mientras no haya `patchelf` en el
corpus para grabar `RUNPATH=$ORIGIN`, que borraría la variable entera.

Y el guardián que sale de este punto ciego, `scripts/test-atuq-codecs.sh`: abre
cinco muestras versionadas en `scripts/fixtures/codecs/` y mira si `currentTime`
AVANZA — «se creó el decodificador» no es «decodifica». El veredicto sale por
`dump()` al stdout, así que no necesita ni red ni servidor, y corre headless para
que sirva en el worker. Con `--negative-control` se saltea el lanzador y EXIGE
que AV1 falle: probado en los dos sentidos, 5/5 y control ✓.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 15:38:02 +00:00
SergioandClaude Opus 5 b6d000eca3 atuq-nested: el runner medía un rootfs MÁS POBRE que la imagen, y su caché no cacheaba
Tres cosas en el mismo fichero, las tres medidas hoy:

1. `ffmpeg` y `libva` entran a las raíces. NO son recetas nuevas: las dos están
   selladas en el corpus y ya viven en la clausura de los cuatro escritorios,
   arrastradas por `mpv` (verificado con `yupana.membresia`, no leyendo el TOML).
   Faltaban acá, o sea que el runner abría un rootfs sin libavcodec y desde ahí
   se concluía que la imagen no tiene códecs. El instrumento otra vez, no el
   artefacto.

2. El bucle de hidratación salía 1 SIEMPRE: `$VIGENTES` termina en `\n`, `echo`
   agrega otro, la última vuelta lee la línea vacía, `[ -n "$d" ]` da 1 y con
   `set -e` el script moría sin imprimir una sola línea, justo después de
   hidratar bien las 41 raíces. Ahora `continue` en la vacía y un `hydrate` que
   falla grita.

3. La comparación del sello NUNCA daba igual: el lado izquierdo lleva su `\n`
   final y `$(cat …)` lo recorta ⇒ «DESACTUALIZADO» en cada corrida y 3013
   ficheros rehidratados de más. Los dos lados pasan ahora por la misma
   sustitución de comandos.

Y `ROOTFS_ONLY=1`, que corta después de hidratar: lo necesita el guardián de
códecs, que corre headless y tiene que usar ESTA lista de raíces y no una copia.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 15:37:46 +00:00