Commit Graph
10 Commits
Author SHA1 Message Date
SergioandClaude Opus 5 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>
2026-09-17 15:19:59 +00:00
SergioandClaude Opus 5 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>
2026-09-17 15:19:22 +00:00
SergioandClaude Opus 5 294959c1da arranque y GC: las dos rutas EFI, guardián de ESP y el kernel vivo como raíz
Primera tanda de los ADR 0017/0018, con las mediciones que la corrigieron.

ADR 0018 §3 — install-image-efi.sh y takana-live-install.sh escriben el
kernel en \EFI\takana\takanax64.efi ADEMAS de la ruta fallback, con un
guardián de capacidad que comprueba ANTES y con los números a la vista
(duplicar el kernel cuesta, y el costo se dice). En el live-install la
verificación es por TAMAÑO, no por presencia: un cp truncado en FAT32
deja el fichero ahí y test -e diría que todo salió bien.

Verificado con imagen real en OVMF: las dos copias dan el mismo sha256
que el bzImage del store, y la imagen arranca hasta arje-zero PID1.
CONTROL NEGATIVO: pisando la fallback con 4K de basura el firmware dice
"No bootable option or device was found" y no aparece HAMMER-EFI ni una
vez ⇒ el escenario de Windows, reproducido.

Y lo que NO se pudo verificar cambia el alcance, así que va en el ADR:
sin entrada NVRAM el firmware NO busca el vendor path. La copia vendor
es hoy un seguro que no se cobra solo — el §3 es necesario y NO
suficiente sin el §1. Eso asciende la receta efibootmgr a bloqueante.

ADR 0017 §4 — store-gc.sh suma como raíz el kernel EN EJECUCIÓN,
identificado por .config byte a byte. Sin esto el artefacto del kernel
vivo cae en "superados" cuando la receta se movió, y se borra: el
sistema sigue andando perfecto hasta el día que hace falta volver atrás.
Probado en los tres sentidos, incluido el control que TIENE que seguir
condenado.

Además, dos bugs preexistentes que aparecieron al ir a medir:

- install-image-efi.sh abortaba con "ROOTFS sin /sbin/init" en TODO
  rootfs sano: /sbin/init es un symlink ABSOLUTO (→/usr/bin/arje-zero) y
  [ -e ] lo sigue contra la raíz del HOST. Un chequeo que validaba algo
  distinto de lo que creía validar. Arreglado resolviendo el destino
  dentro del rootfs.
- (no arreglado, es del entorno) el cp -al del staging da EXDEV si
  ROOTFS y STAGE no están en el MISMO MOUNT — y el bind-mount del store
  cuenta como otro mount aunque sea el mismo /dev/sdb.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHAKWoBqof9XvsYCvDicEj
2026-09-11 20:40:34 +00:00
Sergio 476168bb07 takana etapa 5c: comentarios de scripts, MOTD, y un BUG que introdujo la etapa 4
250 líneas de comentario en 151 scripts. Control verificado: el diff no toca
NI UNA línea que no empiece por #, y la sintaxis de los 151 pasa.

El barrido saltea heredocs y cadenas triples, y el guardián DISPARÓ 3 veces:
las tres eran el MOTD que el script escribe DENTRO de la imagen construida —
texto del producto, no comentario del script. Se cambiaron aparte y a
propósito, que es rebranding, no limpieza.

Y el hallazgo caro:  casaba contra ,
que es el TARGET de tracing — o sea el module_path!, o sea el nombre del crate.
La etapa 4 lo movió a  y el script quedó casando NADA. No fallaba:
imprimía cero atribuciones, indistinguible de un log sin problemas. Comprobado
con el binario (RUST_LOG=info sobre zlib), no deducido. Ahora acepta las dos, y
tiene que seguir aceptándolas porque los logs viejos en disco dicen la vieja.

Además 14 rutas de módulo  en docs, que el barrido anterior no tocó
porque  no es frontera de palabra.
2026-09-09 19:28:48 +00:00
Sergio 1c3e185167 takana: las variables de entorno leen las dos formas, gana la nueva
TAKANA_X con caída a HAMMER_X (ADR 0016). No es un sed: estas variables son
contrato de usuario —knobs del instalador, el entorno del worker, mirror-env.sh—
y viven en perfiles de shell y units FUERA del repo, así que un renombre duro no
falla ruidosamente: la variable no aparece, se toma el default y el build se
comporta distinto sin que nada lo diga.

Rust: takana_core::env::{var,var_os} toma el nombre canónico y deriva el viejo
cambiando el prefijo — se le pasa TAKANA_* para que un grep del nombre nuevo
encuentre todas las lecturas. 5 tests, con DOS controles negativos: sin ninguna
de las dos no hay valor, y un nombre sin prefijo no inventa una caída.
Migrados los 17 sitios directos y los indirectos que el grep no mostraba
(bases_de_mirror, las constantes de kernel_cmd, ROOT_ENV de qorpa, env_path de
recover). takana-recover lleva la caída inline: es un mini-binario que se copia
a /usr/sbin y no vale arrastrarle una dep entera por dos líneas.

Scripts: 30 lecturas pasan a default, manteniendo el
nombre INTERNO de la variable para no tocar sus 190 usos.

Y donde el script EXPORTA en vez de leer, se ponen LAS DOS (mirror-env.sh y el
fragmento in-VM de bootstrap): ahí el lector puede ser un binario viejo —worker
sin recompilar, el /usr/bin/hammer pinado del baseline— que sólo conoce HAMMER_*.
La caída sirve para lectores nuevos; los viejos necesitan que la vieja siga puesta.

Verificado de punta a punta con el binario, no sólo con unit tests: HAMMER_LAB
sigue surtiendo efecto, TAKANA_LAB hace lo mismo, y con las dos gana TAKANA_LAB.
605 tests en verde y el hash de zlib sigue en b3:dc363f26… , intacto.

Cambio de comportamiento que va aparte y hay que decir: el hostname por defecto
de una instalación nueva pasa de 'hammer' a 'takana' (sólo si no se fija ninguna
de las dos variables).

NO se tocan: las rutas /var/lib/hammer de sistemas instalados, el volid
HAMMER_LIVE del ISO, ni el namespace HARKAQ_*, que es de otro subsistema.
2026-09-09 19:14:49 +00:00
Sergio 8730aad34e takana etapa 3b: las 49 invocaciones pasan a ./target/release/takana
Los llamadores EJECUTABLES: scripts/ (incluida toda la granja), los runbooks y
CLAUDE.md. Seguro porque la 3a ya garantiza que el worker emite los dos
binarios, y porque en farm-lab-sync.sh el cargo build remoto precede a la
invocación remota en el mismo script.

Verificado: sintaxis de los 49 (bash -n / py_compile — ojo que
why-differs-barrido.sh es Python con extensión .sh) y `takana hash` devuelve
hash real sobre el store.

NO se toca en esta etapa, a propósito:
- La variable de entorno HAMMER=. Es interfaz de los scripts entre sí y hay
  llamadores que la fijan; renombrarla va con la etapa 4.
- docs/evidencia/ y los HANDOFF: son REGISTRO de lo que se corrió ese día.
  Reescribir un comando dentro de una evidencia la falsifica.
- docs/state/: es generado, se regenera solo.
- Los ADR y los docs de diseño: texto, y `hammer` sigue funcionando. Van con
  la etapa 5, que es la de churn de texto.
2026-09-09 18:25:58 +00:00
SergioandClaude Opus 5 86c3cc787e store-gc: la poda ocurrio y su registro NO se escribio
Hoy `--aplicar` borro los 256 superados y acto seguido murio con

    scripts/store-gc.sh: line 143: work/store-gc-superados.txt: No such file or directory

Esa combinacion —borrado hecho, ledger sin escribir— es exactamente la que
reabre el bucle churn que 2026-08-07 costo 362 artefactos y 24 G: `farm-sync`
usa ese fichero como --exclude-from, y sin el la proxima cosecha del worker nos
devuelve lo mismo que acabamos de borrar. El sintoma seria «dos podas con el
mismo numero», que ya sabemos leer como señal.

El manifiesto de la linea 109 SI se escribio, y la diferencia entre los dos es
que aquel hace `mkdir -p work` antes. Ahora el ledger usa ruta ABSOLUTA
($ROOT/work), hace mkdir+touch, y si aun asi no puede escribirse el script
FALLA RUIDOSAMENTE diciendo que los borrados van a volver y como reconstruir el
registro a mano — en vez de informar exito como hasta ahora.

El ledger de esta tanda queda reconstruido desde su manifiesto (256 entradas).

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

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

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

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

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

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

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

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

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

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 07:44:54 -04:00
sergioandClaude Opus 5 28dc473aca 🧹 store-gc: el recolector que hammer gc no tiene — 702 artefactos, 34G
El disco llegó a 100% y el gordo no estaba en `work/`: el store guarda un artefacto
por cada SELLADO, no uno por receta. Cuando una dep cambia, el ArtifactHash de todo
lo que cuelga cambia y el sellado nuevo SE SUMA al viejo. Medido: 1996 artefactos
para 1130 recetas — 12 copias de libqalculate, 9 de qt6-qtdeclarative, 8 de gtk4.
51G de 74G eran versiones anteriores.

El conjunto VIVO es `hammer hash` sobre todas las recetas de todas las colas: la
respuesta a "¿cuál es el hash vigente HOY?", que el store solo no puede dar. Lo que
no está ahí es rancio — pero rancio son DOS cosas muy distintas, y confundirlas es
la diferencia entre podar y perder:

  SUPERADO — su nombre TIENE un artefacto vigente presente. Versión anterior de algo
             ya sellado al día. Se reconstruye desde la receta, que está en git.
  HUÉRFANO — ningún artefacto con ese nombre es el vigente. El hash de hoy NO está
             sellado y éste es el ÚNICO ejemplar. Borrarlo sí pierde.

Por eso el default borra sólo los superados; `--huerfanos` hay que quererlo aparte.
Aplicado: 702 superados = 34G, de 5,4G libres a 43G. La verificación de que el
criterio era correcto no fue el `df`: fue que `hydrate-cosmic.sh` siguió dando 83/83
recetas y 0 faltantes.

Los 255 huérfanos (17G) quedan INTACTOS y son un hallazgo aparte: casi todos KDE
(kio×8, kparts×8, kcmutils×8), o sea que las recetas KDE se movieron después del
último sellado y el escritorio hidratado vive de artefactos que ya no son vigentes.

Dos avisos que el script lleva escritos porque cuestan al descubrirlos solos: el
espacio NO siempre se libera (los rootfs de work/ son hardlinks al store — borrar el
dir no rompe nada pero tampoco libera hasta que el rootfs se vaya), y `--store` por
defecto apunta a /store, no a ./store.

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