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:
Sergio
2026-09-05 19:31:33 +00:00
parent d5863ae554
commit efe6e001d2
2 changed files with 77 additions and 1 deletions
+19
View File
@@ -0,0 +1,19 @@
# 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
libffi 1df7e7a3eac63694f8d094646cfc231f3843995f6f9f945cda138760f6bc9838 2026-09-05 reproduce
packaging 0378f4a8dba56e6519590778c2dad2291a99e92b7960d38960964f3fd52a7afc 2026-09-05 reproduce
itstool 158e4e38b2124e80856fbf64fdde1b0af39c1073de915d13a4a296f1ac8db0fd 2026-09-05 reproduce
markupsafe 9fcd3a2c0c442b4c9eb57683b6873dd47d227a681258bd8588f21d28327b4f06 2026-09-05 reproduce
musl-obstack 4a5b3327efb9cd5ec015d3f62e9ec85451deb08560d4963b2567d1e2a124acbb 2026-09-05 reproduce
hicolor-icon-theme 98ad44a5c02059b4cac576bbbf24195a229cda1c807f1eb6c7e80424ceab1e16 2026-09-05 reproduce
musl-fts b9681a5205197d51bb777efde1a271e9b189bf35d74091834c5f1aa43d82f16e 2026-09-05 reproduce
npth e5682fca87a9f591d533855b0056a07b5724304fb53402c940524d885908d980 2026-09-05 reproduce
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
1 # repro-verificado.tsv — qué artefactos se comprobaron que REPRODUCEN bit a bit, y cuándo.
2 #
3 # Clave = (receta, ArtifactHash). El hash resume la receta MÁS su cierre de deps, así que una
4 # entrada deja de casar en cuanto algo aguas arriba cambia: no se puede dar por verificado lo
5 # que ya no es lo mismo. Lo escribe scripts/verificar-repro.sh; se lee para medir cobertura y
6 # para no volver a sortear lo ya hecho.
7 #
8 # receta<TAB>hash<TAB>fecha<TAB>veredicto
9 libffi 1df7e7a3eac63694f8d094646cfc231f3843995f6f9f945cda138760f6bc9838 2026-09-05 reproduce
10 packaging 0378f4a8dba56e6519590778c2dad2291a99e92b7960d38960964f3fd52a7afc 2026-09-05 reproduce
11 itstool 158e4e38b2124e80856fbf64fdde1b0af39c1073de915d13a4a296f1ac8db0fd 2026-09-05 reproduce
12 markupsafe 9fcd3a2c0c442b4c9eb57683b6873dd47d227a681258bd8588f21d28327b4f06 2026-09-05 reproduce
13 musl-obstack 4a5b3327efb9cd5ec015d3f62e9ec85451deb08560d4963b2567d1e2a124acbb 2026-09-05 reproduce
14 hicolor-icon-theme 98ad44a5c02059b4cac576bbbf24195a229cda1c807f1eb6c7e80424ceab1e16 2026-09-05 reproduce
15 musl-fts b9681a5205197d51bb777efde1a271e9b189bf35d74091834c5f1aa43d82f16e 2026-09-05 reproduce
16 npth e5682fca87a9f591d533855b0056a07b5724304fb53402c940524d885908d980 2026-09-05 reproduce
17 poppler-render-check 0d29dc69792df003726252fbc381a0133df7654daafccf547f8d5a82e526c1fe 2026-09-05 reproduce
18 when 5b4048a23584aaaca41a7d8c0e50a92c469b1b1710f5d10b2dd7abd908fe57b6 2026-09-05 reproduce (tras poner al día un artefacto derivado)
19 doas 28bc42c24008f73d9541f407562cd4ed81e3788b95ab72fa00b5369ad606064a 2026-09-05 reproduce
+58 -1
View File
@@ -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