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>
This commit is contained in:
sergio
2026-05-22 15:02:19 +00:00
parent 4c3b02c337
commit 4f31146533
5 changed files with 266 additions and 0 deletions
+25
View File
@@ -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 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 el grafo sin un solo fallo. Las cuatro apps WASM siguen su curso, sin un
sobresalto ni un micro-congelamiento. 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.
+18
View File
@@ -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 y renaser ya no se miran fijamente: se hablan, y entre frase y frase, cada cual
atiende lo suyo. 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.* *El diario continúa. La próxima página la escribirá la próxima jornada.*
+97
View File
@@ -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 |
+2
View File
@@ -18,6 +18,7 @@
// drivers — descubrimiento de hardware y E/S de disco asincrona por // drivers — descubrimiento de hardware y E/S de disco asincrona por
// interrupcion: el bus PCI y el virtio-blk (Fases 6.1, 6.2). // interrupcion: el bus PCI y el virtio-blk (Fases 6.1, 6.2).
// almacen — el grafo de objetos direccionado por contenido (Fase 6.1c). // 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]`). // memory — el heap dinamico del kernel (`#[global_allocator]`).
// async_system — el reactor cooperativo: ejecutor, tareas, wakers, teclado // async_system — el reactor cooperativo: ejecutor, tareas, wakers, teclado
// y el reloj que marca el compas de los fotogramas (Fase 5). // y el reloj que marca el compas de los fotogramas (Fase 5).
@@ -50,6 +51,7 @@ mod drivers;
mod gdt; mod gdt;
mod grafico; mod grafico;
mod interrupts; mod interrupts;
mod manifiesto;
mod memory; mod memory;
mod pic; mod pic;
mod sync; mod sync;
+124
View File
@@ -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")
}