645 líneas. Los ADR entran porque en este repo SON documentos vivos, no registros inmutables: el 0013 tiene 5 commits, el 0009 dos. Eso se comprobó antes de decidir, no se asumió por convención general. EXCLUIDOS por ser REGISTRO o generado: docs/evidencia/ (6), el HANDOFF de la noche de KDE (1) y docs/state/ (24, se regenera solo). Reescribir un comando dentro de una evidencia la falsifica. Y el ADR 0016 se excluye de todo barrido, con un aviso adentro para el próximo que barra: habla SOBRE el renombre, así que necesita seguir diciendo 'hammer'. El barrido se lo llevó puesto y lo dejó titulado 'Renombre del sistema: takana → takana'; revertido. Congelados, verificados uno por uno con controles: /opt/hammer, /var/lib/hammer, /usr/bin/hammer, /mnt/vvv/hammer, la URL de gitea, hammer-farm.service, hammer-live-install.sh, BRIEFING-hammer.md, hammerd, hammer-recover y HAMMER_LIVE.
13 KiB
Runbook — el armador de kernel (takana kernel)
Implementa docs/22-configurador-kernel.md (SDD 22), que contesta
tawasuyu/HANDOFF-KERNEL-CONFIG-A-HAMMER.md.
La regla que no se cruza: takana lee el grafo de Kconfig, no lo resuelve. El .config lo
sigue produciendo el olddefconfig del propio kernel. Lo que la app emite son fragmentos
(scripts/config -e/-d) y una receta derivada.
El hecho que ordena todo: las fases de build entran en Recipe::hash_inputs y el config vive en
la fase configure ⇒ el config ES la identidad del artefacto. Cambiar un símbolo cambia el
ArtifactHash. Por eso una perilla de UI no es un parámetro de runtime: es una edición de receta.
0. Preparar el árbol de fuentes
Los comandos necesitan un árbol de fuentes del kernel, del que sólo se leen los Kconfig* y los
Makefile*. No hace falta extraer el tarball entero (1,5 G): con estos dos juegos bastan 38 M.
curl -sSLo work/tarballs/linux-6.16.12.tar.gz \
https://mirrors.edge.kernel.org/pub/linux/kernel/v6.x/linux-6.16.12.tar.gz
mkdir -p work/kconfig-6.16.12
tar -xzf work/tarballs/linux-6.16.12.tar.gz -C work/kconfig-6.16.12 --strip-components=1 \
--wildcards '*/Kconfig*' '*/Makefile' '*/Makefile.*' 'linux-6.16.12/arch/x86/configs/*'
export TAKANA_KCONFIG_ROOT=work/kconfig-6.16.12
work/ está en .gitignore. El sha256 del tarball es el mismo que pinea recipes/linux.toml.
Control de salud del lector — si esto no da 0 avisos, no confíes en lo que sigue:
takana kernel stats
# ficheros 1646 · símbolos 18212 (15169 visibles) · select 15165 · imply 445 · avisos 0
1. Modo reversa — mirar el kernel que ya corre
Cero build, cero riesgo. Lee /proc/config.gz (o /boot/config-<release>).
takana kernel probe
Contesta tres cosas: cuánto de cada bundle ya rige, qué capacidad carga este kernel que esta máquina no usa, y dónde el hardware contradice a un bundle aplicado. La detección va en un solo sentido: sirve para contradecir, nunca para podar sola — no se puede detectar el dock que se enchufa el mes que viene ni el fs del USB de rescate.
Si el config vivo y el catálogo son de series distintas, lo avisa: las clausuras salen aproximadas.
2. El catálogo
docs/state/kernel-bundles.toml. Un bundle no es una lista de símbolos: es una raíz y su
clausura. disable = ["WIRELESS"] son ~400 CONFIG_* que takana calcula.
takana kernel bundles # el catálogo con las clausuras resueltas
takana kernel bundles --check # falla si envejeció respecto de este árbol
--check es el control de frescura y hace dos cosas:
- Símbolos idos: un
-dsobre un símbolo que ya no existe es un no-op silencioso. Así se descubrió que las cuatro recetas de kernel del repo apagabanTHUNDERBOLT(hoyUSB4) yREISERFS_FS(retirado), o sea que no apagaban nada. Corregido en2602218. - Fugas
selectsin declarar: cadaselectnuevo que entra a un bundle es un símbolo que upstream agregó y nadie revisó. Es la mitad barata de la curación del delta entre versiones, y sale de comparar el grafo con el catálogo — sin IA.
Para decidir qué hacer con una fuga:
takana kernel closure WIRELESS --fixpoint
El punto fijo reporta el precio, no lo aplica. Cerrar «sin audio» exige apagar
DRM_I915/NOUVEAU/AMD_DC, que hacen select del códec HDMI: en un escritorio eso es una
decisión, no una limpieza. Se resuelve una vez, con close_leaks o accept_leaks.
3. Planear
takana kernel plan --recipe recipes/linux.toml \
--bundle sin-wifi --bundle sin-audio --bundle solo-ext4 \
--knob jaula-y-eio-moderna \
--out work/kernel-plans/plan.json \
--recipe-out work/kernel-plans/linux-derivada.toml
Emite raíces, no clausuras: 16 banderas, no 1775 líneas. Y lleva el ArtifactHash de la receta
derivada dentro del JSON, así que la UI puede decir «esto ya está construido y firmado» sin
construir.
⚠ Para construir la receta derivada hay que dejarla junto a la receta base, o sus deps.build
no resuelven. plan no escribe en recipes/ por su cuenta: ese directorio es el corpus compartido.
⚠ Y al construirla, takana build va SIEMPRE bajo el lock compartido:
flock work/.farm-build.lock ./target/release/takana --store ./store build recipes/linux-derivada.toml
takana build comparte work/sources/<dep>-<sha> entre todas las recetas. Dos builds simultáneos
que compartan una dependencia se pisan: uno hace fetch y borra el árbol mientras el otro lo usa, y
el árbol queda roto para siempre — reintentar no lo arregla. Medido a escala: al invalidar
libdrm, ~93 de 205 recetas KDE murieron con «/src/.zwrap/cc is not a full path to an existing
compiler tool», que es el wrapper de zig que la receta deja en el árbol de fuente, barrido por el
fetch concurrente de otra. Es el ADR 0012, todavía sin decidir.
Es el mismo fichero de lock que toman scripts/farm/farm-worker-loop.sh y campana-deuda.sh, así
que con esto quedan serializados los tres. Deliberadamente no está dentro de takana build: los
scripts de la granja ya lo toman por fuera y takana se bloquearía contra ellos.
Nada más del armador toca el store: plan calcula el ArtifactHash con hammer_build::artifact_hash,
que es cómputo puro sobre las recetas, y probe/gate/bundles/closure sólo leen.
Si el build muere con «no encuentro el ejecutable zig en …», el zig está bien: falta ESA versión.
takana lo busca por directorio versionado (.dev-fs/tools/zig-x86_64-linux-<ver>/), no por el
symlink tools/zig ni por el PATH. linux.toml no pinea zig_version, pero tres de sus
deps.build sí — flex, openssl y elfutils, las tres a 0.13.0 — y la receta derivada las
hereda enteras.
4. El gate de no-regresión — por objetivo
La regla es «todo dispositivo en uso debe seguir teniendo driver». Aplicada global rechazaría
recipes/linux.toml, que apaga USB, HID e INPUT a propósito por ser el kernel de QEMU con
consola serie. Por eso el gate no corre sin --objective.
En la máquina destino (que no tiene por qué ser la de build):
takana kernel hw --out work/kernel-plans/hw.json
En la de build:
takana kernel gate --plan work/kernel-plans/plan.json \
--objective qemu-serial --devices work/kernel-plans/hw.json
El mismo plan pasa en qemu-serial y bloquea en metal-escritorio. Ése es todo el punto.
El gate mapea driver → símbolo leyendo las reglas obj-$(CONFIG_X) += y.o de los Makefiles (15 789
reglas en 3182 ficheros). Lo que no pueda mapear lo lista como sin comprobar y no lo cuenta
como aprobado: un portón que calla lo que no miró no es un portón. Los que suelen quedar fuera son
built-ins de núcleo (pcieport, serial8250) cuyo nombre de driver no coincide con el del módulo.
4.bis Probar un plan SIN construir (30 s en vez de 40 min)
Lo caro es compile, no configure. La cadena entera del armador vive en configure, así que se
puede validar un plan completo sin pagar un build:
tar -xzf work/tarballs/linux-6.16.12.tar.gz -C work/kernel-test # el árbol ENTERO, 1,7 G
cd work/kernel-test/linux-6.16.12
python3 -c "import json;print(json.load(open('../../kernel-plans/plan.json'))['configure'])" > /tmp/cfg.sh
bash /tmp/cfg.sh # defconfig + fragmento + olddefconfig
takana kernel diff-back --plan ../../kernel-plans/plan.json --config .config
Necesita gcc, make, flex, bison y perl en el host (bc sólo hace falta para compilar).
⚠ Para medir la clausura contra la verdad de campo, la base tiene que ser defconfig pelado, no
el config de linux.toml: esa receta ya apaga wifi/audio/fs a mano, así que el lado disable del
plan no apagaría nada y la predicción no se pondría a prueba. Es el error que cometí la primera vez.
4.ter Caso resuelto: el kernel a medida de gioser
docs/state/kernel-plans/ guarda el plan y la receta derivada del primer kernel que salió del
armador de punta a punta. Reproducirlo:
takana kernel plan --recipe recipes/linux-metal.toml \
--bundle sin-wifi --bundle sin-bluetooth --bundle sin-nfc --bundle sin-radios \
--bundle sin-audio --bundle sin-camaras-tv --bundle sin-infiniband --bundle sin-bus-industrial \
--bundle sin-thunderbolt-firewire --bundle sin-lector-de-tarjetas --bundle sin-virtualizacion \
--bundle sin-gpu-intel --bundle solo-ext4 \
--knob invitado-virtio --knob plataforma-pc --knob jaula-y-eio-moderna \
--out plan.json --recipe-out linux-gioser.toml
Resultado: bzImage de 11,1 MB contra los 14,5 MB de linux-metal (−23%), 1647 símbolos
encendidos contra 1805, diff-back limpio y gate en verde.
⚠ La receta derivada NO se deja en recipes/. build-state.py y yupana barren recipes/ y
recipes/incoming-*/, así que dejarla ahí la mete en el grafo compartido como deuda. Para
construir, copiala a recipes/incoming-kernel/ temporalmente (sus deps.build resuelven contra
el catálogo padre) y sacala después. El artefacto sellado sobrevive; la receta se regenera con el
comando de arriba.
⚠ La copia guardada ya no lleva el -j1 de la primera vuelta: al pagar SDD 25 H1 (MEMCG+PSI)
hubo que regenerarla contra la base nueva, y ese re-hasheo obligado fue el momento barato para
soltar la concesión y volver al -j"$(nproc)" que emite plan — capar el paralelismo en la receta
cambia el ArtifactHash aunque el binario sea idéntico (SDD 22 §13), y el throttling va por
entorno. Historia de los hashes: b3:8ffd5710… era la variante -j1 sin MEMCG; la vigente es
b3:b026fe14….
⚠ Regenerar la derivada NO alcanza para que el MEMCG de la base llegue acá: la receta derivada
inlinea la fase configure de su base. Cambiar recipes/linux-metal.toml y no regenerar deja
la derivada con el configure viejo, y el gate lo ve (el sellado sale como vigente sin la
capacidad).
5. Diff-back — después de construir
Sin esto la UI miente: un -e FOO cuya dependencia no se cumple se pierde en silencio.
takana kernel diff-back --plan work/kernel-plans/plan.json \
--config <artefacto>/boot/config-6.16.12
Clasifica cada símbolo pedido en cumplido / incumplido (el .config dice otra cosa) / ausente
(el kernel ni lo menciona: la bandera fue un no-op), con la procedencia de quién lo pidió. Sale
distinto de cero si el config no honra el plan.
6. El contrato de capacidades — antes de dar un kernel por bueno
diff-back contesta «¿el .config honra el PLAN?». Falta la otra mitad: «¿el kernel trae lo que
el userland usa por nombre?». Son preguntas distintas — un kernel puede honrar su plan al pie de
la letra y no tener CONFIG_MEMCG, que nunca estuvo en ningún plan porque nadie lo pidió (SDD 25
§4: por eso memory.max no existe y arje escribe al vacío dejando sólo un warn!).
takana --store ./store kernel contract --sealed # todos los kernels sellados del store
takana kernel contract --config <art>/boot/config-7.1.2 # uno suelto (el perfil sale del nombre)
takana kernel contract --profile anfitrion-cards # el kernel VIVO de esta máquina
takana kernel contract --list # qué promete takana, y quién lo consume
Sale distinto de cero si falta una capacidad exigida por el perfil. Tres cosas que conviene saber antes de leer su salida:
- Corre por perfil.
docs/state/kernel-contract.tomlmapea artefacto → perfil.linuxesqemu-serial(no hospeda Cards, y su hash es el baseline del selfhost-verify); los demás sonanfitrion-cards. Un artefacto sin fila sale SIN COMPROBAR, que no es lo mismo que aprobado. - Se mira contra el
.configproducido, nunca contra la receta: entre elscripts/config -e Xy el.configestáolddefconfig. - Hoy sale rojo a propósito: 2 de 11 configs sellados cumplen. Es H1 de SDD 25 sin pagar
(
-e MEMCG -e PSIre-hashea los kernels ⇒ va en tanda). Cuando se pague, este comando es la prueba.
Una capacidad nueva entra al contrato con consumidor —ruta y símbolo— o no entra. «Estaría bueno tenerlo» pertenece a un bundle del catálogo, no acá.
Lo que todavía NO está
- Sonda de arranque en VM (#4 del handoff): construir → bootear en QEMU con el perfil de la máquina destino → recién ahí dar de alta en el árbol de generaciones.
- Atestación por huella (#3):
takana kernel hwya da la huella y el plan ya da elArtifactHash; falta el ejefingerprintenPackageEntryy el quórum deminga atestar. Y ojo: «booteó» no basta para atestar — la atestación tiene que llevar qué se comprobó, que es el mismo dato que calcula el gate. - Bisección (#5): un kernel completo tarda 35–60 min ⇒ la bisección ingenua son ~4,5 h. La salida propuesta (SDD 22 §4) es un kernel «superset» con lo bisecable como módulo: 6 reinicios en vez de 6 rebuilds.
- Curación del delta con modelo (#2): hoy
--checkmarca lo no clasificado; falta que alguien proponga a qué bundle va cada símbolo nuevo. - Aplicar las perillas
side = "recipe": hoy se declaran y no se aplican.