3dc442459725cdf31c7bf652bcf257723e43eda3
619
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3dc4424597 |
la jaula que te mapea a root rompe al propio agente
`claude --dangerously-skip-permissions` moría en la caja con «cannot be used with root/sudo privileges»: la instancia de sergio traía `root = true`, que mapea el uid del invocante a 0 ADENTRO (qorpa.rs:1533), así que el CLI se veía a sí mismo como root. Con `root = false` queda uid 1001 y la bandera se acepta — comprobado: `claude --dangerously-skip-permissions --version` contesta. De paso, a esa instancia le faltaba `/store` en `rw` —la de root ya lo tenía—: sin eso el agente lee el store y no puede sellar nada. Ahora escribe (probado y limpiado). Y se corrige el aviso sobre los builds, que ahora falla por otro motivo: con root=false el bwrap anidado SÍ se levanta y /proc monta, pero no puede mapear el uid 0 que takana build pide, porque sergio no tiene rango en /etc/subuid y newuidmap/newgidmap no son setuid en esta caja. Cerrarlo es una decisión con precio (un setuid más, y el /usr/bin de acá está hidratado del store), no un trámite; mientras tanto el build se le pide al anfitrión por ssh. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
984ebdf58f |
la subpágina de humanoid se publica con rsync, y queda dicho por qué
El resto de los sitios se publican con `git pull` porque Caddy sirve de los clones. Con humanoid no se puede: su repo es el proyecto Android y lo que se sirve son APK ya construidos más su índice — salida de release que no vive en ningún repo, y que para regenerarse pide Android SDK + Java, que la caja no tiene. Hasta que la caja construya Android, esto es un puente declarado, no el modelo. `--checksum` a propósito: los APK se reconstruyen con el mismo tamaño y otra fecha, y un rsync por fecha subiría 91 M cada vez; medido hoy, lo que de verdad cambió fueron tres ficheros (index.html, en/index.html y rizoma-launcher.apk) y los otros nueve sólo tenían la marca de tiempo distinta. Y no lleva --delete: borrar a ciegas en lo que se está sirviendo no es publicar, es perder. El control pregunta al SITIO, no al rsync —que "salió bien" y una página vieja conviven sin ruido— y comprueba además un APK, porque el índice puede estar bien y la descarga rota: es lo que la gente se lleva. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
bd8f9fbbb2 |
atuq: la bóveda ANDA hasta la etapa D — y con el navegador abierto quedaba muda por un tercer fallo
Con el arreglo de llimghi sellado, el guardián de metal pasó de la A a la D:
A ✓ sin la app dueña, el host contesta locked:true (y con ok:true, que es la trampa)
B ✓ con la app, vault.status dice ABIERTA — el socket sube en 1 s
C ✓ el diálogo ABRE en el compositor y se lo CONTESTA: «1» ⇒ yes, «2» ⇒ no (los dos sentidos)
D ✓ vault.save con consentimiento real guarda, y vault.match la encuentra SIN contraseña
E ✗ el navegador pide la contraseña y no llega nunca
La E destapó un fallo que no es de atuq ni de llimphi: el dueño de la bóveda atendía de a UN cliente
—`atender_cliente` no vuelve hasta que el cliente se va, y se lo llamaba en el hilo del accept—, así
que la primera conexión se quedaba con él mientras viviera. Y el caso normal es ése: Gecko lanza un
`puriy-costura` por PUERTO, ocho en atuq, y todos conectan al arrancar. La extensión de la bóveda
mandaba `vault.match` y no volvía nunca: sin insignia, sin log y sin error, indistinguible de «este
sitio no tiene contraseñas».
Control sin navegador, en los dos sentidos: un solo cliente contesta en milisegundos; con otro host
conectado y quieto, la misma pregunta queda colgada y la mata el timeout a los 30 s.
Arreglado en tawasuyu (`cf3540460`, un hilo por conexión; los diálogos los serializa ahora el Mutex
de la bóveda) con su test de regresión, que además cazó que el arreglo obvio —clonar el Dueno— borra
el socket en el Drop del primer hilo que termina. Los siete tests que ya había no podían ver el fallo:
abrían un cliente por vez, que es justo lo que producción nunca hace.
⚠ Y subir el pin volvió a chocar con el `Cargo.lock` abierto de tawasuyu. Lo que importa para la
próxima es CUÁL operación lo cierra: `cargo metadata` sin `--locked` da **1 línea** de diff y cero
checksums movidos; `cargo generate-lockfile` da 6.305 líneas y 750 checksums, e invalidaría el
vendoreo de todos. Publicado en `b80f7567c` con índice temporal (estaba MM).
Del lado del guardián, tres cosas que salieron de fallar:
· el contestador INSISTE hasta que la ventana se va — con el mismo `wtype rc=0`, una corrida
contestaba y otra volvía `denied`: la ventana entra al árbol del compositor antes de que llimphi
tenga el teclado enganchado, y un sleep más largo sólo mueve el borde;
· el contestador busca la ventana POR PATRÓN: con el navegador abierto, teclear «la enfocada» le
contestaría a la página, y eso es un sí que nadie dio;
· la sonda del chrome mira `isShownForTab` y la insignia ANTES de apretar — `triggerClickOrPopup`
sale por la puerta de atrás si la acción no está mostrada, y un click que no llega se ve igual que
una bóveda que no contesta. (Y `WebExtensionPolicy` no es global en el scope de una ventana: sale
del módulo. Eso costó una corrida.)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
69a93a9d0d |
publicar los sitios pasa a ser git pull: Caddy sirve de los clones, no de copias
La mudanza dejó los sitios como copias a mano en /work/www —4,2 G del monorepo sólo para tawasuyu.net— sin ninguna forma de refrescarlas. El contenido estaba bien y el modelo era frágil: el día que cambia el repo la web no se entera, y eso no se nota, porque un sitio viejo responde 200 igual que uno nuevo. Ahora Caddy sirve directo de los clones: tawasuyu.net del monorepo y gioser.net/takana/hifas del clon de gioser-web. Los medios que NO están en git —los APK de humanoid, los 265 M de muestras, vivo, ende— siguen en /work/www con su propio `root`, que es la separación correcta. Liberados 4,2 G. El guion hace `pull --ff-only` (no pisa cambios locales de un árbol que está sirviendo), regenera las dos páginas hermanas —hermanas.py trae el destino cableado a una ruta de gioser, así que se le pasa el del clon sin tocar el fichero de ese repo— y después COMPRUEBA: que lo servido sea byte a byte lo que hay en disco, que los medios sigan en 200, y que /.git/config dé 404, porque servir desde un clon no puede exponer el repo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
41b21755b4 |
en la caja /etc/profile.d no lo leía nadie: /etc/profile no existía
El guion dejaba las RUSTFLAGS en /etc/profile.d/rust-enlazador.sh y ninguna shell de login las recogía, porque en esta caja /etc/profile NO EXISTE. O sea que el fichero habría sido exactamente la avería que este repo persigue: presente, con contenido, y sin ningún efecto. Lo descubrió el control nuevo, que pregunta por la VARIABLE en una shell de login en vez de por el fichero. Se crea /etc/profile con el bucle estándar sobre profile.d —de paso revive el bash_completion.sh que ya estaba ahí, igual de mudo— y no se toca el PATH, que lo fija el init. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
69ed6e53c0 |
la caja no tenía NINGÚN driver de C: se lo damos con el zig que ya está en su store
`command -v cc gcc clang zig` en la caja no devolvía nada. Eso no es sólo incómodo: es lo que hacía que rustc, con el compilador propio ya sellado, no pudiera enlazar una sola dylib —y ahí entran todas las proc-macros—. El `-L /usr/lib` obvio empeora las cosas: rompe los estáticos, porque rustc enlaza musl static-PIE con su copia self-contained y ese camino le hace preferir la libc.a del sistema, que no es PIC. El camino de librerías no puede ser global; quien sabe cuál es cuál es un driver de C. NO introduce nada nuevo en el corpus: envuelve el `seed-zig` que ya está en el store —producto del bootstrap, como stage1-rootfs—. Volver a zig el compilador de C OFICIAL de la distro sería otra cosa, y es decisión de ADR. La caché de zig va a /work y no a la raíz de 5,9 G, que ya nos costó una caída. Controles que corre el propio guion, sobre el HECHO y no sobre la intención: compila y ejecuta un hola mundo en C; enlaza una proc-macro, compila el programa que la usa y lo ejecuta; y —el que evita el remedio peor que la enfermedad— comprueba que el estático SIN banderas siga enlazando. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2fafb3a589 |
atuq: el vigésimo guardián entra a la suite, y entra TRUNCADO — con el motivo escrito al lado
`test-atuq-boveda-metal --hasta A` pasa a correr con los otros diecinueve. Corre truncado a propósito: las etapas B..F chocan contra el muro del §7.septies —ninguna app llimphi de escritorio abre ventana sin Vulkan— que no es de la bóveda, y un rojo permanente por una causa ajena se aprende a ignorar en tres días. Lo que la A afirma es la medición que costó tres semanas de no ver: **sin la app dueña, el host contesta `locked:true` con `ok:true`**, o sea que «cerrada» es una respuesta exitosa. La cabecera del runner dice ahora 20 y avisa que un verde de éste NO dice que la bóveda ande. Es la misma advertencia que ese fichero ya se aplicó dos veces: un número escrito como predicción envejece callado, y un cuadro entero en verde puede ser un producto sano o un runner que no mide nada. Probado en los dos sentidos, que es lo único que distingue un guardián de una decoración: · en verde, 3 s, dentro del runner y sobre el sello vigente (`b3:e556024b`); · ROJO con una rotura a propósito —invertir la aserción de la etapa A— ⇒ salida 1. Y el tamaño que faltaba para la decisión del §7.sexies: `boveda` 22 M y `shuma-pregunta` 21 M, ~43 M por perfil. Hoy la respuesta a «en qué imágenes se declaran» es NO, y no por el tamaño: declararlas antes de que llimphi se destrabe sería instalar una bóveda que niega todo sin un error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f611751f86 |
atuq: el diálogo de la bóveda no abre, y el muro no es de la bóveda — ninguna ventana llimphi pinta
`boveda` (b3:59ffd74b) y `shuma-pregunta` (b3:99763eca) sellaron en el worker. Ninguno de los dos abre
ventana, y el error no habla de contraseñas:
panicked at llimphi-hal/src/lib.rs:1032: index out of bounds: the len is 0 but the index is 0
Esa línea es `caps.formats[0]`: la surface no tiene NI UN formato. La cadena, medida eslabón por
eslabón con RUST_LOG=wgpu_hal=debug:
No (or unknown) windowing system ((None, Some(..))) present. Using surfaceless platform
Trying native-render → No config found! · Trying presentation → No config found!
El `None` es `raw_display_handle`, y no es del arnés: es lo que llimphi hace A PROPÓSITO en su camino
de escritorio, con el comentario al lado («Sin display: este camino no tiene ventana todavía»). Sin
display handle wgpu abre el display EGL surfaceless, que no publica configs con WINDOW_BIT ⇒ la
surface no es presentable ⇒ cero formatos ⇒ panic.
No se nota en una máquina con Vulkan, porque ese camino elige Backends::PRIMARY y a Vulkan el display
no le hace falta. Y **ninguna imagen de takana tiene Vulkan**: las tres recetas de mesa se compilan
con `-Dvulkan-drivers=` vacío, y `vulkan-loader` sólo existe en incoming-kde sin que ningún perfil lo
declare (y un loader sin driver no es un driver). ⇒ toda app llimphi de escritorio cae al camino GL y
no puede abrir ventana. Reproducido en TRES binarios y dos versiones de wgpu: shuma-pregunta y boveda
(29) y llimphi-counter (27).
Los controles, porque la conclusión es fuerte: pedir Vulkan explícito da NoAdapter y no hay ICD ni
libvulkan en ninguna capa (la premisa está medida); con softpipe el fallo es OTRO y anterior —wgpu
pide compute shaders y softpipe se queda en GL 3.3—, así que llegar hasta acá ya exige llvmpipe; y el
compositor está levantado con su socket y su WAYLAND_DISPLAY.
Lo que esto dice del producto: el consentimiento de la bóveda no puede pedir permiso en ninguna imagen
de hoy, y como un lanzamiento fallido se traduce a «no» (Command::new falla ⇒ return false), el usuario
vería una bóveda que niega todo sin un solo error.
Se arregla en llimphi (que pase el display handle; la función `instancia_con` ya existe ahí al lado) o
con un driver Vulkan por software en el corpus. No se arregla en takana ni en el arnés.
Entra igual lo que sí se puede afirmar:
· `scripts/test-atuq-boveda-metal.py`, seis etapas declaradas, la A en VERDE — sin la app dueña el
host contesta locked:true, que es la medición del §7.sexies ahora vigilada. Cada etapa dice qué
afirma y la B nombra el muro en vez de dejar que se rediagnostique;
· `recipes/wtype.toml` — el corpus no tenía NINGUNA forma de meter una tecla en un compositor: todos
los arneses de scripts/wlr miran, ninguno toca. Sellado y en ningún perfil: es instrumento.
⚠ Y una trampa del arnés, mía, que costó tres corridas: exportaba GALLIUM_DRIVER=softpipe, o sea que
yo mismo lo clavaba al driver débil. mesa-swrast y mesa-llvmpipe se ven como dos nombres de lo mismo
—«mesa por software»— y la diferencia decide si el arnés existe.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
fca1de2e55 |
yupana: radio mezclaba las aristas de build con las de runtime y sobreestimaba el coste
`radio gcc-libs` contestaba «9 dependientes, 9 sellados caen a deuda», y con ese número avisé de una cascada de horas —firefox con PGO dos veces, waterfox, atuq—. La respuesta correcta es CERO: los cinco declaran gcc-libs en `deps.runtime`, y **`runtime` no entra en `hash_inputs`**, así que re-sellarla no re-hashea a nadie. Se vio al mirar el store después del cambio: los cinco seguían al día. El grafo de `_deps` une build+runtime a propósito —eso es el CIERRE de una imagen, y esa unión arregló en su día que firefox arrancara sin libstdc++—, pero `radio` responde otra pregunta: «¿a quién obligo a reconstruir?». Ahora usa dos grafos: la cascada sale del de BUILD, y las imágenes del de la unión, que es lo correcto para cada uno. Los que dependen sólo por runtime se siguen informando, nombrados, diciendo que NO se reconstruyen. Controles: gcc-libs ⇒ 0 de cascada y 9 sólo-runtime; zlib ⇒ 126 directos y 391 transitivos (sigue viendo las de verdad); zsh ⇒ 0. Y `test-yupana-radio.py` pasa: libdrm sigue cruzando las 5 colas. Un número que sobreestima frena arreglos correctos, que es justo lo que casi pasa acá. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
54283b8a94 |
la cosecha decía «reintenta próximo ciclo» y no reintentaba nada
Si el push del estado se rechaza porque otro agente empujó primero, el ciclo siguiente se rechaza IGUAL —nadie rebasa nunca— y el commit del cron se queda local para siempre. Se encontró en la caja: rama divergente con un `estado: cosecha granja` atascado y la cosecha diciendo «reintenta» cada media hora sin que nada cambiase. El mensaje tranquilizaba y no era cierto. Ahora, cuando lo rechazan, llama a `git-sincronizar.sh`, que es la regla 2 ter automatizada: rebasa, COMPRUEBA por parche que el commit sobrevivió, lo recupera del reflog si no, y empuja. Y si tampoco puede, lo dice y deja el comando para verlo, en vez de prometer un reintento que no existe. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5d16b55e14 |
el visor del grafo reventaba con un estado que no conocía
`build-state-view.py` traía los cuatro estados del corpus cableados (sealed/debt/never/unhashable) y contaba TODOS los nodos contra ese diccionario. `build-state.py` emite dos más —`wanted` (hueco declarado en targets.toml, sin receta) y `ajeno` (imagen de qorpa, que no se construye desde fuente)—, así que el visor moría con `KeyError: 'ajeno'` y el tablero HTML llevaba sin poder generarse. O sea: agregar un estado nuevo rompía el tablero entero, en silencio para quien no lo corriera. Ahora los de fuera del corpus se cuentan aparte y se NOMBRAN en la leyenda —no se cuelan a la barra, que es sobre `totals["recipes"]` y pasaría del 100 %—, y cualquier estado que aparezca mañana cae en esa misma rama en vez de tumbar el visor. De paso, el grafo regenerado: 930 selladas, 2 en deuda, 5 nunca, 3 ajenas; git ya figura sellado en su hash nuevo (b3:215742cb…) y no arrastró a nadie, como decía el radio. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
d4896c2999 |
pagada la deuda: el git del corpus vuelve a clonar por HTTPS
El §6.50 quinquies dejó la caja trayendo fuentes por SSH porque su git no tenía remote-https. Eso era el síntoma; la causa estaba un paso antes de donde mirábamos —incluida la nota que yo mismo había escrito, que culpaba al Makefile—: el `configure` de git prueba libcurl con AC_CHECK_LIB, que enlaza `conftest.c -lcurl` y nada más, y con libcurl estática faltan sus privadas (-lssl -lcrypto -lz). El test falla, config.mak.autogen se lleva NO_CURL=YesPlease, el Makefile excluye los tres helpers y conserva git-http-backend —esa asimetría en el artefacto era la firma, y estuvo a la vista desde el 14 de septiembre—. El build termina en verde. Medido sobre el artefacto: b3:215742cb… trae git-remote-http, -https, git-http-fetch y los ftp, y CLONA por HTTPS (clone, no ls-remote). En la caja, hidratado y con la reescritura apagada, funcionan tanto `git clone` como `git clone --mirror`, que es el comando exacto del fetch de takana. Y el radio, medido en vez de temido: la nota vieja decía «raíz de perfil.base ⇒ radio grande»; `yupana radio git` dice cero dependientes directos y cero transitivos. El miedo escrito a mano había sobrestimado el costo de arreglarlo, y eso lo mantuvo roto tres días. Las reescrituras url.insteadOf quedan retiradas de la caja; el guion se conserva como salida de emergencia y así lo dice su cabecera. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0bb40af496 |
la caja trae fuentes git por SSH: el arreglo, en un guion idempotente
Lo que hasta ahora era estado a mano en la caja queda reproducible. Hace las cinco piezas medidas: reescritura de URLs con url.insteadOf, la clave del host reexpresada SIN puerto (cargo la busca así), la huella de github.com PINEADA contra la publicada —si no coincide, sale con error en vez de aceptarla—, la IdentityFile por host, y `net.git-fetch-with-cli = true` para que cargo delegue en el git de verdad. Además mueve CARGO_HOME fuera de la raíz de 5,9 G, que es lo que tiró la caja. No mueve ningún hash: la URL es un localizador y no entra en hash_inputs (ADR 0013). Termina con un control que CLONA de los dos orígenes; si alguno no llega, sale distinto de cero. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e7b77ff657 |
la cuenta de sergio en la caja, su jaula propia, y shuma corriendo como él
Pedido: «crea mi cuenta sergio con la clave tawasuyu, desde ahí abrir los claudes como lo estoy
haciendo acá con shuma sobre los proyectos».
· cuenta `sergio` uid 1001 (el mismo que en gioser), home /home/sergio, clave verificada
· su propia jaula `claude-sergio`, que concede SU home —su `.claude`: credenciales y memoria— y
NO el de root. Con la instancia de root, sergio moría en «bwrap: Can't mkdir /root/.claude»
· `claude` elige instancia por usuario; `PROY=/otra/ruta claude` abre el agente sobre otro proyecto
· shuma-daemon y shuma-gateway pasan a correr como `sergio`, no como la cuenta de servicio `shuma`:
shuma es su CONSOLA, y cada pestaña que abre tiene que ser una sesión suya — si no, `claude`
buscaría una jaula `claude-shuma` que no existe
⚠ NO se eleva con doas/sudo: en esta caja el setuid NO TOMA («doas: Operation not permitted», con
NoNewPrivs 0 y `/` sin nosuid — queda como deuda entender por qué). No hace falta: `qorpa run`
levanta la jaula con user namespaces y anda sin privilegio, siempre que el directorio de la
instancia sea del usuario.
⚠ Y un detalle que costó una vuelta: sin `shift 2`, el HOME y el proyecto que el lanzador pasa como
posicionales se cuelan COMO ARGUMENTOS del agente — la primera sesión se abrió con el prompt
«/home/sergio», y el agente contestó, con razón, que eso es una ruta y no un comando.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
d6ba65e23c |
el lanzador arranca EN el repo: sin ese cd, el agente empieza sin memoria
La memoria se indexa por ruta de trabajo. `qorpa run` deja el shell en `/`, así que las primeras pruebas usaron el proyecto `-` y el agente arrancaba en blanco aunque sus 163 ficheros estuvieran en disco. Con `cd /opt/takana` usa `-opt-takana`, que es donde se renombró la memoria traída de gioser (allá el repo era /mnt/vvv/takana). Comprobado en la caja, con dos preguntas que sólo puede contestar leyendo: ¿el logo? → «martillo 7×7, cuatro colores exactos» (está en su memoria) ¿la regla 1? → «todo takana build va envuelto en flock, y con -o», con el matiz del lock heredado O sea: el agente de la caja arranca sabiendo lo que sabe éste. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
eb96c9219e |
el agente corre en la caja y puede construir — la condición que el usuario puso para borrar gioser
$ claude -p "Decí exactamente: CLAUDE CORRIENDO EN LA CAJA" CLAUDE CORRIENDO EN LA CAJA Turno real —API, credenciales, respuesta— desde 2.29.29.217. El CLI es un ELF glibc de 360 M y la caja es musl, así que va enjaulado (ADR 0015, el caso de sergioh-api). La instancia declara red, nesting y SEIS directorios: /opt/takana, /work/dev-fs, /store, /root/.claude, /root/.ssh (ro) y el CLI (ro). Es ANCHA y está dicho: un agente que construye, commitea y empuja necesita lo mismo que un humano; lo que la jaula compra es que el alcance esté escrito y acotado. ⚠ El `takana` que construye NO se instala adentro: sale del store, que ya está concedido — es estático musl y corre igual dentro de una imagen glibc. LA CAJA CONSTRUYE: sellados hoy `intel-ucode` (156 signatures) y `sof-firmware` (2 firmware + 8 topologías). Faltaba una pieza que el lab no traía: `.dev-fs/tools` (zig 0.13/0.16 y go, 1 G) — la imagen del lab empaqueta `alpine/` y nada más, y el primer intento murió con «zig no encontrado». 🧱 Y lo que NO anda, con su rodeo: `takana build` dentro de la jaula muere con «bwrap: Can't mount proc on /proc» aunque `nesting = true`. No es el anidamiento —un `bwrap --proc /proc` a mano SÍ funciona adentro— sino el userns anidado del sandbox del build; probado también con `root = false`, que es la sospecha que el propio código documenta, y falla igual. El rodeo que sí anda, escrito en el lanzador: pedirle el build al ANFITRIÓN por ssh a 127.0.0.1:22022. Así se selló sof-firmware desde dentro de la jaula. Manifiesto y lanzador versionados en scripts/servidor/: una instancia que sólo existe en la caja se pierde con la caja. ⚠ Pendiente menor: la memoria del agente está en `-mnt-vvv-takana` y allá el repo vive en /opt/takana ⇒ el índice POR RUTA no coincide y arranca sin memoria del proyecto aunque los 733 ficheros estén en disco. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
597406ad70 |
git-sincronizar: gritaba «el commit desapareció» SIEMPRE — era pipefail + SIGPIPE, no el rebase
El guión avisaba en cada corrida de que el commit se había perdido, iba a recuperarlo, y el cherry-pick le contestaba que el contenido ya estaba. Un guardián que grita siempre se ignora, así que valía la pena encontrar por qué. No era git: era la comprobación. El guión corre con `set -o pipefail` y comprobaba con `git log … | grep -qF "$MSG"`. **`grep -q` sale en cuanto encuentra**, eso le manda SIGPIPE a `git log`, que termina ≠0, y con `pipefail` la tubería entera se lee como FALLO **aunque el grep haya encontrado**. Demostrado en una línea: $ bash -c 'set -o pipefail; seq 1 100000 | grep -q "^5$" && echo OK || echo FALLO' FALLO Arreglado capturando la salida antes de comparar: sin tubería, sin SIGPIPE, sin falso positivo. ⇒ Y de paso desmiente lo que el propio guión daba por hecho: de las cinco «pérdidas» de commit que motivaron escribirlo, al menos las últimas dos fueron ESTE falso positivo, no el rebase. Las primeras sí están en el reflog con su `(start)`/`(finish)` sin `pick`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
b985570788 |
respaldo: un parcial de SÓLO LECTURA envenena esa ruta para siempre — y el bucle lo llamaba «red»
La primera corrida completa desde la caja (puerta 7) se cayó 40 veces seguidas con el MISMO fichero: open ".../fonts/dejavu/.rsync-partial/DejaVuSans-Oblique.ttf" failed: No such file… (2) .. store: corte de red (rsync 23). Intento 39; reanudo en 60s. !! store: 40 intentos y sigue cayéndose. Dejo lo subido y paro. No era red. Una transferencia interrumpida el 11-sep dejó ese PARCIAL en el destino con modo -r--r--r-- (el del origen: el store es de sólo lectura por diseño), y ningún intento posterior puede reescribirlo. El fallo es ETERNO, y el bucle reintentaba cada 60 s contra esa pared — exactamente lo que la cabecera de la función de reintento dice que no hay que hacer. Dos arreglos: 1. `--chmod=Fu+w`: los ficheros del respaldo quedan escribibles por su dueño, así que un corte a mitad se RETOMA en vez de trabarse. El precio es no conservar el bit de sólo-lectura en la copia; es bajo: el contenido es lo que se respalda y los permisos los reconstruye el store. 2. Al rsync 23 se le dan TRES intentos, no cuarenta, y al tercero se NOMBRA la causa probable (un parcial de sólo lectura en el destino) en vez de llamarlo corte de red. El 23 es «some files were not transferred», que tanto puede ser un cable como una pared. Comprobado: borrado el parcial envenenado, ese fichero sube a la primera. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
615cc2f839 |
git-sincronizar: un cherry-pick VACÍO no es un fallo, es que el contenido ya estaba
Estrenando el guión con su propio commit saltó el caso: la comprobación por mensaje dio negativo, el cherry-pick de recuperación salió con «the previous cherry-pick is now empty» y el guión abortó con error… teniendo el contenido ya en el árbol. Ahora distingue los dos casos: vacío ⇒ falso positivo de la comprobación, se aborta el cherry-pick y se sigue; cualquier otro error ⇒ se aborta, se imprime el reflog y se sale ≠0 para que lo mire un humano. Lo que importa es que el CONTENIDO esté, no que el sha coincida. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
17b6ea1a4f |
git-sincronizar.sh: la regla 2 ter automatizada — porque hoy hizo falta cinco veces
En dos días, `git pull --rebase` se llevó por delante CINCO commits propios en este árbol compartido, siempre con el mismo reflog: `commit:` mío, `(start): checkout <otro>`, `(finish): returning to main`, y ni un `pick` en el medio. El comando no es el culpable —en un repo de juguete reaplica bien— sino que alguien mueve `refs/heads/main` mientras el rebase está en vuelo. La receta manual (CLAUDE.md regla 2 ter) funciona: mirar el log y recuperar con reflog + cherry-pick. Esto es esa receta automatizada, porque **una regla que hay que acordarse de aplicar en cada push es una regla que un día no se aplica**. Hace, en orden: anota su HEAD · rebasa · COMPRUEBA que el commit sigue en la rama —por su mensaje, no por el sha, que el rebase reescribe— · si no está lo recupera del reflog · empuja · verifica que gitea quedó donde tiene que quedar · y si el espejo de GitHub quedó divergente lo realinea con `--force-with-lease` (nunca `--force` a secas) y sólo tras comprobar que su contenido ya está en nuestra historia. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
80e04872d7 |
poda-fuentes: no corría en takana — y la causa era que la caja NO TIENE /dev/fd
El latido, corrido desde la caja, moría así: poda-fuentes.sh: line 72: /dev/fd/63: No such file or directory poda-fuentes.sh: line 73: viejos: unbound variable ⚠ poda de fuentes falló (rc=1) — sigo El segundo error es consecuencia del primero (el `mapfile` nunca corrió), y el primero no nombra la causa: **la sustitución de procesos de bash necesita `/dev/fd`, y una caja takana no lo tiene**. gioser sí: `/dev/fd -> /proc/self/fd`. O sea que NINGÚN `<(...)` de NINGÚN script del hub funcionaba allá — esto era sólo el primero en toparse. Dos arreglos, y los dos hacían falta: 1. En el script: `<(...)` fuera (fichero temporal, que anda en cualquier shell y con cualquier /dev) y `df -B1 --output=avail`, que es GNU, por `df -k` + awk. Es la poda de `work/sources`, justo lo que un hub necesita para no llenarse. 2. En la caja: `/dev/fd -> /proc/self/fd`, con una Card OneShot en el genesis para que sobreviva al reinicio (como la de `hostname`). Comprobado después: `bash -c "cat <(echo funciona)"` responde. Lo correcto a futuro es que lo haga el init o el product-rootfs, no una Card — queda anotado. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
1ca210a311 |
store-gc: xargs -d tampoco existe en busybox — el arreglo anterior imprimía «huérfanos ?»
El primer intento cambió `du --files0-from=-` (GNU) por `xargs -d '\n' du -sk`… y `-d` también es extensión GNU. En la caja quedó «espacio: superados 0 · huérfanos ?». Va con `tr '\n' '\0' | xargs -0`, que busybox sí tiene, y se probó la función suelta en las dos máquinas. Lo que SÍ funcionó del intento anterior es el `?`: cuando el total no se puede calcular, la función lo DICE en vez de imprimir 0. Un cero inventado habría dicho «no hay nada que ganar» justo cuando había 150 huérfanos. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
4fe6046200 |
store-gc: decía «espacio: superados ·» y «libres: Available → Available» — dos bugs que salen en takana
Corrido por primera vez en la caja (una máquina busybox, no GNU), el recolector funcionó —337 artefactos borrados y verificados, 26,8 G liberados— pero sus dos NÚMEROS salieron vacíos: ==> espacio: superados · huérfanos ==> 337 artefactos borrados y VERIFICADOS (0 sobrevivientes) · libres: Available → Available 1. `du -sch --files0-from=-` es de GNU coreutils y busybox NO lo tiene ⇒ `espacio()` devolvía vacío. Un número que falta se lee como un número chico: sin él nadie puede decidir si vale la pena correrlo, que es justo para lo que está el dry-run. Ahora `xargs du -sk` + awk, que anda en los dos mundos. 2. `df -h /home` estaba CABLEADO, y el store casi nunca vive ahí: en gioser es un bind-mount del volumen y en una caja takana es `/store`, su propia partición. En la caja no hay `/home`, así que `tail -1` se quedó con la CABECERA y el resultado fue «Available → Available». Se mide `$STORE`. Medido de verdad con `df` a mano: /store pasó de 84,7 G usados (91 %) a 57,9 G (62 %) — 26,8 G liberados, 1657 → 1320 artefactos. Y el control que importa después de un gc: los 11 enlaces de `/usr/bin` que apuntan DENTRO del store siguen resolviendo y los 21 entes siguen corriendo. ⚠ Vale anotar la advertencia que el propio gc imprime y que en esa caja no se puede satisfacer: «sin .config legible ⇒ NO se protege ningún kernel por esta vía». El default (sólo superados) no toca lo vigente, pero `--huerfanos` en una máquina que arranca de su store hay que pensarlo dos veces. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2271cc3c10 |
el latido se pone al día antes de commitear — si no, late y no publica, para siempre
`cosecha-cron.sh` commitea el estado y lo empuja, pero nunca hacía `pull`. En el hub no se notaba: el árbol lo mantiene al día el agente que trabaja ahí. Al mover el latido a una caja donde NO hay nadie trabajando —que es justo lo que pide la puerta 4— el repo se queda atrás en el primer push ajeno y a partir de ahí TODOS los ciclos fallan el push, cada uno anotando «reintenta próximo ciclo». Un latido que late y no publica. `git pull --ff-only`, y nunca `--rebase`: este script corre en un árbol COMPARTIDO con otros agentes y ahí un `pull --rebase` ya se llevó un commit por delante (regla 2 ter). Fast-forward no reescribe nada: o adelanta limpio, o falla sin tocar el árbol y se anota en el log. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
1c237f9da2 |
mudanza: decidir SIN teclas unitarias, /mnt/vvv entra al censo, y 25 rutas de secretos más
Tres cosas que salieron de usar la herramienta de verdad.
1. `--decidir <fichero>`: decidir EN LOTE desde `<ruta-o-nombre> <decision>` por línea.
El usuario: «no sé cómo activar o desactivar cosas aquí desde shuma llimphi remoto, que las
opciones son con teclas unitarias». El modo interactivo lee una tecla por entrada, y hay
terminales donde eso no se puede usar — una herramienta cuya única forma de decidir exige un
tipo de terminal no es una herramienta, es una herramienta PARA ESA TERMINAL. El fichero anda en
cualquiera, se revisa antes de aplicarlo, se versiona y se vuelve a correr.
Una clave que no empareja con nada, o que empareja con varias, es ERROR RUIDOSO y no se escribe
NADA: un lote a medias deja decidido lo que nadie revisó.
2. `/mnt/vvv` entra a las raíces del censo. No estaba, y ahí vive el trabajo: los repos de la
persona, el monorepo, el store, work/sources. El agujero se vio preguntando por `humanoid`: el
censo sólo conocía `/home/sergio/humanoid` —200 M de `build/` de junio— mientras el proyecto de
verdad, con su `.git`, estaba en `/mnt/vvv/humanoid`, fuera de toda raíz censada. `repos_git` SÍ
lo miraba: una parte del censo conocía el árbol y la otra no.
3. 25 rutas más en `rutas-fuera-de-git.txt`, del barrido que hizo el frente tawasuyu sobre su home:
`~/.wawa/seeds` (sin él ninguna app nueva del génesis nace), `~/.tejido` (la identidad de esta
máquina en la flota), `~/.local/share/agora`, `~/.config/{wawa,minga,thasnuna,shuma,mirada,hcloud}`,
`~/keys` y `~/fdroid` (⚠ firma de apps Android), `~/.gnupg`, `~/.pgpass`, `/etc/wireguard`,
`/etc/{shuma,sandokan,tawasuyu/agente,local.d}`. Son identidades y semillas: no se «vuelven a
generar», porque generar otras significa ser OTRA máquina para el resto de la flota.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
5ec4e2b213 |
mudanza: el tercer camino — mudar, RESPALDAR o abandonar, cosa por cosa y con su descripción
Pedido del usuario: «una lista de cada cosa con una corta descripción y la opción de yo elegir mudar, mover a un directorio para respaldar ese directorio, o abandonarlo». Eran tres huecos distintos. 1. «CADA COSA». El censo medía RAÍCES: `/home` era UNA entrada de 25 G y UNA decisión, con repos, SDKs, 4,3 G de caché y trabajo irrepetible adentro. Ahora emite un renglón por cosa —169 en gioser contra 5—, un nivel hacia adentro de cada raíz y DOS en `/home`, porque su primer nivel son usuarios y la decisión no es por usuario. `--solo-totales` conserva la vista vieja, que es la que dimensiona la mudanza; las dos viajan en el censo (`datos` y `datos_raiz`). ⚠ El primer intento mandaba las ~110 sondas en UNA llamada, se pasaba del timeout y devolvía lista VACÍA: «no hay datos» en vez de «no pude medirlos». Va en tandas, y una tanda que falla se nombra. 2. «UNA CORTA DESCRIPCIÓN», y son hechos: lo sirve el servidor web · lo usa un proceso vivo · es repo git y a dónde apunta · tiene cambios sin commitear · adentro hay node_modules/target/venv · lo más nuevo que hay dentro. «Sin señales» también es un hecho, y es el que dice dónde mirar. 3. «ELEGIR ENTRE TRES»: DECISIONES pasa de (muda, muere) a (muda, RESPALDA, muere). Faltaba el camino que se usa de verdad —no lo quiero corriendo allá, tampoco lo quiero perder—; con dos opciones, todo lo dudoso se marcaba `muda` y la mudanza engordaba. La regla de fondo no cambió (qué datos VALEN no lo dice la máquina), pero cuatro hechos sí los sabe: servido/usado ⇒ muda · caché ⇒ muere · repo limpio con remoto ⇒ muere · repo sin remoto o sucio ⇒ respalda. Y el cruce que evita el peor error: un remoto que apunta a ESTA MISMA máquina no es un respaldo, es el mismo disco (medido: /mnt/vvv/humanoid → ssh://git@127.0.0.1:2345). 4. LAS DECISIONES SE GUARDAN. `--decide` las usaba sólo para el plan de esa corrida: cerrabas la terminal y se perdían las 169. Ahora reescribe el censo (temporal+fsync+rename, `.bak`, y releído antes de pisar nada; aborta si perdió alguna). Para eso hubo que hacer el emisor IDEMPOTENTE: escribía `decision = ""` fijo, así que re-emitir un censo decidido duplicaba la clave y el TOML dejaba de parsear — la herramienta no podía reescribir su propio fichero. 5. EL PASO `respaldo`: corral por defecto leído de respaldo-storagebox.sh (no una segunda dirección a mano), nombre aplanado, y NO se instala en el destino. Sin corral, aborta: un rsync a ninguna parte devuelve 0 y se lee como un respaldo. Probarlo con rotura a propósito Y con el control que tiene que pasar destapó dos bugs: `--mkpath` es obligatorio (el primer respaldo siempre estrena un nivel) y un FICHERO no lleva barra final — `rsync fichero/ dst/` es un error, y el paso de `datos` tenía el mismo defecto latente. La verificación es rsync en seco contra el destino real, no `du` ni `find`: el Storage Box no tiene find, no acepta tuberías, y su `du` comprime. 6. De paso: `aplicar.py --dry-run` decía «✅ todos los pasos ejecutados y verificados» sin haber ejecutado nada. Ahora dice EN SECO: N mostrados, NINGUNO ejecutado y NINGUNO verificado. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2098e86ba4 |
atuq: la boveda ANDA — 19 guardianes en verde, y el bloqueo de tres semanas era un fichero sin empujar
El pin de `puriy-costura` subio a `23a292863` y con eso se apagaron los dos rojos del cable. La suite entera sobre `b3:e556024b`: **19 verdes, 0 rojos, 32,1 min**. No se toco ningun guardian — que la suite existiera ANTES del arreglo es lo que permite afirmarlo: el cuadro de ayer y el de hoy son el mismo instrumento. La medicion que cierra es la que abrio el §7.quinquies, leida al reves: `strings` sobre el binario sellado da `vault` **10** veces donde daba 0, con `cas`/`sct` de control; y `vigia-atuq-verbos` pasa de 3 verbos sin dueno a cero, con su control positivo intacto. Lo destrabo publicar el `Cargo.lock` de tawasuyu (`23a292863` alla), regenerado en un arbol limpio: 215 lineas, todas de contabilidad de deps por ruta, cero `checksum` y cero `source` movidos. Y el remate, que es la leccion: **ese lock era byte a byte el que la otra sesion ya tenia en su arbol sin commitear**. Tres semanas de bloqueo y el fichero correcto estaba escrito, sin empujar, a un directorio de distancia. Antes de decir «bloqueado por otro repo», mirar si lo que falta no esta ya hecho y sin publicar. Commiteado alla con indice temporal (`GIT_INDEX_FILE` + `commit-tree`), que es la unica forma de publicar una ruta en `MM` sin llevarse por delante lo del otro agente: su arbol quedo byte a byte igual, comprobado con `cmp` antes y despues. ⚠ Y la cabecera del runner se desmintio SOLA dos veces: anuncio «un rojo» cuando eran dos, y «dos» cuando ya no quedaba ninguno. Un numero escrito como prediccion envejece callado. Con todo en verde el numero ya no distingue un producto sano de un runner que no corre nada, asi que lo que sostiene la corrida queda escrito: la puerta del sello (verificada contra un store vacio) y el control interno de cada guardian. |
||
|
|
e2bb4f9b89 |
censar: «no existe» y «no puedo mirar» se confundían — 572 M invisibles, y el censo de hoy
El censo ya tenía tres estados a propósito (`ausente` / `sin_permiso` / `ok`), pero el tercero sólo se detectaba sobre la PROPIA ruta: cuando el que no deja pasar es un ANCESTRO, `[ -e "$p" ]` contesta que no existe y la ruta caía en `ausente`, que el censo descarta sin decir nada. Medido en gioser: `/root/.local` es 0700 ⇒ un censo corrido como `sergio` daba `/root/.local/bin` por AUSENTE. Como root son 572 M de binarios puestos a mano que ningún paquete provee — justo la clase que la mudanza no puede reconstruir. Arreglado: si un ancestro existe y no es atravesable, el estado es `sin_permiso`. Probado en los dos sentidos, que es lo que separa un guardián de un adorno: ancestro 0700 de otro dueño → sin_permiso (sale en el ⚠ del resumen) ruta que NO existe de verdad → ausente (no se reporta) ← el control que tiene que pasar Con el arreglo, el censo sin privilegio pasa de 2 rutas ciegas a 13, entre ellas el home entero de `artix`, que antes no figuraba de ninguna manera. Y el §6.34 con el estado MEDIDO de la mudanza hoy: de los 29 dominios queda UNO sirviéndose desde gioser (`sergio.gioser.net`); los otros seis vivos ya contestan 200 contra 2.29.29.217. El rescate del §6.20 sigue coincidiendo bit a bit con cuatro de los seis procesos que corren desde un inodo borrado, y los otros dos tienen en disco una versión más nueva. Lo que queda abierto son los pasos 6 y 7: el montón de daemons propios (shuma, willay, pacha, tejido, tupu, matilda…) y los datos. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
fb7dba530d |
atuq: el que estaba «nombrado como hueco» entra a la suite — 17 verdes y 2 rojos en 32 min
La primera version dejaba afuera al guardian de notificaciones (`dunst-headless.sh --via-atuq`) con
la excusa de que vive en el arnes de sway, y lo escribia en la cabecera «para que su ausencia no se
lea como cobertura». Eso dura tres dias: al cuarto la lista ES la cobertura y el parrafo no lo lee
nadie. Corrido aparte: verde en 180 s. Entra.
Es ademas el que mide lo que ningun auditor de ELF puede ver: una pagina llama `new
Notification(...)`, `libxul` hace `dlopen("libnotify.so.4")` —que no es NEEDED de nada—, el bus
activa dunst y dunst DIBUJA. Cinco piezas y la unica forma de saber que estan las cinco es verlo.
Para que entrara, la lista pasa a llevar el COMANDO de cada guardian en vez de su nombre de fichero:
lo que lo tenia afuera era el despacho por extension, no una decision.
Corrida entera de los diecinueve sobre `b3:e556024b`: **17 verdes, 2 rojos, 32,2 min**, que es
exactamente lo que la cabecera predice — y la cabecera es el control, no un adorno.
Los caros: sct 242 s, archivo semantico 238 s, ia 182 s, instalacion 180 s, notificaciones 180 s.
Los cuatro primeros salen en 5 s y no tocan el navegador.
|
||
|
|
eaa7d02725 |
el corte, con hora: 93 minutos — y en un servicio que ya autoriza no va límite por tasa
Del `access.log`, sin ambigüedad. La IP del usuario (`45.234.61.160`, autenticada como `sergio`, 2709 peticiones) tuvo tres silencios: **93,2 min (11:59:37 → 13:32:49 UTC)**, 50,1 min (10:44:58 → 11:35:06) y 10,0 min (11:45:06 → 11:55:05). Los tres son el `timeout` de 10 minutos del ban disparándose una y otra vez: cada vez que volvía, su primera ráfaga lo baneaba de nuevo. El corte grande terminó exactamente cuando vacié los sets. 🧨 Y LA EXENCIÓN QUE TENÍA NO SERVÍA: la ACL histórica de squid exime `45.234.60.0/24` y él entra desde `45.234.61.160` — **una red de al lado**. Parecía cubierto y no lo estaba. La pertenencia se MIDE (`ip in red`), no se deduce del parecido del prefijo. DECISIÓN DE FONDO: en un servicio que YA AUTORIZA no va límite por tasa. El uso de esta caja es INTENSIVO —varias sesiones de agente en paralelo, cada una abriendo túneles a la vez— y un límite calibrado para «una persona normal navegando» no protege de nada: el atacante ajusta su ritmo, el usuario no puede trabajar más despacio. Squid ya pide usuario y contraseña y tiene su ACL. El ban del puerto queda apagado (60000/min, ráfaga 1000) y contra un flood queda el tope GLOBAL `syn_nuevas_por_seg`, que no distingue usuarios ni banea a nadie por ser rápido. Control tras aplicar: su túnel pasando —`45.234.61.160 … CONNECT api.anthropic.com:443 sergio`— y `45.234.61.160 ∈ 45.234.60.0/23` comprobado con aritmética de redes, no a ojo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
618dcfd851 |
🧨 el cortafuegos baneaba a los usuarios del proxy — y squid nunca se cayó
Preguntaron si el proxy estaba caído. NO lo estaba: el ente llevaba horas corriendo con ↻ 0 y contestando 407. Lo caído era el ACCESO — el cortafuegos de ayer baneaba a los autorizados. La prueba en una línea: **154.197.1.2, una IP que está en la ACL de squid (gente con contraseña), apareció en `@ban4`**, con el contador del ban en 38.246 paquetes tirados. LA CAUSA NO ES LA TASA, ES LA RÁFAGA: `limit rate over 300/minute burst 5 packets` tolera cinco conexiones por encima del promedio, y un navegador o un agente detrás de un proxy abre DECENAS de CONNECT en el mismo instante aunque su promedio sea bajísimo. Y como @ban4 es UN SOLO set consultado antes que todo, caer por el puerto del proxy te tira también el web y el git. ⚠ El tipo ya traía el campo `rafaga` (default 5) con un comentario que dice «para un servicio web va alto (100-200)». Otra vez leí el .ron de EJEMPLO y no el TIPO. Hecho, en orden de urgencia: flush de los sets (servicio restaurado en el acto) · los 21 IPs y 5 redes de la ACL de squid a `confiables` · `rafaga` por servicio según su clientela (ssh 5, git 20, web 100, proxy 200) · proxy a 1200/min con ban de 60 s, que es red de contención contra un flood y no control de uso. Control después: CERO autorizados en el set, 24 baneados (escáneres) y tráfico real pasando —`154.194.14.37 … CONNECT api.deepseek.com:443 sigma`, 298 K tunelados—. ⚠⚠ Y un fallo de método que casi lo tapa: **`nft -f` SUMA a la tabla existente**. Cargué el reglaset corregido encima del viejo y quedaron 30 reglas con las VIEJAS ADELANTE, así que los `confiables` nuevos no se evaluaban nunca. Por eso `cortafuegos apply-input` borra la tabla antes de cargar. La lección: un cortafuegos correcto y uno usable no son lo mismo, y la diferencia NO se ve al aplicarlo. El control que lo habría cazado es el que no corrí: una ráfaga de conexiones desde una IP que no esté en `confiables`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e903f486d9 |
el enlazador va DENTRO del compilador — /etc/cargo/config.toml es una ruta que cargo no lee
La prueba de la caja recién instalada —`cargo build` en un proyecto nuevo, sin flags ni rutas a mano— falló con `linker `cc` not found`, y destapó que la pieza que había empaquetado era falsa: **cargo NO LEE `/etc/cargo/config.toml`**. Sólo lee `$CARGO_HOME/config.toml`, los `.cargo/config.toml` del proyecto y sus padres, y lo que se le pase con `--config` — que es lo que yo venía pasando a mano en cada prueba, y por eso «funcionaba». Y el arreglo no era mover el fichero: **el que enlaza es `rustc`, y rustc no lee la config de cargo**. Las flags van DENTRO del compilador, en el spec del triple: · `linker = "ld.lld"` + `linker_flavor = Gnu(Cc::No, Lld::Yes)` — sin esto rustc busca `cc`, que una caja takana no tiene. · `crt_static_default = true` — sin esto enlaza dinámico y pide `-lgcc`, que no existe (la distro se construye con zig, que trae compiler-rt). · `crt_static_allows_dylibs = true` — su pareja obligatoria: sin ella `crt-static` apaga los dylibs y con ellos los PROC-MACROS (serde_derive, clap_derive, las 44 recetas `cargo-*` con `derive`). El compilador en sí sigue dinámico: el `bootstrap.toml` fija `crt-static = false` para el build, y eso gana sobre el default del spec. ⇒ `cargo-config` (receta, árbol y copia en scripts/) se van enteros: un paquete que instala un fichero que nadie abre es peor que no tenerlo. Y de paso, `lld21` instala los cuatro alias como SYMLINKS y no como copias: cmake los duplicaba, 5 × 96 M = 478 M de artefacto para 97 M de enlazador. Ahora 108 M, y `ld.lld` sigue despachando por `argv[0]`, que es como upstream lo diseñó. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
72ba86c290 |
la cadena rehecha: llvm21 arreglado, lld enlaza, rust reconstruido — los seis controles pasan
Un cambio de dos líneas en `llvm21` re-selló tres artefactos y los molió la granja sola, en orden,
por dependencia: llvm21 95956a16→bd4a4094, lld21 27be53c1→95a4794b, rust 015a07fb→702094a8.
llc --version Stack dump: + segfault → LLVM 21.1.2, Optimized build
opt --version ídem → LLVM 21.1.2
ld.lld «not built with zlib» → ENLAZA
rustc --version → 1.97.0 (built from a source tarball)
--print target-list → x86_64-alpine-linux-musl
cargo build + correr → 0,16 s y el binario habla
La config del sitio pasa a usar `lld` (más rápido); el `ld` de binutils queda documentado como
alternativa que también funciona.
LA REGLA QUE SE GANÓ SU LUGAR: **`link = "static"` en el encabezado decide cosas que la receta no
dice**. Van tres, todas medidas: apagó los threads de openssl, le mintió a libtool, y acá rompió los
registros estáticos de LLVM (`cl::opt`, `TargetRegistry`) porque el `static-pie` descarta sus
constructores globales. Con C++ grande la postura por defecto pasa a ser `dynamic` y medirlo — y el
control no es que selle: es CORRER UNA HERRAMIENTA del artefacto.
Y el corolario de por qué nadie lo vio en meses: el único consumidor de llvm21 era rust, que usa
`llvm-config` y las `.a`, nunca una herramienta. **Un artefacto puede estar roto en todo lo que nadie
usa** — como los 44 `cargo-*` sin cargo y los 23 binarios sin cargador.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
ef1f9252a1 |
✅ EL TOOLCHAIN COMPILA, ENLAZA Y CORRE — en una caja sin compilador de C
$ cargo build --offline
Compiling prueba v0.1.0 … Finished in 0.44s
$ ./target/debug/prueba
cargo, rustc y ld: los tres nuestros
`cargo` y `rustc` son los que construyó la granja; el enlazador es el `ld` de nuestro `binutils`.
EL MURO 8 SE CERRÓ SIN `lld`. La cadena, que es lo que vale: `-C linker-flavor=ld.lld` → «linker
`lld` not found» (el build con LLVM externo no genera rust-lld); `rust.lld = true` → el bootstrap lo
RECHAZA; `lld21` propio → crasheaba con cualquier entrada; `lld21` con zlib → **no se puede encender
desde ahí**, porque `LLVMConfig.cmake` dice `LLVM_ENABLE_ZLIB 0` y el build standalone lo hereda.
Lo que faltaba NO era un enlazador: era decirle a rustc que enlace en ESTÁTICO. Con `+crt-static`,
rustc usa su propio libunwind/compiler_builtins y deja de pedir `-lgcc` — que es lo que rompía el
intento con `ld`, porque la distro se construye con zig (compiler-rt) y libgcc no existe. Y estático
es el modo natural de musl.
⇒ `scripts/servidor/cargo-config.toml` (instalado en `/etc/cargo/config.toml`) cierra el pendiente
«config del sitio» del §5, con las tres flags y el porqué de cada una.
🧨 Y `lld21` destapó algo que nadie había visto: **las 79 herramientas de `llvm21` están ROTAS**.
`llc --version` crashea igual —también en el worker— porque salen `static-pie` y el enlace estático
descarta los constructores globales de los que dependen `cl::opt` y `TargetRegistry`. Funciona lo
que no los necesita (`llvm-ar`, `llvm-config`) y revienta lo que sí; nadie lo notó porque `rust` usa
`llvm-config` y las `.a`, nunca una herramienta. Queda como deuda DECLARADA: arreglarlo (o encender
zlib) re-hashea `llvm21` y con él `rust`. Hoy no bloquea nada.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
9f325a3c05 |
🐛 la granja molía UNA receta por cola y por ciclo — el xargs -I{} recibía la cola entera junta
Medido hoy al encolar `lld21` junto a `rust`: el ciclo decía «2 recetas en recipes/incoming» y
construía UNA. La causa está en el filtro de «ya sellado en el hub»: acumula en `filtrada` uniendo
con ESPACIOS, y más abajo la cola se le pasa a `xargs -I{}`, que trata **una línea = un elemento**.
Unidas por espacios, N recetas llegan como UN SOLO argumento:
printf '%s\n' " a.toml b.toml c.toml" | xargs -I{} sh -c 'echo [$1]' _ {}
recibe: [a.toml b.toml c.toml] ← uno solo
⇒ la fase paralela fallaba SIEMPRE con «No such file», y la fase 1b —el reintento serial— salvaba
exactamente UNA receta: la última, porque `basename` de esa ristra da el último nombre. El log
parecía normal, con su ✗ seguido de un ✓ (reintento serial), y ese patrón está en TODAS las colas
(yambar, spidermonkey, rust…). O sea que el reintento serial, que existe para conciliar colisiones
de concurrencia, venía tapando el bug desde que se escribió el filtro.
El arreglo son dos líneas: acumular con salto de línea y descartar la línea vacía con la que arranca
el acumulador (una línea vacía también es un elemento para `xargs -I{}`). Probado en chiquito, en
los dos sentidos: antes 1 elemento, después 3.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
24a6dd3b2f |
el cortafuegos puesto en la caja — y el primer baneado fui yo
`table inet tawasuyu_input` con policy drop, 15 reglas de puerto, y el set `ban4` llenandose solo: a los pocos minutos, 27 IPs baneadas y paquetes tirados por el contador. El reemplazo de fail2ban funcionando sobre trafico real, dentro del kernel y sin daemon. 🧨 EL PRIMER BANEADO FUI YO, EN SEGUNDOS. Aplicada la primera version: ssh y https bien, y `http`, proxy y git-ssh muertos; al minuto la caja entera dejo de contestarme. La causa no es una regla mal escrita: **el set @ban4 es UNO SOLO para todos los servicios** y `ip saddr @ban4 drop` esta antes que todo, asi que pasarse de tasa en UN puerto te tira en TODOS — y el `nuevas_por_min: 5` del puerto de administracion es exactamente lo que me pasa a mi, porque cada comando remoto es una conexion nueva. Lo devolvio el INTERRUPTOR DE HOMBRE MUERTO (un `nft flush ruleset` programado a 180 s que sólo se desarma si la verificacion externa sale bien). Sin consola serie, esa es la diferencia entre un susto y un rescue. ⚠ Y el tipo ya lo avisaba: `PoliticaEntrada` tiene un campo `confiables` cuyo comentario describe palabra por palabra lo que me paso. Copie el .ron de EJEMPLO en vez de leer el TIPO. La cura fue una linea con el hub y el worker, que se aceptan ANTES de la logica de tasa. ⚠ `nft -c` rechazo 5 reglas antes de aplicar nada: `ct count over N` es CONFIG_NFT_CONNLIMIT y este kernel no lo trae. Quedan COMENTADAS y no borradas en el fichero que se aplica — lo que la politica declara y el kernel no puede tiene que verse. El simbolo ya entro a la receta del kernel. Y para que sobreviva al reinicio, `nftables` declara su [[service]] con `lifecycle = "oneshot"`: cargar un reglaset no es un daemon, es un acto. Va PRIMERO en el genesis — entre que la red esta y que las reglas cargan, la caja esta abierta. Probado con `nft flush ruleset` + `arjectl start`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ba202d7826 |
atuq: los dieciocho guardianes en UN comando — y el sello llevaba nueve horas atrás
`atuq` es `source.dir` entero, asi que el commit de la boveda de esta manana (`6f0ba974`) movio el ArtifactHash y dejo el artefacto vigente SIN SELLAR. Durante esas nueve horas ningun guardian podia medir: se niegan a correr contra uno que no sea el vigente, que es lo correcto. Pero un guardian que se niega solo grita CUANDO alguien lo corre, y nadie lo corria — el repo se veia sano y la unica senal era un `hash --check` que nadie tenia motivo para teclear. Resellado en 3,7 s (es derivado y el firefox vigente estaba en el store) y corridos los dieciocho sobre `b3:e556024b`: 16 en verde y 2 en ROJO, los dos por la MISMA causa —los tres verbos de la boveda del §7.quinquies—. O sea que la unidad 12 no rompio nada de al lado, que es lo que habia que saber antes de seguir: toco `atuq.cfg`, la politica y el empaquetado de extensiones. `scripts/test-atuq-suite.sh` deja eso en un comando, en serie (cuatro nucleos sin swap; dos navegadores a la vez es un OOM) y cada guardian a SU fichero, porque dos corridas sobre el mismo log se mezclan y el resultado parece un pase. Lo primero que mira es el sello y NO corre nada si falta: verificado contra un store vacio, sale por ahi y devuelve 1. Dos cosas quedan escritas como control y no como adorno: · el numero de rojos esperados va en la cabecera. Un cuadro entero en verde puede ser un producto sano o un runner que no corre nada, y se ven igual; · esa cabecera decia «un rojo» —prediccion escrita antes de correr— y la primera corrida dijo dos: el guardian de coherencia de la boveda lleva el mismo chequeo del cable adentro. Coste medido en gioser: ~29 min. Los cuatro primeros salen en 10 s y no tocan el navegador. |
||
|
|
0a7afebe9c |
la caja tiene HORA, LATIDO y ROTACIÓN — y el respaldo del gitea dejó de ser a mano
`planear.py --revisar` sobre el censo ordena lo que queda, y lo primero no eran los servicios del usuario sino TRES CAPACIDADES que ninguna metrica reclama: el disparador periodico, la hora y la rotacion de logs. Los tres paquetes ya estaban sellados y declarados en `perfil.servidor` desde que ese perfil nacio — lo que faltaba era el eslabon que los ARRANCA, igual que con squid. · `chronyd`: la caja iba **15 s atrasada** y nadie la corregia. Ahora stratum 3 contra los NTP de Hetzner, 0,000005 s de NTP. Sin hora no hay TLS ni firmas, y el sintoma no se parece a la causa. · `crond`: no existia el latido. Verificado con una entrada `* * * * *` en el crontab REAL. · `logrotate`: `/var/log/squid` sin rotar. Diario, con `squid -k rotate` (squid mantiene los ficheros abiertos: un rename a secas lo deja escribiendo en un inode que nadie puede leer). · `scripts/respaldo-gitea.sh` + `17 3 * * *`: cierra el hueco del §6.15 — los 44 repos vivian en UNA copia y el respaldo se hacia a mano. Sube 23+21 repos (1,5 G) y un `gitea.db` de 332 M tomado con `.backup`, consistente con el servidor vivo. El control no es que el script salga 0: es CONTAR los repos de los dos lados. `chrony` y `cronie` NO declaraban `[[service]]` — el paquete llegaba a la imagen y no lo arrancaba nadie. Ahora lo declaran (fuera de `hash_inputs`: los hashes no se movieron) y el perfil los habilita. TRES MEDICIONES que valen mas que el resultado: 1. **La deriva era de gioser, no de la caja**: despues de sincronizar, el HUB quedo 3 s adelantado. Medir «contra el otro» sin un tercero no dice quien esta mal. 2. **El spool de cronie es `/var/spool/cron/<usuario>`**, no `…/crontabs/<usuario>`: `crontab -l` mostraba las entradas y `crontabs/` estaba vacio. Un crontab restaurado en el sitio equivocado es un fichero perfecto que nadie lee. 3. 🧨 **`sqlite3` seguia INERTE en la caja** (`compressBound: symbol not found`): el ultimo de los 23 del §6.4. Le faltaba `zlib-shared`, sellado y declarado en `perfil.base` desde el 11-09 — pero la caja se instalo antes. Sin el no habia snapshot consistente. Un paquete declarado no es un paquete instalado: en una caja viva vale lo que el `upgrade` proyecto. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
db24d206dc |
arnés de la imagen: el piso del sello se COMPRUEBA — un piso que falla callado no existe
Los 180 s de `PISO_SELLO` son de esta máquina (TCG sin KVM y con la carga que tenga el anfitrión, igual que los +97/+120/+137 de la pintura). Con el anfitrión cargado el mismo arranque puede no llegar al gancho que sella el éxito, y entonces la corrida suma al contador de caídas y envenena la siguiente EN SILENCIO — que es exactamente como empezó todo esto. Ahora que `toolkit.startup.last_success` se lee antes y después, eso se puede decir, así que se dice: ✓ el arranque SELLÓ el éxito (last_success X → Y): esta corrida no envenena la siguiente ⚠⚠ el sello NO se movió: este arranque no terminó ⇒ la corrida SUMA al contador. Subí PISO_SELLO Es la diferencia entre un piso confiado y un piso comprobado, y cuesta cuatro líneas porque el dato ya estaba medido y guardado. |
||
|
|
5b47cd596c |
atuq: el reloj del sello es el del LANZAMIENTO, no el de la pintura — y el piso va en el arnés
Leyendo el perfil en cada captura, el sello (`toolkit.startup.last_success`) y la limpieza del contador llegan al disco entre +100 s y +137 s DESDE EL LANZAMIENTO, no desde el primer cuadro. Y es UN gancho y no dos: el mismo arranque que limpia escribe el sello, con su propia hora de arranque. Las dos cotas salen de corridas independientes, que es lo que hace al intervalo creíble: · a los +100 s NO había sellado — la corrida S1 vivió 100 s desde el lanzamiento (primer cuadro a +97 s, fin del proceso tres segundos después, medido con los tiempos de sus propios ficheros); · a los +137 s YA había sellado — la corrida con `--watch-prefs` lo ve aparecer en esa captura. S1 parecía la excepción del modelo y resulta ser su cota inferior: si el reloj fuera el de la pintura tendría que haber sellado, y no selló. La consecuencia es contraintuitiva y es la que muerde: cuanto MÁS RÁPIDO pinta, más fácil es envenenar el perfil, porque `--until-paint` corta antes. La serie de ayer no se rompió por ser larga sino por ser rápida. Por eso la regla va en el arnés y no en un runbook: `PISO_SELLO = 180`, margen sobre la cota alta, y `--until-paint` no corta antes de ese piso. El comentario del piso lleva los dos números medidos al lado — un piso que se lee como elegido lo baja el próximo que lo vea. Las corridas `cierre-1`/`cuanto-1`, el mando `--watch-prefs` y el piso los midió y escribió la otra sesión del frente; acá van con la cota de S1 que los cierra por abajo. |
||
|
|
1de1fc31f6 |
atuq: lo que limpia el contador es SOBREVIVIR un rato después de pintar, no pintar
El par aísla la variable —mismo pin, mismo perfil, misma imagen, y la única diferencia es cuánto vive el navegador después del primer cuadro—: S1 +97 s, muerta en el acto (`--until-paint`) ⇒ contador después: 1 cierre-1 +120 s, ~360 s de vida después de pintar ⇒ contador después: AUSENTE Con eso queda contestada la pregunta que dos secciones atrás este documento daba por cerrada y no lo estaba, y A deja de ser una anomalía: también siguió viva minutos después del primer cuadro. Se dice además lo que la tabla NO prueba: si ese arranque además SELLÓ el éxito. El volcado del «después» sólo grepeaba el contador; ahora `toolkit.startup.last_success` va en los DOS volcados y se guarda en la medición (`last_success_antes` / `_despues`), que es lo que faltaba para no repetir la corrida. Si el sello se mueve, es un gancho que sella y limpia a la vez; si no, son dos. Y queda escrito que el 16 sigue sin explicación, ahora con una contradicción medida encima: desapareció en una corrida diálogo + ATUQ-EXIT=0, y las fases k2/k3 del umbral fueron diálogo + ATUQ-EXIT=0 y no limpiaron nada. La respuesta buena no tapa la sucia. La corrida `cierre-1` y el mando `--watch-prefs` son de la otra sesión del frente. |
||
|
|
c30905e2b9 |
atuq: vigía de las dos puntas del cable — y el extractor de verbos que no veía la mitad
El chequeo del commit anterior miraba una extensión. El cable tiene la misma forma para las diez, y
este vigía lo recorre entero: qué verbos manda cada extensión y si el commit que `recipes/
puriy-costura.toml` pinea los atiende. Corre en un segundo y no construye nada.
Medido hoy, 13 verbos sobre 8 extensiones: sólo los TRES de la bóveda están sin dueño. O sea que
esto no se podía deducir mirando cuándo se tocó la receta por última vez — las otras nueve
extensiones conviven bien con el mismo pin viejo.
⚠ Y lo que costó más que el vigía: **el extractor de verbos no veía la mitad de lo que hay**. Estaba
buscando `verb: "…"`, que es como lo escribe la bóveda. Pero la extensión de IA arma el mensaje con
`postMessage({ id, verb: verbo, … })` —el verbo llega por VARIABLE—, así que ese patrón encontraba
cero verbos en `ia` y la declaraba sana sin haber mirado nada: cero verbos encontrados y cero verbos
faltando son el mismo cero con dos causas. Ahora recoge toda cadena con forma `ns.verbo` y descarta
las que son nombres de fichero, que es lo único que se les parece. Con eso aparecen `ai.ask` y
`archive.ask`, que antes no estaban en ninguna cuenta.
De ahí sale el tercer control, que no es negativo ni positivo sino de COBERTURA: una extensión de la
que no se extrae ningún verbo se REPORTA. Y las dos que de verdad no le hablan al host —`inicio` y
`proxy`, que viven enteras dentro del navegador— están nombradas en el código, para que «no manda
verbos» no se pueda confundir con «el extractor se quedó mudo».
Los otros tres controles: `sct.observe` tiene que aparecer en el commit pineado (si no, lo roto es
el vigía y lo dice); `--negative-control` agrega un verbo inventado a cada extensión y exige que lo
vea; y si el clon de tawasuyu no está, o no conoce el commit pineado, esto FALLA — «no se pudo
comprobar» no es «está bien». Los cuatro probados, incluida la rama del commit desconocido.
Pregunta por `git grep '"<verbo>" =>' <commit> -- '*.rs'` leyendo del objeto: sin checkout, porque
el árbol de tawasuyu es compartido y siempre tiene ficheros en vuelo de otras sesiones.
|
||
|
|
e7369efe82 |
atuq: la bóveda le habla a un host que no sabe sus verbos — y eran CINCO lugares, no cuatro
Este guardián nació esta mañana diciendo que una extensión está enchufada en CUATRO sitios y que
ninguno da error si falta. Los cuatro estaban bien. El quinto no, y es el que hoy está roto.
El quinto lugar vive en otra receta y en otro repo: `recipes/puriy-costura.toml` pinea un commit de
tawasuyu, y es ESE commit el que decide si `vault.match` es un verbo o una cadena que nadie atiende.
Medido, con su control al lado, sobre el artefacto SELLADO y vigente (`hash --check` dice SELLADO
para `b3:1eb2b692`):
strings del binario sellado → «vault» 0 veces · «cas» 5 veces ⇐ EL CONTROL
El cero solo no probaba nada —un grep que devuelve cero puede ser una ausencia o un sitio
equivocado, y las dos se ven igual—; lo que lo convierte en evidencia es el «cas» que TENÍA que dar
positivo en el mismo binario. Y del lado de la fuente sale lo mismo: la receta pinea `e19bb0e5`, que
es ANCESTRO de `bc6903f9e` —el commit que agregó los verbos—, así que el host sellado es anterior a
la bóveda por construcción.
Cómo se ve ese fallo desde la silla del usuario, que es por qué esto merece guardián: el manifiesto
está, el permiso está, la política instala, `connectNative` CONECTA, la extensión manda
`vault.match` y el host contesta `verbo desconocido`. `fondo.js` lee `r.ok !== true`, borra la
insignia y se calla. Un navegador sin bóveda, y todos los ficheros en orden.
El chequeo pregunta `git grep '"<verbo>" =>' <commit> -- '*.rs'` sobre el clon, sin checkout: el
árbol de tawasuyu es compartido y siempre tiene ficheros en vuelo de otras sesiones. Los verbos los
saca de `fondo.js` y no de una lista escrita acá, que se desincronizaría justo en el único sitio
donde eso no se ve.
Tres controles, porque el chequeo es un grep que espera cero:
- POSITIVO: `sct.observe` tiene que aparecer en el commit pineado. Si no aparece, lo roto es el
guardián y lo dice así, en vez de acusar a la receta;
- `--negative-control-verbo`: le agrega un verbo inventado y exige que lo detecte;
- y si el clon no está, «no se pudo comprobar» NO es «está bien»: falla ruidosamente.
Lo que este commit NO hace: subir el pin. Eso es su propia unidad y tiene un muro medido delante —el
`Cargo.lock` de tawasuyu en `main` todavía describe a `puriy-costura` con sus once deps viejas, sin
`pacha-boveda` ni `pacha-cifrador`, y `pacha-boveda-daemon` no está en el lock en absoluto. Con eso,
`cargo vendor --locked` muere sin nombrar al culpable, que es exactamente la trampa que la propia
receta dejó escrita.
|
||
|
|
98c9e4daa6 |
atuq: la tasa con perfil sano es +97 y +98 s — y PINTAR NO ES TERMINAR el arranque
Cuatro arranques fríos en serie, el perfil partiendo del 5 que dejó la corrida del umbral: S1 pin 0 antes 5 después 1 el navegador ✓ +97 s S2 sin pin antes 1 después 2 el navegador ✓ +98 s S3 sin pin antes 2 después 3 (matada a los 60 s) S4 sin pin antes 3 después 4 EL DIÁLOGO ✗ en 420 s Tres cosas: · la tasa, por fin con perfil sano: dos corridas independientes y pegadas, +97 y +98 s, contra los +123/+195/+197/+204 de antes. Con el anfitrión descargado el mismo artefacto pinta en la mitad del tiempo ⇒ el número es de la máquina y la carga tanto como del producto, y se publica con sus condiciones; · el umbral se reprodujo solo y en otra secuencia: S4 arrancó con 3, sumó a 4 y abrió el diálogo — segunda medición independiente del mismo borde, esta vez identificando la ventana por `min_size` y no por el título (que está traducido, así que un grep por «Troubleshoot Mode» en una imagen en castellano no engancha y su ausencia se lee como la conclusión contraria); · ⚠ y una corrida que PINTA no limpia el contador: S2 pintó y dejó 2. Más todavía, `toolkit.startup.last_success` vale lo mismo en las CUATRO (1789494795, las 17:53) ⇒ el gancho de cierre no corrió ni una vez, ni en las que pintaron. Pintar no es terminar de arrancar. Entonces «quién limpia el contador» SIGUE SIN MEDIR, y el propio documento decía que esta serie lo cerraría: no lo cierra, y queda escrito así. Hace falta una corrida que deje al navegador terminar de arrancar y salir limpio. Lo más caro es para el instrumento: con `--until-paint` cada corrida suma uno, pinte o no, así que una serie se rompe sola a la cuarta — que es lo que pasó ayer sin que nadie lo viera. El arnés ahora AVISA cuando el contador está en 3 («esta corrida va a abrir el diálogo, no el navegador») y la receta para una serie queda escrita: pinnear `--crashes 0` en cada corrida, que resetea la base (S1 lo muestra partiendo de 5). También cae una candidata mía, con un cero que viene con control (medido por la otra sesión): el pref no tiene default en este build, así que fijarlo en 0 SÍ se persiste y el «ausente» no era el pin. |
||
|
|
7997464337 |
atuq: el umbral son 4 — y mostrar el diálogo NO limpia el contador, como este documento decía
Medido en los dos lados, partiendo del perfil en 2 y con tres fases más en un arranque: el que encuentra 2 guardado PINTA (y queda en 3); el que encuentra 3 lo sube a 4, compara 4 > 3 y abre el diálogo (y queda en 4); el siguiente queda en 5 y vuelve a abrir el diálogo. O sea que la comparación va DESPUÉS del incremento del propio arranque: contando desde la última corrida que terminó bien, son cuatro arranques interrumpidos seguidos y el quinto lanzamiento ya no abre el navegador. `toolkit.startup.last_success` vale lo mismo en las tres fases (1789494795, el arranque de la última corrida sana): el contador sube mientras esa marca no se mueve. Con las dos cifras juntas, un contador que no cambió deja de ser ambiguo entre «no subió» y «subió y se limpió». ⚠ Y eso refuta una atribución que yo había escrito ayer y que estaba en tres sitios: mostrar el diálogo NO limpia el contador (k2 lo dejó en 4, k3 en 5). Queda retirada del SDD y del comentario del arnés. La desaparición del 16 entre las 17:12 y las 17:20 pasa a ser un hecho SIN explicación medida, con dos candidatas escritas y ninguna dada por buena — y una de ellas es mía y fea: todas las corridas que pintaron llevaban `--crashes 0` pinneado, que es el valor por DEFECTO, y un pref igual al default no se persiste. El «ausente» puede ser eso y no una limpieza. Queda dicho cómo se cierra: dejar el contador en 2 SIN pinnearlo, correr hasta que pinte y leerlo después. Un discriminador que salió gratis: `ATUQ-EXIT=137` es «la matamos» (128+9) y `ATUQ-EXIT=0` es «se fue sola» por el diálogo. El código de salida contesta lo que una captura no podía. Medido por la otra sesión del frente (work/umbral-1.txt), con la predicción escrita antes de mirar. |
||
|
|
2f90d8eb40 |
arnés de la imagen: el contador de caídas viaja con la medición, y las envenenadas se apartan
`atuq-en-imagen.py` ahora lee `toolkit.startup.recent_crashes` del perfil ANTES y DESPUÉS de cada corrida y lo anota junto al tiempo de primera pintura (`recent_crashes_antes` / `_despues` / `_fijado`). Los dos volcados, no uno: Gecko BORRA el contador al mostrar el diálogo de Modo de resolución de problemas, así que un perfil envenenado se ve limpio si se lo mira después — y la corrida siguiente parece arreglarse sola mientras la anterior parece intermitente. Con eso, `vigia-imagen.py` saca de la tasa las corridas con el contador > 3 (ahí el sujeto no es el navegador sino un diálogo modal) y para las 21 anteriores al campo dice que NO SE PUEDEN CLASIFICAR en vez de contarlas como buenas. Un denominador se marca, no se borra. Mandos y guardas nuevas: · `--crashes N` fija el contador en el `user.js` del perfil: 0 limpia, >3 reproduce. Es el control en los dos sentidos, que es lo único que distingue causa de correlación con suerte; · `WidgetScreen:5` en el MOZ_LOG y una sonda `ATUQ-DIAG` en la propia página (screen/outer/inner por `dump()`), que es lo que refutó la carrera con `wl_output`; · la lista ordenada del protocolo ya no se ahoga en los ~60 modos que anuncia QEMU —el `head` cortaba antes del `set_window_geometry`— y trae `set_title`/`set_app_id`, que es lo que identificó la ventana; · si QEMU no arranca, se dice en el primer segundo leyendo `qemu.log`. El fallo real es «Failed to get write lock» con otra VM sobre la misma imagen, y sin esta guarda se veía como diez minutos esperando una marca del serial: el socket queda creado y el `connect()` funciona. Las fases (`--xulstore`), el `ATUQ-EXIT=$?`, la copia del log del arranque anterior y el `find` del perfil sobre la raíz entera los escribió la otra sesión que trabaja este frente; conviven acá porque el fichero es compartido. |
||
|
|
6f0ba974c7 |
feat(atuq): la boveda de la suite guarda las contrasenas, y el gestor de Gecko se aparta
La decima extension de atuq. Ofrece la credencial que corresponde al sitio abierto y la pone en el formulario, pero NO puede sacar una contrasena de la boveda por su cuenta: pregunta cuales corresponden —y eso contesta titulo y usuario, nada mas— y la contrasena sale por otro verbo que pregunta en el escritorio antes de contestar. Si la extension queda comprometida, o si una pagina consigue hablarle, lo que obtiene es la lista de titulos de los sitios que coinciden con su propia direccion. La direccion siempre la pone el chrome y nunca la pagina. Lo unico que el trozo que corre dentro del sitio aporta es que HAY un formulario y lo que el usuario tipeo; si pudiera decir de quien es la pagina, podria decir que es el banco. Y la extension no decide nada: quien sabe que credencial va en que sitio es la funcion del otro lado del cable. Ponerlo en JS seria reimplementar el original y, peor, poner en la pagina la decision de a quien se le entrega una contrasena. EL GESTOR DE GECKO SE APAGA, pero como valor de arranque y no como politica. Dos gestores peleando por el mismo campo es la peor experiencia que puede tener alguien que solo quiere entrar a su correo: el sitio se rellena dos veces, o ninguna, y no hay forma de saber cual tiene la buena. De fabrica manda la boveda; el que prefiera el de Gecko lo prende y listo. Prohibirselo seria decidir por el, que es lo que este fichero dice en su cabecera que atuq no quiere ser. Y un guardian nuevo, que corre en un segundo y sin construir nada. Una extension esta enchufada en CUATRO lugares distintos y ninguno da error si falta: el identificador que ella declara, la politica que la instala, el permiso para hablarle al host, y las preferencias que la acompanan. Si falta la politica no se instala. Si falta el permiso se instala y se conecta a nada, en silencio, y la insignia no aparece nunca. Si faltan las preferencias los dos gestores se pelean. Ninguno de los tres se ve como un error: se ven como "no anda". El guardian los mira todos y trae su control negativo, que le saca el permiso del host y exige que lo detecte. No reemplaza al guardian de metal —servidor, navegador real, login real, el dialogo a la vista—: lo precede, y ahorra descubrir construyendo que faltaba un renglon en un JSON. Los cinco verbos del otro lado ya estan en tawasuyu (bc6903f9e), con trece tests que leen el cable. |
||
|
|
798cfab7e8 |
atuq: el 117x70 lo pide el CLIENTE, no el compositor
Ordenando el protocolo numerado en los dos sentidos: cosmic-comp manda configure_bounds(0,0)+configure(0,0) («elegi vos»), el cliente contesta set_min_size(117,37), set_max_size(348,16332) y set_window_geometry(117,70), y RECIEN ahi el compositor devuelve configure(117,70) teniendo 1280x692 para dar. No hay nada que arreglar en COSMIC. El MOZ_LOG dice como: «Initial resize to 1 x 1» — Gecko crea la ventana de 1x1 y nunca la agranda; 117x70 es lo que queda cuando el chrome se mide a si mismo. El frente se mueve a Gecko. |
||
|
|
f4e1e99f19 |
atuq: la ventana SI esta — mide 117x70 px (lo dice el protocolo)
Como cosmic-comp no emite DEBUG (macros compiladas fuera), se le pregunto por el protocolo con WAYLAND_DEBUG=1. Manda configure_bounds(0,0) al mapear, despues bounds(1280,692) y configure(117,70): la ventana queda de 117x70. El pixel lo confirma — restando el cuadro de antes aparece un cluster de 286x86 con la decoracion de COSMIC en (496,115). Cae la hipotesis del §6.10.nonies: hay 43 wl_callback.done, o sea que SI hay frame callbacks. Y la tasa 2 de 10 no medía «pinto/no pinto» sino «ventana grande / ventana de 117x70». Arnes: --wayland-debug y --set-rust-log (perilla por /etc/cosmic-mode, que cosmic-start lee antes de RUST_LOG); margen de espera 420->780s porque con carga ~13 la VM no llegaba al shell. |
||
|
|
21381b598f |
atuq: la serie con UNA sola tarjeta — 2 de 10, y el hueco resulta real
Con -vga none la captura ya no tiene punto ciego, asi que un ✗ significa «la ventana no esta». Clasificado el ultimo cuadro de las 10: 2 con la pagina pintada, 1 con la ventana en blanco y 7 con SOLO el escritorio. Las dos salidas explicaban los ✗ inflados de las series VGA=1, no el fenomeno. El MOZ_LOG separa los dos regimenes: las que tienen ventana escriben 952-1505 lineas hasta el apagado; las 7 sin ventana se cortan a los ~40s y quedan 6 minutos de silencio, sin crash (WebRender arranca). Quedan como hipotesis NO medida los frame callbacks que el compositor no manda. Ademas: tasa-primera-pintura.sh nombra el log por VGA (la serie nueva habia sobrescrito los crudos de la anterior). |