gioser

tawasuyu/brahman

Rust 567TOML 214Markdown 67JSON 198130 Definiciones

⎇ main scripts/split-changelog.py Python · 147 líneas · 6.9 KB Historia Quién tocó qué crudo

refactor(monorepo): reorganización lógica + renames + SDDs + split CHANGELOG 550c98f27 · sergio ·

1#!/usr/bin/env python3
2"""
3Particiona CHANGELOG.md raíz por proyecto/intención.
4
5Cada sección `### tipo(componente):` se rutea a `docs/changelog/<proyecto>.md`
6según el componente. CHANGELOG.md raíz queda como índice apuntando a las
7particiones.
8
9Reglas de ruteo:
10  brahman-*               → protocol
11  ente-zero|kernel|soma|... → init
12  ente-bus|cas|wasm|brain|echo → runtime
13  ente-*-compat|policy-provider → compat
14  minga-*                 → minga
15  yahweh-*|nahual-*       → nahual
16  lapaloma-*|pineal-*     → pineal
17  nakui-*                 → nakui
18  nouser-*|akasha-*       → akasha
19  shipote-*|shuma-*       → shuma
20  gioser-*                → gioser
21  pluma-*                 → pluma
22  vista-*                 → vista
23  barra-*                 → barra
24  cosmobiologia-*         → cosmobiologia
25"""
26
27import re, sys
28from pathlib import Path
29from collections import defaultdict
30
31ROOT = Path("/home/sergio/brahman")
32CHANGELOG = ROOT / "CHANGELOG.md"
33OUT_DIR = ROOT / "docs" / "changelog"
34
35INIT_NAMES   = {"ente-zero", "ente-kernel", "ente-soma",
36                "ente-snapshot", "ente-incarnate"}
37RUNTIME_NAMES = {"ente-bus", "ente-cas", "ente-wasm",
38                 "ente-brain", "ente-echo"}
39
40PROTOCOL_BARE = {"card", "cards", "sidecar", "handshake", "broker",
41                  "admin", "net", "card-wit", "core", "arje"}
42
43def route_single(c: str) -> str | None:
44    """Devuelve bucket o None si no matchea."""
45    c = c.strip()
46    if c.startswith("brahman-"):           return "protocol"
47    if c in PROTOCOL_BARE:                 return "protocol"
48    if c in INIT_NAMES:                    return "init"
49    if c in RUNTIME_NAMES:                 return "runtime"
50    if c.startswith("ente-") and (c.endswith("-compat") or c == "ente-policy-provider"):
51        return "compat"
52    if c.startswith("ente-"):              return "init"
53    if c.startswith("minga-") or c == "minga": return "minga"
54    if c.startswith("yahweh-") or c.startswith("nahual-") or c in {"yahweh", "nahual"}:
55        return "nahual"
56    if c.startswith("lapaloma-") or c.startswith("pineal-") or c in {"lapaloma", "pineal"}:
57        return "pineal"
58    if c.startswith("nakui-") or c == "nakui": return "nakui"
59    if c.startswith("nouser-") or c.startswith("akasha-") or c in {"nouser", "akasha", "nous", "nous-real", "nous-mock"}:
60        return "akasha"
61    if c.startswith("shipote-") or c.startswith("shuma-") or c in {"shipote", "shuma"}:
62        return "shuma"
63    if c.startswith("gioser-") or c == "gioser": return "gioser"
64    if c.startswith("pluma-") or c == "pluma":   return "pluma"
65    if c.startswith("vista-") or c == "vista":   return "vista"
66    if c.startswith("barra-") or c == "barra":   return "barra"
67    if c.startswith("cosmobiologia-") or c == "cosmobiologia": return "cosmobiologia"
68    # explorer + daemon: dominio variable. "explorer" solo sugiere apps/X-explorer.
69    if c == "explorer" or c == "daemon": return None  # forzar inspección de otro componente
70    return None
71
72def route(components: str) -> str:
73    """Componentes pueden venir como 'a+b' o 'a,b' o 'a'. Buscamos el
74    primero que matchee un bucket conocido."""
75    parts = re.split(r"[+,]", components)
76    for p in parts:
77        b = route_single(p)
78        if b is not None:
79            return b
80    return "misc"
81
82# Notas de header por proyecto (incluyen el rename si aplica).
83HEADERS = {
84    "protocol":      "# Changelog — protocol/\n\nContratos canónicos + routing entre módulos. Antes: `core/brahman-*` + `shared/brahman-*`.\n",
85    "init":          "# Changelog — init/\n\nInit (PID 1) + encarnación Linux. Antes: `core/ente-{zero,kernel,soma,snapshot}` + `shared/ente-incarnate`.\n",
86    "runtime":       "# Changelog — runtime/\n\nInfraestructura de ejecución (bus + cas + wasm + brain + echo). Antes: `core/ente-{bus,cas,wasm,brain,echo}`.\n",
87    "compat":        "# Changelog — compat/\n\nShims D-Bus systemd. Antes: `core/ente-*-compat`.\n",
88    "minga":         "# Changelog — minga (semantic_dht)\n",
89    "nahual":        "# Changelog — nahual\n\nMotor GPUI: libs + widgets. Renombrado de `yahweh` el 2026-05-19.\n",
90    "pineal":        "# Changelog — pineal\n\nData-viz agnóstica con backends. Renombrado de `lapaloma` el 2026-05-19; promovido fuera de `ui_engine/`.\n",
91    "nakui":         "# Changelog — nakui\n\nERP categórico.\n",
92    "akasha":        "# Changelog — akasha\n\nExplorador semántico de Mónadas. Renombrado de `nouser` el 2026-05-19.\n",
93    "shuma":         "# Changelog — shuma\n\nRuntime de espacios aislados. Renombrado de `shipote` el 2026-05-19.\n",
94    "gioser":        "# Changelog — gioser\n\nLanding WASM (chacana + 4 elementos).\n",
95    "pluma":         "# Changelog — pluma\n\nMarkdown agnóstico + visor web.\n",
96    "vista":         "# Changelog — vista\n\nDeck horizontal swipe (PageView).\n",
97    "barra":         "# Changelog — barra\n\nTaskbar agnóstica (Windows-like).\n",
98    "cosmobiologia": "# Changelog — cosmobiologia\n\nEstudio de astrología profesional.\n",
99    "misc":          "# Changelog — misc\n\nEntradas que no encajan en otro proyecto (chore, ci, gitignore, etc.).\n",
100}
101
102def main():
103    text = CHANGELOG.read_text()
104    lines = text.splitlines(keepends=True)
105
106    # 1) Captura todas las secciones que empiezan con `### `
107    header_re = re.compile(r"^### (?:feat|fix|refactor|docs|chore|test|perf|build|ci|style)\(([^)]+)\):")
108    sections: list[tuple[int, str]] = []  # (line_index, component)
109    for i, line in enumerate(lines):
110        m = header_re.match(line)
111        if m:
112            sections.append((i, m.group(1)))
113
114    # 2) Construye los bloques: desde header hasta siguiente header (excl).
115    buckets: dict[str, list[str]] = defaultdict(list)
116    for j, (idx, comp) in enumerate(sections):
117        end = sections[j + 1][0] if j + 1 < len(sections) else len(lines)
118        block = "".join(lines[idx:end]).rstrip() + "\n\n"
119        proj = route(comp)
120        buckets[proj].append(block)
121
122    # 3) Escribe particiones
123    OUT_DIR.mkdir(parents=True, exist_ok=True)
124    for proj, blocks in sorted(buckets.items()):
125        path = OUT_DIR / f"{proj}.md"
126        content = HEADERS.get(proj, f"# Changelog — {proj}\n") + "\n"
127        content += "".join(blocks)
128        path.write_text(content)
129        print(f"  {path.relative_to(ROOT)}: {len(blocks)} entradas")
130
131    # 4) Reescribe CHANGELOG.md como índice
132    index_lines = [
133        "# Changelog\n",
134        "\n",
135        "Histórico particionado el 2026-05-19 por proyecto/intención. Cada\n",
136        "archivo bajo `docs/changelog/` consolida las entradas de un\n",
137        "subdirectorio del workspace.\n",
138        "\n",
139    ]
140    for proj in sorted(buckets.keys()):
141        n = len(buckets[proj])
142        index_lines.append(f"- [`{proj}`](docs/changelog/{proj}.md) — {n} entradas\n")
143    CHANGELOG.write_text("".join(index_lines))
144    print(f"\nCHANGELOG.md reescrito como índice ({len(buckets)} proyectos)")
145
146if __name__ == "__main__":
147    main()