gioser

tawasuyu/brahman

Rust 496TOML 195Markdown 55JSON 197001 Definiciones

feat(renaser): Fase 7 — apertura: plan y andamiaje del Manifiesto de Génesis

Abre la Fase 7 («apps que nacen del grafo»): destierra el include_bytes!
del userspace — las apps pasan a ser objetos del grafo, gobernadas por
un Manifiesto de Génesis que también vive en el grafo.

Este commit es sólo plan + andamiaje; el kernel se comporta idéntico a
la Fase 6.2.

- FASE7.md — el plan de ataque: el problema de la génesis, las
  sub-fases 7a/7b/7c y los guardarraíles.
- kernel/src/manifiesto.rs — andamiaje: tipos Manifiesto/EntradaApp +
  (de)serialización postcard completos; cargar/sembrar_genesis son
  esbozos hasta la 7a. Declarado en main.rs, aún sin cablear a
  kernel_main (#![allow(dead_code)] temporal).

CHANGELOG y DIARIO al día.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

4f31146533699ae931dbf21f680c8d06337d7b5d · por sergio · (hace 5 meses) · padre 4c3b02c33 · ver el árbol en este commit

Delta semántico · 9

renaser/kernel/src/main.rs

renaser/kernel/src/manifiesto.rs

5 archivos · +266 −0

diff --git a/renaser/CHANGELOG.md b/renaser/CHANGELOG.md
index db1b321..0de6bcc 100644
--- a/renaser/CHANGELOG.md
+++ b/renaser/CHANGELOG.md
@@ -455,3 +455,28 @@ REACTIVA, guiada por la interrupción física del dispositivo.
   testigo de integridad del DAG en verde — la nueva E/S asíncrona lee y escribe
   el grafo sin un solo fallo. Las cuatro apps WASM siguen su curso, sin un
   sobresalto ni un micro-congelamiento.
+
+## 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
+- `FASE7.md` — el plan de ataque: el objetivo, el problema huevo-y-gallina
+  de la génesis, las sub-fases 7a (manifiesto + carga desde el grafo, con
+  semilla por el kernel), 7b (siembra de la imagen por `boot`, muerte del
+  `include_bytes!`) y 7c (persistencia inter-sesión), y los guardarraíles.
+- `kernel/src/manifiesto.rs` — andamiaje del Manifiesto de Génesis. Los
+  tipos `Manifiesto` / `EntradaApp` y la (de)serialización `postcard` están
+  completos; `cargar` y `sembrar_genesis` son esbozos hasta la Fase 7a.
+  La sub-región se guarda en campos `u32` de ancho fijo (formato en disco),
+  no como `RegionPantalla` (`usize`, ancho de plataforma).
+
+### Notas
+- El módulo se declara en `main.rs` pero aún no se cablea a `kernel_main`
+  (`#![allow(dead_code)]` temporal, hasta que la 7a le dé un llamador). El
+  kernel compila y se comporta idéntico a la Fase 6.2 — nada observable que
+  verificar en QEMU en esta apertura.
diff --git a/renaser/DIARIO.md b/renaser/DIARIO.md
index 64bb2cc..e183f79 100644
--- a/renaser/DIARIO.md
+++ b/renaser/DIARIO.md
@@ -221,6 +221,24 @@ casilla más cada vez— pero que se sentirá en todo lo que venga después. El
 y renaser ya no se miran fijamente: se hablan, y entre frase y frase, cada cual
 atiende lo suyo.
 
+## El plano antes de la obra — abrir la Fase 7
+
+Hay jornadas en las que no se levanta un muro: se dibuja. Esta fue una de
+ésas. Antes de tocar el corazón del kernel hubo que decidir, con calma,
+hacia dónde mover la siguiente piedra.
+
+La pregunta era vieja y conocida: las aplicaciones de renaser todavía viajan
+escondidas dentro del propio kernel, cosidas a su binario. Pero renaser tiene
+un disco que recuerda, un tejido de objetos que perdura entre apagones. ¿Por
+qué, entonces, las apps no nacen de ahí, como todo lo demás?
+
+Se escribió el plano de esa mudanza —el plan de la Fase 7— y se dejó puesta
+la primera viga: un cuaderno nuevo, el «Manifiesto», donde algún día estará
+escrito qué programas deben despertar, cuánta memoria se les presta y en qué
+rincón de la pantalla viven. Por ahora el cuaderno está en blanco y sus
+páginas son sólo molde; pero el molde ya tiene la forma exacta de lo que
+vendrá. La casa no cambió aún — sólo sabe, por fin, hacia dónde va a crecer.
+
 ---
 
 *El diario continúa. La próxima página la escribirá la próxima jornada.*
diff --git a/renaser/FASE7.md b/renaser/FASE7.md
new file mode 100644
index 0000000..1379c0d
--- /dev/null
+++ b/renaser/FASE7.md
@@ -0,0 +1,97 @@
+# renaser — Fase 7 :: Apps que nacen del Grafo
+
+Plan de ataque. Para el estado general ver `ROADMAP.md`; para la arquitectura,
+`ARCHITECTURE.md`.
+
+## El objetivo
+
+Hoy el kernel lleva el userspace **empotrado en su propio binario**:
+`main.rs` tiene cuatro `include_bytes!` (`app.wasm`, `discola.wasm`,
+`glotona.wasm`, `cronista.wasm`) y cinco llamadas `encender_app` con regiones
+escritas a mano. Eso contradice la tesis de renaser: el almacenamiento es un
+**grafo de objetos direccionado por contenido**, no un binario monolítico.
+
+La Fase 7 **destruye el `include_bytes!` del userspace**. Las aplicaciones
+pasan a ser objetos del grafo; el kernel las descubre, las verifica por su
+hash y las inyecta en caliente en `wasmi`. Qué apps arrancan, con qué cuota y
+en qué región lo dicta un **Manifiesto de Génesis** que también vive en el
+grafo. (La fuente `font.ttf` sigue empotrada: es del kernel, no del userspace.)
+
+## El problema de la génesis
+
+Hay un huevo-y-gallina: el kernel lee el manifiesto para saber qué cargar,
+pero el manifiesto y el bytecode tienen que haber sido escritos antes. En un
+disco virgen no hay nada. Dos vías para sembrarlo:
+
+- **A — el kernel siembra.** En un disco sin manifiesto, el kernel graba en el
+  grafo el bytecode de unas apps de génesis y un manifiesto por defecto. El
+  bytecode de génesis llega vía `include_bytes!` — todavía.
+- **B — `boot` siembra la imagen.** El constructor de imagen (anfitrión)
+  pre-puebla el disco con los objetos de bytecode y el manifiesto. El kernel
+  jamás empotra una app.
+
+B es el estado final puro, pero exige enseñarle a `boot` el formato del grafo
+(objetos, BLAKE3, log, superbloque) — trabajo de anfitrión considerable. Se
+ataca **incremental**, como las Fases 6.1a/b/c.
+
+## Sub-fases
+
+### 7a — El Manifiesto y la carga desde el grafo (semilla por el kernel)
+
+1. **`manifiesto.rs`** *(andamiaje ya creado)* — tipos `Manifiesto` /
+   `EntradaApp`, (de)serialización postcard. Hoy es un módulo `no_std` del
+   kernel; no se comparte con las apps (la frontera app↔kernel es el ABI WASM
+   numérico, no tipos Rust).
+2. **`almacen.rs`** — el `SuperBloque` gana un segundo ancla,
+   `manifiesto: Option<Hash>`, gemelo de `raiz`. `VERSION` sube de `1` a `2`
+   (un disco v1 se reformatea, como ya hace `init` con discos ajenos).
+   Getters `manifiesto()` / `fijar_manifiesto()`.
+3. **`manifiesto::sembrar_genesis()`** — en un disco sin manifiesto: graba el
+   bytecode de las apps de génesis (`include_bytes!`, **sólo aquí**), compone
+   un `Manifiesto` por defecto con sus regiones y cuotas, lo graba y lo ancla.
+4. **`manifiesto::cargar()`** — lee el ancla del superbloque, recupera el
+   objeto, lo deserializa.
+5. **`kernel_main`** — reemplaza las cinco `encender_app` escritas a mano por:
+   cargar el manifiesto (o sembrarlo si falta) e iterar sus `EntradaApp`.
+6. **`wasm/mod.rs`** — el techo de memoria deja de ser la constante
+   `TECHO_MEMORIA` y pasa a venir por-app del manifiesto (`EntradaApp`).
+7. **Verificar en QEMU** — la pantalla debe verse idéntica a la Fase 6.2
+   (cinco apps en sus regiones), pero ahora **nacidas del grafo**.
+
+### 7b — La imagen sembrada por `boot`; muere `include_bytes!`
+
+- `boot` (anfitrión) aprende el formato del grafo y pre-puebla la imagen de
+  disco con los objetos de bytecode + el manifiesto.
+- El kernel pierde los `include_bytes!` del userspace; `sembrar_genesis` se
+  retira o queda como camino vacío de respaldo.
+
+### 7c — Persistencia inter-sesión
+
+- Una app (cronista evolucionada, o una nueva interactiva) guarda su estado
+  mutado como un objeto nuevo del grafo al recibir teclado.
+- Cada `EntradaApp` usa su campo `estado: Option<Hash>` (ya previsto en el
+  tipo). Al guardar, el kernel reescribe el manifiesto; al arrancar, le pasa
+  ese estado a la app para que despierte donde quedó.
+- Probablemente una capacidad host nueva para que el kernel se entere del
+  hash de estado nuevo que la app produjo.
+
+## Guardarraíles (directiva de la arquitecta)
+
+- **No romper `check-shared-cores.sh`.** Si algún tipo del manifiesto llegara
+  a compartirse entre apps y kernel, debe habitar un núcleo estrictamente
+  `no_std`. Hoy no se comparte: `manifiesto.rs` es kernel-only.
+- **Errores recuperables con `Result`.** Si el kernel lee del grafo un objeto
+  de bytecode corrupto (su hash recomputado ≠ su id), `almacen::recuperar` ya
+  lo detecta — devuelve `Err`. El kernel **niega esa instanciación**, pinta la
+  baliza de desalojo en la región de esa app, y **sigue levantando el resto**.
+  Un módulo podrido nunca tumba el arranque.
+
+## Estructura de archivos
+
+| archivo | estado | rol en la Fase 7 |
+| --- | --- | --- |
+| `kernel/src/manifiesto.rs` | **nuevo (7a)** | tipos `Manifiesto`/`EntradaApp` + (de)serialización + carga/siembra |
+| `kernel/src/almacen.rs` | a modificar (7a) | `SuperBloque.manifiesto`, `VERSION` 2, getters |
+| `kernel/src/main.rs` | a modificar (7a) | `kernel_main` itera el manifiesto en vez de `include_bytes!` |
+| `kernel/src/wasm/mod.rs` | a modificar (7a) | techo de memoria por-app desde el manifiesto |
+| `boot/` | a modificar (7b) | siembra la imagen de disco con el grafo |
diff --git a/renaser/kernel/src/main.rs b/renaser/kernel/src/main.rs
index 1c24ec9..2639674 100644
--- a/renaser/kernel/src/main.rs
+++ b/renaser/kernel/src/main.rs
@@ -18,6 +18,7 @@
 //    drivers      — descubrimiento de hardware y E/S de disco asincrona por
 //                   interrupcion: el bus PCI y el virtio-blk (Fases 6.1, 6.2).
 //    almacen      — el grafo de objetos direccionado por contenido (Fase 6.1c).
+//    manifiesto   — el Manifiesto de Genesis: que apps nacen del grafo (Fase 7).
 //    memory       — el heap dinamico del kernel (`#[global_allocator]`).
 //    async_system — el reactor cooperativo: ejecutor, tareas, wakers, teclado
 //                   y el reloj que marca el compas de los fotogramas (Fase 5).
@@ -50,6 +51,7 @@ mod drivers;
 mod gdt;
 mod grafico;
 mod interrupts;
+mod manifiesto;
 mod memory;
 mod pic;
 mod sync;
diff --git a/renaser/kernel/src/manifiesto.rs b/renaser/kernel/src/manifiesto.rs
new file mode 100644
index 0000000..d3b7467
--- /dev/null
+++ b/renaser/kernel/src/manifiesto.rs
@@ -0,0 +1,124 @@
+// =============================================================================
+//  renaser :: kernel/src/manifiesto.rs — Fase 7 :: el Manifiesto de Génesis
+// -----------------------------------------------------------------------------
+//  Hasta la Fase 6, el userspace venia EMPOTRADO en el binario del kernel:
+//  `include_bytes!` de cada `.wasm` y regiones escritas a mano. La Fase 7 lo
+//  destierra: las aplicaciones pasan a ser OBJETOS DEL GRAFO, y lo que arranca
+//  —con que cuota, en que region— lo dicta este Manifiesto de Genesis, que
+//  tambien habita el grafo. El superbloque guarda su hash en un ancla propia.
+//
+//  El kernel, al despertar: lee el ancla del superbloque, recupera el objeto
+//  del manifiesto, lo deserializa, y por cada `EntradaApp` recupera el objeto
+//  de bytecode —verificado por su hash— y lo inyecta en `wasmi`.
+//
+//  ESTADO: andamiaje de la Fase 7a. Los tipos y la (de)serializacion estan
+//  completos; `cargar` y `sembrar_genesis` son esbozos — se implementan al
+//  abordar la 7a, cuando el superbloque gane su campo `manifiesto`. Ver
+//  `FASE7.md` para el plan completo.
+// =============================================================================
+
+// Fase 7a en construccion: el modulo aun no se cablea a `kernel_main`. El
+// `allow` cae en cuanto `cargar`/`sembrar_genesis` tengan llamador real.
+#![allow(dead_code)]
+
+use alloc::string::String;
+use alloc::vec::Vec;
+
+use serde::{Deserialize, Serialize};
+
+use crate::almacen::Hash;
+use crate::grafico::RegionPantalla;
+
+/// Version del formato del manifiesto serializado. Independiente de la
+/// version del superbloque (`almacen::VERSION`): el manifiesto es un objeto
+/// del grafo, no una estructura de disco.
+pub const VERSION_MANIFIESTO: u32 = 1;
+
+/// El Manifiesto de Genesis: la lista de aplicaciones que el kernel instancia
+/// al arrancar. Vive como un objeto del grafo de objetos; el superbloque
+/// guarda su hash en el campo `manifiesto`.
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Manifiesto {
+    /// Version del formato — debe ser [`VERSION_MANIFIESTO`].
+    pub version: u32,
+    /// Las aplicaciones del userspace, en orden de arranque.
+    pub apps: Vec<EntradaApp>,
+}
+
+/// Una entrada del manifiesto: una aplicacion del userspace y todo lo que el
+/// kernel necesita para darle vida — su bytecode, su ventana, su cuota de
+/// memoria y, si lo tuviera, su ultimo estado persistido.
+#[derive(Serialize, Deserialize, Clone)]
+pub struct EntradaApp {
+    /// Nombre legible — para los rotulos de la consola y la baliza.
+    pub nombre: String,
+    /// Hash del objeto del grafo que contiene el bytecode WASM de la app.
+    pub bytecode: Hash,
+    /// Sub-region del framebuffer asignada a la app. Campos de ancho fijo
+    /// `u32` A PROPOSITO: esto es un formato EN DISCO. `RegionPantalla` usa
+    /// `usize` (ancho dependiente de plataforma) y no sirve para serializar.
+    pub region_x: u32,
+    pub region_y: u32,
+    pub region_ancho: u32,
+    pub region_alto: u32,
+    /// Techo de memoria lineal de la app, en bytes. Sustituye a la constante
+    /// global `wasm::TECHO_MEMORIA` — cada app lleva su cuota.
+    pub techo_memoria: u32,
+    /// Hash del ultimo estado persistido de la app (Fase 7c). `None` hasta
+    /// que la app guarde estado por primera vez.
+    pub estado: Option<Hash>,
+}
+
+impl EntradaApp {
+    /// Construye la `RegionPantalla` que el kernel entiende a partir de los
+    /// campos de ancho fijo del manifiesto.
+    pub fn region(&self) -> RegionPantalla {
+        RegionPantalla {
+            x: self.region_x as usize,
+            y: self.region_y as usize,
+            ancho: self.region_ancho as usize,
+            alto: self.region_alto as usize,
+        }
+    }
+}
+
+impl Manifiesto {
+    /// Serializa el manifiesto a su forma binaria `postcard` — la carga util
+    /// del objeto del grafo que lo aloja.
+    pub fn serializar(&self) -> Result<Vec<u8>, &'static str> {
+        postcard::to_allocvec(self).map_err(|_| "manifiesto :: serializacion fallida")
+    }
+
+    /// Reconstruye un manifiesto desde la carga util de su objeto. Rechaza
+    /// un formato de version desconocida en lugar de malinterpretarlo.
+    pub fn deserializar(bytes: &[u8]) -> Result<Manifiesto, &'static str> {
+        let (manifiesto, _) = postcard::take_from_bytes::<Manifiesto>(bytes)
+            .map_err(|_| "manifiesto :: deserializacion fallida")?;
+        if manifiesto.version != VERSION_MANIFIESTO {
+            return Err("manifiesto :: version de formato desconocida");
+        }
+        Ok(manifiesto)
+    }
+}
+
+/// Lee el manifiesto del grafo: toma su hash del ancla del superbloque,
+/// recupera el objeto y lo deserializa. `Ok(None)` si el disco aun no tiene
+/// manifiesto anclado — el caller debe entonces sembrar la genesis.
+///
+/// ANDAMIAJE (Fase 7a-4): depende de `almacen::manifiesto()` —el nuevo ancla
+/// del superbloque— todavia por implementar (tarea 7a-2).
+pub fn cargar() -> Result<Option<Manifiesto>, &'static str> {
+    todo!("Fase 7a-4: leer almacen::manifiesto(), recuperar el objeto y deserializar")
+}
+
+/// Siembra el grafo en un disco sin manifiesto: graba el bytecode de las
+/// aplicaciones de genesis, compone un `Manifiesto` por defecto con sus
+/// regiones y cuotas, lo graba y lo ancla en el superbloque. Devuelve el
+/// hash del manifiesto recien anclado.
+///
+/// ANDAMIAJE (Fase 7a-3): la semilla TRANSITORIA — en la 7a el bytecode aun
+/// llega vacia `include_bytes!`; la 7b mueve la siembra al constructor de
+/// imagen `boot` y elimina el empotrado del kernel.
+pub fn sembrar_genesis() -> Result<Hash, &'static str> {
+    todo!("Fase 7a-3: grabar los bytecodes de genesis + el manifiesto por defecto, y anclarlo")
+}