sergio/takana
> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…
SEGUNDA corrección: lo que medía el test era DERIVA, no no-determinismo — el corpus SÍ reproduce
1#!/usr/bin/env bash
2# verificar-repro.sh — ¿reproducen los artefactos bit a bit? Reconstruye y COMPARA. Etapa 1 del SDD 23.
3#
4# ── POR QUÉ ESTE SCRIPT Y NO UN GREP ───────────────────────────────────────────────────────────
5# El no-determinismo se mide RECONSTRUYENDO Y COMPARANDO, no buscando cadenas sospechosas en los
6# binarios. El plan heredado daba 92 paquetes por «no-deterministas probables» porque tenían rutas
7# `/src` en `.debug_*`; al mirarlo resultó que `/src` es un bind CONSTANTE del lab, y que las únicas
8# rutas realmente ajenas venían de terceros (la ruta de compilación de Alpine horneada en el musl
9# prebuilt, cadenas literales del fuente, rutas de autores en assets SVG). Un grep sobre binarios da
10# CANDIDATOS, no veredictos. Ver SDD 23 §1.
11#
12# ── ⚠ QUÉ MIDE ESTE SCRIPT, EXACTAMENTE (leer antes de sacar conclusiones) ─────────────────────
13# Compara el artefacto GUARDADO en el store contra una reconstrucción de hoy. Eso NO es lo mismo que
14# medir no-determinismo, y confundirlos costó dos conclusiones equivocadas en el SDD 23:
15# · si el artefacto guardado es viejo, se construyó con OTRO estado del lab ⇒ una diferencia es
16# DERIVA («el mundo se movió»), no no-determinismo;
17# · el no-determinismo es: mismas entradas, MISMO lab, salidas distintas.
18# ⇒ Para medir NO-DETERMINISMO hay que correr el script DOS VECES: la primera pone el artefacto al
19# día con el lab actual, y es la SEGUNDA la que contesta la pregunta. Medido: anew, gron y age
20# divergían en la primera corrida y REPRODUCEN en la segunda.
21#
22# ── EL MÉTODO, Y POR QUÉ ES BARATO ─────────────────────────────────────────────────────────────
23# Se APARTA el artefacto (no se borra) y se reconstruye. Como las DEPS siguen en el store, el rebuild
24# es sólo el paquete en cuestión, no su cadena — que es lo que hace viable verificar decenas. Después
25# `hammer why-differs` compara los dos árboles y nombra la causa si divergen.
26#
27# ── LA RED DE SEGURIDAD ────────────────────────────────────────────────────────────────────────
28# Si el rebuild FALLA (y fallará en alguna: hay recetas que sólo construyen en ciertas máquinas), el
29# artefacto original se RESTAURA. Un verificador que destruye lo que verifica es peor que no tenerlo.
30# Por eso se aparta a un directorio temporal en vez de borrar, y hay `trap` para restaurar todo lo
31# pendiente si se interrumpe.
32#
33# Uso: scripts/verificar-repro.sh [receta.toml ...] (sin args: muestra por clase)
34# Env: STORE (def ./store) N (def 30, tamaño de la muestra)
35set -uo pipefail
36ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
37STORE="${STORE:-./store}"; HAMMER="${HAMMER:-./target/release/hammer}"; N="${N:-30}"
38GUARDA="$(mktemp -d)"; PENDIENTE=""
39
40restaurar_todo() {
41 for h in $PENDIENTE; do
42 [ -d "$GUARDA/$h" ] && [ ! -d "$STORE/$h" ] && mv "$GUARDA/$h" "$STORE/$h" && echo " ↩ restaurado $h"
43 done
44 rm -rf "$GUARDA"
45}
46trap restaurar_todo EXIT INT TERM
47
48if [ $# -gt 0 ]; then RECETAS="$*"
49else
50 # Muestra por CLASE de build: si sólo se verifican paquetes C con meson, el resultado no dice nada
51 # de Rust ni de Go, que son justamente donde el no-determinismo suele vivir (codegen paralelo,
52 # orden de símbolos, timestamps de módulos).
53 RECETAS=$(ls recipes/*.toml recipes/incoming-wlr/*.toml 2>/dev/null | shuf -n "$N" --random-source=/dev/zero | tr '\n' ' ')
54fi
55
56ok=0; dif=0; fallo=0; saltada=0; DIVERGEN=""
57for f in $RECETAS; do
58 [ -f "$f" ] || continue
59 n=$(awk -F'"' '/^name[[:space:]]*=/{print $2; exit}' "$f"); [ -n "$n" ] || n=$(basename "$f" .toml)
60 H=$("$HAMMER" --store "$STORE" hash "$f" 2>/dev/null | tail -1 | sed 's/^b3://')
61 [ -n "$H" ] || { saltada=$((saltada+1)); continue; }
62 D="$H-$n"
63 [ -d "$STORE/$D" ] || { saltada=$((saltada+1)); continue; }
64
65 mv "$STORE/$D" "$GUARDA/$D"; PENDIENTE="$PENDIENTE $D"
66 if "$HAMMER" --store "$STORE" build "$f" >/dev/null 2>&1 && [ -d "$STORE/$D" ]; then
67 if "$HAMMER" why-differs "$GUARDA/$D" "$STORE/$D" >/dev/null 2>&1; then
68 printf " ✓ %-26s REPRODUCE\n" "$n"; ok=$((ok+1))
69 rm -rf "$GUARDA/$D"
70 else
71 printf " ✗ %-26s DIVERGE\n" "$n"; dif=$((dif+1)); DIVERGEN="$DIVERGEN $n"
72 "$HAMMER" why-differs "$GUARDA/$D" "$STORE/$D" 2>&1 | tail -4 | sed 's/^/ /'
73 rm -rf "$GUARDA/$D"
74 fi
75 else
76 # El rebuild falló: restaurar y NO contarlo como divergencia (no es lo mismo «no reproduce» que
77 # «no construye acá»). Distinguirlos importa: mezclarlos inventaría un problema de determinismo.
78 printf " ⚠ %-26s no construyó (artefacto restaurado)\n" "$n"; fallo=$((fallo+1))
79 rm -rf "$STORE/$D"; mv "$GUARDA/$D" "$STORE/$D"
80 fi
81 PENDIENTE=$(echo "$PENDIENTE" | sed "s| $D||")
82done
83
84echo "════════════════════════════════════════════════════════════"
85echo " REPRODUCEN: $ok · DIVERGEN: $dif · no construyeron: $fallo · sin artefacto: $saltada"
86[ "$dif" -gt 0 ] && echo " ⚠ divergen:$DIVERGEN"
87[ "$dif" -eq 0 ] && [ "$ok" -gt 0 ] && echo " ✅ PUERTA SUPERADA: 0 divergencias sobre $ok verificadas"
88exit $(( dif > 0 ? 1 : 0 ))