Commit Graph
1255 Commits
Author SHA1 Message Date
Sergio 92215bf937 estado: cosecha granja 2026-09-15T08:31:39Z — avance del árbol KDE 2026-09-15 08:31:39 +00:00
Sergio d13d14ef10 estado: cosecha granja 2026-09-15T08:01:40Z — avance del árbol KDE 2026-09-15 08:01:40 +00:00
Sergio 13494bee5d estado: cosecha granja 2026-09-15T07:31:19Z — avance del árbol KDE 2026-09-15 07:31:19 +00:00
Sergio 6faa9927a5 estado: cosecha granja 2026-09-15T07:01:17Z — avance del árbol KDE 2026-09-15 07:01:17 +00:00
Sergio cdd1eb6d2c estado: cosecha granja 2026-09-15T06:31:23Z — avance del árbol KDE 2026-09-15 06:31:23 +00:00
Sergio d8d5dff113 estado: cosecha granja 2026-09-15T06:01:19Z — avance del árbol KDE 2026-09-15 06:01:19 +00:00
Sergio 798e45c539 estado: cosecha granja 2026-09-15T05:31:14Z — avance del árbol KDE 2026-09-15 05:31:14 +00:00
Sergio 362250c877 estado: cosecha granja 2026-09-15T05:01:18Z — avance del árbol KDE 2026-09-15 05:01:18 +00:00
Sergio c34a3f2508 estado: cosecha granja 2026-09-15T04:31:17Z — avance del árbol KDE 2026-09-15 04:31:17 +00:00
Sergio 6f1520f60d estado: cosecha granja 2026-09-15T04:01:16Z — avance del árbol KDE 2026-09-15 04:01:16 +00:00
Sergio 12943b3d1f estado: cosecha granja 2026-09-15T03:31:14Z — avance del árbol KDE 2026-09-15 03:31:14 +00:00
Sergio aa3dacea4a estado: cosecha granja 2026-09-15T03:01:14Z — avance del árbol KDE 2026-09-15 03:01:14 +00:00
Sergio 15cfcd769d estado: cosecha granja 2026-09-15T02:31:14Z — avance del árbol KDE 2026-09-15 02:31:14 +00:00
Sergio 7cedd90c09 estado: cosecha granja 2026-09-15T02:01:16Z — avance del árbol KDE 2026-09-15 02:01:16 +00:00
Sergio 17317a2d5a estado: cosecha granja 2026-09-15T01:31:14Z — avance del árbol KDE 2026-09-15 01:31:14 +00:00
Sergio 889623afe3 estado: cosecha granja 2026-09-15T01:01:21Z — avance del árbol KDE 2026-09-15 01:01:21 +00:00
Sergio e428f6b092 estado: cosecha granja 2026-09-15T00:31:34Z — avance del árbol KDE 2026-09-15 00:31:34 +00:00
Sergio f4a78e8b03 estado: cosecha granja 2026-09-15T00:01:40Z — avance del árbol KDE 2026-09-15 00:01:40 +00:00
Sergio 1a4da25989 estado: cosecha granja 2026-09-14T23:31:38Z — avance del árbol KDE 2026-09-14 23:31:38 +00:00
Sergio 30765ca376 estado: gocryptfs cerrado — CERO no-determinismos vigentes en el corpus
Las otras tres recetas cgo (sq, usql, gitea) dan DERIVA, no no-determinismo: sus dos
reconstrucciones coinciden entre sí y el artefacto guardado era el de antes del arreglo.
Ya están al día.

Y de paso se les fue una deuda que NO era de reproducibilidad sino de PORTABILIDAD: el
`x_cgo_sigaction` de sq y usql —código C de cgo, sin guarda de CPU en runtime— venía con
AVX2 horneado, de cuando el driver pisaba el `-mcpu=baseline`. Un binario así no falla al
construir ni al sellar: falla al EJECUTAR en una CPU sin AVX2, con SIGILL. Ahora los tres
salen baseline (cero ymm, cero zmm en esa función).

── El límite del libro, anotado en su encabezado ──────────────────────────────────────
`gocryptfs` acabó con CUATRO filas al MISMO hash: dos NO-DETERMINISMO y dos reproduce, y
las cuatro son ciertas. La clave (receta, hash) no puede distinguir antes y después de un
arreglo del FRAMEWORK, porque la fase generada no entra en `hash_inputs` — a propósito,
para no re-sellar 362 recetas cada vez que se toca un driver.

Corolario, que es lo caro: tras tocar un driver, lo sellado NO se rehace solo. Hay que
forzarlo receta por receta, y si nadie lo hace el store se queda con artefactos que ya
nadie construiría así.
2026-09-14 23:10:29 +00:00
Sergio 33056de44b estado: cosecha granja 2026-09-14T23:03:01Z — avance del árbol KDE 2026-09-14 23:03:01 +00:00
Sergio 1728a77a3a go/cgo: GOTMPDIR fuera del módulo — era la única causa del no-determinismo de gocryptfs
`gocryptfs` era el último no-determinismo vigente del corpus. Ya REPRODUCE.

── La medición, que primero hice mal ──────────────────────────────────────────────────
Comparé los dos ficheros de `store/.divergen/` y salían 1.874.136 bytes distintos, con
`.text` entre ellos y funciones de OpenSSL cambiando de AVX2 a AVX-512. Era el par
EQUIVOCADO: `.divergen/<D>` es el artefacto VIEJO archivado, no una reconstrucción. El
par que define el veredicto es `<D>.r1` (1ª reconstrucción) contra el que queda en el
store (2ª).

Comparado bien, el no-determinismo son **7 bytes, todos en `.debug_str`**: los dígitos
de `go-build598798091` contra `go-build270591055`.

(Lo de AVX no era ruido: era DERIVA real contra el artefacto viejo, sellado antes de que
467a87cd pusiera `-mcpu=baseline` en el CC de cgo. Se trata aparte, abajo.)

── La causa ───────────────────────────────────────────────────────────────────────────
`-trimpath` reescribe rutas, pero cuál gana depende de dónde esté el work dir de Go:
dentro del módulo gana la reescritura del módulo y el componente ALEATORIO sobrevive;
fuera, Go reescribe el work dir entero a `/tmp/go-build` y el número desaparece. El
driver ponía `GOTMPDIR=/src/.gotmp`, que es justo dentro del módulo.

Con CGO off da igual —no hay fuentes C generadas, ninguna ruta del work dir entra en el
DWARF— y por eso las 15 recetas Go puras verificadas reproducen. Con cgo sí entra. Por
eso el cambio va acotado a `cgo = true`: las otras 358 no lo necesitan.

── Medido en los dos sentidos, con un módulo cgo de juguete y cachés limpias ─────────
  · GOTMPDIR dentro  ⇒ `go-build3806217352` en el binario; dos builds DIFIEREN
  · GOTMPDIR fuera   ⇒ ninguna cadena `go-build`; dos builds IDÉNTICOS
Y sobre gocryptfs de verdad: DERIVA primero (las dos reconstrucciones ya coincidían
entre sí y el guardado era el viejo), REPRODUCE en la pasada siguiente. En el binario
sólo queda `/tmp/go-build`, el marcador constante de trimpath.

── Va a `/cache` y no a `/tmp`, y se COMPRUEBA que exista ─────────────────────────────
El sandbox monta `--tmpfs /tmp` (~½ RAM) y un proyecto Go grande lo desborda: ésa era la
razón de poner esto en `/src`. `/cache` es un bind RW a disco (el de la caché de zig),
así que conserva el disco y queda fuera del módulo.

Y no se da por hecho que esté: con `--tmp-overlay /` la raíz es escribible, así que un
`mkdir -p /cache/gotmp` a ciegas TRIUNFARÍA creando el directorio en la capa tmpfs —en
RAM, el desbordamiento que se quería evitar, y en silencio. Si el bind falta, el build
muere diciéndolo.
2026-09-14 22:57:47 +00:00
Sergio 9de1ac57b8 estado: anotar que dos NO-DETERMINISMO del libro son de hashes ya superados
`grep NO-DETERMINISMO` daba 3 y sugería tres problemas abiertos. Dos ya no existen:
kirigami y syntax-highlighting se arreglaron el mismo día y sus filas están clavadas
al hash VIEJO, que es justo lo que la clave (receta, hash) hace bien — pero quien
cuenta líneas no lo ve.

Se deja el veredicto medido intacto y se añade a qué hash fue superado. El único
no-determinismo VIGENTE al hash de hoy es `gocryptfs`, que no está en ningún perfil.
2026-09-14 22:36:23 +00:00
Sergio 477ba96d98 estado: powerdevil y ktexteditor verificados tras el re-sellado — reproducen
Los dos ya reconstruidos contra el syntax-highlighting arreglado.
REPRODUCEN 2 · DERIVA 0 · NO-DETERMINISMO 0.

Sellar de nuevo no demuestra nada por sí solo: lo que cierra el arreglo es que el
árbol reconstruido REPRODUZCA.
2026-09-14 22:35:50 +00:00
Sergio 4e718e243d estado: cosecha granja 2026-09-14T22:33:03Z — avance del árbol KDE 2026-09-14 22:33:04 +00:00
Sergio 6df63d4774 estado: cascada de syntax-highlighting reconstruida — 6/6 al hash nuevo
El arreglo movió el hash (4c1638a0… → 3db73c19…) y con él sus 4 dependientes
directos y 5 transitivos: kate, ktexteditor, plasma-desktop, plasma-workspace y
powerdevil. Comprobado aparte del log: para las seis existe
`store/<hash-de-hoy>-<nombre>`.

⚠ `kate` falló en el primer intento con «No space left on device» y esta vez NO era
el disco: `/mnt/vvv` tenía 15 G libres. El que se agotaba era un **tmpfs**, y un
tmpfs sin sitio significa RAM sin sitio — la caja tenía 855 MB disponibles, zram ya
con 6,3 G, otro agente compilando Rust y un QEMU encima. Al reintentar con 1,7 G
disponibles construyó en 242 s a la primera.

Que el mismo mensaje signifique dos cosas distintas —disco lleno o RAM llena— es
justo lo que hace caro el diagnóstico: hay que mirar QUÉ sistema de ficheros, no
sólo `df` del que uno tiene en la cabeza.
2026-09-14 22:25:30 +00:00
Sergio 1db707b8c5 estado: cosecha granja 2026-09-14T22:08:47Z — avance del árbol KDE 2026-09-14 22:08:47 +00:00
SergioandClaude Opus 5 41ab76c8a0 RUST COMPILA EN LA CAJA — paso 1 cerrado, y tres paquetes que estaban en el perfil equivocado
`rustc 1.97.0` y `cargo 1.97.0` responden en `2.29.29.217`, y un `fn main(){println!("ok");}`
compila y **corre**. El toolchain oficial de rust-lang, verificado por su sha256 y sellado tal cual
con `foreign = true` (son bytes ajenos, no un build nuestro: clase `ajeno`, fuera del recuento del
corpus).

Lo que costó hacerlo correr, y ninguna de las dos cosas se ve en el grafo porque las dos estaban
`sealed` y en el perfil equivocado:

1. **El cargador musl.** `rustc` es PIE dinámico (`NEEDED librustc_driver-*.so`, `NEEDED libc.so`).
   En el worker ni arranca —`cannot execute: required file not found`, que es como se ve la falta
   del intérprete—; en la caja sí, porque ahí está `musl-shared`.
2. **`libgcc_s.so.1`**: `Error loading shared library … _Unwind_Resume: symbol not found`. Lo
   publica `gcc-libs`, sellado y declarado **sólo en los cuatro perfiles de escritorio**. Una línea
   en `base`. Es la tercera vez HOY que aparece la misma figura —`tar`, `os-release`, `gcc-libs`—:
   el paquete existe, está sellado, y no viaja en la imagen que lo necesita.

Y una propiedad que vale anotar: **no hace falta un `cc`**. El primer `rustc` falla con
`linker \`cc\` not found` y no hay que traer un compilador de C — el toolchain **trae su propio
lld** (`rustlib/<target>/bin/rust-lld`), y con `-C linker-flavor=ld.lld -C link-self-contained=yes`
enlaza y el binario corre. Una distro sin compilador de C puede compilar Rust igual.

Va en `perfil.servidor` y no en `base`: son 799 M y una imagen de escritorio no compila nada.

Pendiente, que es config del SITIO y no de la receta: que esas flags sean el default
(`/etc/cargo/config.toml`), para que `cargo build` funcione sin recordarlas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Tf9T4vGzsMoT7eS8YzMFn
2026-09-14 22:07:50 +00:00
SergioandClaude Opus 5 cd78bb98c9 el logo OFICIAL de takana, extraído del PNG — y rustc entra al corpus como foreign
EL LOGO. El usuario pasó el PNG oficial. El ASCII no se inventa: se MIDE. Extraído celda por celda
del original — bbox 348×348 en un lienzo de 512, cuadrícula **7×7**, celda de 49,7 px — con cuatro
colores exactos y ninguno más:

    #a63a25 ladrillo · #df6b2a naranja · #e3a63d dorado · #fff6de crema

La primera versión que puse era un martillo dibujado a ojo: era *un* martillo, no *el* logo. Cada
celda son DOS caracteres de bloque porque un carácter de terminal es alto y angosto, y `██` es lo que
conserva la geometría cuadrada del original; reconstruirlo a ojo la pierde y deja de ser el logo.

Van los dos ficheros: `logo.png` (el oficial, tal cual) y `logo.txt` (el mismo en ANSI de color
verdadero), más la config global de fastfetch con `file-raw` — con `file` los escapes se imprimirían
como texto. Y `os-release` se mueve de `cli` a **`base`**: toda imagen de takana debe saber decir qué
es, incluida la más pelada. «No sólo para esta máquina, para siempre en takana».

De paso, dos cosas que sólo se ven construyendo: `cp -a` aborta en el sandbox («failed to preserve
ownership»), va `cp -r`; y un config de fastfetch que sólo define `logo` dibuja el logo y NI UN DATO
— `modules` hay que listarlo aunque parezca redundante.

RUSTC, PASO 1. `rust-toolchain-bin` 1.97.0, el tarball oficial de rust-lang verificado por su sha256
y sellado tal cual, con `foreign = true` porque son bytes ajenos y no un build nuestro (clase
`ajeno`: no infla el recuento del corpus, ADR 0015). Target **musl**, no gnu: un toolchain gnu
traería el cargador de glibc y sería needed-colgante otra vez.

Por qué importa, y no es gusto: TODO el instrumental de takana es Rust —12 crates, 42 555 líneas— y
el `rustc` que los construye sale del LAB, un rootfs ajeno que ENTRA en el ArtifactHash. La distro
depende de bytes de otro para reconstruirse a sí misma. Y el corpus tenía **44 recetas `cargo-*` y
CERO de rustc**: subcomando-sin-driver a escala de toolchain.

Es el paso 1 de tres, y los otros dos no son opcionales: (2) rustc desde fuente con el `llvm18` que
YA está sellado (379 M), y (3) la cadena mrustc→1.91.1 que ya existe documentada en el selfhost pero
vive en el bootstrap, no en el corpus.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Tf9T4vGzsMoT7eS8YzMFn
2026-09-14 22:03:30 +00:00
SergioandClaude Opus 5 0d0ab79092 el tar de busybox no sirve para un rootfs ajeno — y takana ya no dibuja un pingüino
`qorpa pull` de un `.tar.zst` moría imprimiendo **la ayuda de busybox** y un exit 1: ni una palabra
sobre `--zstd`, que es la opción que no entiende. El código ya elegía el descompresor por MAGIC y
pasaba las flags correctas; lo que faltaba era un `tar` de verdad.

Dos arreglos, y el segundo es de una línea:

· `untar` comprueba `tar --version` y, si no es GNU, ABORTA nombrando la causa y el arreglo. Diez
  minutos de diagnóstico se vuelven una línea.
· **`tar` (GNU) entra en `perfil.base`.** Estaba sellado desde hace meses y declarado en UN solo
  perfil (`escritorio-kde`). Es la lección de `foot` otra vez: el paquete existe y no viaja en la
  imagen que lo necesita. Ya había costado dos veces — la caja tampoco podía desempacar su propio
  LAB por lo mismo.

Y el logo: fastfetch dibujaba **el pingüino genérico de Linux** porque elige por el `ID` de
os-release y no conoce `takana`. Ahora la receta `os-release` publica también
`/usr/share/takana/logo.txt` (un martillo, que es lo que significa el nombre en quechua) y
`/etc/xdg/fastfetch/config.jsonc` — la config GLOBAL por XDG, no la de un usuario. Van en la receta
de la IDENTIDAD y no en la de fastfetch a propósito: mañana el que dibuje puede ser otro programa y
el logo seguirá siendo el mismo fichero.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Tf9T4vGzsMoT7eS8YzMFn
2026-09-14 21:46:08 +00:00
SergioandClaude Opus 5 93362fc21b el TLS arreglado en la caja, la distro con nombre, y el ENOSPC que no era de la raíz
**curl ya valida**: `ca-certificates` re-sellado con **119 hash-links en el capath**, generados en el
build, y la caja baja por HTTPS sin apaño (200). Tardó tres intentos y los dos primeros los cazó la
guarda de la propia receta: `c_rehash` no estaba en el PATH (lo instala esa misma receta en
`/out/usr/bin`) y los certificados no están sueltos en `usr/share/ca-certificates/` sino en
`mozilla/`, así que sólo veía el bundle y lo saltaba —correctamente— con «does not contain exactly
one certificate». Sin la guarda habría sellado un capath vacío las tres veces.

**fastfetch 2.68.1 corre en la caja** (pedido del usuario) y al correrlo destapó que
**`/etc/os-release` no existía**: la distro era anónima para cualquier programa que no fuera suyo.
Ahora `OS: takana x86_64`. Va como receta propia (`source.dir`) y no como constante de
`takana-bootstrap`: meterlo ahí re-sellaría el product-rootfs —el baseline del selfhost— por cinco
líneas. Sin VERSION_ID ni fecha: un sello con la fecha del build cambiaría el hash cada día sin
motivo, y con él la clausura de toda imagen que lo lleve.

🧨 Y el hallazgo estructural: `upgrade apply` falló dos veces con **No space left on device teniendo
3 G libres en la raíz**. No era `/` ni los inodos (9%): el estado de generaciones vive en
**`/var/lib/hammer`, que es sda3 y mide 487 MB** — la partición «estado» del layout. Cada generación
guarda una copia del árbol aplicado (272 MB el nuestro), así que dos no caben. Movido a `/work` por
symlink, igual que qorpa, y la generación 6 entró. El layout de la imagen necesita revisión: 512 MB
no alcanzan para un mecanismo que guarda un árbol por generación, y el síntoma apunta al sitio
equivocado.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Tf9T4vGzsMoT7eS8YzMFn
2026-09-14 21:40:45 +00:00
SergioandClaude Opus 5 64d48e743e minga abierto a P2P (la ventana oficial), fastfetch al corpus, y el rehash por su ruta
**minga con su daemon escuchando.** Decisión del usuario: minga es la ventana oficial al mundo y git
queda como espejo de compatibilidad, así que el daemon no es opcional — es el punto de entrada. El
binario ya está sellado (`b3:121cf4a8…`, 22 M, static-pie) y ahora trae su `[[user]]` (uid 970, home
`/work/minga`: en la raíz no cabe y `/work` sobrevive a un re-`dd`) y su `[[service]]`:
`listen /ip4/0.0.0.0/tcp/4001` — la convención libp2p, libre en la caja (ocupados: 22022 admin,
2345 git-SSH, 3002 gitea en loopback, 80/443 caddy). Declarar ambos **no movió el ArtifactHash**.

⚠⚠ **La identidad no se sella ni se inventa.** El keypair lo genera el usuario con `minga init`;
tawasuyu avisó de que el suyo era provisorio. Si la card hiciera un `init` automático, la caja se
inventaría su identidad soberana en el primer arranque y todo lo que se firme después colgaría de
ella. La guarda comprueba que el repo exista y sale 78: un peer sin identidad decidida es peor que
un peer ausente.

**fastfetch** (2.68.1) entra al corpus y a `perfil.cli` — lo heredan servidor y los escritorios.
Receta CMake con `-DCMAKE_LINK_DEPENDS_USE_LINKER=OFF` (sin eso el lld de zig segfaultea y el error
no nombra a zig) y con la detección opcional APAGADA explícitamente: con `link = "static"` no hay
`dlopen`, y una detección que entra «porque la librería estaba en el lab ese día» produce artefactos
distintos bajo el mismo nombre.

Y `ca-certificates`: el rehash se llamaba por nombre y **`c_rehash` lo instala esta misma receta en
`/out/usr/bin`**, no está en el PATH del sandbox. Se invoca por su ruta. La guarda que añadí ayer
hizo su trabajo: el build falló ruidosamente en vez de sellar otra vez un capath sin índice.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Tf9T4vGzsMoT7eS8YzMFn
2026-09-14 21:34:42 +00:00
Sergio 2d8a0a72e9 estado: cosecha granja 2026-09-14T21:33:25Z — avance del árbol KDE 2026-09-14 21:33:25 +00:00
Sergio 981709d241 syntax-highlighting: arreglado el no-determinismo — era el generador de Jinja
Antes: dos reconstrucciones daban `libKF6SyntaxHighlighting.so` distintos —
8.800.310 bytes diferentes desde el offset 41, y hasta el tamaño cambiaba
(10.345.888 contra 10.345.840). Ahora: REPRODUCE.

Hash: 4c1638a0… → 3db73c19… (arrastra 4 dependientes directos, 5 transitivos).

── Dónde NO estaba, que también cuenta ────────────────────────────────────────────────
No era el indexer: `katehighlightingindexer` arma el índice con `QVariantMap`, que es
QMap y va ordenado. No eran los generadores de Perl: ninguno de los cuatro itera un
hash. Mirarlo antes ahorró parchear lo que no era.

── Dónde estaba: `data/generators/generate_jinja.py`, con tres dependencias del azar ──
1. `to_do.pop()` sobre un `set` saca un elemento ARBITRARIO. En `--dry-run` el orden de
   los `print(out_file)` es lo que CMake recoge en `out_xmls`, y eso acaba siendo **el
   orden de las entradas del `.qrc`** ⇒ el recurso compilado cambiaba de disposición
   entera. Se toma el menor: mismo conjunto, orden fijo.
2. `version = str(round(time.time()))` hornea la HORA DEL BUILD en el XML generado. Se
   honra `SOURCE_DATE_EPOCH`, que es la convención de reproducible-builds y que el
   sandbox de takana ya fija (=1).
3. `os.listdir()` sin ordenar. Sólo importa si dos ficheros declaran el mismo lenguaje,
   pero quitar la dependencia del readdir no cuesta nada.

── Medido en los dos sentidos ANTES de tocar la receta ────────────────────────────────
Corriendo el generador a mano, sin builds de por medio:

  · antes  — tres PYTHONHASHSEED distintos ⇒ TRES md5 distintos, y el orden salta a la
             vista: `jinja-json, jinja-yaml, jinja-toml…` contra
             `jinja-qml, jinja-dockerfile, jinja-typescript…`
  · después — las mismas tres semillas ⇒ el MISMO md5
  · generando de verdad con semillas Y momentos distintos ⇒ los 35 XML IDÉNTICOS,
    con `version="1"`
  · control negativo — sin `SOURCE_DATE_EPOCH` sigue poniendo la hora actual
    (comprobado: coincidía con `date +%s` al segundo) ⇒ fuera del sandbox no cambia nada

Primer aviso de esta medición: mi primera comparación dio «idéntico con las tres
semillas» y era MENTIRA — el script salía con «Destination folder does not exist» y yo
comparaba md5 de un mensaje de error. Comparar salidas sin mirar que la herramienta
hiciera algo es inventarse un control.
2026-09-14 21:15:00 +00:00
Sergio 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).
2026-09-14 21:11:18 +00:00
Sergio 42325dd1ca estado: cosecha granja 2026-09-14T21:02:09Z — avance del árbol KDE 2026-09-14 21:02:09 +00:00
Sergio 8b9cf35026 estado: 4 dependientes de la cascada verificados — reproducen al hash nuevo
ksvg, qqc2-desktop-style, kirigami-addons y libplasma, ya reconstruidos contra el
kirigami arreglado. REPRODUCEN 4 · DERIVA 0 · NO-DETERMINISMO 0.

No es adorno: los cuatro llevan QML, que es donde vivía la carrera del AOT. Que el
árbol reconstruido reproduzca es lo que cierra el arreglo — sellar de nuevo no
demuestra nada por sí solo.
2026-09-14 20:54:03 +00:00
Sergio c719fe211a estado: cascada de kirigami reconstruida — 39/39 al hash nuevo, cero fallos
El arreglo del no-determinismo de kirigami movió su ArtifactHash (41eea38e… →
a72b1626…) y con él el de todo lo que cuelga: 37 dependientes directos, 39
transitivos (entran `frameworkintegration` y `plasma5support` por vía indirecta).

Reconstruidas las 39 en orden topológico y con UN solo `flock -o` para toda la
tanda. Comprobado aparte del log: para las 39 existe `store/<hash-de-hoy>-<nombre>`.
Las gordas: kwin 3027s, plasma-workspace 1958s, okular 810s, kirigami-addons 816s.

⚠ A mitad de tanda, `kirigami-addons` y `kdf` murieron con «No space left on
device» — y es otra vez la trampa de siempre: NO eran recetas rotas. El disco de
`/mnt/vvv` estaba al 100%. Las dos reconstruyen bien con sitio libre.

Dos cosas se arreglaron a raíz de eso:
  · `poda-fuentes.sh` con su suelo de 24 h liberaba CERO, porque los 30 árboles
    eran todos de hoy — la lección de `cache-ci-no-envejece` otra vez: un guardián
    calibrado a una escala que el dato nunca alcanza no protege. Con `--horas 1`
    y luego `--horas 0` se recuperaron 14 G.
  · la tanda ahora poda el árbol de CADA receta al terminarla (sólo el suyo: las
    deps son compartidas, borrarlas en vuelo es el ADR 0012). Con eso
    `work/sources` se mantuvo en 54-71 M durante el resto de la cascada en vez de
    crecer sin freno.

El `never: 1` que aparece en el grafo KDE es `minga`, una receta nueva de otro
agente, sin perfil y ajena a esta cascada.
2026-09-14 20:41:34 +00:00
Sergio 5257726d64 estado: cosecha granja 2026-09-14T20:32:59Z — avance del árbol KDE 2026-09-14 20:33:00 +00:00
Sergio 920d276b45 estado: cosecha granja 2026-09-14T20:03:29Z — avance del árbol KDE 2026-09-14 20:03:29 +00:00
SergioandClaude Opus 5 69526768b7 CUTOVER: el gitea sirve desde la caja nueva — TLS válido, HTTPS y SSH, supervisado por arje
`git.gioser.net` y `git.tawasuyu.net` responden **200 con TLS válido desde 2.29.29.217**, `git clone`
funciona por HTTPS **y** por SSH:2345, y `gitea` + `caddy` corren supervisados por `arje-zero`. El
gitea de gioser está parado. Es el primer servicio real que deja la máquina que se va a borrar.

Mudanza INCREMENTAL: los DNS de gioser son casi todos `CNAME → www`, así que mover `www` habría
mudado quince dominios cuyos backends siguen allá. Se convirtió sólo `git` (las dos zonas) de CNAME
a A propio con TTL 60 — reversible en un minuto.

Cinco cosas que sólo se aprenden haciéndolo:

· **Parar el origen no es `rc-service gitea stop`**: dice «already stopped» con el proceso vivo, y
  matarlo no alcanza — lo revive arje-zero, que lo tiene como card `openrc-gitea` con Restart y
  **9001 reinicios** en el contador. Se paró con `arjectl stop openrc-gitea` (el arjectl que
  construimos hoy, hablando con el arje de gioser), y para eso hubo que extraer su card del genesis
  y escribirla en `cards.d` — que es justo el hueco del §6.12.
· **El token de `hcloud` también gestiona el DNS** (Cloud API unificada, `/v1/zones`); la API vieja
  `dns.hetzner.com/api/v1` redirige a la consola web. Y un `PUT` sobre el rrset no puede cambiar el
  TIPO: hay que DELETE del CNAME y POST del A.
· **ACME falló primero contra gioser** (502) porque el challenge salió antes de que propagara el
  DNS. Con el DNS al día, `arjectl restart caddy` → certificate obtained successfully.
· **La identidad SSH se muda con el servicio**: `REMOTE HOST IDENTIFICATION HAS CHANGED` hasta que
  se copiaron las claves de host de gioser a la caja. Así los clones existentes no notan nada; el
  precio es limpiar el known_hosts propio del `:22` de administración.
· El `sshd` del producto escucha sólo en `:22` ⇒ el git por SSH necesitó `Port 2345` y que el
  usuario `gitea` tenga shell real (su authorized_keys fuerza `command="gitea serv …"`).

`caddy` gana su `[[service]]` (con guarda del Caddyfile, HOME propio para los certificados de ACME
—o cada reinicio pediría certificados nuevos y se comería el límite de emisión— y la nota de por qué
corre como root) y el perfil lo arranca.

Consecuencia para la granja: las 23 recetas que clonan por `https://git.tawasuyu.net/…` **ya apuntan
a la caja**. Comprobado: el worker resuelve 2.29.29.217 y `git ls-remote` responde.

Revertir, si hiciera falta: `arjectl start openrc-gitea` en gioser y los dos `git` de vuelta a CNAME.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Tf9T4vGzsMoT7eS8YzMFn
2026-09-14 19:53:09 +00:00
Sergio e5fefa35d2 estado: cosecha granja 2026-09-14T19:33:51Z — avance del árbol KDE 2026-09-14 19:33:51 +00:00
Sergio 2238913a4e estado: cosecha granja 2026-09-14T19:03:35Z — avance del árbol KDE 2026-09-14 19:03:35 +00:00
Sergio 5b98d1b2bf atuq: no era el perfil — son DOS SALIDAS, y el «3 de 13» medía la cámara
Dos cosas antes de la medición: el MOZ_LOG de las que pintan y las que no termina IDÉNTICO (las dos
commitean WaylandBufferSHM de 1280x696), y el cruce de las 15 corridas está confundido — las 13 de
«perfil del usuario» salieron todas con VGA=1.

`screendump` de QMP fotografía UN dispositivo. Con VGA=1 hay dos, y hasta ahora se miraba uno. Con
`id=` en cada uno y capturando los dos en cada toma, en la misma corrida y el mismo instante:

    +312s  ██ PRIMERA PINTURA — 704456 px EN vga0
           gpu0 = 962675 px de fondo + panel y dock (el escritorio, SIN navegador)
           vga0 = 703766 px magenta                 (la ventana, con la página)

⇒ cosmic-comp maneja las DOS salidas y la ventana cae en una u otra. «Pinta 3 de 13» era cuántas
veces cayó en la pantalla que yo fotografiaba: una tasa de mi cámara, no del producto. El §6.10.ter
tiene la misma explicación (aquel arranque también traía `drm: card0 card1`).

Queda en pie, ya sin confundido: el navegador arranca, mapea y commitea SIEMPRE; cuando la ventana
está en la pantalla que se mira, se ve entre +184 y +312 s; el perfil no era la causa y cosmic-comp
nesteado tampoco.

⚠ Tercera vez en el día con la misma forma: un ✗ de una captura de UNA pantalla no dice «la ventana
no está», dice «no está en ESA» — igual que `mapped 1` no probaba que se viera. El estado anota ahora
en qué pantalla apareció y cuántas se fotografiaron, y el vigía NO cuenta las ciegas: las nombra.
2026-09-14 18:57:00 +00:00
Sergio 26dd2233dc estado: cosecha granja 2026-09-14T18:33:41Z — avance del árbol KDE 2026-09-14 18:33:41 +00:00
Sergio 49b2842ddb atuq: la TASA — pinta 3 de 13 con el perfil del usuario, y cuando pinta siempre a ~200 s
Diez corridas idénticas con `scripts/cosmic/tasa-primera-pintura.sh` (--as-user, VGA=1, ventana 480 s,
captura cada 30 s), más las cinco anteriores, todas anotadas en docs/state/primera-pintura.json:

    perfil del usuario     3 de 13 pintaron   184, 199, 204 s
    perfil nuevo en tmpfs  2 de 2             197, 197 s

Dos cosas que la tasa dice y una anécdota no podía: cuando pinta, pinta SIEMPRE en la misma ventana
(184…204 s) y nunca a los 300, 400 ni 900 ⇒ no es una cola larga, son DOS REGÍMENES —o sale a los
~200 s o no sale—; y con el perfil del usuario falla ~3 de cada 4, mientras que con perfil nuevo en
tmpfs no falló (2 de 2, pocas corridas para afirmar que nunca, suficientes para saber dónde mirar:
qué hace el primer arranque del perfil que el perfil ya hecho no hace).

⚠ Condición de la medida, parte del número: las diez salieron con el anfitrión a load ~10 (tanda de
KDE de otro agente + una VM de otra sesión, en 4 cores). El número es un PISO, no una constante.

⚠ Y una trampa del arnés medida en las corridas 8–10: con esa carga el guest tarda más de 420 s en
llegar al shell y los `esperar` vencen. No las invalida —se verificó una por una que el navegador se
lanzó y dejó su MOZ_LOG de 1200–5000 líneas— pero una corrida abortada de verdad se ve casi igual:
por eso el guion cuenta las abortadas APARTE en vez de sumarlas a los fallos.
2026-09-14 18:27:29 +00:00
Sergio 4456b65829 estado: cosecha granja 2026-09-14T18:04:26Z — avance del árbol KDE 2026-09-14 18:04:26 +00:00
SergioandClaude Opus 5 fd09ca77b0 arjectl en la imagen: relanzar en caliente — y el genesis NO alcanzaba para eso
El servicio que falla y agota su backoff sólo se podía recuperar reiniciando la máquina entera.
Ya no. Y no hubo que escribir nada: **el cliente existía en tawasuyu y el crate se llama `arje-ctl`**
(el binario, `arjectl`). Buscarlo por `arjectl` no lo encontraba, y de ahí salió mi conclusión falsa
de que había que implementarlo — el protocolo ya traía ListEntes, SpawnCardFromDisk,
StopCardFromDisk, KillEnte y EnteStatus.

`recipes/arjectl.toml` lo construye del MISMO commit que `arje-zero` (98db584f), y no por comodidad:
el bus es un protocolo entre dos binarios y un cliente de otro árbol puede conectar sin entenderse
con el init. Publica sólo `arjectl`; el crate también produce un `systemctl` de camuflaje que acá no
se instala — en una distro sin systemd, ese nombre en el PATH invita a escribir runbooks con el
verbo ajeno.

⚠ EL HUECO QUE SÓLO SE VE USÁNDOLO: el genesis de la seed dice qué arranca AL BOOT, pero
`start`/`restart` usan `SpawnCardFromDisk`, que lee `/etc/arje/cards.d/<label>.json` — y el armado
no lo escribía:

    $ arjectl start gitea
    Error: arje-zero rechazó: card gitea: No such file or directory
           (buscada en /etc/arje/cards.d/gitea.json)

Son dos preguntas distintas —qué arranca solo, y qué se puede encarnar a pedido— y arje las responde
desde sitios distintos. `inyectar-cards.py` escribe ahora los dos árboles, y en `cards.d` escribe
TODAS las cards, no sólo las nuevas: `sshd` viene del product-rootfs y tampoco era relanzable.

Medido con la VM arrancada UNA sola vez: la imagen trae `cards.d/{gitea,sshd}.json` · `list-units`
da PID/CPU/MEM/HILOS/reinicios · poner el `app.ini` + `arjectl start gitea` ⇒ **GET / 200 sin
reiniciar** (uptime 6 min) · `arjectl restart gitea` cambia el PID (118 → 192) y sigue en 200.

⚠ Y un aviso que costó un HTTP intermitente: **`arjectl start` sobre un Ente YA VIVO lo DUPLICA** —
`SpawnCardFromDisk` no deduplica por label y arje le da un ULID nuevo. En gitea el síntoma fue
`unable to lock level db … resource temporarily unavailable` y un `[F]`: dos servidores peleando por
el mismo estado. Para relanzar se usa `restart`, o se mira `list-units` antes. Un `start` idempotente
es trabajo de arje, no de esta imagen.

⚠ Deuda anotada, no barrida: `arjectl` va en `perfil.servidor` porque este frente es el que lo pagó.
TODA imagen de takana corre arje-zero como PID 1 y ninguna se puede operar sin él ⇒ el argumento
para subirlo a `base` es fuerte, y es una línea. Se deja como decisión.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Tf9T4vGzsMoT7eS8YzMFn
2026-09-14 17:59:11 +00:00
Sergio 8054119429 estado: cosecha granja 2026-09-14T17:34:29Z — avance del árbol KDE 2026-09-14 17:34:30 +00:00
Sergio 446c209fd2 estado: cosecha granja 2026-09-14T17:04:07Z — avance del árbol KDE 2026-09-14 17:04:08 +00:00