docs(renaser): plan de la Fase 21 — Atlas, el explorador del grafo

Plan trazado para mañana. Tres capas:

  1. Cuatro capacidades de host read-only (sys_grafo_manifiesto,
     sys_grafo_raiz, sys_grafo_recuperar, sys_grafo_hijo) que abren
     el grafo de objetos al userspace. Mismo patron de validacion
     de memoria que sys_net_*.

  2. La app 'atlas': lienzo ~520x400, lee el grafo perezosamente con
     cache LRU de 16 entradas, navega con flechas / Enter / Backspace
     desde el manifiesto hacia los hijos.

  3. Representacion radial: foco central con su firma cromatica
     (3 primeros bytes del hash), hijos en circulo, padre en cima
     si hay historial, cartela inferior con hash completo + tamaño
     + previsualizacion (texto si pasa el test ASCII, hex si no).

Subfases 21a (caps), 21b (app navegable), 21c (paseo guiado).

Sinergias y mejoras del sistema documentadas: dedup visual del
grafo direccionado por contenido, integridad por uso (cada
navegacion rehashea), validacion del almacen, camino natural a la
escritura (Fase 22), encaje con el faro Akasha de la Fase 20
(quien recibe AnunciarRaiz puede explorar la raiz del par).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-23 05:24:35 +00:00
parent 42fee6fcbc
commit 12e3b1d4d0
+245
View File
@@ -0,0 +1,245 @@
# Fase 21 — Atlas :: el explorador del grafo
> *El paralelo a un explorador de archivos en un sistema que no tiene
> archivos. renaser nunca tuvo carpetas: tiene un grafo de objetos
> direccionado por contenido (BLAKE3 + postcard sobre virtio-blk). Lo
> que ha estado bajo el agua desde la Fase 7 sale a la superficie con
> una piel navegable.*
---
## 1. La idea
`atlas` es la primera app que **lee** del grafo persistente desde el
userspace. Hasta hoy, solo el kernel toca el grafo: las apps ven su
ranura `EntradaApp.estado` y nada más. Esta fase invierte la asimetría:
las apps reciben tres capacidades de inspección read-only y `atlas` las
ejerce mostrando, en pantalla, la topología del grafo —el manifiesto,
los bytecodes, los estados, las raíces— como un atlas de regiones
conectadas por aristas.
La elección de la metáfora es deliberada. Un explorador de archivos
representa un sistema jerárquico (Unix) o un sistema con etiquetas y
versiones (Plan 9, Git). El grafo de renaser no es ninguno de los dos:
es un DAG inmutable, direccionado por contenido, con dos anclas
(`raiz`, `manifiesto`). El paralelo natural es un **atlas**: un mapa de
territorios donde cada nodo es una localidad, cada arista una frontera,
y la identidad de la localidad es su contenido —no un nombre—.
## 2. Tres capas, tres responsabilidades
### Capa 1 — capacidades de host `sys_grafo_*` (kernel/src/wasm/env.rs)
Cuatro caps nuevas. Todas read-only. Validan rango contra la memoria
lineal del módulo. Idéntico patrón a los `sys_net_*` de la Fase 19.
| Capacidad | Firma | Salida | Errores |
|---|---|---|---|
| `sys_grafo_manifiesto` | `(hash_out: u32) -> i32` | escribe 32 B del hash del manifiesto | `-1` sin ancla |
| `sys_grafo_raiz` | `(hash_out: u32) -> i32` | escribe 32 B del hash de la raíz | `-1` sin ancla |
| `sys_grafo_recuperar` | `(hash_in, datos_out, datos_cap, info_out) -> i32` | escribe `datos` truncados a `datos_cap` + struct `{datos_total_len, n_hijos, datos_escritos}` (12 B) en `info_out` | `-1` objeto no encontrado, `-2` rango fuera de memoria |
| `sys_grafo_hijo` | `(hash_in, indice, hash_out) -> i32` | escribe el hash del hijo `indice` | `-1` objeto ausente, `-2` índice fuera de rango |
Punto delicado: `sys_grafo_recuperar` **rehashea** el payload servido
para que el módulo nunca reciba bytes corruptos atribuidos a un hash
que no les corresponde. El grafo ya hace esto en `almacen::recuperar`;
heredamos esa garantía sin código nuevo.
### Capa 2 — la app `atlas` (apps/atlas/, wasm32)
Lienzo natural ~520×400, en la misma paleta índigo del compositor.
Estado mínimo:
```rust
struct Vista {
foco: Hash, // nodo central
historial: Vec<Hash>, // breadcrumbs para volver
cache: BTreeMap<Hash, NodoInfo>, // LRU pequeña (16 entradas)
seleccionado: usize, // qué hijo está marcado
}
struct NodoInfo {
hash: Hash,
datos_preview: [u8; 96], // primeros bytes del payload
datos_len_total: u32, // tamaño completo del payload
n_hijos: u32, // cuántas aristas salen
hijos: Vec<Hash>, // resueltos perezosamente
}
```
Flujo:
1. Al `init`, llamar `sys_grafo_manifiesto` y empezar centrados ahí.
2. En cada `tick`, recoger teclas del compositor (ya las recibe la
ventana enfocada vía la Fase 8c) y mover el cursor / cambiar de
nodo en consecuencia.
3. Cualquier nodo nuevo se carga perezosamente con `sys_grafo_recuperar`
y se cachea.
Navegación:
- ← / → mueven el cursor entre hijos del foco actual.
- Enter desciende: el hijo seleccionado pasa a ser el nuevo foco; el
anterior va al historial.
- Backspace asciende: vuelve al último foco del historial.
- Esc reinicia al manifiesto.
- `r` re-ancla al `raiz` si la hay (toggle manifiesto / raíz).
### Capa 3 — la pintura: «la elegancia de la graficación»
La representación es **radial**, no lista. Es lo que distingue a
`atlas` de un explorador de archivos clásico:
```
┌──────────────┐
│ PADRE │
│ hash... │
└──────┬───────┘
┌──────┴───────┐
│ │
│ FOCO │ ← centrado, grande
│ hash + meta │
└──┬──┬──┬─────┘
│ │ │
┌───────┘ │ └───────┐
┌───┴──┐ ┌────┴───┐ ┌──┴──┐
│ HIJO │ │ HIJO │ │HIJO │
│ ● │ │ ● │ │ ● │ ← círculo radial
└──────┘ └────────┘ └─────┘
```
- **Foco**: en el centro del lienzo, recuadro grande con su hash
abreviado (primeros 8 hex), tamaño del payload, número de hijos.
Color del recuadro derivado de los primeros 3 bytes del hash —cada
objeto tiene su propia firma cromática estable—.
- **Hijos**: hasta 8 a la vez, dispuestos en círculo alrededor del foco.
Si hay más, una pequeña indicación `+N` en la circunferencia, con
teclas `[` / `]` para rotar la página de hijos visible.
- **Padre**: si hay historial, se dibuja como una pista flotante arriba,
conectada al foco por una arista vertical.
- **Aristas**: trazadas con Bresenham, color tenue (`TINTA_TENUE`); la
arista al hijo seleccionado se realza en `ETIQUETA`.
- **Cartela inferior**: la huella completa (32 hex), el tamaño total y
la previsualización del payload —si los primeros bytes pasan el test
ASCII imprimible, se muestra como texto; si no, como hex—.
Cada **previsualización** es lo que cierra la metáfora: un objeto que
contiene texto (la bitácora persistida, el manifiesto serializado) se
delata por su payload legible. Uno binario muestra su silueta en hex.
## 3. Subfases y criterios de cierre
**21a — capacidades de host** *(≈ 1 sesión corta)*
- [ ] Añadir las 4 caps al `wasm/env.rs` con sus rangos validados.
- [ ] Almacenar tabletas de tests: pedir el manifiesto y un nodo
conocido desde una app de prueba.
- [ ] Cerrar cuando una mini-app de prueba imprime el hash del
manifiesto en pantalla.
**21b — la app `atlas` con navegación radial** *(≈ 1 sesión larga)*
- [ ] Crate `apps/atlas/` con `font8x8`, `init` carga el manifiesto.
- [ ] Pintar el foco + sus hijos en círculo, sin selección.
- [ ] Teclado: cursor entre hijos, Enter descender, Backspace volver.
- [ ] Cartela con hash + tamaño + previsualización.
- [ ] Añadir a `GENESIS` (10ª app); `CELDA_TASKBAR_ANCHO` 116→106 px
para que las diez pestañas caben holgadas en 1280 px.
- [ ] Cerrar cuando se verifica en QEMU: arranca apuntando al
manifiesto, se navega a un hijo (un bytecode WASM), se ve el
tamaño y los primeros bytes; se vuelve con Backspace.
**21c — el paseo guiado** *(opcional, si queda margen)*
- [ ] Tecla `r` alterna entre las dos anclas (manifiesto / raíz).
- [ ] Indicador de la naturaleza del nodo: si su `datos` deserializa
como `Manifiesto`, mostrar «manifiesto»; si es un `.wasm`
(magic bytes `\0asm`), mostrar «bytecode»; si es texto ASCII,
mostrar «texto»; el resto «opaco».
- [ ] Pruebas integradas con la sustitución del disco (eliminar
`target/disk.img` y re-sembrar): `atlas` muestra el árbol de
semilla; tras editar en `bitacora`, `atlas` revela el nuevo
`estado` colgando del manifiesto.
## 4. Qué eficiencia aprovecha
- **Direccionamiento por contenido**: cuando dos apps comparten
bytecode (ej. dos `hola` clonadas), `atlas` lo muestra como UN nodo
con dos aristas entrantes —la dedup ya inherente al grafo se vuelve
visible—.
- **`almacen::recuperar` es O(1)**: el índice `BTreeMap<Hash, sector>`
ya está construido; cada navegación es una sola consulta + una
lectura de disco asíncrona (Fase 6.2).
- **Lazy loading + cache**: solo cargamos lo que está a un salto del
foco actual. El cache LRU de 16 entradas cubre la cuenca de
navegación humana.
- **El grafo es pequeño hoy** (~25 objetos). Una visualización completa
brute-force ya cabría —no necesitamos algoritmos sofisticados de
layout—.
## 5. Qué mejora el sistema (no solo añade)
1. **Primer caso de uso del grafo desde el userspace**. Asentamos el
patrón que toda app futura que quiera leer datos compartidos seguirá.
2. **Surface lo invisible**. El grafo era una propiedad estructural
indirecta; ahora es un objeto observable. Los bugs del almacén (si
los hubiera) se delatan visualmente.
3. **Validación de integridad por uso**. Cada navegación rehashea el
payload servido. Si un sector se corrompe en virtio-blk, `atlas` lo
descubrirá antes que ningún test.
4. **Camino natural a Fase 22 (escritura)**. Una vez que las apps leen,
la siguiente capacidad lógica es escribir: `sys_grafo_almacenar`,
`sys_grafo_anclar_raiz`. `atlas` se convertirá entonces en un
editor del grafo, no solo un explorador.
5. **Sinergia con Akasha** (Fase 20). El `AnunciarRaiz` que emite el
kernel anuncia el hash del manifiesto. Quien reciba el faro y use
`atlas` podrá descubrir qué tiene un par —el explorador y el
protocolo distribuido se complementan—.
## 6. Riesgos y mitigaciones
- **WASM lento bajo wasmi**: el descubrimiento de la Fase 20 (los apps
degradan la cadencia del reactor a ~600 ms/iter cuando hay muchos)
aplica aquí. `atlas` debe pintar UNA vez por interacción, no por
fotograma; usar el lienzo cacheado entre teclas.
- **Lienzo grande**: 520×400 a 4 B/píxel = 832 KiB. Cabe en el techo
de 4 MiB de cada app. Sin holgura para optimizar, pero suficiente.
- **Profundidad del grafo**: si el grafo creciera mucho, la navegación
con cursor entre 8 hijos visibles a la vez se quedaría corta. El
toggle `[` / `]` de rotación basta para hoy; un Fase 22 puede
introducir búsqueda por prefijo de hash.
## 7. Cosecha visual prevista
Al cerrar la Fase 21, una captura headless mostrará algo así (el
contenido exacto cambia con el grafo):
```
┌── atlas :: el explorador del grafo ──────────────────────┐
│ │
│ ┌─────────────┐ │
│ │ raiz·?? │ (si la hay, en cima) │
│ └──────┬──────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ MANIFIESTO │ ← foco │
│ │ 2f3deadf... │ │
│ │ 9 hijos │ │
│ │ 510 bytes │ │
│ └─┬─┬─┬─┬─┬───┘ │
│ ┌─────────┘ │ │ │ │ └─────────┐ │
│ ┌──┴──┐ ┌───┴─┴─┴───┐ ┌────┴──┐ │
│ │ ● │ │ ● │ │ ● │ │
│ │bita-│ │ pregon.. │ │ tonada│ ... │
│ │cora │ │ │ │ │ │
│ └─────┘ └─────────────┘ └───────┘ │
│ │
│ ─── 2f3deadfcc7dae25c4d... (32 hex) ─ 510 B ─ texto: ─── │
│ {"version":1,"apps":[{"nombre":"bitacora","bytecode":.. │
└──────────────────────────────────────────────────────────┘
```
El sistema, que hasta hoy era un grafo silencioso, gana un mapa.