Files
takana/scripts/seed-gnome.py
T
2026-07-23 15:50:17 -04:00

97 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# seed-gnome.py — siembra el SUBTREE transitivo de GNOME desde metadata de nixpkgs (NO construye con
# nix, ADR 0004: sólo lee deps). Corre `sembrar_cierre` de seed-graph sobre las raíces de una sesión
# GNOME y clasifica el cierre:
# conocida → ya hay receta en el corpus (cache-hit potencial)
# frontera → nixpkgs lo pide y hammer NO tiene receta → HAY QUE AUTORAR (esto es el trabajo real)
# nix-ismo/lab → ruido de nixpkgs o lo provee el sandbox (se descarta)
#
# Cada `frontera` es una HIPÓTESIS a clasificar/autorar por un humano (o un subagente-por-receta), NO
# se promueve a receta automáticamente. Salida durable a docs/state/seed-gnome.json.
import json, sys, pathlib, importlib.util
# el guion en `seed-graph.py` impide `import seed_graph`: lo cargo por ruta.
_spec = importlib.util.spec_from_file_location(
"seed_graph", str(pathlib.Path(__file__).parent / "seed-graph.py"))
sg = importlib.util.module_from_spec(_spec); _spec.loader.exec_module(sg)
ROOT = pathlib.Path(__file__).resolve().parent.parent
OUT = ROOT / "docs/state/seed-gnome.json"
# Raíces de una sesión GNOME usable: el compositor (mutter), el shell y su runtime JS (gjs), la
# sesión y sus daemons, el display manager, el portal, y las apps mínimas (files, terminal, ajustes).
# El cierre transitivo trae el resto (mozjs bajo gjs, gcr/libsecret bajo el keyring, etc.).
RAICES = [
"mutter", "gnome-shell", "gjs", "gnome-session", "gnome-settings-daemon",
"gsettings-desktop-schemas", "gnome-desktop", "gnome-menus", "gnome-keyring",
"gdm", "xdg-desktop-portal", "xdg-desktop-portal-gnome", "libadwaita",
"nautilus", "gnome-console", "gnome-control-center", "gnome-text-editor",
]
MINIMA = [ # lo que una sesión GNOME necesita para BOOTEAR a un shell usable (sin apps)
"mutter", "gnome-shell", "gjs", "gnome-session", "gnome-settings-daemon",
"gsettings-desktop-schemas", "gnome-desktop", "gdm", "xdg-desktop-portal-gnome",
]
def main():
recs = sg.recetas()
raices = MINIMA if (len(sys.argv) > 1 and sys.argv[1] == "--minima") else RAICES
print(f"== raíces GNOME: {len(raices)}", file=sys.stderr)
grafo = sg.sembrar_cierre(raices, recs, prof_max=6, tope=4000)
out_path = ROOT / ("docs/state/seed-gnome-minima.json" if raices is MINIMA else "docs/state/seed-gnome.json")
# clasificar TODO el cierre
clases = {"conocida": [], "frontera": [], "nix-ismo": [], "lab": [], "sin-attr": []}
for n, aristas in grafo.items():
if aristas is None:
clases["sin-attr"].append(n); continue
# nodos del cierre = todo lo referido como dep, clasificado por su presencia
todos_deps = {}
for n, aristas in grafo.items():
for a in (aristas or []):
d = a["dep"]
# el nodo se clasifica por su MEJOR evidencia (conocida > frontera)
prev = todos_deps.get(d)
rank = {"conocida": 3, "lab": 2, "nix-ismo": 1, "frontera": 0}
if prev is None or rank[a["clase"]] > rank[prev["clase"]]:
todos_deps[d] = a
conocidas = sorted(d for d, a in todos_deps.items() if a["clase"] == "conocida")
frontera = sorted(d for d, a in todos_deps.items() if a["clase"] == "frontera")
nixismos = sorted(d for d, a in todos_deps.items() if a["clase"] == "nix-ismo")
lab = sorted(d for d, a in todos_deps.items() if a["clase"] == "lab")
sin_attr = sorted(clases["sin-attr"])
# ¿cuántas veces piden cada frontera? (prioridad de autoría)
demanda = {}
for n, aristas in grafo.items():
for a in (aristas or []):
if a["clase"] == "frontera":
demanda.setdefault(a["dep"], []).append(n)
print(f"\n== SUBTREE GNOME (cierre transitivo, {len(grafo)} nodos visitados)")
print(f" ya tenemos receta (conocida): {len(conocidas)}")
print(f" FRONTERA a autorar: {len(frontera)}")
print(f" nix-ismos (ruido): {len(nixismos)}")
print(f" provisto por el lab: {len(lab)}")
print(f" sin attr en nixpkgs: {len(sin_attr)}"
+ (f" ({' '.join(sin_attr[:8])}…)" if sin_attr else ""))
print(f"\n== frontera por demanda (cuántas recetas la piden) — top 40:")
for dep, quienes in sorted(demanda.items(), key=lambda kv: (-len(kv[1]), kv[0]))[:40]:
print(f" {len(quienes):3}× {dep:30}{' '.join(sorted(set(quienes))[:4])}"
+ (" …" if len(set(quienes)) > 4 else ""))
doc = dict(
schema="hammer-seed-gnome/1",
fuente="nixpkgs metadata (NO se construye con nix — ADR 0004)",
advertencia="HIPÓTESIS. Cada frontera es dep-opcional / nix-ismo / hueco real. Autorar a mano.",
raices=raices,
resumen=dict(visitados=len(grafo), conocida=len(conocidas), frontera=len(frontera),
nix_ismo=len(nixismos), lab=len(lab), sin_attr=len(sin_attr)),
conocida=conocidas, frontera=frontera, nix_ismo=nixismos, lab=lab, sin_attr=sin_attr,
demanda={d: sorted(set(q)) for d, q in sorted(demanda.items(), key=lambda kv:(-len(kv[1]),kv[0]))},
)
out_path.write_text(json.dumps(doc, indent=1, ensure_ascii=False) + "\n")
print(f"\n{out_path.relative_to(ROOT)}")
return 0
if __name__ == "__main__":
sys.exit(main())