repro: «la 2ª reconstrucción no construyó» NO es no-determinismo — mi propio bug, y lo pagó el libro
Corriendo una tanda con `MAX_SECONDS=45` se llenó el disco a mitad, los rebuilds empezaron a morir por ENOSPC, y la rama que yo mismo había escrito para distinguir DERIVA de NO-DETERMINISMO metió «falló» y «difiere» en la misma condición ⇒ **21 recetas sanas quedaron anotadas como NO-DETERMINISMO** en un libro que pretende ser autoritativo. Un registro falso y persistente es peor que no tener registro: el propósito del libro es que nadie tenga que volver a mirar, así que una entrada equivocada se cree para siempre. Es la trampa de siempre —un disco lleno se lee como receta rota, y la pista es que mueren varias seguidas— y el script ORIGINAL ya la evitaba: «no es lo mismo «no reproduce» que «no construye acá». Distinguirlos importa: mezclarlos inventaría un problema de determinismo». Lo rompí al añadir la segunda reconstrucción. Restituido: si la 2ª no construye, se restaura el artefacto, se cuenta como fallo y **no se anota nada** — sin veredicto es un estado legítimo. Reparado además el daño: las 21 entradas falsas purgadas del libro (quedan 27 verificaciones buenas), y `angle-grinder` repuesto desde `store/.divergen/`, donde lo había dejado la clasificación errónea. Eso sí funcionó como debía: el ejemplar estaba guardado y entero, no perdido. Y queda anotado en el propio script el límite del filtro por tiempo que provocó todo: el sidecar de `store/.times/` lo escribe el WORKER, así que acota el build de esa receta EN LA MÁQUINA QUE LO MIDIÓ. Una receta Rust de 40 s allá con la caché de cargo caliente son muchos minutos acá en frío —`angle-grinder` y `amp` entraron por el filtro y se comieron el disco— y encima el número no cuenta la CASCADA de deps que falten en el store local, que es la misma sorpresa que dio `gjs`. Con disco justo conviene pasar las recetas a mano.
This commit is contained in:
@@ -17,3 +17,21 @@ npth e5682fca87a9f591d533855b0056a07b5724304fb53402c940524d885908d980 2026-09-05
|
||||
poppler-render-check 0d29dc69792df003726252fbc381a0133df7654daafccf547f8d5a82e526c1fe 2026-09-05 reproduce
|
||||
when 5b4048a23584aaaca41a7d8c0e50a92c469b1b1710f5d10b2dd7abd908fe57b6 2026-09-05 reproduce (tras poner al día un artefacto derivado)
|
||||
doas 28bc42c24008f73d9541f407562cd4ed81e3788b95ab72fa00b5369ad606064a 2026-09-05 reproduce
|
||||
zstd 1ceb6215a5a3061ac385e036da642341da61a585abe62a1c904bd0c8c2f4d5fb 2026-09-05 reproduce
|
||||
actionlint 12e0640aa73729d170d992eb442bcca6b9d7b891a074d2d8b9f6583710717be3 2026-09-05 reproduce
|
||||
addlicense e918fed592bc9eac2989262f030dba05e4dbdb32ab9ff715abe1f05bf9e688d3 2026-09-05 reproduce
|
||||
adwaita-cursors 22ae0ff6369e6f15cb6810ddd979d95ebe38307e8da668c1210d9434ef9b5b05 2026-09-05 reproduce
|
||||
adwaita-hello 09cf170c6a780bd09f46347c61aa48a16eccda81e27aee42184938242b026918 2026-09-05 reproduce
|
||||
age b8ae28a4e360b61aa53667af7c6261951085285e62c2201f615173731ca36716 2026-09-05 reproduce
|
||||
agora-cli e05c41f1d4e46974195fbfb76a3efb591217e6733be8e17b007ba5d9cc3a153d 2026-09-05 reproduce
|
||||
air 3761c6a516ab0bbcae825625e10c0fbd8015a15f25080e3b7dc36bfb7597d59f 2026-09-05 reproduce
|
||||
ali 9017cf46ef5dc33c726a2fe4adf91c7463bee749ce605a94757eeea51f260c06 2026-09-05 reproduce
|
||||
zoxide 83359c57babdd2e051b7c53bd63694d319969cd9bcb5a27922707655f7b572af 2026-09-05 reproduce
|
||||
alsa-lib 73fb200aae06b0743035dc9f961121fc94295e80a12d39dc358920d698b7bfe0 2026-09-05 reproduce
|
||||
alterx 415465a247da8489bfec54eac84d8bb2234e822423ba717974cbcbedfeb8905d 2026-09-05 reproduce
|
||||
amass 385b4a140f8e8fa04282494f01e7523625aff42ae90f6eb38cd0230e6914aa56 2026-09-05 reproduce
|
||||
amfora 10560630b5d8302979b7844a22d2e30696a046c834968bd713efa7fcbc2b993a 2026-09-05 reproduce
|
||||
amp 7b800350e79dda56eb1c3fca511700761cd91b2cd49ebf58f453fb4a01a62944 2026-09-05 reproduce (tras poner al día un artefacto derivado)
|
||||
anew 68ef0da2c17b474bee65caec2f61e4b24ccd5ea8e2d8357540ce572482bb5782 2026-09-05 reproduce
|
||||
npth e5682fca87a9f591d533855b0056a07b5724304fb53402c940524d885908d980 2026-09-05 reproduce
|
||||
musl-fts b9681a5205197d51bb777efde1a271e9b189bf35d74091834c5f1aa43d82f16e 2026-09-05 reproduce
|
||||
|
||||
|
@@ -143,6 +143,47 @@ else
|
||||
# Muestra por CLASE de build: si sólo se verifican paquetes C con meson, el resultado no dice nada
|
||||
# de Rust ni de Go, que son justamente donde el no-determinismo suele vivir (codegen paralelo,
|
||||
# orden de símbolos, timestamps de módulos).
|
||||
# ── `MAX_SECONDS`: sortear sólo entre builds BARATOS ─────────────────────────────────────────
|
||||
# Elegir muestra «por artefacto chico» es una trampa: el artefacto de `nodejs` pesa 0,2 MB y su
|
||||
# build son horas de V8. Lo que hace cara una verificación es el TIEMPO DE CONSTRUIR, y ese dato
|
||||
# ya existe — `scripts/farm/build-timed.sh` deja un sidecar por build en `store/.times/` y llega
|
||||
# con la cosecha. Con `MAX_SECONDS=60` la muestra sale de ahí.
|
||||
#
|
||||
# Las recetas SIN medición quedan fuera cuando se pone el tope: no se puede prometer que algo es
|
||||
# barato sin haberlo medido nunca. El modo de ampliar esa lista no es relajar el filtro sino
|
||||
# correr builds instrumentados en el worker, que es donde el tiempo se mide.
|
||||
#
|
||||
# ⚠ DOS LÍMITES DEL NÚMERO, medidos el 2026-09-05 con `MAX_SECONDS=45` en el hub:
|
||||
# · El sidecar lo escribe el WORKER. Una receta Rust que allá tarda 40 s con la caché de cargo
|
||||
# caliente puede tardar muchos minutos acá en frío — `angle-grinder` y `amp` entraron por el
|
||||
# filtro y se comieron el disco. El tope acota el build DE ESA RECETA en LA MÁQUINA QUE LO
|
||||
# MIDIÓ, no acá.
|
||||
# · Y no cuenta la CASCADA: `hammer build` reconstruye las deps que falten en el store local, y
|
||||
# eso no está en el tiempo del sidecar. Es la misma sorpresa que dio `gjs`, cuyo grafo decía
|
||||
# «una receta» y se puso a rehacer spidermonkey entero.
|
||||
# ⇒ con disco justo, mejor pasar las recetas a mano que confiar en el tope.
|
||||
if [ -n "${MAX_SECONDS:-}" ]; then
|
||||
TODOS=$(python3 - "$MAX_SECONDS" <<'PY'
|
||||
import glob, json, os, sys, re
|
||||
from statistics import median
|
||||
cap = float(sys.argv[1])
|
||||
m = {}
|
||||
for f in glob.glob("store/.times/*.json"):
|
||||
try: d = json.load(open(f))
|
||||
except Exception: continue
|
||||
if d.get("name") and isinstance(d.get("seconds"), (int, float)):
|
||||
m.setdefault(d["name"], []).append(d["seconds"])
|
||||
baratas = {k for k, v in m.items() if median(v) < cap}
|
||||
for ruta in glob.glob("recipes/*.toml") + glob.glob("recipes/incoming-wlr/*.toml"):
|
||||
txt = open(ruta, encoding="utf8", errors="replace").read(4000)
|
||||
mm = re.search(r'^name\s*=\s*"([^"]+)"', txt, re.M)
|
||||
if mm and mm.group(1) in baratas:
|
||||
print(ruta)
|
||||
PY
|
||||
)
|
||||
[ -n "$TODOS" ] || { echo "── no hay recetas medidas por debajo de ${MAX_SECONDS}s (¿falta store/.times?)"; exit 0; }
|
||||
fi
|
||||
|
||||
# Se sortea entre lo que NO está en el libro, para que corridas sucesivas ACUMULEN cobertura en
|
||||
# vez de repetir las mismas. Con `TODO=1` se sortea entre todas (para re-verificar a propósito).
|
||||
TODOS=$(ls recipes/*.toml recipes/incoming-wlr/*.toml 2>/dev/null)
|
||||
@@ -187,8 +228,20 @@ for f in $RECETAS; do
|
||||
# que estaba fuera de fecha era el guardado ⇒ deriva. Si no coinciden, es no-determinismo de
|
||||
# verdad. Sólo se paga el rebuild extra cuando ya hubo una diferencia.
|
||||
mv "$STORE/$D" "$GUARDA/$D.r1"; PENDIENTE="$PENDIENTE $D.r1"
|
||||
if "$HAMMER" --store "$STORE" build "$f" >/dev/null 2>&1 && [ -d "$STORE/$D" ] \
|
||||
&& "$HAMMER" why-differs "$GUARDA/$D.r1" "$STORE/$D" >/dev/null 2>&1; then
|
||||
# ⚠ «la segunda reconstrucción FALLÓ» NO es «difiere». Meterlas en la misma rama fue un error
|
||||
# mío del 2026-09-05 y lo pagó el libro: se llenó el disco a mitad de una tanda, los rebuilds
|
||||
# empezaron a morir por ENOSPC, y quedaron anotadas 21 recetas sanas como NO-DETERMINISMO —
|
||||
# un registro falso y persistente, que es peor que no tener registro. Es la misma trampa de
|
||||
# siempre: un disco lleno se lee como receta rota, y la pista es que mueren varias seguidas.
|
||||
# El script original ya distinguía «no reproduce» de «no construye acá»; esto lo restituye.
|
||||
if ! "$HAMMER" --store "$STORE" build "$f" >/dev/null 2>&1 || [ ! -d "$STORE/$D" ]; then
|
||||
printf " ⚠ %-26s la 2ª reconstrucción no construyó ⇒ SIN VEREDICTO (no lo anoto)\n" "$n"
|
||||
fallo=$((fallo+1))
|
||||
rm -rf "$STORE/$D"; mv "$GUARDA/$D" "$STORE/$D"; rm -rf "$GUARDA/$D.r1"
|
||||
PENDIENTE=$(echo "$PENDIENTE" | sed "s| $D.r1||;s| $D||")
|
||||
continue
|
||||
fi
|
||||
if "$HAMMER" why-differs "$GUARDA/$D.r1" "$STORE/$D" >/dev/null 2>&1; then
|
||||
printf " ~ %-26s DERIVA (el guardado era viejo; dos reconstrucciones coinciden)\n" "$n"
|
||||
deriva=$((deriva+1))
|
||||
# Se anota igual: las dos reconstrucciones coincidieron entre sí, que es justo lo que
|
||||
|
||||
Reference in New Issue
Block a user