From e5d56a41687bba38bd188705686033925b8f6d6a Mon Sep 17 00:00:00 2001 From: Sergio Date: Tue, 1 Sep 2026 16:31:55 +0000 Subject: [PATCH] grafo de estado: un directorio VACIO se contaba como sellado MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `state_of` decidia `sealed` con `os.path.isdir`, o sea por presencia del nombre y no del contenido. Hoy se vio en vivo: una cosecha que murio a mitad dejo `...-mise` sin un solo fichero y el grafo lo conto sellado — misma cifra, misma linea, indistinguible de la corrida buena. Es el eslabon que describe la regla 3 del CLAUDE.md: `Store::has` ya rechaza los vacios, pero cada consumidor que mire `isdir` los vuelve a leer como presencia. Un ausente falla ruidosamente; un vacio llega hasta el final diciendo que todo fue bien. Los cinco grafos salen identicos tras el cambio (no hay vacios en el store ahora mismo), asi que corrige el criterio sin mover ningun numero. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4 --- scripts/build-state.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/scripts/build-state.py b/scripts/build-state.py index f1f8084b..a7292c17 100755 --- a/scripts/build-state.py +++ b/scripts/build-state.py @@ -176,11 +176,28 @@ for _d in SELLADOS_REMOTOS: _REMOTOS_POR_INAME.setdefault(_d.split("-", 1)[1] if "-" in _d else _d, []).append(_d) REMOTO_ONLY = set() +def _artefacto_presente(ruta): + """¿Hay un ARTEFACTO en `ruta`, o sólo un nombre? + + ⚠ Esto era `os.path.isdir(ruta)` y por eso un directorio VACÍO se contaba como `sealed` + (2026-09-01). No es hipotético: una cosecha que murió a mitad dejó `…-mise` sin un solo fichero + y el grafo lo dio por sellado — mismo número, misma línea, imposible de distinguir de la + corrida buena. Es el eslabón que describe la regla 3 del CLAUDE.md: `Store::has` ya rechaza los + vacíos, pero cada consumidor que mire presencia de directorio los vuelve a leer como presencia. + **Un ausente falla ruidosamente; un vacío llega hasta el final diciendo que todo fue bien.** + """ + try: + with os.scandir(ruta) as it: + return next(it, None) is not None + except (NotADirectoryError, FileNotFoundError): + return False + + def state_of(iname, h): if h is None: return "unhashable" dirname = f"{h[3:]}-{iname}" - if os.path.isdir(os.path.join(STORE, dirname)): + if _artefacto_presente(os.path.join(STORE, dirname)): return "sealed" if dirname in SELLADOS_REMOTOS: REMOTO_ONLY.add(dirname)