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:
@@ -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.
|
||||||
|
|
||||||
Reference in New Issue
Block a user