Files
sergio 550c98f275 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>
2026-05-19 14:48:34 +00:00

316 lines
12 KiB
Python

#!/usr/bin/env python3
"""
Reorganización del monorepo brahman (2026-05-19).
Pasos:
1. git mv directorios según MOVES
2. Renombra package names en Cargo.toml según PKG_RENAMES
3. Reescribe paths relativos (path = "...") en TODOS los Cargo.toml
4. Reescribe imports `use OLD_::` → `use NEW_::` en .rs
5. Reescribe root Cargo.toml (members + workspace.dependencies)
Renames:
shipote → shuma
nouser → akasha
yahweh → nahual (carpeta ui_engine también renombrada)
lapaloma → pineal (promovida fuera de ui_engine)
Uso:
python3 scripts/reorg.py --dry-run
python3 scripts/reorg.py
"""
import os, re, subprocess, sys
from pathlib import Path
ROOT = Path("/home/sergio/brahman")
DRY = "--dry-run" in sys.argv
# ============================================================
# 1) MOVES: old_canonical_dir → new_canonical_dir (relativo a ROOT)
# ============================================================
MOVES: dict[str, str] = {}
PROTOCOL = ["brahman-card", "brahman-card-wit", "brahman-cards",
"brahman-handshake", "brahman-broker", "brahman-admin",
"ente-card"]
INIT_C = ["ente-zero", "ente-kernel", "ente-soma", "ente-snapshot"]
RUNTIME = ["ente-bus", "ente-cas", "ente-wasm", "ente-brain", "ente-echo"]
COMPAT = ["ente-logind-compat", "ente-hostnamed-compat", "ente-timedated-compat",
"ente-localed-compat", "ente-journald-compat", "ente-resolved-compat",
"ente-polkit-compat", "ente-machined-compat", "ente-systemd1-compat",
"ente-notify-compat", "ente-timer-compat", "ente-tmpfiles-compat",
"ente-binfmt-compat", "ente-policy-provider"]
for c in PROTOCOL: MOVES[f"crates/core/{c}"] = f"crates/protocol/{c}"
for c in INIT_C: MOVES[f"crates/core/{c}"] = f"crates/init/{c}"
for c in RUNTIME: MOVES[f"crates/core/{c}"] = f"crates/runtime/{c}"
for c in COMPAT: MOVES[f"crates/core/{c}"] = f"crates/compat/{c}"
MOVES["crates/shared/brahman-sidecar"] = "crates/protocol/brahman-sidecar"
MOVES["crates/shared/brahman-net"] = "crates/protocol/brahman-net"
MOVES["crates/shared/ente-incarnate"] = "crates/init/ente-incarnate"
# shipote → shuma
for c in ["card", "core", "protocol", "discern"]:
MOVES[f"crates/modules/shipote/shipote-{c}"] = f"crates/modules/shuma/shuma-{c}"
for c in ["cli", "daemon", "shell", "gateway"]:
MOVES[f"crates/apps/shipote-{c}"] = f"crates/apps/shuma-{c}"
# nouser → akasha (mantiene naming interno sin prefijo)
for c in ["card", "core", "nous", "nous-mock", "nous-real"]:
MOVES[f"crates/modules/nouser/{c}"] = f"crates/modules/akasha/{c}"
MOVES["crates/apps/nouser-explorer"] = "crates/apps/akasha-explorer"
# ui_engine → nahual (libs/* + widgets/* salvo lapaloma-*)
NAHUAL_LIBS = ["core", "theme", "launcher", "bus", "meta-schema", "meta-runtime",
"providers/fs", "providers/sqlite"]
for c in NAHUAL_LIBS:
MOVES[f"crates/modules/ui_engine/libs/{c}"] = f"crates/modules/nahual/libs/{c}"
NAHUAL_WIDGETS = ["tree", "container_core", "splitter", "tabs", "tiled",
"text_input", "meta-form", "banner", "card", "stat-card",
"app-header", "theme-switcher"]
for c in NAHUAL_WIDGETS:
MOVES[f"crates/modules/ui_engine/widgets/{c}"] = f"crates/modules/nahual/widgets/{c}"
# lapaloma → pineal (promovida fuera de ui_engine)
MOVES["crates/modules/ui_engine/libs/lapaloma-core"] = "crates/modules/pineal/core"
PINEAL_W = ["render", "cartesian", "stream", "mesh", "financial",
"polar", "heatmap", "treemap", "flow", "phosphor", "export"]
for c in PINEAL_W:
MOVES[f"crates/modules/ui_engine/widgets/lapaloma-{c}"] = f"crates/modules/pineal/{c}"
MOVES["crates/modules/ui_engine/widgets/lapaloma"] = "crates/modules/pineal/umbrella"
# Apps: renames de yahweh, lapaloma
MOVES["crates/apps/yahweh-shell"] = "crates/apps/nahual-shell"
MOVES["crates/apps/file_explorer"] = "crates/apps/nahual-file-explorer"
MOVES["crates/apps/database_explorer"] = "crates/apps/nahual-database-explorer"
MOVES["crates/apps/text_viewer"] = "crates/apps/nahual-text-viewer"
MOVES["crates/apps/image_viewer"] = "crates/apps/nahual-image-viewer"
for c in ["demo", "stream-demo", "phosphor-demo", "financial-demo"]:
MOVES[f"crates/apps/lapaloma-{c}"] = f"crates/apps/pineal-{c}"
# ============================================================
# 2) PKG_RENAMES: nombre paquete viejo → nombre paquete nuevo
# ============================================================
PKG_RENAMES: dict[str, str] = {}
for s in ["card", "core", "protocol", "discern", "cli", "daemon", "shell", "gateway"]:
PKG_RENAMES[f"shipote-{s}"] = f"shuma-{s}"
for s in ["card", "core", "nous", "nous-mock", "nous-real", "explorer"]:
PKG_RENAMES[f"nouser-{s}"] = f"akasha-{s}"
YAHWEH = [
"core", "theme", "launcher", "bus", "meta-schema", "meta-runtime",
"provider-fs", "provider-sqlite",
"widget-tree", "widget-container-core", "widget-splitter", "widget-tabs",
"widget-tiled", "widget-text-input", "widget-meta-form", "widget-banner",
"widget-card", "widget-stat-card", "widget-app-header",
"widget-theme-switcher",
"shell", "file-explorer", "database-explorer", "text-viewer", "image-viewer",
]
for s in YAHWEH: PKG_RENAMES[f"yahweh-{s}"] = f"nahual-{s}"
LAPAL = ["core", "render", "cartesian", "stream", "mesh", "financial",
"polar", "heatmap", "treemap", "flow", "phosphor", "export",
"demo", "stream-demo", "phosphor-demo", "financial-demo"]
for s in LAPAL: PKG_RENAMES[f"lapaloma-{s}"] = f"pineal-{s}"
PKG_RENAMES["lapaloma"] = "pineal"
# Bare names (binarios umbrella que se llaman como el proyecto)
PKG_RENAMES["shipote"] = "shuma"
PKG_RENAMES["yahweh"] = "nahual"
PKG_RENAMES["nouser"] = "akasha"
# ============================================================
# 3) Helpers
# ============================================================
def sh(cmd, check=True):
if DRY:
print(f" DRY: {' '.join(cmd)}")
return None
return subprocess.run(cmd, check=check, cwd=str(ROOT))
def git_mv(src: str, dst: str):
src_p = ROOT / src
dst_p = ROOT / dst
if not src_p.exists():
print(f" SKIP {src} (no existe)")
return
if dst_p.exists():
print(f" WARN {dst} ya existe — saltando")
return
dst_p.parent.mkdir(parents=True, exist_ok=True)
sh(["git", "mv", src, dst])
def all_cargo_tomls() -> list[Path]:
res = []
for p in (ROOT / "crates").rglob("Cargo.toml"):
if "target" in p.parts: continue
res.append(p)
return res
def all_rs() -> list[Path]:
res = []
for p in (ROOT / "crates").rglob("*.rs"):
if "target" in p.parts: continue
res.append(p)
return res
# ============================================================
# 4) Reescritura de paths relativos en Cargo.toml
# ============================================================
# Después de los git mv, owner está en su NEW path. El path = "..."
# dentro del file aún tiene el valor viejo (relativo al old owner).
# Estrategia: probamos resolver el path desde el OLD owner; si el
# target existe en MOVES o en disco, lo redirigimos al NEW target
# y recomputamos relativo al NEW owner.
CURRENT_TO_OLD = {new: old for old, new in MOVES.items()}
def owner_old_for(toml: Path) -> str:
cur = str(toml.parent.relative_to(ROOT))
return CURRENT_TO_OLD.get(cur, cur)
def resolve_canon(base_dir: str, rel: str) -> str | None:
try:
abs_t = (ROOT / base_dir / rel).resolve()
return str(abs_t.relative_to(ROOT))
except ValueError:
return None
def rewrite_paths_in_toml(toml: Path):
text = toml.read_text()
owner_old = owner_old_for(toml)
owner_new = MOVES.get(owner_old, owner_old)
def repl(m: re.Match) -> str:
rel_in_file = m.group(1)
# Try resolve from owner_old first (file values are pre-move).
# Si owner no se movió, owner_old == owner_new — sigue válido.
for base in (owner_old, owner_new):
tgt = resolve_canon(base, rel_in_file)
if tgt is None:
continue
# ¿Conocemos un MOVES para este target?
if tgt in MOVES:
new_tgt = MOVES[tgt]
elif (ROOT / tgt).exists():
new_tgt = tgt
else:
continue
new_rel = os.path.relpath(str(ROOT / new_tgt),
str(ROOT / owner_new))
return f'path = "{new_rel}"'
return m.group(0)
new_text = re.sub(r'path\s*=\s*"([^"]+)"', repl, text)
if new_text != text:
if DRY:
print(f" DRY paths: {toml.relative_to(ROOT)}")
else:
toml.write_text(new_text)
# ============================================================
# 5) Reescritura de package names en Cargo.toml
# ============================================================
def rewrite_pkg_names_in_toml(toml: Path):
text = toml.read_text()
orig = text
# Ordenamos de más largo a más corto para evitar que `shipote` reemplace
# antes que `shipote-core` cuando este último también está en el map.
for old, new in sorted(PKG_RENAMES.items(), key=lambda kv: -len(kv[0])):
# name = "OLD" / package = "OLD"
text = re.sub(rf'(\bname\s*=\s*)"{re.escape(old)}"',
rf'\1"{new}"', text)
text = re.sub(rf'(\bpackage\s*=\s*)"{re.escape(old)}"',
rf'\1"{new}"', text)
# Dep key al inicio de línea: OLD = ...
text = re.sub(rf'^([ \t]*){re.escape(old)}(\s*=)',
rf'\1{new}\2', text, flags=re.MULTILINE)
# Features y strings: "dep:OLD" → "dep:NEW"
text = re.sub(rf'"dep:{re.escape(old)}"', f'"dep:{new}"', text)
# Features: "OLD/feature" → "NEW/feature"
text = re.sub(rf'"{re.escape(old)}/', f'"{new}/', text)
# Features: "OLD" → "NEW" (referencia bare en feature list)
text = re.sub(rf'"{re.escape(old)}"', f'"{new}"', text)
if text != orig:
if DRY:
print(f" DRY names: {toml.relative_to(ROOT)}")
else:
toml.write_text(text)
# ============================================================
# 6) Reescritura de imports en .rs
# ============================================================
USE_RENAMES = {k.replace("-", "_"): v.replace("-", "_")
for k, v in PKG_RENAMES.items()}
def rewrite_imports_in_rs(rs: Path):
text = rs.read_text()
orig = text
for old, new in USE_RENAMES.items():
text = re.sub(rf'(?<![A-Za-z0-9_]){re.escape(old)}(?![A-Za-z0-9_])',
new, text)
if text != orig:
if DRY:
print(f" DRY imports: {rs.relative_to(ROOT)}")
else:
rs.write_text(text)
# ============================================================
# 7) Workspace root Cargo.toml
# ============================================================
def rewrite_workspace_toml():
p = ROOT / "Cargo.toml"
text = p.read_text()
orig = text
for old, new in MOVES.items():
text = text.replace(f'"{old}"', f'"{new}"')
for old, new in PKG_RENAMES.items():
text = re.sub(rf'^([ \t]*){re.escape(old)}(\s*=)',
rf'\1{new}\2', text, flags=re.MULTILINE)
if text != orig:
if DRY:
print(f" DRY workspace toml")
else:
p.write_text(text)
# ============================================================
# Driver
# ============================================================
def run():
print(f"=== reorg.py (DRY={DRY}) ===")
print(f" {len(MOVES)} moves, {len(PKG_RENAMES)} pkg renames")
print("\n-- step 1: git mv --")
for old, new in MOVES.items():
git_mv(old, new)
print("\n-- step 2: package names --")
for toml in all_cargo_tomls():
rewrite_pkg_names_in_toml(toml)
print("\n-- step 3: paths relativos --")
for toml in all_cargo_tomls():
rewrite_paths_in_toml(toml)
print("\n-- step 4: imports en .rs --")
for rs in all_rs():
rewrite_imports_in_rs(rs)
print("\n-- step 5: workspace Cargo.toml --")
rewrite_workspace_toml()
print("\nOK")
if __name__ == "__main__":
run()