refactor(monorepo): reorganización lógica + renames + SDDs + split CHANGELOG

Reorganización física de crates/:
- core/ (mezclaba 6 propósitos) se divide en protocol/, init/, runtime/, compat/
- shared/ (3 crates) se redistribuye en protocol/ e init/
- lapaloma (sub-módulo de ui_engine) se promueve a modules/pineal/

Renames de proyectos:
- shipote → shuma (runtime de sandboxes)
- nouser → akasha (explorador de Mónadas)
- yahweh → nahual (motor GPUI, antes ui_engine/)
- lapaloma → pineal (data-viz agnóstica)

Fraccionamiento UI → core agnóstico:
- vista-core (DeckState + snap, 175 LOC, 5 tests verdes)
- barra-core (Task + render_html + sanitize, 90 LOC, 5 tests verdes)
- vista-web y barra-web ahora son thin DOM bindings

Documentación nueva:
- 16 SDDs por subdirectorio (≤80 LOC c/u): protocol/init/runtime/compat
  + 10 módulos + apps/
- docs/STATUS.md con cifras reales por proyecto
- docs/ROADMAP.md con plan a finalización (6 hitos, ~6-8 semanas)
- CHANGELOG.md particionado en docs/changelog/<proyecto>.md (7 buckets)

Automatización:
- scripts/reorg.py — script idempotente que: git mv directorios, renombra
  package names, recomputa path = refs, reescribe imports rust, actualiza
  workspace Cargo.toml. Soporta --dry-run.
- scripts/split-changelog.py — particiona CHANGELOG por componente.

Validación:
- cargo check --workspace pasa (124 crates + 2 nuevos cores).
- 10 tests adicionales (5 en vista-core + 5 en barra-core) verdes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-19 14:48:34 +00:00
parent 86fb6ae20b
commit 550c98f275
375 changed files with 8512 additions and 7155 deletions
@@ -0,0 +1,119 @@
//! Validación de los 6 módulos demo en `examples/nakui-modules/`.
//!
//! Si esto verde, garantizamos que un usuario que clone el repo y
//! corra `NAKUI_MODULES_DIR=examples/nakui-modules cargo run -p nakui-ui`
//! va a obtener los 6 módulos cargados sin tocar nada.
use nahual_meta_schema::{load_modules_from_dir, FieldKind, View};
fn examples_dir() -> std::path::PathBuf {
// Tests corren desde el dir del crate; el repo root está dos
// niveles arriba: crates/modules/nakui/ui-schema → repo.
// Tras el lift a nahual, el crate vive en
// `crates/modules/ui_engine/libs/meta-schema`, así que el repo
// root queda 5 niveles arriba.
let here = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
here.join("../../../../..").join("examples/nakui-modules")
}
#[test]
fn loads_all_demo_modules() {
let dir = examples_dir();
let mods = load_modules_from_dir(&dir).unwrap_or_else(|e| {
panic!("load failed for {}: {e}", dir.display());
});
let ids: Vec<&str> = mods.iter().map(|m| m.id.as_str()).collect();
assert_eq!(
ids,
vec![
"customers",
"inventory_movements",
"invoices",
"products",
"sales_engine",
"sales_orders",
"suppliers",
],
"expected 7 modules in alphabetical order \
(sales_engine se sumó al wirear Action::Morphism)"
);
}
#[test]
fn sales_engine_declares_nakui_module_dir_and_morphism() {
// Sanity del módulo demo de morphism: nakui_module_dir set,
// y al menos una vista con Action::Morphism en su on_submit.
let mods = load_modules_from_dir(examples_dir()).unwrap();
let sales = mods
.iter()
.find(|m| m.id == "sales_engine")
.expect("sales_engine debe estar");
assert!(
sales.nakui_module_dir.is_some(),
"sales_engine debería declarar nakui_module_dir"
);
let has_morphism_view = sales.views.values().any(|v| match v {
nahual_meta_schema::View::Form(form) => {
matches!(form.on_submit, nahual_meta_schema::Action::Morphism { .. })
}
_ => false,
});
assert!(
has_morphism_view,
"sales_engine debería tener al menos una Form con Action::Morphism"
);
}
#[test]
fn every_demo_module_has_list_and_form_views() {
let mods = load_modules_from_dir(examples_dir()).unwrap();
for m in &mods {
let mut has_list = false;
let mut has_form = false;
for v in m.views.values() {
match v {
View::List(_) => has_list = true,
View::Form(_) => has_form = true,
}
}
assert!(
has_list && has_form,
"module {} should expose at least one list + one form view",
m.id
);
}
}
#[test]
fn every_demo_form_field_kind_is_recognized() {
// Sanity: ningún módulo demo usa un kind que no esté en el enum
// (sería rechazado al parsear, pero check explícito no daña).
let mods = load_modules_from_dir(examples_dir()).unwrap();
for m in &mods {
for v in m.views.values() {
if let View::Form(form) = v {
for f in &form.fields {
let _ok = matches!(
f.kind,
FieldKind::Text
| FieldKind::Multiline
| FieldKind::Number
| FieldKind::Boolean
| FieldKind::Date
);
}
}
}
}
}
#[test]
fn every_module_validates_clean() {
// validate() chequea que cada MenuItem.view exista en views.
// Un typo en cualquiera de los 6 módulos haría fallar este test.
let mods = load_modules_from_dir(examples_dir()).unwrap();
for m in &mods {
m.validate()
.unwrap_or_else(|e| panic!("module {} failed validate: {e}", m.id));
}
}