gioser

tawasuyu/brahman

Rust 582TOML 222Markdown 73JSON 198635 Definitions

⎇ main renaser/CHANGELOG.md Markdown · 1310 lines · 70.3 KB History raw

feat(renaser): Fase 20 — Akasha Over Ether (grafo distribuido) 42fee6fcb · sergio ·

1# Registro de cambios — renaser
2
3Registro técnico detallado, fase a fase, en orden cronológico. Formato
4inspirado en *Keep a Changelog*. Para la crónica en lenguaje llano, ver
5`DIARIO.md`; para el estado y los planes, `ROADMAP.md`.
6
7---
8
9## Fase 1 — El primer microsegundo — 2026-05-21
10
11### Infraestructura
12- Espacio de trabajo Cargo, `kernel/Cargo.toml`, `.cargo/config.toml`,
13  `rust-toolchain.toml`.
14- Target inicial: especificación JSON propia `x86_64-renaser.json` — PIC,
15  soft-float, sin SSE, enlazador `lld`.
16
17### Añadido
18- `kernel/src/main.rs`: punto de entrada con el macro `entry_point!` de
19  `bootloader_api` 0.11.
20- Adopción y verificación con seguridad de tipos del framebuffer GOP (ancho,
21  alto, formato de píxel, *stride*).
22- Lienzo intermedio estático de 8 MiB en `.bss`, alineado a página: la técnica
23  de doble búfer para evitar parpadeos.
24- `#[panic_handler]` que dibuja una franja roja directamente sobre el
25  framebuffer mediante escrituras volátiles.
26- Dependencias: `bootloader_api` 0.11, `x86_64` 0.15, `embedded-graphics` 0.8.
27
28### Notas
29- Corrección clave: `bootloader` (constructor de imagen, lado anfitrión) no es
30  `bootloader_api` (la API `no_std` que consume el kernel).
31
32---
33
34## Fase 1.5 — Empaquetado y arranque — 2026-05-21
35
36### Cambiado
37- Migración del target del kernel: de la JSON propia `x86_64-renaser.json` al
38  target nativo precompilado `x86_64-unknown-none`. Elimina `build-std` y la
39  burocracia de compilador asociada.
40
41### Añadido
42- Miembro `boot/`: orquestador de anfitrión. Dependencia de artefacto
43  (RFC 3028) sobre el kernel; construcción de la imagen de disco UEFI con
44  `bootloader::UefiBoot`; lanzador de QEMU.
45
46### Toolchain
47- Instalación de la toolchain nightly (la máquina solo tenía la estable).
48
49### Corregido
50- Los nightly recientes exigen `-Zjson-target-spec` para usar specs JSON
51  (resuelto al migrar al target nativo).
52- Sintaxis del acelerador de QEMU: `-machine q35,accel=kvm:tcg`.
53
54### Verificado
55- Arranque en QEMU: superficie índigo limpia a 1280×800.
56- Baliza de pánico (franja roja) confirmada inyectando un pánico de prueba.
57
58---
59
60## Fase 2.0 — Cimientos del manejo de fallos — 2026-05-21
61
62### Añadido
63- `kernel/src/gdt.rs`: GDT propia, TSS y un stack de emergencia (IST)
64  reservado para el manejador de doble fallo.
65- `kernel/src/interrupts.rs`: IDT con manejadores de excepción de CPU. El
66  breakpoint (#BP) es recuperable; #UD, #DE, #GP, #PF y #DF son fatales y
67  encienden la baliza.
68- `#![feature(abi_x86_interrupt)]`.
69
70### Corregido
71- La GDT debe recargar los registros SS/DS/ES con un segmento de datos del
72  kernel: el cargador deja `SS = 0x10`, valor que en la GDT nueva pasa a ser el
73  descriptor del TSS; el primer `iretq` de una rutina de excepción provocaba un
74  #GP. Diagnosticado con la traza `-d int` de QEMU.
75
76### Verificado
77- Excepción `int3` atrapada y superada; línea-latido teal dibujada como prueba.
78
79---
80
81## Fase 2.1 — Interrupciones de hardware — 2026-05-21
82
83### Añadido
84- `kernel/src/pic.rs`: remapeo del par 8259 (PIC) fuera del rango de las
85  excepciones (vectores 0x20+); programación del temporizador PIT a 100 Hz;
86  IRQ1 de teclado.
87- Bucle de render en `kernel_main`, despertado por el temporizador, en
88  sustitución del `hlt` estático.
89
90### Verificado
91- La línea-latido se anima (temporizador en marcha); las pulsaciones de teclado
92  inyectadas cambian el cuadro-eco (teclado en marcha).
93
94---
95
96## Fase 3 — Memoria dinámica y reactor asíncrono — 2026-05-21
97
98### Añadido
99- `kernel/src/memory/`: `linked_list_allocator` como `#[global_allocator]`
100  sobre una región estática en `.bss`.
101- `#[alloc_error_handler]`: franja naranja de agotamiento de memoria.
102  `#![feature(alloc_error_handler)]`.
103- `kernel/src/async_system/`: reactor cooperativo — `Executor`, `Task`/`TaskId`,
104  `Waker` (vía `alloc::task::Wake`) y `ScancodeStream`.
105- `kernel/src/texto.rs`: rasterización de tipografía vectorial con `fontdue`;
106  TTF Adwaita Mono empotrada con `include_bytes!`.
107- Dependencias: `linked_list_allocator` 0.10, `spin` 0.9, `crossbeam-queue`
108  0.3, `futures-util` 0.3, `fontdue` 0.9.
109
110### Cambiado
111- Heap ampliado de 16 MiB a 64 MiB: `fontdue`, al analizar una tipografía real,
112  agotaba los 16 MiB iniciales. El `#[alloc_error_handler]` lo delató pintando
113  la franja naranja — el guardarraíl funcionó en silicio real.
114
115### Corregido
116- El modo `no_std` de `fontdue` está condicionado a su feature `hashbrown`; sin
117  ella recae en `std`. Se fija `default-features = false, features = ["hashbrown"]`.
118
119### Verificado
120- Rótulo de bienvenida rasterizado al arranque; el texto tecleado aparece en vivo.
121
122---
123
124## Fase 4 — El escudo de aislamiento WASM — 2026-05-21
125
126### Añadido
127- `kernel/src/wasm/`: intérprete `wasmi` 1.0.9 en modo `no_std`. `env.rs`
128  define la matriz de capacidades — exactamente dos funciones de host:
129  `sys_render_frame(ptr, len)`, con validación infranqueable de los límites de
130  la memoria lineal, y `sys_get_scancode()`.
131- `apps/hello_wasm/`: primera aplicación del userspace — módulo
132  `wasm32-unknown-unknown` (cdylib), un cuadrado móvil dirigido por teclado. Se
133  empotra en el kernel con `include_bytes!`.
134- Dependencia: `wasmi` 1.0.
135
136### Cambiado
137- El kernel se EXCLUYE del espacio de trabajo (`exclude = ["kernel", "apps"]`):
138  al añadir `wasmi`, `cargo build` intentaba compilar el kernel para el
139  anfitrión y fallaba por un símbolo `_start` duplicado. El kernel pasa a
140  construirse únicamente como dependencia de artefacto de `boot`.
141
142### Corregido
143- `wasmi` `no_std` necesita `default-features = false, features = ["hash-collections"]`.
144- `wasmi` 1.0.9 usa `Linker::instantiate_and_start` (no existe `instantiate`).
145
146### Verificado
147- La aplicación WASM pinta su propia superficie y responde a las pulsaciones de
148  teclado inyectadas, sin más vía hacia el kernel que las dos capacidades.
149
150---
151
152## Mantenimiento — Estructura, documentación e integración — 2026-05-21
153
154### Cambiado
155- Refactorización: `main.rs` dividido de 692 a ~155 líneas. Se extraen los
156  módulos `sync.rs`, `grafico.rs`, `consola.rs` y `baliza.rs`. Sin cambios de
157  comportamiento (verificado en QEMU).
158
159### Añadido
160- Documentación: `CLAUDE.md`, `README.md`, `ARCHITECTURE.md`, `ROADMAP.md`,
161  `CHANGELOG.md` y `DIARIO.md`.
162- Integración con git: repositorio inicializado, remoto `origin` en Gitea,
163  `.gitignore`.
164
165### Seguridad
166- `renaser.txt` —un borrador de trabajo que contenía una credencial— se subió
167  por error en el commit inicial. Se purgó de todo el historial con
168  `git filter-branch` y se reescribió el remoto con `push --force`. La
169  credencial expuesta debe rotarse.
170
171---
172
173## Fase 5 — Multitarea cooperativa, guardarrail de fuel y reloj — 2026-05-22
174
175Unificación de la Fase 3 (reactor) y la Fase 4 (WASM): el userspace deja de ser
176una sola app que monopoliza la CPU y pasa a ser un conjunto de aplicaciones
177cooperativas, aisladas también en el TIEMPO.
178
179### Añadido
180- `kernel/src/async_system/reloj.rs`: convierte la IRQ0 (PIT, 100 Hz) en una
181  primitiva asíncrona. `CONTADOR_PULSOS` (`AtomicU64`) y `EsperaFrame`, un
182  `Future` que se resuelve en el siguiente pulso — la unidad de cesión
183  cooperativa del userspace. Censo de wakers tras `Mutex`, drenado por la IRQ0.
184- `kernel/src/wasm`: ABI de fotograma. `AplicacionWasm` —instancia PERSISTENTE
185  entre fotogramas (`Store` + `TypedFunc<(), ()>` + región)— sustituye al
186  `wasm::ejecutar` de fuego-y-olvido. El módulo del userspace exporta ahora
187  `init()` (una vez) y `tick()` (un fotograma, y retorna).
188- Escudo de combustible: `Engine` con `Config::consume_fuel(true)` y
189  `CompilationMode::Eager` (el `fuel` mide solo ejecución). Presupuesto recargado
190  antes de cada `tick` — `FUEL_ARRANQUE` (20 M) y `FUEL_FOTOGRAMA` (2 M).
191- `kernel/src/wasm/env.rs`: capacidad `sys_render_frame` con **regiones de
192  dibujo** — cada fotograma se compone desplazado por el `(offset_x, offset_y)`
193  de la app; un tamaño ajeno a la región se rechaza. Canal de teclado **por
194  aplicación**: la IRQ1 difunde cada scancode a TODOS los canales, de modo que
195  varias apps reciben la entrada en paralelo sin robársela.
196- `kernel/src/grafico.rs`: `RegionPantalla` y `Color::DESALOJO` (púrpura).
197- `apps/discola/`: aplicación WASM construida para portarse mal — su `tick` es
198  un bucle cerrado. Demuestra el guardarrail de fuel en vivo.
199
200### Cambiado
201- `apps/hello_wasm`: migrada al ABI `init`/`tick`; su lienzo se dimensiona a la
202  región (480×560). El estado del cuadrado persiste en su memoria lineal.
203- `interrupts.rs`: la IRQ0 avanza el `reloj`; la IRQ1 difunde a los canales.
204- `consola.rs`: `volcar_marco` compone en una sub-región; `pintar_desalojo`
205  tatúa la baliza púrpura.
206- Contención de fallos: el subsistema WASM ya no usa `.expect()`. Toda falla
207  —carga, instanciación, desbordamiento, agotamiento de fuel— se devuelve como
208  `Result`; la tarea desaloja la app (baliza púrpura) y el kernel sigue vivo.
209
210### Verificado
211- QEMU: tres apps concurrentes. Dos instancias de `hello_wasm` (mismo bytecode,
212  regiones izquierda y derecha) renderizan y responden a W/A/S/D **en paralelo**,
213  con movimiento idéntico. La app díscola es desalojada en su primer fotograma
214  por agotamiento de combustible: su región queda púrpura y el sistema —kernel y
215  apps vecinas— no sufre un solo sobresalto.
216
217---
218
219## Fase 6.0 — Cuotas de memoria y ciclo de vida del userspace — 2026-05-22
220
221El aislamiento de las aplicaciones, que la Fase 5 hizo temporal (combustible),
222se completa ahora en la dimensión espacial (memoria) y se cierra la fuga de
223recursos del ciclo de vida.
224
225### Añadido
226- Techo de memoria lineal por aplicación: `4 MiB`. `wasm/mod.rs` construye un
227  `StoreLimits` (`StoreLimitsBuilder::memory_size` + `trap_on_grow_failure`) y lo
228  liga al `Store` con `Store::limiter`. Un `memory.grow` que rebase la cuota se
229  convierte en trampa; el kernel la captura y desaloja la app.
230- `FallaApp::SinMemoria` y `Color::DESALOJO_MEMORIA` (amarillo pálido). El
231  desalojo distingue la causa por color: púrpura (tiempo/aborto), amarillo
232  (memoria). La clasificación es robusta — `Error::as_trap_code()` da un código
233  público y unívoco: `TrapCode::GrowthOperationLimited`.
234- `Drop` para `AplicacionWasm`: al morir una app desalojada, su canal de teclado
235  se da de baja del censo de difusión de la IRQ1 — cierra la fuga señalada en la
236  Fase 5. `async_system::teclado` se reorganiza en `crear_canal` /
237  `registrar_canal` / `cerrar_canal`; el canal se inscribe al FINAL de la carga,
238  de modo que una carga fallida no deja canales huérfanos.
239- `apps/glotona/`: aplicación WASM construida para devorar memoria — su `tick`
240  invoca `memory.grow` sin freno. Demuestra el guardarrail espacial en vivo.
241
242### Verificado
243- QEMU: cuatro apps concurrentes. Las dos `hello_wasm` renderizan y responden al
244  teclado en paralelo; la app díscola es desalojada por combustible (franja
245  púrpura) y la app glotona por cuota de memoria (franja amarilla), ambas en su
246  primer fotograma, sin que el kernel ni las apps honradas se inmuten.
247
248---
249
250## Fase 6.1a — Sonda PCI y disco de pruebas — 2026-05-22
251
252Primer paso de la estrategia incremental hacia el almacenamiento: derribar el
253muro del descubrimiento de hardware antes de diseñar nada encima.
254
255### Añadido
256- `kernel/src/drivers/`: nuevo subsistema de drivers. `pci.rs` enumera el bus
257  PCI por fuerza bruta mediante el mecanismo de configuración #1 —puertos de E/S
258  `0xCF8` (dirección) y `0xCFC` (datos)—; recorre buses y dispositivos leyendo
259  el Vendor/Device ID, y localiza el disco virtio-blk (vendor `0x1AF4`, device
260  `0x1001`/`0x1042`), devolviendo su ubicación y sus seis BARs.
261- `kernel_main`: tras fundar la consola, sondea el bus PCI y deja constancia
262  visual del hallazgo.
263
264### Cambiado
265- `boot/src/main.rs`: forja un disco de pruebas `target/disk.img` (fichero
266  disperso de 32 MiB; se respeta si ya existe) y lo adjunta a QEMU como
267  `virtio-blk-pci`. **Corrección de plataforma:** la máquina `q35` es x86_64 y
268  exige la transmisión PCI; `virtio-blk-device` (su gemelo MMIO) es de ARM.
269
270### Verificado
271- QEMU: el kernel enumera el bus y reporta en pantalla
272  `virtio-blk en bus 0 dev 3 :: BAR0 E/S 0x6000` — un dispositivo virtio-blk
273  transicional (device `0x1001`), con su BAR0 en espacio de E/S. El muro del
274  descubrimiento PCI queda derribado; las cuatro apps del userspace siguen
275  operando sin alteración.
276
277---
278
279## Fase 6.1b — HAL, DMA y lectura del sector 0 — 2026-05-22
280
281Segundo paso del sustrato de almacenamiento: el diálogo real con el disco. El
282kernel monta el dispositivo virtio-blk y lee su primer sector por DMA.
283
284### Añadido
285- Dependencia `virtio-drivers` 1.13 (`no_std`, `default-features = false`,
286  feature `alloc`).
287- `kernel/src/drivers/disco.rs`:
288  - **Asignador de marcos** «bump» — reparte páginas físicas de 4 KiB para el
289    DMA, tomadas de la mayor región de RAM libre que el cargador reporta. No
290    libera: suficiente para una sonda (la gestión fina llegará con el grafo).
291  - **`KernelHal`** — implementa el `trait Hal` de `virtio-drivers`. `dma_alloc`
292    entrega marcos físicos a cero; `mmio_phys_to_virt` traduce los BARs (el
293    cargador mapea ≥ 4 GiB de memoria física, que cubre todo MMIO de PCI);
294    `share`/`unshare` usan un buffer rebote para que cualquier región del kernel
295    pueda viajar al dispositivo.
296  - **`montar_y_leer_sector0`** — enumera el bus, habilita E/S + memoria +
297    bus-master, monta el `PciTransport` y el `VirtIOBlk`, y lee el sector 0 por
298    **sondeo** del *used ring* (sin depender aún de interrupciones).
299- `kernel/src/drivers/pci.rs`: reescrito como `CamPuertos`, la implementación de
300  `ConfigurationAccess` de `virtio-drivers` sobre los puertos `0xCF8`/`0xCFC`.
301- `kernel_main` captura `physical_memory_offset` y la región de RAM de
302  `BootInfo`, funda el subsistema de disco y reporta el resultado de la sonda.
303
304### Cambiado
305- `boot/src/main.rs`: al forjar el disco de pruebas graba una firma
306  (`renaser-6.1b`) en su sector 0 — el testigo del viaje de ida y vuelta.
307
308### Verificado
309- QEMU: el kernel reporta `virtio-blk :: bus 0 dev 3 :: 65536 sectores ::
310  s0=renaser-6.1b`. La firma grabada por el anfitrión se lee de vuelta intacta:
311  descubrimiento PCI, transporte, DMA y transferencia funcionan de punta a
312  punta. Las cuatro apps del userspace siguen operando sin alteración.
313
314---
315
316## Fase 6.1c — El grafo de objetos direccionado por contenido — 2026-05-22
317
318Tercer y último paso del sustrato de almacenamiento. El disco deja de ser un
319dispositivo que se sondea y pasa a ser una MEMORIA QUE PERDURA: un grafo
320dirigido acíclico de objetos direccionados por contenido — no un sistema de
321archivos plano POSIX.
322
323### Añadido
324- `kernel/src/almacen.rs` — el grafo de objetos:
325  - **Objeto** — una carga útil de bytes (`datos`) y una lista de aristas
326    (`hijos`: hashes de otros objetos). Las aristas hacen del almacén un DAG.
327  - **Direccionamiento por contenido** — la identidad de un objeto es el hash
328    BLAKE3 de su forma serializada. De ello se siguen dos propiedades que un
329    sistema de archivos jamás regala: INTEGRIDAD (el contenido leído se
330    rehashea y se verifica contra el hash pedido) y DEDUPLICACIÓN (contenido
331    idéntico produce el mismo hash; se almacena una sola vez).
332  - **Disco como log** — el sector 0 es el superbloque (magia `RENASGRF`,
333    versión, cursor del log y hash de la raíz); tras él se anexan los registros
334    de objetos, `[longitud u32 LE][payload postcard][relleno a cero]`. Un
335    índice en memoria (hash -> sector) se reconstruye al arrancar recorriendo
336    el log de cabo a rabo.
337  - API: `init` —monta el disco, lee o forja el superbloque, reconstruye el
338    índice—, `almacenar`, `recuperar`, `raiz` y `fijar_raiz`.
339- Cinco capacidades nuevas del host en `wasm/env.rs` — `sys_object_put`,
340  `sys_object_datos`, `sys_object_hijo`, `sys_object_raiz` y
341  `sys_object_fijar_raiz` —, con la misma validación infranqueable de límites
342  de la memoria lineal que `sys_render_frame`. Distinguen dos clases de fallo:
343  un puntero inválido ABORTA la app —es su culpa, se traduce en trampa—; un
344  fallo del almacenamiento le devuelve un código de error negativo —no lo es—.
345- `apps/cronista/` — la primera aplicación del userspace que escribe en el
346  almacenamiento PERSISTENTE. En cada arranque consulta la raíz del grafo,
347  graba un objeto nuevo —`datos`: el número de arranque; `hijos`: la raíz
348  anterior, el eslabón del DAG— y lo corona como raíz. Pinta una celda por
349  arranque registrado y un testigo de integridad que recorre la cadena entera.
350- Dependencias: `serde` 1 y `postcard` 1 (serialización binaria compacta, el
351  formato que viaja al disco) y `blake3` 1 (la función hash). Las tres `no_std`.
352
353### Cambiado
354- `kernel/src/drivers/disco.rs` reescrito para un sustrato PERMANENTE:
355  - El asignador de marcos «bump» de la Fase 6.1b cede el paso a uno de MAPA DE
356    BITS con liberación real. `dma_dealloc` y `unshare` devuelven los marcos a
357    la arena: un almacén vivo, con su trasiego incesante de DMA, ya no la agota.
358  - El `VirtIOBlk` deja de montarse y destruirse en cada llamada: se monta UNA
359    vez y queda tras un `Mutex` global. `leer_sectores` / `escribir_sectores`
360    exponen la E/S de bloques — el disco deja de ser de solo lectura. Se retira
361    `montar_y_leer_sector0`, la sonda de un solo uso de la Fase 6.1b.
362- `boot/src/main.rs`: el disco de pruebas pasa a ser el disco de objetos. Ya
363  no se le graba una firma — se forja virgen, a cero, y el kernel lo formatea
364  la primera vez que no halle el superbloque.
365- `main.rs`: `informar_disco` (la sonda de la Fase 6.1b) se sustituye por
366  `informar_almacen`, que funda el grafo. El userspace pasa de cuatro a cinco
367  apps; las regiones de discola y glotona se reajustan para alojar a cronista.
368
369### Notas
370- **blake3 forzado a escalar.** El target del kernel corre sin SSE; un camino
371  SIMD de blake3 activado por detección en tiempo de ejecución ejecutaría
372  instrucciones que la CPU, sin `CR4.OSFXSR`, rechazaría con un #UD. Se fija
373  blake3 con `pure` + los cuatro `no_*` (`sse2`, `sse41`, `avx2`, `avx512`):
374  implementación puramente escalar, sin SIMD ni ensamblador.
375
376### Verificado
377- QEMU, TRES arranques consecutivos sobre el mismo disco persistente: la app
378  cronista pinta 1, luego 2, luego 3 celdas — la cuenta de arranques sobrevive
379  a los reinicios porque vive en el grafo, en el disco, no en la RAM. El
380  testigo de integridad queda VERDE en los tres: el DAG entero se recorre, de
381  la raíz al primer eslabón, y su profundidad cuadra con la cuenta. El
382  superbloque en disco confirma la magia `RENASGRF`, versión 1, cursor 4 y la
383  raíz del tercer arranque; el registro del primer arranque guarda `datos = 1`
384  y cero hijos. Las otras cuatro apps siguen su curso sin alteración — las dos
385  `hello_wasm` renderizando, discola desalojada en púrpura, glotona en amarillo.
386
387---
388
389## Fase 6.2 — E/S de disco asíncrona por interrupción — 2026-05-22
390
391La Fase 6.1 hizo hablar al disco, pero por SONDEO: el procesador se quedaba en
392espera activa vigilando el *used ring* de virtio, incapaz de atender nada más.
393La 6.2 libera el planificador cooperativo — la E/S de bloques pasa a ser
394REACTIVA, guiada por la interrupción física del dispositivo.
395
396### Añadido
397- `EsperaDisco` (`drivers/disco.rs`): una transferencia de bloques expresada
398  como `Future` nativo. Posee sus buferes DMA —`BlkReq`, `BlkResp` y los datos,
399  en el heap para una dirección estable—; su `poll` envía la petición por la
400  API NO BLOQUEANTE de `virtio-drivers` (`read_blocks_nb`/`write_blocks_nb`),
401  consulta el *used ring* (`peek_used`) y, si la transferencia sigue en vuelo,
402  inscribe el waker y cede. `leer_bloques`/`escribir_bloques` lo construyen.
403- La IRQ del disco: `montar` descubre la línea de IRQ legada del dispositivo
404  (registro «Interrupt Line», offset 0x3C del espacio de configuración PCI),
405  registra un manejador en la IDT y abre la línea en el PIC.
406  `interrupts::irq_disco` → `disco::atender_irq` reconoce la interrupción en el
407  dispositivo —leer su registro ISR baja la línea INTx— y despierta, vía un
408  waker de ranura única, a la tarea que aguardaba el bloque.
409- `bloquear_en` — el puente para los contextos SÍNCRONOS (el arranque, las
410  capacidades WASM, que no pueden `.await`): lleva un `Future` de disco hasta
411  su final durmiendo la CPU con `hlt` —la despiertan la IRQ del disco o el
412  temporizador, como red de seguridad—; jamás en espera activa con el sistema
413  ya en marcha.
414- `pic::desenmascarar` / `pic::vector_irq`: abrir una línea concreta del par
415  8259 (con su cascada, si vive en el esclavo) y mapear línea → vector de IDT.
416- `pci::linea_irq`: leer el registro «Interrupt Line» de un dispositivo.
417- `tarea_sonda_disco` (`main.rs`): una tarea del reactor que lee el sector 0 de
418  forma asíncrona — la prueba viva de que la IRQ conduce la E/S sin detener a
419  las aplicaciones.
420
421### Cambiado
422- `leer_sectores`/`escribir_sectores` se reescriben sobre la maquinaria
423  asíncrona (`bloquear_en` + `EsperaDisco`). El `almacen` y las capacidades
424  `sys_object_*` NO cambian una línea: heredan la E/S por interrupción de
425  forma transparente — la espera de disco deja de quemar ciclos en sondeo.
426- El `VirtIOBlk` pide al dispositivo que emita interrupciones al completar cada
427  petición (`enable_interrupts`).
428
429### Decisiones de ingeniería
430- **PIC, no IOAPIC.** El kernel corre íntegramente sobre el par 8259 (`pic.rs`)
431  y la crate `x86_64` no ofrece abstracción de APIC. Las IRQ legadas de PCI de
432  la máquina `q35` se enrutan por el 8259: basta leer la línea que el firmware
433  UEFI ya asignó y abrirla. Migrar al IOAPIC habría exigido levantar LAPIC +
434  IOAPIC + parseo de tablas ACPI — un subsistema entero, desproporcionado para
435  el objetivo. El resultado funcional es idéntico: E/S conducida por la
436  interrupción física.
437- **Las capacidades WASM no `.await`-ean.** El intérprete `wasmi` ejecuta las
438  funciones de host de forma SÍNCRONA: no hay manera de suspender un módulo a
439  mitad de una llamada de host. Por eso `sys_object_*` no se vuelven
440  asíncronas; usan `bloquear_en`, que duerme la CPU con `hlt` en lugar de
441  sondear. El verdadero solapamiento E/S ↔ render lo aprovechan las TAREAS del
442  reactor (`tarea_sonda_disco` hoy; la carga dinámica de módulos, mañana).
443- Una IRQ legada de PCI es de NIVEL: el manejador reconoce primero al
444  dispositivo —lo que baja su línea— y sólo después cierra el EOI del PIC; el
445  orden inverso reavivaría la interrupción en bucle.
446- Todo acceso al disco toma su `Mutex` con las interrupciones desactivadas
447  (`without_interrupts`): la IRQ del disco jamás encuentra el cerrojo ocupado,
448  lo que hace imposible el interbloqueo por interrupción.
449
450### Verificado
451- QEMU: el disco virtio-blk se enruta a la IRQ 11. La `tarea_sonda_disco`
452  reporta «sonda asíncrona OK -- 2 IRQ de disco atendidas»: la interrupción del
453  disco dispara de verdad. La app cronista, sobre el disco persistente heredado
454  de la Fase 6.1c, continúa la cuenta de arranques (3 → 4 → 5 celdas) con el
455  testigo de integridad del DAG en verde — la nueva E/S asíncrona lee y escribe
456  el grafo sin un solo fallo. Las cuatro apps WASM siguen su curso, sin un
457  sobresalto ni un micro-congelamiento.
458
459## Fase 7 — Apertura: Apps que nacen del Grafo — 2026-05-22
460
461Apertura de la Fase 7. Este commit asienta el plan y el andamiaje; **no
462cambia el comportamiento del kernel**. La Fase 7 destierra el
463`include_bytes!` del userspace: las aplicaciones pasan a ser objetos del
464grafo, y un Manifiesto de Génesis —también en el grafo— dicta qué arranca,
465con qué cuota y en qué región.
466
467### Añadido
468- `FASE7.md` — el plan de ataque: el objetivo, el problema huevo-y-gallina
469  de la génesis, las sub-fases 7a (manifiesto + carga desde el grafo, con
470  semilla por el kernel), 7b (siembra de la imagen por `boot`, muerte del
471  `include_bytes!`) y 7c (persistencia inter-sesión), y los guardarraíles.
472- `kernel/src/manifiesto.rs` — andamiaje del Manifiesto de Génesis. Los
473  tipos `Manifiesto` / `EntradaApp` y la (de)serialización `postcard` están
474  completos; `cargar` y `sembrar_genesis` son esbozos hasta la Fase 7a.
475  La sub-región se guarda en campos `u32` de ancho fijo (formato en disco),
476  no como `RegionPantalla` (`usize`, ancho de plataforma).
477
478### Notas
479- El módulo se declara en `main.rs` pero aún no se cablea a `kernel_main`
480  (`#![allow(dead_code)]` temporal, hasta que la 7a le dé un llamador). El
481  kernel compila y se comporta idéntico a la Fase 6.2 — nada observable que
482  verificar en QEMU en esta apertura.
483
484## Fase 7a — Apps que nacen del Grafo: el Manifiesto — 2026-05-22
485
486El kernel deja de empotrar el userspace. Las cinco aplicaciones ya no llegan
487por `include_bytes!` en `main.rs`: nacen del grafo de objetos, gobernadas por
488un Manifiesto de Génesis que también vive en el grafo.
489
490### Añadido
491- `kernel/src/manifiesto.rs` — el Manifiesto de Génesis implementado: tipos
492  `Manifiesto` / `EntradaApp`, (de)serialización `postcard`, `cargar` (lee el
493  manifiesto del grafo vía el ancla del superbloque) y `sembrar_genesis`
494  (puebla un disco virgen con las cinco apps de génesis).
495- `almacen`: el `SuperBloque` gana el ancla `manifiesto: Option<Hash>` —
496  gemela de `raiz`, pero del lado del kernel. Accesores `manifiesto()` y
497  `fijar_manifiesto()`.
498
499### Cambiado
500- `almacen::VERSION` 1 → 2: el superbloque cambia de forma. Un disco v1 se
501  reformatea al arrancar, como cualquier disco ajeno — la cuenta de la
502  cronista reinicia una vez.
503- `kernel_main`: las cinco llamadas `encender_app` escritas a mano se
504  sustituyen por `cargar_userspace`, que carga el manifiesto —o lo siembra si
505  el disco está virgen— e itera sus `EntradaApp`.
506- `encender_app` recibe una `EntradaApp` y recupera el bytecode del grafo.
507  `recuperar` ya recomputa y verifica el hash: un bytecode corrupto se niega,
508  se pinta la baliza de desalojo en su región y el arranque continúa.
509- `wasm::AplicacionWasm::cargar` recibe el techo de memoria por-app (del
510  manifiesto) en vez de la constante global `TECHO_MEMORIA`.
511
512### Notas
513- La siembra es TRANSITORIA: el bytecode aún viaja empotrado en
514  `manifiesto.rs` (`include_bytes!`) como semilla. La Fase 7b lo moverá al
515  constructor de imagen `boot` y el `include_bytes!` morirá del todo.
516
517### Verificado
518- QEMU (captura headless por el monitor, `screendump`): los dos caminos del
519  Manifiesto confirmados.
520  - **Disco virgen** — el almacén reporta «disco formateado :: 65536 sectores
521    :: 0 objetos», la consola imprime «manifiesto :: genesis sembrada en disco
522    virgen» y «manifiesto :: 5 apps nacidas del grafo». La pantalla es
523    idéntica a la Fase 6.2: dos `hello_wasm` renderizando, discola desalojada
524    en púrpura, glotona en amarillo, cronista pintando su primera celda.
525  - **Disco ya sembrado** — sin línea de siembra: el almacén reporta «grafo
526    montado :: 6 objetos :: raíz presente» (las 5 semillas más el objeto que
527    la cronista persistió), la consola imprime sólo «manifiesto :: 5 apps
528    nacidas del grafo», y la cronista pinta su segunda celda — la cuenta de
529    arranques sobrevivió al apagón. El userspace nace del grafo en ambos
530    casos.
531
532## Fase 7b — La imagen sembrada por `boot`; muere el `include_bytes!` — 2026-05-22
533
534El kernel deja de empotrar el userspace POR COMPLETO. Hasta la 7a, el bytecode
535de las cinco apps de génesis aún viajaba dentro del binario del kernel
536(`include_bytes!`), como semilla transitoria. La 7b lo mata: es el constructor
537de imagen `boot` —en el anfitrión— quien siembra el disco con el grafo ya
538poblado. El binario del kernel ya no carga ni un solo `.wasm`.
539
540### Añadido
541- **`formato`** — nueva crate: un núcleo `#![no_std]` COMPARTIDO que define el
542  formato del grafo de objetos en disco. Tipos (`Objeto`, `SuperBloque`,
543  `Manifiesto`, `EntradaApp`), su (de)serialización `postcard`, la función hash
544  BLAKE3 (escalar pura) y el trazado de un registro del log
545  (`componer_registro` / `longitud_registro`). Lo enlazan el kernel bare-metal
546  Y el anfitrión `boot`: una sola verdad del formato, imposible de divergir
547  entre los dos lados. 5 pruebas de ida y vuelta. Excluida del workspace, como
548  el kernel.
549- `boot`: `sembrar_grafo` — siembra un disco virgen con el grafo ya poblado.
550  Graba el bytecode de cada app de génesis como un objeto (deduplicado:
551  `app.wasm`, usado dos veces, se guarda una sola), compone el Manifiesto de
552  Génesis con sus regiones y cuotas, lo graba con las aristas hacia los objetos
553  de bytecode, y forja el superbloque que lo ancla. El `.wasm` se lee de
554  `kernel/assets/` en tiempo de ejecución.
555
556### Cambiado
557- `kernel/manifiesto.rs`: pierde los cuatro `include_bytes!`, el descriptor
558  `AppGenesis`, `genesis()` y `sembrar_genesis()`. Se reduce a `cargar()` —leer
559  el manifiesto del grafo— y `region()` —traducir la región en disco a la del
560  kernel—. Los tipos `Manifiesto` / `EntradaApp` migran a `formato`.
561- `kernel/almacen.rs`: los tipos `Objeto` / `SuperBloque`, las constantes del
562  formato, el hash y el trazado de registros migran a `formato`. El módulo
563  queda como el almacén VIVO: cursor, índice y E/S contra virtio-blk.
564- `kernel_main` / `cargar_userspace`: sin la rama de siembra. Un disco sin
565  manifiesto anclado ya no se siembra desde el kernel —`boot` lo hace siempre
566  al forjar la imagen—; el kernel se levanta sin userspace y lo reporta.
567- `kernel/Cargo.toml`: deja de declarar `serde` / `postcard` / `blake3` por su
568  cuenta; los hereda —mismas features, BLAKE3 escalar puro— a través de
569  `formato`.
570- `wasm/mod.rs`: se retira la constante `TECHO_MEMORIA`, ya sin uso (el techo
571  por-app lo dicta el manifiesto; el valor de génesis vive ahora en `boot`).
572- `.cargo/config.toml`: el alias `cargo kernel` pasa de `-p kernel` a
573  `--manifest-path kernel/Cargo.toml`. Con `formato` compartido entre el kernel
574  y `boot`, pedir `-p kernel` dentro del workspace —con el kernel a la vez como
575  dependencia de artefacto— hace caer al resolvedor de features de cargo;
576  apuntar al manifiesto del kernel lo compila como raíz y esquiva el grafo.
577
578### Verificado
579- QEMU (captura headless por el monitor, `screendump`), los dos caminos:
580  - **Disco virgen** — `boot` imprime «disco de objetos sembrado :: 5 objetos,
581    manifiesto anclado»; el kernel monta el grafo de `boot` —«grafo montado ::
582    5 objetos :: raíz ausente», SIN «disco formateado» ni «génesis sembrada»—
583    e instancia las cinco apps. Pantalla idéntica a la Fase 6.2.
584  - **Segundo arranque** — `boot` respeta el disco existente («el grafo
585    perdura»); el kernel carga «6 objetos :: raíz presente» y la cronista pinta
586    su segunda celda: la persistencia sobrevive a través de un disco sembrado
587    por `boot`.
588- `cargo build -p boot` y `cargo kernel` compilan limpios; `cargo test` de
589  `formato` — 5/5 en verde.
590
591## Fase 7c — Persistencia inter-sesión: cada app recuerda lo suyo — 2026-05-22
592
593La cronista dejaba huella, pero en la RAÍZ del grafo — un ancla única que sólo
594una app puede usar. La 7c estrena la persistencia POR-APP: cada `EntradaApp`
595del Manifiesto de Génesis tiene su propia ranura `estado`, y una app guarda y
596recobra lo suyo sin pisar a nadie. El estado de cada aplicación sobrevive, por
597separado, a un reinicio.
598
599### Añadido
600- **`apps/memoriosa`** — nueva app WASM interactiva. Cuenta las teclas pulsadas
601  en toda su historia y persiste el recuento; al reiniciar, despierta con su
602  cuenta intacta. Una celda violeta por pulsación; el testigo de la esquina es
603  verde si nació limpia, ámbar si despertó con memoria de una vida anterior.
604- `kernel` — dos capacidades del host nuevas:
605  - `sys_estado_cargar(salida, capacidad)` — copia el estado persistido de la
606    app que llama en su memoria lineal; 0 si aún no tiene estado previo.
607  - `sys_estado_guardar(datos, datos_len)` — graba `datos` como objeto del
608    grafo y ancla su hash en la `EntradaApp` de la app, re-grabando y
609    re-anclando el manifiesto.
610- `manifiesto.rs` — el manifiesto VIVO: `VIVO`, un `Mutex<Manifiesto>` que el
611  kernel custodia. `instalar` (en el arranque), `estado_de` (lee la ranura de
612  una app) y `fijar_estado` (la muta, re-graba el manifiesto y lo re-ancla).
613- `ContextoCapacidades` gana `indice_app`: la identidad de cada app en el
614  manifiesto, con la que las capacidades de estado hallan SU ranura — jamás la
615  de otra.
616
617### Cambiado
618- `AplicacionWasm::cargar` y `encender_app` reciben el `indice_app` de la app.
619- `cargar_userspace` instala el manifiesto VIVO ANTES de instanciar las apps:
620  el `init` de una app ya consulta su estado persistido al despertar.
621- `almacen::fijar_manifiesto` deja de ser `dead_code` — lo invoca `fijar_estado`.
622- La génesis (`boot`) sustituye la segunda instancia de `hola` por `memoriosa`
623  (región 700,120 de 360×80).
624
625### Verificado
626- QEMU (captura headless + `sendkey` por el monitor):
627  - **Disco virgen** — memoriosa arranca con 0 celdas y el testigo verde.
628  - **Cinco pulsaciones** (`sendkey`) — memoriosa pinta 5 celdas violetas y
629    persiste el recuento; `sys_estado_guardar` re-ancla el manifiesto.
630  - **Reinicio** — `boot` respeta el disco; memoriosa despierta con las 5
631    celdas intactas y el testigo en ámbar: su `init` releyó el estado del
632    grafo. El estado por-app sobrevivió al apagón.
633
634## Fase 8a — El compositor teselante — 2026-05-22
635
636Hasta la Fase 7, cada app llevaba su región escrita a mano en el manifiesto —
637coordenadas fijas, una composición rígida. La Fase 8 entrega esa decisión a un
638COMPOSITOR: el kernel ya no coloca las ventanas a mano, las TESELA con
639`mirada-layout`.
640
641### Añadido
642- **`kernel/compositor.rs`** — el compositor teselante. Enlaza `mirada-layout`
643  —el mismo núcleo `no_std` que ordena las ventanas del compositor Wayland de
644  brahman, ya enlazado por `path` cruzando la frontera de workspace— y, con su
645  algoritmo `MasterStack`, calcula un marco para cada app. `disponer(n, …)`
646  devuelve un marco por ventana; `area_apps`, la zona teselable (la pantalla
647  menos la franja superior de la consola).
648- `consola`: `pintar_escenario` —inunda el área de apps y tiñe cada marco con
649  el color de panel, de modo que el teselado se vea como una rejilla— y el
650  color `Color::PANEL`.
651
652### Cambiado
653- `consola::volcar_marco` ya no deposita el fotograma en `(region.x, region.y)`:
654  CENTRA el fotograma natural de la app dentro de su marco teselado y lo
655  recorta a sus bordes.
656- `ContextoCapacidades` lleva ahora `marco` (el rectángulo teselado) y
657  `natural_ancho`/`natural_alto` (el tamaño del lienzo de la app);
658  `sys_render_frame` valida el fotograma contra el tamaño natural.
659- `AplicacionWasm` / `encender_app` / `cargar_userspace` cablean el marco
660  teselado. `cargar_userspace` recibe las dimensiones de pantalla, tesela con
661  el compositor y pinta el escenario antes de encender las apps.
662- `RegionPantalla::pixeles` se retira — sin uso desde que `sys_render_frame`
663  mide contra el tamaño natural.
664- **Las apps NO cambian.** Renderizan su lienzo natural, fijo; el kernel lo
665  compone centrado en el marco. El compositor reordena la pantalla sin que
666  ninguna app toque una sola instrucción.
667
668### Verificado
669- QEMU (captura headless): las cinco apps de génesis aparecen teseladas en
670  `MasterStack` — `hola` como ventana maestra (izquierda, 60% del ancho) y
671  memoriosa, discola, glotona y cronista apiladas a la derecha. Cada lienzo,
672  centrado en su panel; las apps desalojadas (discola, glotona) tiñen su marco
673  entero con la baliza. El registro de arranque permanece legible en la franja
674  superior de la consola.
675
676## Fases 8b y 8c — El escritorio interactivo — 2026-05-22
677
678El compositor de la Fase 8a teselaba, pero era inmóvil. Las Fases 8b y 8c lo
679hacen VIVO: el teclado reordena el escritorio en caliente y mueve el foco. Y
680para que las apps estáticas no se queden atrás en una reordenación, el kernel
681asume la persistencia visual con una caché de fotogramas.
682
683### Añadido
684- **Caché de fotogramas (backbuffer).** Cada ventana del compositor guarda, en
685  RAM del kernel, el último fotograma que su app envió. La caché se reserva UNA
686  vez, acotada al lienzo natural de la app —jamás crece—. Al re-teselar o mover
687  el foco, el kernel recompone cada ventana desde su caché: una app que sólo
688  pintó en su `init` —la cronista— conserva su imagen intacta a través de
689  cualquier reordenación, sin enterarse del cambio geométrico.
690- **`compositor`: el escritorio interactivo.** Un registro `ESCRITORIO` —las
691  ventanas, sus marcos, sus cachés, el modo de teselado—; `presentar_fotograma`
692  (cachea y compone), `desalojar`, `atender_mandos`, `ciclar_layout`,
693  `mover_foco`. El foco vive en un `AtomicUsize`; los mandos del teclado, en
694  una cola lock-free.
695- **`tarea_compositor`** — atiende los mandos del teclado en cada fotograma del
696  reactor: el contexto cooperativo donde es seguro re-teselar y recomponer.
697- `Color::FOCO` (índigo brillante) y `Color::SIN_FOCO` (gris mate) — el borde
698  de cada ventana, que delata de un vistazo quién tiene el foco.
699
700### Cambiado
701- **Teclado (Fase 8c).** El manejador de IRQ1 deja de difundir a ciegas:
702  - La tecla **Alt** es el modificador del sistema. `Alt+Espacio` cicla el
703    modo de teselado; `Alt+J` / `Alt+K` mueven el foco. Estos mandos se
704    consumen en la IRQ — jamás llegan a una app.
705  - Una tecla ordinaria se entrega SÓLO a la app ENFOCADA. El censo de canales
706    se reindexó por `indice_app` (`Vec<Option<CanalTeclado>>`): el foco —un
707    índice— elige el canal exacto.
708- `consola::volcar_marco` y `pintar_desalojo` trazan el borde de foco de la
709  ventana. `sys_render_frame` ya no compone directo: delega en
710  `compositor::presentar_fotograma`, que cachea antes de componer.
711- `AplicacionWasm` / `ContextoCapacidades` pierden el `marco` —ahora vive en el
712  registro del compositor, mutable—; conservan sólo el `indice_app`.
713
714### Exclusión de interrupciones (guardarraíl)
715- El registro `ESCRITORIO` lo tocan SÓLO tareas cooperativas; el manejador de
716  IRQ1 jamás lo bloquea. La IRQ se comunica con el mundo cooperativo por un
717  canal estrecho y a prueba de interbloqueos: dos atómicos —el foco y el estado
718  de Alt— y una cola lock-free de mandos. Ningún cerrojo en disputa entre la
719  interrupción y una tarea.
720- Las cachés se reservan en `compositor::fundar`, una sola vez, al tamaño
721  natural de cada app: ninguna asignación dinámica dentro del bucle del reactor.
722
723### Verificado
724- QEMU (captura headless + `sendkey` por el monitor):
725  - **Arranque** — cinco apps teseladas en `MasterStack`; `hola` enfocada luce
726    el borde índigo, las demás el gris.
727  - **`Alt+Espacio`** — el escritorio cicla a `CenteredMaster`; discola,
728    glotona y cronista conservan su contenido a través del re-teselado: la
729    caché de fotogramas en acción.
730  - **`Alt+J`** — el foco salta de `hola` a `memoriosa`; el borde índigo se
731    mueve con él.
732  - **Enrutamiento** — con `memoriosa` enfocada, cuatro pulsaciones llegan sólo
733    a ella —cuatro celdas violetas—; las demás apps, intactas.
734
735## Fase 8d — Manipulación de ventanas — 2026-05-22
736
737El escritorio de la 8c se podía recorrer con la vista —el foco— pero no
738reordenar. La 8d lo hace MANIPULABLE: el orden de teselado se separa de la
739identidad de las ventanas, y el teclado promueve y reordena.
740
741### Añadido
742- `Escritorio` gana `orden: Vec<usize>` — una permutación que dice qué ventana
743  ocupa cada celda del teselado. Separar el orden de la identidad
744  (`indice_app`) permite mover una ventana sin tocar su canal de teclado ni su
745  ranura de estado: cambia de pared, no de identidad.
746- `aplicar_teselado` — recalcula los marcos y los reparte según el orden.
747- Mandos `Promover`, `MoverAdelante`, `MoverAtras`:
748  - `Alt+Enter` — promueve la ventana enfocada a la celda maestra; las demás
749    se desplazan una posición.
750  - `Alt+L` / `Alt+H` — mueven la ventana enfocada adelante / atrás en el
751    orden, intercambiándola con su vecina.
752
753### Cambiado
754- `mover_foco` recorre ahora el ORDEN de teselado —no los índices crudos—: el
755  foco salta entre ventanas visualmente contiguas.
756- `ciclar_layout` y `fundar` delegan en `aplicar_teselado`.
757
758### Verificado
759- QEMU (`sendkey`): con `memoriosa` enfocada, `Alt+Enter` la promueve a la
760  ventana maestra y `hola` baja a la pila; `Alt+L` la devuelve a la pila y
761  `hola` recupera la maestra. El foco —el borde índigo— viaja siempre con la
762  ventana, no con la celda.
763
764## Fase 9 — Orden-Z y ventanas flotantes — 2026-05-22
765
766Hasta la 8d el escritorio era TESELADO puro: las ventanas repartían la pantalla
767sin solaparse jamás. La Fase 9 introduce un segundo modelo de composición —el
768SOLAPAMIENTO—: una ventana puede abandonar el teselado y FLOTAR, con un marco
769propio y libre, por encima de las demás.
770
771### Añadido
772- `Escritorio` gana `flotantes: Vec<usize>` — la pila de ventanas flotantes en
773  orden-Z, de atrás hacia adelante; `flotantes.last()` es la frontal. Junto con
774  `orden` forma una partición de las ventanas: cada una está teselada o flota,
775  nunca en ambas ni en ninguna.
776- Mando `Flotar` (`Alt+F`): alterna la ventana enfocada entre teselada y
777  flotante. Al flotar, abandona el teselado —que se recalcula para las que
778  quedan—, recibe un marco propio (su lienzo natural más un reborde de cromo)
779  colocado en CASCADA, y sube al frente del orden-Z. Al volver, se reincorpora
780  al final del orden de teselado.
781- `compositor::recomponer` — recompone el escritorio entero de una pasada: arma
782  la lista de capas (las teseladas al fondo; las flotantes encima, de atrás
783  hacia adelante) y se la entrega a la consola.
784- `consola::recomponer` y los tipos `Capa` / `Contenido`: la consola funde las
785  capas EN ORDEN sobre el lienzo; el solapamiento se resuelve por el orden del
786  pintado, sin recortes ni máscaras. Una sola presentación cierra la pasada.
787- `consola::componer_fotograma` — el volcado de un fotograma natural centrado
788  en su marco, extraído de `volcar_marco` para compartirlo entre el camino
789  rápido y la recomposición.
790
791### Cambiado
792- `presentar_fotograma` y `desalojar` bifurcan: sin ventanas flotantes
793  conservan el camino RÁPIDO de la Fase 8 —pintar sólo la ventana que cambia—;
794  con flotantes vivas recomponen el escritorio entero respetando el orden-Z.
795- `mover_foco` recorre ahora TODAS las ventanas —las teseladas y las
796  flotantes—; al enfocar una flotante, la alza al frente del orden-Z: la
797  flotante con el foco está siempre delante.
798- `componer_escenario`, `ciclar_layout`, `promover` y `mover_ventana` delegan
799  el repintado en `recomponer`; desaparecen `redibujar_todo`,
800  `redibujar_ventana` y `pintar_escenario`.
801
802### Verificado
803- QEMU (`sendkey`): `Alt+F` saca la ventana maestra del teselado y la deja
804  flotando sobre las demás, que se re-teselan. Un segundo `Alt+F` sobre otra
805  ventana la flota en cascada, solapando a la primera. `Alt+K` devuelve el foco
806  a la ventana grande y ésta sube al frente, tapando por completo a la pequeña.
807  Un `Alt+F` final la reintegra al teselado.
808
809## Fase 10 — Alta y baja de aplicaciones en vivo — 2026-05-22
810
811Hasta la Fase 9 el censo de aplicaciones se fijaba en el arranque: las apps
812nacían del manifiesto y sólo morían al fallar. La Fase 10 lo vuelve DINÁMICO —
813una app puede nacer o cerrarse con el reactor ya en marcha.
814
815### Añadido
816- **El reactor admite nacimientos en vivo.** `executor`: una cola de
817  NACIMIENTOS (`Vec<FuturoTarea>` tras un `Mutex`) y la función `engendrar`.
818  El ejecutor la drena al inicio de cada vuelta (`recoger_nacimientos`) y
819  adopta cada futuro como tarea. `Task::adoptar` acoge un futuro ya
820  empaquetado. `dormir_si_inactivo` cuenta los nacimientos como trabajo.
821- **Baja en vivo — `Alt+Q`.** Mando `Cerrar`: el compositor marca la ventana
822  enfocada como `cerrada`, libera su caché de respaldo, la saca del teselado y
823  del orden-Z, y traslada el foco a una ventana viva contigua. La app, en su
824  tarea, consulta `compositor::ventana_cerrada` cada fotograma; al verla
825  cerrada concluye su tarea — y `AplicacionWasm::drop` libera su memoria
826  lineal, su combustible y su canal de teclado. Una baja LIMPIA, sin baliza.
827- **Alta en vivo — `Alt+N`.** Mando `Lanzar`: el compositor cuenta la petición
828  (`PARTOS`); la tarea del compositor la atiende con `partos_pendientes` y
829  `lanzar_app`. `compositor::nacer_ventana` añade la ventana y devuelve su
830  índice; el orquestador instancia el WASM con ese índice y `engendra` su
831  tarea. Las apps de génesis dejan su bytecode cacheado en RAM como
832  `Plantilla`; cada `Alt+N` instancia la siguiente en rotación, sin volver al
833  disco —una E/S por sondeo en mitad del reactor sería un mal vecino—.
834- `compositor`: campo `Ventana.cerrada`, mandos `Cerrar` / `Lanzar`, y las
835  funciones `cerrar`, `nacer_ventana`, `ventana_cerrada`, `partos_pendientes`.
836- `teclado`: `Alt+Q` → `Cerrar`, `Alt+N` → `Lanzar`.
837
838### Cambiado
839- `encender_app` devuelve la `Plantilla` de la app —su bytecode y geometría—
840  para los lanzamientos en vivo.
841- `tarea_aplicacion` consulta `ventana_cerrada` antes de cada `tick`.
842- `presentar_fotograma` y `desalojar` ignoran una ventana ya cerrada: una baja
843  limpia gana a un fotograma o a un desalojo que lleguen tarde.
844
845### Verificado
846- QEMU (`sendkey`): tres `Alt+N` dan a luz tres apps nuevas y el escritorio se
847  re-tesela de 5 a 8 ventanas. Tres `Alt+Q` cierran la app enfocada una a una
848  y el teselado reclama su espacio, de 8 de vuelta a 5. El kernel sigue estable
849  a través de todas las altas y bajas.
850
851## Fase 11 — El reloj del sistema como capacidad de host — 2026-05-22
852
853Hasta la Fase 10 una aplicación sólo sabía CUÁNTAS veces la habían llamado —un
854`tick` tras otro—, no CUÁNTO tiempo había pasado. La Fase 11 le da al userspace
855un sentido del tiempo: el reloj monótono del sistema, como capacidad.
856
857### Añadido
858- **Capacidad `sys_tiempo_mono() -> u64`** — la décima función del host. Los
859  milisegundos transcurridos desde el arranque. El temporizador (PIT) ya late a
860  100 Hz; `reloj` expone esa cuenta como `milisegundos()` y `env` la inyecta.
861  Es una lectura PURA —no toca la memoria lineal del módulo, no hay puntero que
862  validar— y MONÓTONA: jamás retrocede.
863- **App `pulso`** (`apps/pulso/`, `wasm32`). Un compás visual: una cabeza
864  brillante que recorre una pista y vuelve, con un período de 6 s. Su escena es
865  una FUNCIÓN PURA de `sys_tiempo_mono` —no guarda estado entre fotogramas—.
866  De ahí su prueba: dos instancias de `pulso`, nazca una al arrancar y otra
867  mucho después con un `Alt+N`, laten exactamente al unísono.
868- `pulso` se suma al userspace de génesis: `GENESIS` pasa de 5 a 6 apps, con
869  `pulso` como la primera —la ventana maestra del escritorio—.
870
871### Verificado
872- QEMU (`sendkey`): la barra de `pulso` avanza con el tiempo de pared entre dos
873  capturas. Un `Alt+N` da a luz un segundo `pulso` ~15 s después del arranque;
874  flotado junto al primero, su barra está en la MISMA fase — la prueba de que
875  el compás se rige por el reloj absoluto, no por una cuenta de fotogramas.
876
877## Fase 12 — La bocina del PC como capacidad de host — 2026-05-22
878
879La Fase 11 le dio al userspace un reloj; la Fase 12, una voz. La bocina del PC
880—el canal 2 del PIT como generador de onda cuadrada— se suma a la matriz de
881capacidades. Hasta hoy renaser sólo sabía DIBUJAR para llamar la atención.
882
883### Añadido
884- **Driver `drivers/altavoz`.** Programa el canal 2 del PIT a una frecuencia y
885  abre la compuerta del puerto 0x61. El canal 0 del PIT es el latido del
886  kernel; el canal 2 es de la bocina y de nadie más — no se perturban—.
887  `tono(hz)` es su única vía; un `0` la silencia.
888- **Capacidad `sys_tono(frecuencia_hz)`** — la undécima función del host. La
889  bocina es un recurso ÚNICO y global: para que dos apps no se la disputen,
890  pertenece —como el teclado desde la Fase 8c— a la ventana ENFOCADA. Una app
891  sin foco puede pedir un tono; sencillamente, no se oye. Al cambiar el foco,
892  el compositor calla la bocina (`mover_foco`, `cerrar`): la nueva dueña la
893  reclama en su próximo fotograma.
894- **App `tonada`** (`apps/tonada/`, `wasm32`). Toca una escala de Do mayor en
895  bucle y la dibuja como una escalera de ocho barras, con la nota que suena
896  encendida. Junta las dos capacidades nuevas del kernel: el reloj
897  (`sys_tiempo_mono`) le marca el compás, la bocina (`sys_tono`) le da voz.
898- `tonada` encabeza el userspace de génesis: `GENESIS` pasa de 6 a 7 apps, con
899  `tonada` como la ventana maestra —enfocada al arrancar, así suena de
900  inmediato—.
901
902### Verificado
903- QEMU. Visual: la escalera de `tonada` enciende la nota en curso y ésta
904  recorre la escala con el tiempo (capturas con la 6.ª y la 1.ª nota vivas).
905  Sonido: con la bocina enrutada a un WAV (`-audiodev wav` + `-machine
906  pcspk-audiodev`), el PCM capturado —50 s— es una onda cuadrada oscilante de
907  ±24 000 de amplitud, con una frecuencia media de ~375 Hz: la de la escala de
908  Do mayor.
909
910## Fase 13 — Ratón, puntero y arrastre de flotantes — 2026-05-22
911
912renaser tenía teclado y bocina como entrada/salida del usuario, pero no
913puntero. Las ventanas flotantes de la Fase 9 nacían en cascada y se quedaban
914ahí, clavadas. La Fase 13 trae el ratón: un puntero en pantalla, clic-para-
915enfocar y arrastre del marco de las ventanas flotantes.
916
917### Añadido
918- **Driver `drivers/raton`** — el ratón PS/2 cuelga del dispositivo auxiliar
919  del 8042 (la misma controladora que el teclado) y anuncia cada movimiento
920  por la IRQ12. El driver despierta el aux, enciende su IRQ, le ordena
921  reportar, y ensambla los paquetes de 3 bytes con la guarda del bit-3 que
922  detecta desincronización. Como el teclado, la IRQ12 sólo toca atómicos
923  —posición del puntero— y una cola lock-free de eventos.
924- **El puntero, capa de presentación.** `Pantalla` gana `formato` y la
925  función `estampar_puntero`: un sprite de flecha de 12×18 que se pinta
926  DIRECTAMENTE sobre el framebuffer, justo después de copiar el lienzo. El
927  lienzo permanece libre de puntero —hace de save-under natural—; cada
928  `presentar` sella el puntero al final.
929- **Capacidad del compositor**: `atender_raton` drena eventos del ratón cada
930  fotograma. El botón izquierdo bajando es un CLIC: enfoca la ventana viva
931  bajo el puntero (consistente con `mover_foco` — silencia la bocina, alza al
932  frente si flota). Si la enfocada es flotante, arranca un ARRASTRE con el
933  desfase de agarre; con el botón sostenido, la ventana sigue al puntero;
934  soltarlo lo termina.
935- **`refrescar_puntero`**: en una vuelta tranquila en que ninguna app pinte,
936  reestampa el framebuffer si el puntero se ha movido — el centinela
937  empacado evita repintar dos veces el mismo instante.
938- `Escritorio` gana `arrastre: Option<Arrastre>` y `raton_izq: bool`.
939  `Arrastre` guarda el índice de la ventana asida y el desfase de agarre.
940  `cerrar` libera el arrastre si la ventana cerrada era la arrastrada.
941
942### Verificado
943- QEMU (`mouse_move` + `mouse_button` del monitor). El puntero aparece en el
944  centro al arrancar; `mouse_move` lo lleva por la pantalla. Clic sobre la
945  ventana `pulso` (en la pila) la enfoca: el borde índigo deja la maestra y
946  envuelve a `pulso`. `Alt+F` la flota; el ratón la agarra y la arrastra de
947  la esquina superior izquierda al centro-abajo de la pantalla con el botón
948  sostenido. El kernel sigue estable a través de los gestos.
949
950## Mapeador MMIO en el kernel — 2026-05-23
951
952OVMF aloja los BAR prefetchables 64-bit de virtio en la ventana PCI de 64
953bits, que el `bootloader_api` no mapea. `KernelHal::mmio_phys_to_virt`
954devolvía `phys + offset` a ciegas y el primer registro MMIO leído reventaba
955con #PF, dejando una franja roja sin pista visible. La autopsia: el commit
956anterior añadió el dump del panic-handler por COM1, que sacó a la luz el IP
957en `unchecked_mul::precondition_check` y la dirección de fallo
958`offset + 0x800000014` — phys 32 GiB, donde OVMF había puesto el BAR.
959
960### Añadido
961- `memory::mmio`: envuelve la tabla L4 activa (vía `Cr3` + el mapeo de
962  memoria física del cargador) en un `OffsetPageTable`. `mapear(fisica,
963  tam)` abre páginas hacia la región pedida con `PRESENT | WRITABLE |
964  NO_CACHE | WRITE_THROUGH`, tratando `PageAlreadyMapped` y
965  `ParentEntryHugePage` como éxito silencioso.
966- Los marcos para tablas intermedias salen del banco DMA del disco
967  (`asignar_marco_para_tabla`, sin pánico).
968- `KernelHal::mmio_phys_to_virt` llama a `memory::mmio::mapear` antes de
969  devolver el puntero, asegurándose de que el BAR sea accesible.
970
971### Cambiado
972- `baliza::Serie` es ahora `pub(crate)`: cualquier módulo puede dejar
973  trazas por COM1. El panic-handler y el OOM-handler dejan en COM1 su
974  mensaje y ubicación además de pintar la franja. `kernel_main` traza
975  cada hito del arranque para diagnóstico remoto.
976
977## Fase 14 — La identidad del escritorio: nombres y barra de tareas — 2026-05-23
978
979Las ventanas eran anónimas: el escritorio reía con sus colores pero no sabía
980cómo se llama lo que muestra. La Fase 14 le da nombre a cada cuarto y una
981barra al pie con la lista de quien vive en la casa.
982
983### Añadido
984- Cada `Ventana` lleva un `nombre: String` —el que dicta su `EntradaApp` del
985  manifiesto, o el que el orquestador pasa al engendrarla en vivo (Fase 10)—.
986  `Plantilla` se acuerda del nombre para los lanzamientos posteriores.
987- **`FRANJA_TASKBAR`** (40 px al pie) y `area_taskbar`: la barra de tareas
988  vive en su propia franja. `area_apps` la descuenta — las ventanas teselan
989  y flotan SIN tapar la barra ni la consola de la cima.
990- `consola`: tipos `Taskbar` y `CeldaTaskbar`; métodos `pintar_taskbar`
991  (fondo + línea divisoria + una pestaña por ventana viva) y
992  `pintar_etiqueta` (rasteriza una cadena en un punto fijo, sin tocar la
993  pluma de la consola). La pestaña enfocada lleva el índigo del foco; las
994  desalojadas, su color de baliza; las demás, el slate del panel.
995- `compositor::recomponer` arma la lista de celdas y la pasa a la consola
996  junto con las capas: un único repintado, una única presentación.
997- `atender_raton`: un clic dentro de la franja de la barra busca su pestaña
998  con `celda_taskbar_en` y enfoca la ventana correspondiente (sin iniciar
999  arrastre). El comportamiento sobre el área de apps queda intacto.
1000
1001### Cambiado
1002- `compositor::fundar` y `nacer_ventana` exigen un nombre para cada ventana.
1003  `cargar_userspace` lo toma de `EntradaApp.nombre`; `lanzar_app`, de
1004  `Plantilla.nombre`.
1005
1006### Verificado
1007- QEMU. Tras arrancar se ve la barra al pie con las 7 pestañas (`tonada`
1008  enfocada, índigo), las desalojadas en sus colores de baliza. Un clic
1009  sobre `pulso` cambia el foco al instante: el borde índigo del compositor
1010  deja la maestra y envuelve a `pulso`, y la pestaña `pulso` se ilumina.
1011
1012## Fase 15 — La voz del sistema — 2026-05-23
1013
1014La bocina pertenecía a la ventana enfocada (Fase 12), pero el kernel necesita
1015hablar también: un acorde al abrir la casa, un repique al recibir un inquilino
1016nuevo, un bajo al echar a uno que se rompió. La Fase 15 le da al sistema su
1017propia voz, prioritaria sobre las de los apps.
1018
1019### Añadido
1020- **`altavoz::SECUENCIA`** — una `VecDeque<(u32, u32)>` (frecuencia, duración
1021  ms) que el kernel encola con `altavoz::agendar(&[(...)])`. `FIN_NOTA` (un
1022  `AtomicU64`) recuerda el milisegundo del reloj monótono en que la nota
1023  actual debe terminar.
1024- **`altavoz::atender()`** — invocada por la tarea del compositor cada
1025  fotograma; si la nota actual ya terminó, saca la siguiente de la cola y la
1026  toca; si la cola está vacía, silencia.
1027- **`altavoz::kernel_sonando()`** — `true` mientras `FIN_NOTA` esté en el
1028  futuro. `sys_tono` lo consulta y, en ese caso, ignora la llamada del app:
1029  el kernel no se interrumpe a sí mismo.
1030- **Catálogo de voces**: `VOZ_BIENVENIDA` (Do5-Mi5-Sol5 ascendente, 500 ms),
1031  `VOZ_LANZAR` (repique 700→1050 Hz), `VOZ_CERRAR` (descendente 900→520 Hz),
1032  `VOZ_DESALOJO` (bajo 180 Hz, 260 ms).
1033- **Hitos sonoros**: `kernel_main` agenda `VOZ_BIENVENIDA` justo antes de
1034  `ejecutor.run()`. `nacer_ventana` agenda `VOZ_LANZAR`. `cerrar` agenda
1035  `VOZ_CERRAR`. `desalojar` agenda `VOZ_DESALOJO`.
1036
1037### Cambiado
1038- Las pestañas de la barra de tareas calculan su tinta por brillo del fondo
1039  (ITU-R BT.601): la pestaña amarilla pálida del desalojo por memoria, que
1040  llevaba texto blanco invisible, ahora luce su nombre en tinta oscura.
1041
1042### Verificado
1043- QEMU con `-audiodev wav -machine pcspk-audiodev=spk`. El PCM crudo revela,
1044  en orden, las tres notas del acorde de bienvenida (≈520, 630, 760 Hz),
1045  inmediatamente un brevísimo bajo de 180 Hz (la baliza de discola/glotona
1046  desalojadas), y después la escala de `tonada` tomando la bocina.
1047- Captura: la pestaña de `glotona` (crema) muestra ahora su nombre legible
1048  en tinta oscura; la de `discola` (púrpura) sigue clara, como antes.
1049
1050## Fase 16 — La barra viva: lanzador y reloj — 2026-05-23
1051
1052La barra de tareas era informativa pero pasiva: nombraba a los inquilinos sin
1053ofrecerse a ningún gesto propio. La Fase 16 le añade los dos accesorios de
1054toda barra de tareas digna: un **botón lanzador** a la izquierda y un **reloj**
1055a la derecha, y le pone el reloj a latir cada segundo.
1056
1057### Añadido
1058- **Botón lanzador («+»)** a la izquierda de la barra (36 px de ancho, fondo
1059  índigo del foco, cruz blanca). Un clic incrementa `PARTOS` — el mismo
1060  contador que `Alt+N` —, así que la tarea del compositor lo recoge y lanza
1061  la siguiente app de la rotación. El teclado y el ratón comparten ahora la
1062  misma vía para crear ventanas.
1063- **Reloj** a la derecha de la barra (80 px), formato `mm:ss` desde el
1064  arranque, leído de `reloj::milisegundos()`. Tinta blanca sobre slate.
1065- **`compositor::tick_reloj()`** — la invoca la tarea del compositor cada
1066  fotograma. Si el segundo del reloj cambió respecto al último mostrado
1067  (`ULTIMO_SEGUNDO: AtomicU64`), recompone; si no, vuelve. Cero coste cuando
1068  no hace falta refrescar.
1069- `Taskbar` gana los campos `launcher: RegionPantalla`, `reloj: &str` y
1070  `reloj_region: RegionPantalla`. `pintar_taskbar` los pinta como dos cuñas
1071  fijas de la barra: el lanzador con su cruz dibujada en píxeles directos
1072  (independiente de la tipografía), el reloj con la etiqueta de costumbre.
1073
1074### Cambiado
1075- El layout de la barra se recalcula: `cells_x0` empieza tras el lanzador,
1076  `cells_x_max` termina antes del reloj. `celda_taskbar_en` respeta esos
1077  límites; un clic en el lanzador (`clic_en_launcher`) tiene su propia rama
1078  en `atender_raton`, antes que la búsqueda de pestaña.
1079- `CELDA_TASKBAR_ANCHO` baja de 156 a 150 para que el lanzador, las siete
1080  pestañas y el reloj quepan holgados en una pantalla de 1280 píxeles.
1081
1082### Verificado
1083- QEMU. La barra al arrancar muestra el botón «+» indigo a la izquierda, las
1084  siete pestañas (con `glotona` ya legible en tinta oscura sobre crema), y el
1085  reloj `0:17` a la derecha (el tiempo que el kernel lleva vivo al capturar).
1086  Diez segundos después, el reloj marca `0:29` — la barra se ha refrescado
1087  doce veces sin intervención del ratón ni del teclado.
1088
1089## Fase 17 — `bitacora` :: el editor que recuerda — 2026-05-23
1090
1091`memoriosa` (Fase 7c) demostró que un app podía persistir un contador a
1092través de los reinicios. `bitacora` extiende esa demostración a un EDITOR de
1093texto: tecleas, los caracteres aparecen; reinicias el kernel, el texto
1094sigue ahí. La huella vive en el grafo de objetos, como todo lo demás.
1095
1096### Añadido — app `bitacora` (`apps/bitacora/`)
1097- **Lienzo 480×280** con título "bitacora :: el texto persiste" en índigo
1098  arriba y, debajo, las últimas líneas del buffer. Wrap automático a 28
1099  columnas; `Enter` salta de línea; Backspace borra el último carácter.
1100- **Tipografía 8×8** embebida (crate `font8x8 = "0.3"`), escalada x2 a 16×16.
1101  El app rasteriza cada glifo pixel a pixel desde su propia memoria lineal.
1102- **Persistencia automática**: cada cambio invoca `sys_estado_guardar`. Al
1103  arrancar, `init` llama a `sys_estado_cargar` y restaura el buffer.
1104- Buffer de 512 bytes; al desbordarse descarta los 64 primeros para hacer
1105  hueco (amortiza el coste — no es una mudanza por cada pulsación).
1106- Mapeo de scancodes US a ASCII para letras, dígitos y puntuación común;
1107  Enter genera `\n`, Backspace borra. Sin mayúsculas ni modificadores.
1108
1109### Cambiado
1110- **`GENESIS` crece de 7 a 8 apps** con `bitacora` como la PRIMERA — gana
1111  la celda maestra al arrancar, así que la primera ventana grande del
1112  escritorio te invita a teclear.
1113- **`CELDA_TASKBAR_ANCHO`** baja de 150 a 130 píxeles para que las ocho
1114  pestañas + el lanzador + el reloj quepan holgadas en 1280 px.
1115
1116### Verificado
1117- QEMU (`sendkey` del monitor): tras escribir `hola renaser` y `quit` →
1118  relanzar QEMU con el mismo `disk.img`, la `bitacora` muestra de nuevo el
1119  texto justo donde quedó. El `almacen` reporta 24 objetos en el grafo
1120  (frente a 9 antes de escribir) y `raiz presente`: cada `guardar` anexó
1121  una versión al log direccionado por contenido.
1122
1123## Fase 18 — Red: virtio-net y el primer hola al exterior — 2026-05-23
1124
1125renaser hablaba consigo mismo. Con la misma plantilla del disco —enumerar
1126PCI, montar el transporte virtio, ceder a `virtio-drivers` el diálogo de
1127bajo nivel— y reutilizando el `KernelHal` y el mapeador `memory::mmio`,
1128el kernel abre ahora una boca y una oreja al exterior: una tarjeta de red.
1129
1130### Añadido
1131- **Driver `drivers/red`** — monta `VirtIONet<KernelHal, PciTransport, 16>`,
1132  enruta su IRQ vía el PIC, expone `enviar(frame)` y `drenar_rx(callback)`.
1133  La cabeza del archivo guarda el MAC del dispositivo y las constantes de
1134  IP de QEMU user-mode (`10.0.2.15` invitado, `10.0.2.2` gateway).
1135- **`componer_arp_request(mac, ip, objetivo)`** — construye un frame
1136  Ethernet + ARP listo para enviar.
1137- **`interrupts::registrar_irq_red`** + `extern "x86-interrupt" irq_red`
1138  — mismo patrón que la IRQ del disco. Acknowledge en el dispositivo,
1139  EOI al PIC.
1140- **`KernelHal` se hace `pub`** para que `red` lo reutilice como su Hal
1141  de DMA — la misma arena de marcos físicos que el disco. El mapeador
1142  MMIO (Fase 13.5) cubre los BAR del nuevo dispositivo sin tocar nada.
1143- **`tarea_red`**: tras 10 fotogramas para que la cola RX se estabilice,
1144  envía un ARP request al gateway de QEMU. Después, cada fotograma drena
1145  la cola RX y vuelca cada paquete a COM1.
1146- **QEMU args**: `-netdev user,id=net0 -device virtio-net-pci,netdev=net0`
1147  (user-mode networking, NAT virtual al host).
1148
1149### Verificado
1150- QEMU con `-object filter-dump,...,file=/tmp/red.pcap`. Trazas COM1:
1151  ```
1152  red :: virtio-net :: MAC 52:54:00:12:34:56 :: IRQ Some(11)
1153  red :: ARP REQUEST enviado :: ¿quien tiene 10.0.2.2?
1154  red :: RX 64 bytes :: dst=52:54:00:12:34:56 src=52:55:0a:00:02:02 type=0x0806
1155  ```
1156  El src del paquete entrante codifica la IP del gateway dentro del MAC
1157  (`52:55:0a:00:02:02` = QEMU prefix + `10.0.2.2`): la respuesta ARP es
1158  legítima. Renaser ya habla con la red.
1159
1160---
1161
1162## Fase 19 — Voz del userspace hacia la red — 2026-05-23
1163
1164La Fase 18 hizo que el kernel hablara con la red. La Fase 19 le da la
1165misma capacidad a los apps: tres capacidades nuevas (`sys_net_mac`,
1166`sys_net_enviar`, `sys_net_recibir`) y una primera app que las usa.
1167
1168### Añadido
1169- **`drivers::red::recibir_en(buf) -> usize`** — recibe UN paquete por
1170  llamada, lo copia al buffer del llamante, recicla el descriptor a la
1171  cola RX. Devuelve 0 si no hay paquete pendiente. El gemelo cooperativo
1172  de `drenar_rx`, pensado para la interfaz que el host expone a los apps.
1173- **Tres capacidades nuevas en `wasm/env`** (índices 12, 13, 14):
1174  - `sys_net_mac(salida: u32) -> i32` — escribe seis bytes con la MAC del
1175    dispositivo en `(salida, 6)`. Devuelve `0` OK o `-1` si no hay red.
1176  - `sys_net_enviar(ptr: u32, len: u32) -> i32` — envía el frame Ethernet
1177    crudo. Devuelve `0` OK, `-1` red caída, `-2` longitud inválida,
1178    `-3` rango fuera de la memoria lineal.
1179  - `sys_net_recibir(salida: u32, capacidad: u32) -> i32` — drena UN
1180    paquete (si hay) hacia `(salida, capacidad)`. Devuelve los bytes
1181    copiados, `0` si nada pendiente, `-1` si no hay red, `-2` si la
1182    capacidad excede la memoria lineal.
1183  Todas validan `rango()` contra la memoria del módulo y registran
1184  diagnóstico por COM1 si falla la validación.
1185- **App `pregon` (`apps/pregon/`, 4.2 KiB WASM)** — la primera voz del
1186  userspace hacia la red. Al arrancar:
1187  1. Pide su MAC al host vía `sys_net_mac`.
1188  2. Si la tiene, anuncia su presencia con un broadcast Ethernet
1189     (destino `FF:FF:FF:FF:FF:FF`, EtherType experimental `0x88B5`,
1190     payload ASCII `"renaser :: hola desde mi red"` — 42 bytes en el cable).
1191  En cada `tick` drena un paquete con `sys_net_recibir` y pinta:
1192  el título, el MAC propio, las cuentas TX/RX, y los datos del último
1193  frame entrante (tamaño, EtherType, src MAC) — todo en tipografía 8×8
1194  embebida (crate `font8x8`), escalada x2 sobre un lienzo 480×160 índigo.
1195- **`pregon` en el manifiesto de genesis** (`boot/src/main.rs`): la novena
1196  app, posición 2 (detrás de `bitacora`). El disco se siembra ahora con
1197  9 entradas; el kernel reporta `manifiesto :: 9 apps nacidas del grafo`.
1198
1199### Cambiado
1200- **`tarea_red`** del kernel ya no drena la cola RX: la posesión de la
1201  cola pasa al userspace vía `sys_net_recibir`. La tarea conserva el
1202  envío del ARP de prueba al gateway en cuanto el dispositivo se
1203  estabiliza, y termina.
1204- **`CELDA_TASKBAR_ANCHO`** baja de 130 a 116 px para que las nueve
1205  pestañas + lanzador + reloj caben holgadas en 1280 px.
1206- **`ETHER_TYPE_RENASER`** del driver pasa a `#[allow(dead_code)]` —
1207  ahora vive en el userspace (en `pregon`); el kernel la conserva como
1208  referencia para futuras pilas nativas.
1209
1210### Verificado
1211- QEMU headless con `-object filter-dump,id=fd,netdev=net0,file=/tmp/renaser.pcap`.
1212  El pcap captura tres frames en orden:
1213  ```
1214  #1 incl=42 dst=ff:ff:ff:ff:ff:ff src=52:54:00:12:34:56 etype=0x88b5
1215     payload: 'renaser :: hola desde mi red'
1216  #2 incl=42 dst=ff:ff:ff:ff:ff:ff src=52:54:00:12:34:56 etype=0x0806 (ARP REQ)
1217  #3 incl=64 dst=52:54:00:12:34:56 src=52:55:0a:00:02:02 etype=0x0806 (ARP REP)
1218  ```
1219  El primero es `pregon`; el segundo el kernel; el tercero es el gateway
1220  respondiéndonos — el ciclo completo de salida y entrada en una sola
1221  fase.
1222- Captura de pantalla: la ventana `pregon :: voz hacia la red` aparece
1223  teselada, su pestaña sale en la barra de tareas entre `bitacora` y
1224  `tonada`, y la consola anuncia `manifiesto :: 9 apps nacidas del grafo`.
1225
1226---
1227
1228## Fase 20 — Akasha Over Ether: el grafo distribuido — 2026-05-23
1229
1230La red dejo de ser un puro medio para mensajes ad-hoc y se vuelve el cable
1231por donde el GRAFO de objetos se extiende a otras maquinas renaser. Tres
1232mensajes bastan; `postcard` los serializa; el EtherType `0x88B5` los
1233transporta; ningun byte de TCP, IP, DNS o cabecera transporte de los anos 80.
1234
1235### Añadido
1236- **Crate `akasha/`** — nucleo `#![no_std]` compartido (gemela de `formato`),
1237  excluida del workspace anfitrion. Define `MensajeAkasha` con tres
1238  variantes —`SolicitarObjeto(id)`, `ProveedorObjeto(id, payload)`,
1239  `AnunciarRaiz(id)`—, las helpers `componer_frame(src, dst, msg)` y
1240  `analizar_frame(bytes) -> Result<(Mac, MensajeAkasha)>`, y las constantes
1241  `ETHER_TYPE_AKASHA = 0x88B5`, `MAX_PAYLOAD_AKASHA = 1486`. Seis pruebas
1242  unitarias cubren el ida-y-vuelta de cada variante, la distincion entre
1243  EtherType ajeno, frame truncado y payload basura.
1244- **Modulo `kernel/src/akasha.rs`** — el respondedor del nucleo. Tres oficios:
1245  1. **Demuxer** (`drenar_y_demultiplexar`): drena la cola RX del dispositivo
1246     virtio-net y demultiplexa: frames AoE con payload valido los procesa
1247     `procesar`; el resto va a la cola del userspace.
1248  2. **Atencion de mensajes** (`procesar`): `SolicitarObjeto` busca en
1249     `almacen` y, si tiene el objeto, responde `ProveedorObjeto` unicast;
1250     `ProveedorObjeto` verifica integridad (`blake3(payload) == id`) y
1251     absorbe al grafo local con `almacen::almacenar`; `AnunciarRaiz` se
1252     contabiliza y, si no tenemos el nodo, le solicita al emisor.
1253  3. **Faro** (`difundir_raiz` cada 5 s): difunde por broadcast el hash del
1254     manifiesto actual — el faro Akasha que delata nuestra presencia en la
1255     red de capa-2.
1256- **Cola del userspace** — el demuxer empuja los frames no-AoE a una
1257  `VecDeque<Vec<u8>>` con profundidad maxima 64; `sys_net_recibir` ahora
1258  pasa por `akasha::pop_usuario(buf)` en lugar de leer directamente del
1259  driver. Asi el kernel filtra Akasha sin disputarle paquetes al userspace.
1260- **`tic_compositor`** — el punto de entrada de los oficios per-fotograma de
1261  AoE, llamado desde el tic del compositor (cuyo latido es fiable). El
1262  demuxer corre en cada vuelta; el faro se dispara cuando el reloj monotono
1263  cruza `INTERVALO_FARO_MS` (5 s).
1264- **Contadores** (`ResumenAkasha`): `rx_solicitudes`, `rx_proveedores`,
1265  `rx_anuncios`, `tx_*` equivalentes, `rx_descartados`, `usuario_encolados`,
1266  `usuario_desbordados`. Quedaran de fundamento para un futuro indicador AoE
1267  en la barra de tareas.
1268
1269### Cambiado
1270- **`tarea_red`** vuelve a su rol simple: enviar UN ARP request al gateway de
1271  QEMU al arrancar. El demuxer y el faro pasaron al tic del compositor —
1272  cuyo latido cooperativo es el unico GARANTIZADO bajo carga del reactor.
1273  Un bucle propio en `tarea_red` (intentado primero) escala mal porque la
1274  cadencia de `EsperaFrame::await` se degrada cuando los apps WASM bajo
1275  `wasmi` ocupan el reactor (cada tic de app son decenas de ms de
1276  interprete); medirlo contra el reloj monotono y enganchar al compositor
1277  resuelve la falla con limpieza.
1278- **`sys_net_recibir`** lee de la cola del userspace que mantiene `akasha`,
1279  no del driver. El bloque doc en `wasm/env.rs` lo refleja.
1280- **`drivers::red::recibir_en`** queda como primitiva del driver con
1281  `#[allow(dead_code)]`: ya no la consume el sistema, pero la conservamos
1282  para futuras herramientas de diagnostico que quieran leer la cola RX cruda.
1283- **Demuxer politico**: frames `0x88B5` con payload basura para postcard —
1284  por ejemplo el saludo en texto plano de `pregon`— se cuentan en
1285  `RX_DESCARTADOS` pero TAMBIEN viajan a la cola del userspace. Asi pregon
1286  o cualquier otro app que comparta el EtherType experimental pueda
1287  recibirlos sin que el kernel se los robe.
1288
1289### Verificado
1290- `cargo test -p akasha` — seis pruebas en verde:
1291  ```
1292  test pruebas::anuncio_de_raiz_viaja_compacto ... ok
1293  test pruebas::componer_y_analizar_solicitud_es_simetrico ... ok
1294  test pruebas::ethertype_ajeno_se_distingue ... ok
1295  test pruebas::frame_demasiado_corto_se_distingue ... ok
1296  test pruebas::payload_invalido_se_distingue ... ok
1297  test pruebas::proveedor_lleva_payload_arbitrario ... ok
1298  ```
1299- QEMU headless 60 s, `-object filter-dump,file=/tmp/renaser-aoe.pcap`. El
1300  pcap captura 14 frames:
1301  ```
1302  ARP: 2  (request del kernel + reply del gateway)
1303  pregon: 1  (saludo texto plano)
1304  AnunciarRaiz: 11  (faros del kernel, Δ promedio 5.86 s sobre 60 s)
1305  ```
1306  Cada AnunciarRaiz lleva como payload el hash del manifiesto:
1307  `2f3deadfcc7dae25..` (postcard de 33 bytes — variante + hash — sobre un
1308  frame Ethernet total de 47 bytes).
1309- COM1 vuelca `akasha :: ANUNCIO emitido :: raiz=2f3deadfcc7dae25..` por
1310  cada disparo; en una corrida de 60 s aparecen 11 anuncios consecutivos.

CHANGELOG.md

Registro de cambios — renaser

Registro técnico detallado, fase a fase, en orden cronológico. Formato inspirado en Keep a Changelog. Para la crónica en lenguaje llano, ver DIARIO.md; para el estado y los planes, ROADMAP.md.


Fase 1 — El primer microsegundo — 2026-05-21

Infraestructura

Añadido

Notas


Fase 1.5 — Empaquetado y arranque — 2026-05-21

Cambiado

Añadido

Toolchain

Corregido

Verificado


Fase 2.0 — Cimientos del manejo de fallos — 2026-05-21

Añadido

Corregido

Verificado


Fase 2.1 — Interrupciones de hardware — 2026-05-21

Añadido

Verificado


Fase 3 — Memoria dinámica y reactor asíncrono — 2026-05-21

Añadido

Cambiado

Corregido

Verificado


Fase 4 — El escudo de aislamiento WASM — 2026-05-21

Añadido

Cambiado

Corregido

Verificado


Mantenimiento — Estructura, documentación e integración — 2026-05-21

Cambiado

Añadido

Seguridad


Fase 5 — Multitarea cooperativa, guardarrail de fuel y reloj — 2026-05-22

Unificación de la Fase 3 (reactor) y la Fase 4 (WASM): el userspace deja de ser una sola app que monopoliza la CPU y pasa a ser un conjunto de aplicaciones cooperativas, aisladas también en el TIEMPO.

Añadido

Cambiado

Verificado


Fase 6.0 — Cuotas de memoria y ciclo de vida del userspace — 2026-05-22

El aislamiento de las aplicaciones, que la Fase 5 hizo temporal (combustible), se completa ahora en la dimensión espacial (memoria) y se cierra la fuga de recursos del ciclo de vida.

Añadido

Verificado


Fase 6.1a — Sonda PCI y disco de pruebas — 2026-05-22

Primer paso de la estrategia incremental hacia el almacenamiento: derribar el muro del descubrimiento de hardware antes de diseñar nada encima.

Añadido

Cambiado

Verificado


Fase 6.1b — HAL, DMA y lectura del sector 0 — 2026-05-22

Segundo paso del sustrato de almacenamiento: el diálogo real con el disco. El kernel monta el dispositivo virtio-blk y lee su primer sector por DMA.

Añadido

Cambiado

Verificado


Fase 6.1c — El grafo de objetos direccionado por contenido — 2026-05-22

Tercer y último paso del sustrato de almacenamiento. El disco deja de ser un dispositivo que se sondea y pasa a ser una MEMORIA QUE PERDURA: un grafo dirigido acíclico de objetos direccionados por contenido — no un sistema de archivos plano POSIX.

Añadido

Cambiado

Notas

Verificado


Fase 6.2 — E/S de disco asíncrona por interrupción — 2026-05-22

La Fase 6.1 hizo hablar al disco, pero por SONDEO: el procesador se quedaba en espera activa vigilando el used ring de virtio, incapaz de atender nada más. La 6.2 libera el planificador cooperativo — la E/S de bloques pasa a ser REACTIVA, guiada por la interrupción física del dispositivo.

Añadido

Cambiado

Decisiones de ingeniería

Verificado

Fase 7 — Apertura: Apps que nacen del Grafo — 2026-05-22

Apertura de la Fase 7. Este commit asienta el plan y el andamiaje; no cambia el comportamiento del kernel. La Fase 7 destierra el include_bytes! del userspace: las aplicaciones pasan a ser objetos del grafo, y un Manifiesto de Génesis —también en el grafo— dicta qué arranca, con qué cuota y en qué región.

Añadido

Notas

Fase 7a — Apps que nacen del Grafo: el Manifiesto — 2026-05-22

El kernel deja de empotrar el userspace. Las cinco aplicaciones ya no llegan por include_bytes! en main.rs: nacen del grafo de objetos, gobernadas por un Manifiesto de Génesis que también vive en el grafo.

Añadido

Cambiado

Notas

Verificado

Fase 7b — La imagen sembrada por boot; muere el include_bytes! — 2026-05-22

El kernel deja de empotrar el userspace POR COMPLETO. Hasta la 7a, el bytecode de las cinco apps de génesis aún viajaba dentro del binario del kernel (include_bytes!), como semilla transitoria. La 7b lo mata: es el constructor de imagen boot —en el anfitrión— quien siembra el disco con el grafo ya poblado. El binario del kernel ya no carga ni un solo .wasm.

Añadido

Cambiado

Verificado

Fase 7c — Persistencia inter-sesión: cada app recuerda lo suyo — 2026-05-22

La cronista dejaba huella, pero en la RAÍZ del grafo — un ancla única que sólo una app puede usar. La 7c estrena la persistencia POR-APP: cada EntradaApp del Manifiesto de Génesis tiene su propia ranura estado, y una app guarda y recobra lo suyo sin pisar a nadie. El estado de cada aplicación sobrevive, por separado, a un reinicio.

Añadido

Cambiado

Verificado

Fase 8a — El compositor teselante — 2026-05-22

Hasta la Fase 7, cada app llevaba su región escrita a mano en el manifiesto — coordenadas fijas, una composición rígida. La Fase 8 entrega esa decisión a un COMPOSITOR: el kernel ya no coloca las ventanas a mano, las TESELA con mirada-layout.

Añadido

Cambiado

Verificado

Fases 8b y 8c — El escritorio interactivo — 2026-05-22

El compositor de la Fase 8a teselaba, pero era inmóvil. Las Fases 8b y 8c lo hacen VIVO: el teclado reordena el escritorio en caliente y mueve el foco. Y para que las apps estáticas no se queden atrás en una reordenación, el kernel asume la persistencia visual con una caché de fotogramas.

Añadido

Cambiado

Exclusión de interrupciones (guardarraíl)

Verificado

Fase 8d — Manipulación de ventanas — 2026-05-22

El escritorio de la 8c se podía recorrer con la vista —el foco— pero no reordenar. La 8d lo hace MANIPULABLE: el orden de teselado se separa de la identidad de las ventanas, y el teclado promueve y reordena.

Añadido

Cambiado

Verificado

Fase 9 — Orden-Z y ventanas flotantes — 2026-05-22

Hasta la 8d el escritorio era TESELADO puro: las ventanas repartían la pantalla sin solaparse jamás. La Fase 9 introduce un segundo modelo de composición —el SOLAPAMIENTO—: una ventana puede abandonar el teselado y FLOTAR, con un marco propio y libre, por encima de las demás.

Añadido

Cambiado

Verificado

Fase 10 — Alta y baja de aplicaciones en vivo — 2026-05-22

Hasta la Fase 9 el censo de aplicaciones se fijaba en el arranque: las apps nacían del manifiesto y sólo morían al fallar. La Fase 10 lo vuelve DINÁMICO — una app puede nacer o cerrarse con el reactor ya en marcha.

Añadido

Cambiado

Verificado

Fase 11 — El reloj del sistema como capacidad de host — 2026-05-22

Hasta la Fase 10 una aplicación sólo sabía CUÁNTAS veces la habían llamado —un tick tras otro—, no CUÁNTO tiempo había pasado. La Fase 11 le da al userspace un sentido del tiempo: el reloj monótono del sistema, como capacidad.

Añadido

Verificado

Fase 12 — La bocina del PC como capacidad de host — 2026-05-22

La Fase 11 le dio al userspace un reloj; la Fase 12, una voz. La bocina del PC —el canal 2 del PIT como generador de onda cuadrada— se suma a la matriz de capacidades. Hasta hoy renaser sólo sabía DIBUJAR para llamar la atención.

Añadido

Verificado

Fase 13 — Ratón, puntero y arrastre de flotantes — 2026-05-22

renaser tenía teclado y bocina como entrada/salida del usuario, pero no puntero. Las ventanas flotantes de la Fase 9 nacían en cascada y se quedaban ahí, clavadas. La Fase 13 trae el ratón: un puntero en pantalla, clic-para- enfocar y arrastre del marco de las ventanas flotantes.

Añadido

Verificado

Mapeador MMIO en el kernel — 2026-05-23

OVMF aloja los BAR prefetchables 64-bit de virtio en la ventana PCI de 64 bits, que el bootloader_api no mapea. KernelHal::mmio_phys_to_virt devolvía phys + offset a ciegas y el primer registro MMIO leído reventaba con #PF, dejando una franja roja sin pista visible. La autopsia: el commit anterior añadió el dump del panic-handler por COM1, que sacó a la luz el IP en unchecked_mul::precondition_check y la dirección de fallo offset + 0x800000014 — phys 32 GiB, donde OVMF había puesto el BAR.

Añadido

Cambiado

Fase 14 — La identidad del escritorio: nombres y barra de tareas — 2026-05-23

Las ventanas eran anónimas: el escritorio reía con sus colores pero no sabía cómo se llama lo que muestra. La Fase 14 le da nombre a cada cuarto y una barra al pie con la lista de quien vive en la casa.

Añadido

Cambiado

Verificado

Fase 15 — La voz del sistema — 2026-05-23

La bocina pertenecía a la ventana enfocada (Fase 12), pero el kernel necesita hablar también: un acorde al abrir la casa, un repique al recibir un inquilino nuevo, un bajo al echar a uno que se rompió. La Fase 15 le da al sistema su propia voz, prioritaria sobre las de los apps.

Añadido

Cambiado

Verificado

Fase 16 — La barra viva: lanzador y reloj — 2026-05-23

La barra de tareas era informativa pero pasiva: nombraba a los inquilinos sin ofrecerse a ningún gesto propio. La Fase 16 le añade los dos accesorios de toda barra de tareas digna: un botón lanzador a la izquierda y un reloj a la derecha, y le pone el reloj a latir cada segundo.

Añadido

Cambiado

Verificado

Fase 17 — bitacora :: el editor que recuerda — 2026-05-23

memoriosa (Fase 7c) demostró que un app podía persistir un contador a través de los reinicios. bitacora extiende esa demostración a un EDITOR de texto: tecleas, los caracteres aparecen; reinicias el kernel, el texto sigue ahí. La huella vive en el grafo de objetos, como todo lo demás.

Añadido — app bitacora (apps/bitacora/)

Cambiado

Verificado

Fase 18 — Red: virtio-net y el primer hola al exterior — 2026-05-23

renaser hablaba consigo mismo. Con la misma plantilla del disco —enumerar PCI, montar el transporte virtio, ceder a virtio-drivers el diálogo de bajo nivel— y reutilizando el KernelHal y el mapeador memory::mmio, el kernel abre ahora una boca y una oreja al exterior: una tarjeta de red.

Añadido

Verificado


Fase 19 — Voz del userspace hacia la red — 2026-05-23

La Fase 18 hizo que el kernel hablara con la red. La Fase 19 le da la misma capacidad a los apps: tres capacidades nuevas (sys_net_mac, sys_net_enviar, sys_net_recibir) y una primera app que las usa.

Añadido

Cambiado

Verificado


Fase 20 — Akasha Over Ether: el grafo distribuido — 2026-05-23

La red dejo de ser un puro medio para mensajes ad-hoc y se vuelve el cable por donde el GRAFO de objetos se extiende a otras maquinas renaser. Tres mensajes bastan; postcard los serializa; el EtherType 0x88B5 los transporta; ningun byte de TCP, IP, DNS o cabecera transporte de los anos 80.

Añadido

Cambiado

Verificado