feat: dominium standalone — simulador de campo medio sobre Llimphi

Front-door publicable de dominium: los 9 crates propios como path
members; Llimphi, app-bus, rimay-localize, wawa-config y pluma-notebook
por git-dep al monorepo tawasuyu.git (branch=main). cargo check
--workspace --all-targets pasa exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-16 23:22:40 +00:00
commit 1860b51f70
70 changed files with 19902 additions and 0 deletions
@@ -0,0 +1,115 @@
//! Showcase end-to-end del `DominiumKernel` sobre el motor de
//! notebooks.
//!
//! Arma un notebook hardcoded con la cadena:
//!
//! ```text
//! world ───┐
//! params ──┼─► tick(0) ─► tick(50) ─► tick(50) ─► stats
//! seed ────┘
//! ```
//!
//! - `world`: resetea la grilla a 32×24.
//! - `seed`: siembra 150 lemmings con `seed=7` (determinista).
//! - `params`: ajusta tres campos escalares.
//! - `tick(0)`: snapshot inicial — corre 0 ticks (ya con seed + params),
//! imprime stats t=0.
//! - `tick(50)` y `tick(50)` encadenados: avanzan 100 ticks en total
//! en dos celdas para mostrar reactividad parcial.
//! - `stats`: lectura final sin avanzar el reloj.
//!
//! Corré con: `cargo run -p dominium-notebook-kernel --example
//! notebook_dominium_demo --release`.
//!
//! El demo no abre ventana; imprime el stdout de cada celda. Para
//! visualizar el DAG arrástralo a `pluma-notebook-graph-llimphi`
//! (consume el mismo `Notebook`).
use pluma_notebook_core::{CellId, CellKind, Notebook, OutputPayload};
use pluma_notebook_exec::run_all;
use dominium_notebook_kernel::DominiumKernel;
#[tokio::main]
async fn main() {
let mut nb = Notebook::new();
let world = code(&mut nb, "dominium-world", "32 24");
let seed = code(&mut nb, "dominium-seed", "150 7");
let params = code(
&mut nb,
"dominium-param",
"move_speed=0.4\nsync_rate=0.05\nclimb_cost=0.1",
);
let tick0 = code(&mut nb, "dominium-tick", "0");
let tick50_a = code(&mut nb, "dominium-tick", "50");
let tick50_b = code(&mut nb, "dominium-tick", "50");
let stats = code(&mut nb, "dominium-stats", "");
// Edges del DAG: world se setea primero; seed y params dependen de
// world (ambos lo necesitan listo); tick(0) depende de seed + params;
// los dos tick(50) van en cadena; stats al final.
assert!(nb.add_dependency(seed, world));
assert!(nb.add_dependency(params, world));
assert!(nb.add_dependency(tick0, seed));
assert!(nb.add_dependency(tick0, params));
assert!(nb.add_dependency(tick50_a, tick0));
assert!(nb.add_dependency(tick50_b, tick50_a));
assert!(nb.add_dependency(stats, tick50_b));
let kernel = DominiumKernel::new();
let report = run_all(&mut nb, &kernel).await.expect("notebook sin ciclo");
println!("=== notebook_dominium_demo — corrida completa ===");
println!(
"ejecutadas: {} · falladas: {} · saltadas: {}\n",
report.executed.len(),
report.failed.len(),
report.skipped.len()
);
for cell in nb.cells() {
let lang = match &cell.kind {
CellKind::Code { language } => language.as_str(),
_ => "n/a",
};
let stdout = cell
.last_output
.as_ref()
.map(|o| o.stdout.as_str())
.unwrap_or("(sin output)");
println!(
"--- celda {} [{lang}] state={:?} ---",
cell.id, cell.state
);
println!("source: {}", cell.source.replace('\n', ""));
println!("{stdout}");
println!();
}
// Imprime también el digest reproducible — dos corridas idénticas
// dan el mismo número en cualquier laptop.
if let Some(d) = nb.notebook_digest() {
println!(
"notebook_digest = {}",
d.iter()
.map(|b| format!("{b:02x}"))
.collect::<String>()
);
}
// Sanity: la última celda debe ser una tabla con la fila "n".
let stats_cell = nb.cell(stats).unwrap();
if let Some(out) = &stats_cell.last_output {
if let OutputPayload::Table { rows, .. } = &out.payload {
let n_row = rows.iter().find(|r| r[0] == "n").unwrap();
println!("\nlemmings vivos al final: {}", n_row[1]);
}
}
}
fn code(nb: &mut Notebook, language: &str, source: &str) -> CellId {
nb.push(
CellKind::Code { language: language.to_string() },
source.to_string(),
)
}