repro: un libro de lo verificado, y por fin un número de cobertura — era 2 de 1157
`verificar-repro.sh` sorteaba una muestra y se olvidaba. Sin registro no había forma de contestar la
pregunta que importa —¿qué fracción del corpus se comprobó alguna vez que reproduce?— y encima las
mismas recetas salían sorteadas una y otra vez mientras otras no se miraban jamás. La primera medida
con el libro puesto: **2 de 1157**. El invariante central del proyecto no se había verificado de
forma acumulativa prácticamente en nada, y eso no se veía porque cada corrida daba «✅ PUERTA
SUPERADA» sobre su propia muestra.
`docs/state/repro-verificado.tsv` va con la misma clave auto-invalidante que el libro de fuzz:
(receta, **ArtifactHash**). El hash resume la receta MÁS su cierre de deps, así que una entrada deja
de casar en cuanto algo aguas arriba cambia. Un «ya lo verifiqué» que sobreviviera a un re-hash sería
una mentira; éste no puede serlo.
Con eso el muestreo pasa a sortear entre lo NO verificado, así que corridas sucesivas ACUMULAN
cobertura en vez de repetir (con `TODO=1` se sortea entre todas, para re-verificar a propósito), y
`--coverage` da el número sin construir nada.
Verificadas en esta tanda: itstool, markupsafe, musl-obstack, hicolor-icon-theme, musl-fts, npth,
poppler-render-check, doas, libffi y packaging REPRODUCEN; `when` derivaba y quedó al día. Cero
no-determinismos. Van 11 de 1157.
⚠ Y una trampa que casi me come, anotada acá porque el próximo la va a pisar: elegí candidatos «por
artefacto chico» y la lista empezó con **nodejs**, cuyo artefacto pesa 0,2 MB y cuyo build son horas
de V8. El tamaño del artefacto NO dice nada del costo de construirlo. Para elegir muestra barata hay
que mirar tiempos de build, no bytes de salida.
This commit is contained in:
@@ -100,14 +100,66 @@ for d in "$GUARDA"/*; do
|
||||
if [ -d "$STORE/$b" ]; then rm -rf "$d"; else mv "$d" "$STORE/$b"; echo " ↩ repuesto de una corrida anterior: $b"; fi
|
||||
done
|
||||
|
||||
# ── EL LIBRO DE LO YA VERIFICADO ───────────────────────────────────────────────────────────────
|
||||
# Sin registro, cada corrida es una muestra aleatoria que se olvida: nadie puede decir qué fracción
|
||||
# del corpus se verificó nunca, y las mismas recetas salen sorteadas una y otra vez mientras otras
|
||||
# no se miran jamás. La clave es (receta, ArtifactHash) — el hash de HOY. Si la receta o cualquiera
|
||||
# de sus deps cambia, el hash cambia, la entrada deja de casar y hay que volver a verificar. Un
|
||||
# «ya lo verifiqué» que sobrevive a un re-hash sería una mentira; éste no puede.
|
||||
LIBRO="${LIBRO:-docs/state/repro-verificado.tsv}"
|
||||
[ -s "$LIBRO" ] || printf '%s\n' \
|
||||
'# repro-verificado.tsv — qué artefactos se comprobaron que REPRODUCEN bit a bit, y cuándo.' \
|
||||
'#' \
|
||||
'# Clave = (receta, ArtifactHash). El hash resume la receta MÁS su cierre de deps, así que una' \
|
||||
'# entrada deja de casar en cuanto algo aguas arriba cambia: no se puede dar por verificado lo' \
|
||||
'# que ya no es lo mismo. Lo escribe scripts/verificar-repro.sh; se lee para medir cobertura y' \
|
||||
'# para no volver a sortear lo ya hecho.' \
|
||||
'#' \
|
||||
'# receta<TAB>hash<TAB>fecha<TAB>veredicto' > "$LIBRO"
|
||||
|
||||
ya_verificado() { grep -qF "$(printf '%s\t%s\t' "$1" "$2")" "$LIBRO" 2>/dev/null; }
|
||||
anotar() { printf '%s\t%s\t%s\t%s\n' "$1" "$2" "$(date -u +%F)" "$3" >> "$LIBRO"; }
|
||||
|
||||
# `--coverage`: qué fracción del corpus PRESENTE en este store se comprobó, al hash de hoy. Es la
|
||||
# pregunta que el libro existe para poder contestar — antes no había forma de saberlo.
|
||||
if [ "${1:-}" = "--coverage" ]; then
|
||||
tot=0; hechas=0; sin_art=0
|
||||
for f in recipes/*.toml recipes/incoming-*/*.toml; do
|
||||
[ -f "$f" ] || continue
|
||||
nn=$(awk -F'"' '/^name[[:space:]]*=/{print $2; exit}' "$f"); [ -n "$nn" ] || nn=$(basename "$f" .toml)
|
||||
hh=$("$HAMMER" --store "$STORE" hash "$f" 2>/dev/null | tail -1 | sed 's/^b3://')
|
||||
[ -n "$hh" ] || continue
|
||||
[ -d "$STORE/$hh-$nn" ] || { sin_art=$((sin_art+1)); continue; }
|
||||
tot=$((tot+1)); ya_verificado "$nn" "$hh" && hechas=$((hechas+1))
|
||||
done
|
||||
echo "── cobertura de reproducibilidad (contra el hash de HOY)"
|
||||
echo " verificadas $hechas de $tot artefactos presentes en $STORE"
|
||||
echo " sin artefacto $sin_art (selladas en otro lado, o en deuda: acá no hay qué comparar)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $# -gt 0 ]; then RECETAS="$*"
|
||||
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).
|
||||
RECETAS=$(ls recipes/*.toml recipes/incoming-wlr/*.toml 2>/dev/null | shuf -n "$N" --random-source=/dev/zero | tr '\n' ' ')
|
||||
# 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)
|
||||
if [ -z "${TODO:-}" ]; then
|
||||
PENDIENTES=""
|
||||
for f in $TODOS; do
|
||||
nn=$(awk -F'"' '/^name[[:space:]]*=/{print $2; exit}' "$f"); [ -n "$nn" ] || nn=$(basename "$f" .toml)
|
||||
hh=$("$HAMMER" --store "$STORE" hash "$f" 2>/dev/null | tail -1 | sed 's/^b3://')
|
||||
[ -n "$hh" ] || continue
|
||||
ya_verificado "$nn" "$hh" || PENDIENTES="$PENDIENTES $f"
|
||||
done
|
||||
[ -n "$PENDIENTES" ] && TODOS="$PENDIENTES"
|
||||
fi
|
||||
RECETAS=$(printf '%s\n' $TODOS | shuf -n "$N" --random-source=/dev/zero | tr '\n' ' ')
|
||||
fi
|
||||
|
||||
|
||||
ok=0; dif=0; fallo=0; saltada=0; deriva=0; DIVERGEN=""
|
||||
for f in $RECETAS; do
|
||||
[ -f "$f" ] || continue
|
||||
@@ -121,6 +173,7 @@ for f in $RECETAS; do
|
||||
if "$HAMMER" --store "$STORE" build "$f" >/dev/null 2>&1 && [ -d "$STORE/$D" ]; then
|
||||
if "$HAMMER" why-differs "$GUARDA/$D" "$STORE/$D" >/dev/null 2>&1; then
|
||||
printf " ✓ %-26s REPRODUCE\n" "$n"; ok=$((ok+1))
|
||||
anotar "$n" "$H" "reproduce"
|
||||
rm -rf "$GUARDA/$D"
|
||||
else
|
||||
# ── DERIVA vs NO-DETERMINISMO: SE DECIDE ACÁ, NO EN LA CABEZA DE QUIEN LEE ──────────────
|
||||
@@ -138,10 +191,14 @@ for f in $RECETAS; do
|
||||
&& "$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
|
||||
# significa «reproduce». Lo viejo era el ejemplar guardado, no el determinismo.
|
||||
anotar "$n" "$H" "reproduce (tras poner al día un artefacto derivado)"
|
||||
rm -rf "$GUARDA/$D" "$GUARDA/$D.r1"
|
||||
else
|
||||
printf " ✗ %-26s NO-DETERMINISMO (dos reconstrucciones difieren entre sí)\n" "$n"
|
||||
dif=$((dif+1)); DIVERGEN="$DIVERGEN $n"
|
||||
anotar "$n" "$H" "NO-DETERMINISMO"
|
||||
"$HAMMER" why-differs "$GUARDA/$D.r1" "$STORE/$D" 2>&1 | tail -4 | sed 's/^/ /'
|
||||
# ⚠ NO se borran los ejemplares. Borrarlos destruía la evidencia justo en el único caso que
|
||||
# este guardián existe para encontrar: decía «no reproduce» y tiraba las copias con las que
|
||||
|
||||
Reference in New Issue
Block a user