gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1572Shell 203Rust 109Markdown 993728 Definitions

⎇ main docs/adr/0010-arranque-grafo-mirada.md Markdown · 161 lines · 10.6 KB History raw

renombre: los instaladores pasan a `takana-*` — y el peligro no era el fichero, era el PROTOCOLO 008dd3925 · Sergio ·

1# ADR 0010 — Arranque por grafo: el menú de boot como navegación del grafo de estados (mirada + takana)
2
3**Estado:** aceptado; lado takana implementado (pasos 1–5 ✅). Instalador live EFI validado 2-stage en
4OVMF ✅; cero-parpadeo validado por captura de framebuffer ✅ (salvo el *seamless* i915, metal-only).
5Falta metal real y el lado mirada. **Fecha:** 2026-07-06 (impl. EFI-stub + cero-parpadeo 2026-07-10).
6**Contexto cruzado:** takana (arranque/kernel/generaciones) + tawasuyu/mirada (render). El
7espejo de este ADR para el lado mirada vive en `tawasuyu/HANDOFF-arranque-grafo.md`.
8
9## Contexto
10
11takana quiere instalarse en metal real con un arranque **soberano**: nada de `systemd-boot`
12(sigue siendo systemd) y GRUB-EFI está roto en este stack (GRUB 2.14 cuelga la rama UEFI, ver
13[etapa-metal-usb]). El compositor **mirada** (tawasuyu, `02_ruway/mirada`) ya es "lo que ves al
14arrancar": compositor Wayland + greeter + launcher sobre DRM/KMS. La idea del usuario: que el
15**menú de arranque sea de mirada**, no de un bootloader ajeno — "sólo falta ponerle su menú tipo
16GRUB".
17
18Al mirarlo, el menú no es un port de GRUB. En un Linux normal el menú es una **lista** de kernels.
19En takana, donde el sistema es un **grafo content-addressed** de estados (SDD 15: config = paquete =
20función = proceso), el menú puede ser la **navegación de ese grafo**: elegir un nodo = activar un
21estado del sistema. Eso cierra el eslabón **proceso** que SDD 15 §H4 dejó abierto (las tres primeras
22lentes ya coinciden en el hash; faltaba el lado proceso = replay de un log monótono de estados).
23
24## Decisión
25
26El menú de arranque es un **selector gráfico sobre el grafo de estados del sistema**, renderizado por
27**mirada** sobre KMS, respaldado por el modelo de generaciones/DAG de **takana**. La frontera entre
28los dos agentes es un **contrato de datos** (takana describe el grafo; mirada lo dibuja y pide activar
29un nodo). Invariante de UX compartido: **cero parpadeo** de DRM desde la firmware hasta el escritorio.
30
31### Quién reside sobre qué
32
33| Capa | Reside en | Notas |
34|---|---|---|
35| Cargador EFI + entrada en el disco instalado | **takana** | install-to-disk EFI; ver "loader" abajo. |
36| Kernel con KMS sin costura (simpledrm→i915) | **takana** | `linux-generic.toml` ya trae `SIMPLEDRM+SYSFB_SIMPLEFB+FB_EFI+I915+FBDEV_EMULATION`. |
37| El grafo de estados (nodos que el menú ofrece) + activar nodo | **takana** | generaciones (takana-upgrade/recover), overlay, DAG de wawa-memo. |
38| Render + navegación del menú sobre KMS | **tawasuyu/mirada** | reusa Prezi espacial / Zoom-Z (árbol fractal) / constelaciones. |
39| Sostener el DRM master sin parpadeo menú→escritorio | **mirada** (con el kernel de takana) | un solo framebuffer, abierto una vez, nunca re-modeset. |
40
41### El contrato (la frontera limpia)
42
43**takana → mirada** — takana escribe el grafo en un path bien conocido (propuesta:
44`/run/hammer/boot-graph.json`, regenerable, world-readable):
45
46```json
47{
48  "version": 1,
49  "current": "<blake3-del-nodo-vivo>",
50  "default": "<blake3-a-arrancar-si-no-hay-elección>",
51  "nodes": [
52    {
53      "id": "<blake3>",
54      "label": "texto corto para el usuario",
55      "kind": "generation | snapshot | base | recovery",
56      "created": "2026-07-06T12:00:00Z",
57      "parents": ["<blake3>", "..."],
58      "bootable": true,
59      "summary": "qué cambió respecto del padre (opcional)"
60    }
61  ]
62}
63```
64
65**mirada → takana** — mirada pide activar el nodo elegido (propuesta: escribir el id en
66`/run/hammer/boot-select` **o** invocar `takana boot activate <id>`). takana valida el id contra el
67grafo, activa la generación (pivota overlay/root) y sigue el arranque. Un nodo `recovery` mapea al
68`hammer-recover --rollback` que ya existe.
69
70> El grafo es content-addressed: cada `id` es un hash, `parents` forma un DAG (no una lista lineal).
71> El modelo in-place actual de takana (`hammer-recover`: "no hay menú de generaciones tipo NixOS")
72> se **sube** a un grafo navegable — es trabajo nuevo de takana, no un refactor cosmético.
73
74### Cero parpadeo (invariante compartido)
75
76Un solo modo de video desde la firmware hasta el compositor:
77
781. Firmware GOP framebuffer → **`SYSFB_SIMPLEFB`** toma ese mismo FB → **i915 hace *seamless
79   takeover*** sin cambio de modo. (El kernel ya tiene los tres bits `=y`.)
802. Sin texto de fbcon ni cursor durante el arranque, sin getty en el VT gráfico. **Diagnóstico
81   preciso (2026-07-10, evidencia por captura de framebuffer en OVMF-GOP, `scripts/`):** hoy el kernel
82   metal pinta TODO el log de boot sobre el framebuffer gráfico (simpledrm "switching to colour frame
83   buffer device 160x50" + decenas de líneas). Causa: `CONFIG_VT_CONSOLE=y` registra el VT como consola
84   automáticamente (quitar `console=tty0` NO alcanza — probado), y `quiet loglevel=0` por LoadOptions NO
85   lo frena (fbcon redibuja el ring buffer al tomar el FB a ~0.6s; además el `ignore_loglevel earlyprintk`
86   HORNEADO en `CONFIG_CMDLINE` fuerza la salida). **Fix (requiere rebuild del kernel metal, cambia el
87   hash firmado):** (a) `CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y` — fbcon no toca el FB hasta el
88   primer output ⇒ queda negro; (b) cmdline horneada → quitar `earlyprintk=efi,keep ignore_loglevel`,
89   añadir `quiet loglevel=3 vt.global_cursor_default=0`; (c) `CONFIG_LOGO` ya está off ✓. GOTCHA de
90   validación: con `quiet` los marcadores serie kernel-side desaparecen ⇒ los harness EFI deben apoyarse
91   en los marcadores userspace tee'ados a `/dev/ttyS0` (que sobreviven `quiet`). El *seamless* i915 (1)
92   sólo se valida en metal Intel real (OVMF no trae i915).
933. **mirada abre el DRM master directo** y lo sostiene desde el menú hasta el escritorio — nunca
94   suelta, nunca re-modeset.
95
96### El loader (cómo se lanza el kernel sin systemd-boot ni GRUB)
97
98El cuelgue del EFI-stub directo en el metal del usuario era por el **initrd de 100MB** (medición TPM
99+ quirk de LoadOptions del firmware). `linux-generic.toml` ya trae **squashfs+overlay** ⇒ el
100initramfs se vuelve **minúsculo** (sólo monta el squashfs). Con initrd chico, **EFI-stub directo
101vuelve a ser viable y soberano** — sin GRUB ni systemd. Es decir: squashfs+overlay no es un desvío,
102es lo que *destraba* el arranque EFI soberano y de paso el cero-parpadeo (initrd chico = menos tiempo
103muerto antes del takeover).
104
105## Plan (lado takana)
106
1071. **Este ADR + el handoff a tawasuyu** con el contrato (hecho en este commit).
1082. **initrd chico → EFI-stub directo reprobado en OVMF** ✅ (2026-07-10): el disco instalado NO usa un
109   rootfs en RAM sino un **initramfs mínimo de pivote** (~770K) que resuelve `takana-root` por LABEL y
110   hace `switch_root` a la ext4 real. Ese initrd chico es lo que destraba EFI-stub directo (el cuelgue
111   del metal era por un initrd de 100MB). Validado en OVMF por `scripts/efi-disk-boot-test.sh`. (La vía
112   squashfs+overlay del [post-etapa-e] queda como optimización futura; el pivote a ext4 ya cumple.)
1133. **Contrato de generaciones-grafo** ✅ (módulo `takana_upgrade::boot_graph` + subcomando `takana
114   boot`): el modelo in-place de generaciones se lee como un **grafo de estados** navegable (id =
115   `of_tree`, DAG por `parent`), `takana boot graph` emite `/run/hammer/boot-graph.json` con el
116   formato del contrato, y `takana boot activate <id>` (o `--from-select`) deja el sistema en un nodo
117   (no-op si ya vivo · rollback paso a paso a un ancestro · nodo `recovery` sintético · error honesto
118   ante un "redo" hacia una generación huérfana). Validado e2e por el CLI. **mirada ya puede maquetar
119   contra el grafo real.**
1204. **Afinar kernel/cmdline para el cero-parpadeo** ✅ (2026-07-10): `linux-metal` con
121   `CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y` + cmdline horneada `quiet loglevel=3
122   vt.global_cursor_default=0` (sin `earlyprintk/ignore_loglevel`) + ttyS0 último (la shell de arje-zero
123   sale por serial, no pinta tty0). **Validado por captura de framebuffer en OVMF-GOP**
124   (`scripts/efi-flicker-test.sh`): el kernel NO toca el FB (Δ media de brillo = 0.000 entre handoff y
125   post-boot; el viejo saltaba ~3→~11) ⇒ el contenido de la firmware sobrevive intacto hasta que mirada
126   abra el DRM. Los harness EFI pasan a marcadores userspace tee'ados a `/dev/ttyS0` (sobreviven `quiet`).
127   El *seamless* i915 (firmware GOP → simplefb → i915 sin re-modeset) sólo se valida en metal Intel real.
1285. **install-to-disk EFI** ✅ (2026-07-10): el bzImage `linux-metal` ES el binario EFI
129   (`\EFI\BOOT\BOOTX64.EFI`, ruta fallback removible, sin GRUB ni systemd-boot). Como no hay LoadOptions
130   en esa ruta, el `CONFIG_CMDLINE` horneado (`… initrd=/initramfs.cpio.gz rdinit=/init`) activa el
131   pivote. Dos caminos, mismos artefactos:
132   - **host-side** `scripts/install-image-efi.sh` — GPT+ESP+root/store/state, ESP con mtools de takana,
133     ext4 con `mke2fs -d` bajo `unshare -r`; **validado VERDE en OVMF** (`scripts/efi-disk-boot-test.sh`:
134     firmware → BOOTX64.EFI → pivote → arje-zero PID1).
135   - **live-side** `scripts/takana-live-install.sh` rama EFI — detecta `/sys/firmware/efi` y hace
136     **MBR con ESP tipo 0xEF** (UEFI arranca una ESP MBR igual que GPT ⇒ alcanza busybox
137     fdisk/mkfs.vfat/mount, sin GPT-tool ni mtools en el live). El kernel EFI-stub viaja en el payload
138     (`iso-image.sh INSTALLER=1` bundlea `bzImage-efi`). **Validación 2-stage VERDE en OVMF**
139     (`scripts/efi-install-test.sh`): ISO live UEFI → `takana-install` detecta `/sys/firmware/efi` y toma
140     la rama EFI → el disco instalado bootea SOLO por EFI-stub soberano (sin GRUB) → arje-zero PID1.
141     Falta sólo el metal real (quemar USB + bootear, Secure Boot OFF).
142
143## Consecuencias
144
145- La frontera es un **fichero + un comando**, no una API compartida ⇒ los dos agentes avanzan sin
146  bloquearse. takana puede emitir el grafo y probar EFI-stub sin esperar a mirada; mirada puede
147  maquetar el menú contra un `boot-graph.json` de ejemplo sin esperar a takana.
148- El menú-grafo es la primera **manifestación del lado proceso** de SDD 15 §H4 — arrancar = activar
149  un nodo del DAG. No colapsa todo el modelo, pero lo hace visible.
150- No mueve la frontera de confianza (ADR 0007/0009): activar un nodo es reproducir/hidratar un árbol
151  sellado del store, no ejecutar algo nuevo.
152
153## Referencias
154
155- SDD 15 §H3/§H4 (`docs/15-frontier-ai-native.md`) — config = paquete = función = proceso; el lado
156  proceso = replay del MonotonicLog.
157- ADR 0007 (arje-zero como init propio), ADR 0009 (código content-addressed).
158- Memoria de proyecto: etapa-metal-usb (GRUB 2.14 roto, EFI-stub colgó por initrd 100MB, metal
159  booteado vía systemd-boot como crutch descartable), post-etapa-e-refinamientos (squashfs+overlay,
160  rollback-al-boot).
161- `tawasuyu/HANDOFF-arranque-grafo.md` — el espejo, lado mirada.

0010-arranque-grafo-mirada.md

ADR 0010 — Arranque por grafo: el menú de boot como navegación del grafo de estados (mirada + takana)

Estado: aceptado; lado takana implementado (pasos 1–5 ✅). Instalador live EFI validado 2-stage en OVMF ✅; cero-parpadeo validado por captura de framebuffer ✅ (salvo el seamless i915, metal-only). Falta metal real y el lado mirada. Fecha: 2026-07-06 (impl. EFI-stub + cero-parpadeo 2026-07-10). Contexto cruzado: takana (arranque/kernel/generaciones) + tawasuyu/mirada (render). El espejo de este ADR para el lado mirada vive en tawasuyu/HANDOFF-arranque-grafo.md.

Contexto

takana quiere instalarse en metal real con un arranque soberano: nada de systemd-boot (sigue siendo systemd) y GRUB-EFI está roto en este stack (GRUB 2.14 cuelga la rama UEFI, ver [etapa-metal-usb]). El compositor mirada (tawasuyu, 02_ruway/mirada) ya es "lo que ves al arrancar": compositor Wayland + greeter + launcher sobre DRM/KMS. La idea del usuario: que el menú de arranque sea de mirada, no de un bootloader ajeno — "sólo falta ponerle su menú tipo GRUB".

Al mirarlo, el menú no es un port de GRUB. En un Linux normal el menú es una lista de kernels. En takana, donde el sistema es un grafo content-addressed de estados (SDD 15: config = paquete = función = proceso), el menú puede ser la navegación de ese grafo: elegir un nodo = activar un estado del sistema. Eso cierra el eslabón proceso que SDD 15 §H4 dejó abierto (las tres primeras lentes ya coinciden en el hash; faltaba el lado proceso = replay de un log monótono de estados).

Decisión

El menú de arranque es un selector gráfico sobre el grafo de estados del sistema, renderizado por mirada sobre KMS, respaldado por el modelo de generaciones/DAG de takana. La frontera entre los dos agentes es un contrato de datos (takana describe el grafo; mirada lo dibuja y pide activar un nodo). Invariante de UX compartido: cero parpadeo de DRM desde la firmware hasta el escritorio.

Quién reside sobre qué

CapaReside enNotas
Cargador EFI + entrada en el disco instaladotakanainstall-to-disk EFI; ver "loader" abajo.
Kernel con KMS sin costura (simpledrm→i915)takanalinux-generic.toml ya trae SIMPLEDRM+SYSFB_SIMPLEFB+FB_EFI+I915+FBDEV_EMULATION.
El grafo de estados (nodos que el menú ofrece) + activar nodotakanageneraciones (takana-upgrade/recover), overlay, DAG de wawa-memo.
Render + navegación del menú sobre KMStawasuyu/miradareusa Prezi espacial / Zoom-Z (árbol fractal) / constelaciones.
Sostener el DRM master sin parpadeo menú→escritoriomirada (con el kernel de takana)un solo framebuffer, abierto una vez, nunca re-modeset.

El contrato (la frontera limpia)

takana → mirada — takana escribe el grafo en un path bien conocido (propuesta: /run/hammer/boot-graph.json, regenerable, world-readable):

{
  "version": 1,
  "current": "<blake3-del-nodo-vivo>",
  "default": "<blake3-a-arrancar-si-no-hay-elección>",
  "nodes": [
    {
      "id": "<blake3>",
      "label": "texto corto para el usuario",
      "kind": "generation | snapshot | base | recovery",
      "created": "2026-07-06T12:00:00Z",
      "parents": ["<blake3>", "..."],
      "bootable": true,
      "summary": "qué cambió respecto del padre (opcional)"
    }
  ]
}

mirada → takana — mirada pide activar el nodo elegido (propuesta: escribir el id en /run/hammer/boot-select o invocar takana boot activate <id>). takana valida el id contra el grafo, activa la generación (pivota overlay/root) y sigue el arranque. Un nodo recovery mapea al hammer-recover --rollback que ya existe.

El grafo es content-addressed: cada id es un hash, parents forma un DAG (no una lista lineal). El modelo in-place actual de takana (hammer-recover: "no hay menú de generaciones tipo NixOS") se sube a un grafo navegable — es trabajo nuevo de takana, no un refactor cosmético.

Cero parpadeo (invariante compartido)

Un solo modo de video desde la firmware hasta el compositor:

  1. Firmware GOP framebuffer → SYSFB_SIMPLEFB toma ese mismo FB → i915 hace seamless takeover sin cambio de modo. (El kernel ya tiene los tres bits =y.)
  2. Sin texto de fbcon ni cursor durante el arranque, sin getty en el VT gráfico. Diagnóstico preciso (2026-07-10, evidencia por captura de framebuffer en OVMF-GOP, scripts/): hoy el kernel metal pinta TODO el log de boot sobre el framebuffer gráfico (simpledrm "switching to colour frame buffer device 160x50" + decenas de líneas). Causa: CONFIG_VT_CONSOLE=y registra el VT como consola automáticamente (quitar console=tty0 NO alcanza — probado), y quiet loglevel=0 por LoadOptions NO lo frena (fbcon redibuja el ring buffer al tomar el FB a ~0.6s; además el ignore_loglevel earlyprintk HORNEADO en CONFIG_CMDLINE fuerza la salida). Fix (requiere rebuild del kernel metal, cambia el hash firmado): (a) CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y — fbcon no toca el FB hasta el primer output ⇒ queda negro; (b) cmdline horneada → quitar earlyprintk=efi,keep ignore_loglevel, añadir quiet loglevel=3 vt.global_cursor_default=0; (c) CONFIG_LOGO ya está off ✓. GOTCHA de validación: con quiet los marcadores serie kernel-side desaparecen ⇒ los harness EFI deben apoyarse en los marcadores userspace tee'ados a /dev/ttyS0 (que sobreviven quiet). El seamless i915 (1) sólo se valida en metal Intel real (OVMF no trae i915).
  3. mirada abre el DRM master directo y lo sostiene desde el menú hasta el escritorio — nunca suelta, nunca re-modeset.

El loader (cómo se lanza el kernel sin systemd-boot ni GRUB)

El cuelgue del EFI-stub directo en el metal del usuario era por el initrd de 100MB (medición TPM

Plan (lado takana)

  1. Este ADR + el handoff a tawasuyu con el contrato (hecho en este commit).
  2. initrd chico → EFI-stub directo reprobado en OVMF ✅ (2026-07-10): el disco instalado NO usa un rootfs en RAM sino un initramfs mínimo de pivote (~770K) que resuelve takana-root por LABEL y hace switch_root a la ext4 real. Ese initrd chico es lo que destraba EFI-stub directo (el cuelgue del metal era por un initrd de 100MB). Validado en OVMF por scripts/efi-disk-boot-test.sh. (La vía squashfs+overlay del [post-etapa-e] queda como optimización futura; el pivote a ext4 ya cumple.)
  3. Contrato de generaciones-grafo ✅ (módulo takana_upgrade::boot_graph + subcomando takana boot): el modelo in-place de generaciones se lee como un grafo de estados navegable (id = of_tree, DAG por parent), takana boot graph emite /run/hammer/boot-graph.json con el formato del contrato, y takana boot activate <id> (o --from-select) deja el sistema en un nodo (no-op si ya vivo · rollback paso a paso a un ancestro · nodo recovery sintético · error honesto ante un "redo" hacia una generación huérfana). Validado e2e por el CLI. mirada ya puede maquetar contra el grafo real.
  4. Afinar kernel/cmdline para el cero-parpadeo ✅ (2026-07-10): linux-metal con CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y + cmdline horneada quiet loglevel=3 vt.global_cursor_default=0 (sin earlyprintk/ignore_loglevel) + ttyS0 último (la shell de arje-zero sale por serial, no pinta tty0). Validado por captura de framebuffer en OVMF-GOP (scripts/efi-flicker-test.sh): el kernel NO toca el FB (Δ media de brillo = 0.000 entre handoff y post-boot; el viejo saltaba ~3→~11) ⇒ el contenido de la firmware sobrevive intacto hasta que mirada abra el DRM. Los harness EFI pasan a marcadores userspace tee'ados a /dev/ttyS0 (sobreviven quiet). El seamless i915 (firmware GOP → simplefb → i915 sin re-modeset) sólo se valida en metal Intel real.
  5. install-to-disk EFI ✅ (2026-07-10): el bzImage linux-metal ES el binario EFI (\EFI\BOOT\BOOTX64.EFI, ruta fallback removible, sin GRUB ni systemd-boot). Como no hay LoadOptions en esa ruta, el CONFIG_CMDLINE horneado (… initrd=/initramfs.cpio.gz rdinit=/init) activa el pivote. Dos caminos, mismos artefactos:
    • host-side scripts/install-image-efi.sh — GPT+ESP+root/store/state, ESP con mtools de takana, ext4 con mke2fs -d bajo unshare -r; validado VERDE en OVMF (scripts/efi-disk-boot-test.sh: firmware → BOOTX64.EFI → pivote → arje-zero PID1).
    • live-side scripts/takana-live-install.sh rama EFI — detecta /sys/firmware/efi y hace MBR con ESP tipo 0xEF (UEFI arranca una ESP MBR igual que GPT ⇒ alcanza busybox fdisk/mkfs.vfat/mount, sin GPT-tool ni mtools en el live). El kernel EFI-stub viaja en el payload (iso-image.sh INSTALLER=1 bundlea bzImage-efi). Validación 2-stage VERDE en OVMF (scripts/efi-install-test.sh): ISO live UEFI → takana-install detecta /sys/firmware/efi y toma la rama EFI → el disco instalado bootea SOLO por EFI-stub soberano (sin GRUB) → arje-zero PID1. Falta sólo el metal real (quemar USB + bootear, Secure Boot OFF).

Consecuencias

Referencias