3 Commits
Author SHA1 Message Date
Sergio 6809ef4be2 simi: el /bin/sh propio del producto — 4448 líneas, sólo libc, y CERO divergencias contra el ash
«simi» es boca/lengua en quechua. Es el sh que el frente C necesitaba: busybox
está en la raíz de confianza del arranque (STAGE1_COMPONENTS, ATTEST_PATHS) y en
el exec de cada card, y el candidato ajeno que se midió —brush 0.4.0— pesa 6,9 MB,
arranca 4x más lento que el ash y trae dos divergencias medidas (#1394, #1396).

Alcance MEDIDO, no supuesto: las 23 cards con shell (8 builtins, 13
construcciones), los 21 /init empotrados de las imágenes (+ if, funciones, case,
aquí-documentos) y el POSIX de takana-live-install.sh.

Resultado del banco diferencial, 159 casos contra el ash de busybox 1.36.1
(10 fixtures de regresión, 107 de tortura de comillas, 23 fragmentos de cards,
19 /init empotrados):

    simi   DIVERGE=0        bash  DIVERGE=8        brush  DIVERGE=35

Incluye el bucle REAL de config.status que genera cualquier configure de
autotools — el que tumbó a brush. 499 KB release, 1,4x la latencia del ash
(brush: 4,1x). 47 pruebas propias en verde.

Seis bugs que el banco destapó, y ninguno se veía sin un control al lado:

1. El lexer fundía lo entrecomillado con lo desnudo, así que x="a b" se ejecutaba
   como ORDEN y el error decía «no encontrado» — mandando a buscar al lugar
   equivocado. Ahora el comillado es por carácter.
2. Usaba la barra invertida como marca interna y la quitaba al final, así que una
   barra PRODUCIDA por una sustitución se perdía. La quita de comillas es sobre
   las comillas de la palabra, nunca sobre el resultado de una expansión: hace
   falta una máscara de protección, no un escape dentro del texto.
3. set -e no se suspendía dentro del cuerpo de una función llamada desde una
   condición. Ahora es un contador dinámico, que entra solo donde corresponde.
4. Los delimitadores de la sustitución de orden y de ${ } no respetaban la barra
   invertida: un paréntesis escapado cerraba la sustitución.
5. Las clases entre corchetes no entendían la barra invertida — y la del
   config.status de libtool es literalmente una clase con cuatro escapes.
6. Dentro de comillas dobles el cuerpo de un backtick sigue entre comillas, así
   que la barra también se elimina ante la comilla doble.

Y uno que el banco NO vio y sí vio la prueba de regresión: el trap EXIT HEREDADO
se disparaba en el subshell. POSIX §2.12 manda resetearlo — es la mitad
complementaria del #1396 de brush, y al revés. Los dos oráculos hacen falta; el
caso quedó agregado al fixture 08.
2026-09-21 20:22:40 +00:00
Sergio 208d545e68 sh: el trap del subshell, reportado — y la lección de que upstream ya lo tenía en sus PRUEBAS
brush#1396 abierto. Antes de escribirlo apareció que reubeno/brush ya marcaba
el fallo como known_failure en trap.yaml:226 (y :269, :323 para las otras dos
variantes), sin issue que lo siguiera.

Queda anotado porque es método, no anécdota: un known_failure en la suite del
candidato es evidencia de primera y cuesta un gh api mirarla. Buscar en las
PRUEBAS del candidato, no sólo en sus issues, antes de decir «hallazgo nuevo».

Lo que el reporte sí aporta sobre esos TODOs: que las tres variantes son
probablemente un mismo bug (on_exit() sólo lo llaman los puntos de entrada de
nivel superior; el subshell de interp.rs:662-683 clona el shell y devuelve el
exit code sin pasar por ahí), que también pega en pipeline y en segundo plano,
y que falla en silencio con rc intacto.
2026-09-21 19:11:08 +00:00
Sergio ddc6e4845b sh: el banco DIFERENCIAL — compara el ÁRBOL producido, no sólo stdout, y el generador saca el bug de brush sin saber que existe
El banco POSIX de 57 casos lo pasa brush 56/57 y aun así no construye una
receta autotools: mide lo que a uno se le ocurrió preguntar. Este pregunta si
el candidato hace LO MISMO que el control sobre el material que hay.

- compara (rc, stdout, árbol de ficheros producido); stderr es aviso, no
  divergencia — el texto de error diverge legítimamente
- el árbol es la columna que faltaba: el caso 10 diverge SÓLO ahí, que es la
  clase de bug del libtool mal escapado (rc=0, stdout idéntico)
- toda divergencia se confirma re-corriendo control y candidato: separa caso
  no determinista de shell inestable de divergencia real
- fuentes: fixtures de regresión, generador de tortura de comillas, las 1130
  fases del corpus y configure/libtool reales en sh -n

Medido, 1273 casos en 5,5 s: brush 35 duras, bash 8, y CERO en los 1156 casos
de parseo. El generador saca el #1394 entero sin conocerlo. Hallazgo nuevo:
brush no ejecuta nunca el trap EXIT de un subshell — y eso golpea el frente
del PRODUCTO, donde el veredicto del paso 3 decía que no había evidencia en
contra.
2026-09-21 18:58:09 +00:00