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…
atuq: el vigésimo guardián entra a la suite, y entra TRUNCADO — con el motivo escrito al lado
1#!/usr/bin/env bash
2# test-atuq-suite.sh — corre TODOS los guardianes de `atuq`, en orden de coste, y da un cuadro.
3#
4# ══ POR QUÉ EXISTE ════════════════════════════════════════════════════════════════════════════
5# Los guardianes de atuq son VEINTE y ninguno corre solo. Ya pasó lo que tenía que pasar: el de
6# descargas estuvo ROJO dos días y nadie lo corrió (commit `29831e62`), y el 2026-09-15 el artefacto
7# vigente estuvo SIN SELLAR desde el commit de la bóveda hasta que alguien miró — durante esas horas
8# ningún guardián podía medir nada y el repo se veía igual de sano. Un guardián que nadie corre es
9# documentación, no una medición.
10#
11# ══ LO PRIMERO QUE MIRA ES EL SELLO, Y NO ES UN DETALLE ═══════════════════════════════════════
12# Todo `recipes/atuq/` es `source.dir` ⇒ tocar una extensión, `atuq.cfg`, la política o el README
13# cambia el ArtifactHash, y los guardianes se NIEGAN a medir un artefacto que no sea el vigente. El
14# ciclo es **editar todo → resellar UNA vez → medir**; al revés se pagan 340 M de store por edición.
15# Si falta el sello, esto NO corre nada: dice cómo construirlo y sale.
16#
17# ══ EL CONTROL DE ESTA CORRIDA ════════════════════════════════════════════════════════════════
18# Hoy (2026-09-18) el resultado CORRECTO es **20 en verde** — el vigésimo entró ese día y corre
19# TRUNCADO (`--hasta A`) a propósito: ver su renglón en la lista y el §7.septies. Un verde suyo
20# NO dice que la bóveda ande; dice que el host contesta lo que tiene que contestar sin dueño.
21# Lo fue «19 en verde» desde el 2026-09-16. Lo fue «17 y dos rojos» hasta esta
22# tarde: los dos eran los verbos de la bóveda, y se apagaron al subir el pin de `puriy-costura`
23# (SDD 26 §7.quinquies.bis). Si mañana hay un rojo, cambió algo que hay que ir a ver.
24#
25# ⚠ **Un cuadro entero en verde puede ser un producto sano o un runner que no corre nada, y se ven
26# igual.** Mientras hubo un rojo conocido, el número lo distinguía solo. Ahora no, así que lo que
27# sostiene la corrida son otras dos cosas, las dos comprobadas:
28#
29# · **la puerta del sello** — contra un store sin el artefacto, esto NO corre nada y devuelve 1
30# (verificado a propósito con un store vacío);
31# · **el control de cada guardián** — todos traen el suyo (una rotura que TIENE que matarlos, o un
32# positivo que TIENE que aparecer). El runner no los reemplaza: los junta.
33#
34# ⚠ Y la advertencia que este fichero se aplicó a sí mismo dos veces: la cabecera anunció «un rojo»
35# y eran dos, y después «dos» cuando ya no quedaba ninguno. **Un número escrito como predicción
36# envejece callado.** Si el cuadro no coincide con esto, el sospechoso es esta cabecera.
37#
38# ══ LO QUE CUESTA ═════════════════════════════════════════════════════════════════════════════
39# **~34 min** en gioser, en serie (32,1 min medidos el 2026-09-16 sobre 19; el vigésimo suma ~2). Los cuatro primeros salen en 10 s y no tocan
40# el navegador: para «¿rompí algo del cruce política↔XPI?» alcanza con `--only` y no hace falta la
41# media hora. Los caros son `sct` (241 s, seis cargas), `archivo-semantico` (227 s, embeddings),
42# `instalacion` (180 s, tres sesiones) y `ia` (180 s, levanta `llama-server`).
43#
44# ══ QUÉ NO ESTÁ ACÁ ═══════════════════════════════════════════════════════════════════════════
45# Nada, hoy. El de notificaciones (`scripts/wlr/dunst-headless.sh --via-atuq`) vive en el arnés de
46# sway y por eso se había quedado afuera; entra igual, porque una lista con un hueco nombrado en la
47# cabecera se lee como cobertura al tercer día. Si mañana alguien saca uno, que lo nombre acá.
48#
49# NO CONSTRUYE NADA y NO TOMA EL LOCK de la granja: sólo lee artefactos ya sellados, así que se puede
50# correr con la granja a pleno. Va en SERIE a propósito — cada guardián levanta un navegador y esta
51# máquina tiene cuatro núcleos sin swap; dos a la vez es un OOM. Y cada uno escribe en SU fichero:
52# dos corridas sobre el mismo log se MEZCLAN y el resultado parece un pase.
53#
54# scripts/test-atuq-suite.sh # los diecinueve, en orden de coste
55# scripts/test-atuq-suite.sh --list # qué afirma cada uno, sin correr nada
56# scripts/test-atuq-suite.sh --only sct # sólo los que contengan «sct» en el nombre
57# scripts/test-atuq-suite.sh --store DIR --logs DIR
58set -u
59
60STORE="./store"
61LOGS="work/atuq-suite"
62SOLO=""
63LISTAR=0
64
65while [ $# -gt 0 ]; do
66 case "$1" in
67 --store) STORE="$2"; shift 2 ;;
68 --logs) LOGS="$2"; shift 2 ;;
69 --only) SOLO="$2"; shift 2 ;;
70 --list) LISTAR=1; shift ;;
71 -h|--help) sed -n '2,40p' "$0"; exit 0 ;;
72 *) printf 'no entiendo «%s» — ver --help\n' "$1" >&2; exit 2 ;;
73 esac
74done
75
76# etiqueta|comando|qué AFIRMA. El orden es de barato a caro: lo que se rompe gratis
77# se descubre en el primer minuto y no después de media hora de navegadores.
78GUARDIANES=(
79 "vigia-atuq-verbos|python3 scripts/vigia-atuq-verbos.py|el host PINEADO atiende los verbos que mandan las diez extensiones (§7)"
80 "test-atuq-boveda-coherente|python3 scripts/test-atuq-boveda-coherente.py|la bóveda está enchufada en los CINCO lugares (SDD-BOVEDA §8)"
81 # ⚠ CORRE TRUNCADO, `--hasta A`, y el motivo está en el §7.septies: `boveda` y `shuma-pregunta`
82 # están sellados y NO ABREN VENTANA —ninguna app llimphi de escritorio puede, porque no hay Vulkan
83 # en ninguna imagen y el camino GL de llimphi arma la instancia sin display handle—, así que las
84 # etapas B..F miden un muro ajeno a la bóveda. Lo que la A afirma es la medición del §7.sexies, que
85 # es la que costó tres semanas de no ver: **sin la app dueña, el host contesta `locked:true` con
86 # `ok:true`**. Cuando llimphi se destrabe, se saca el `--hasta A` y esta nota se va con él.
87 "test-atuq-boveda-metal|python3 scripts/test-atuq-boveda-metal.py --hasta A|sin el dueño de la bóveda, el host contesta CERRADA — y «cerrada» es un ok (§7.sexies)"
88 "test-atuq-politica|python3 scripts/test-atuq-politica.py|el cruce XPI empaquetado ↔ política, con cinco roturas a propósito"
89 "test-atuq-rootfs|python3 scripts/test-atuq-rootfs.py|el rootfs resuelve todos los NEEDED y nada se cae al LAB"
90 "test-atuq-inicio|python3 scripts/test-atuq-inicio.py|la página de inicio y la pestaña nueva son las NUESTRAS (§4.e)"
91 "test-atuq-chrome|python3 scripts/test-atuq-chrome.py|el JS de atuq.cfg llega al gBrowser, y la vista dividida (§4.g)"
92 "test-atuq-nativo|python3 scripts/test-atuq-nativo.py|una extensión habla con un proceso NATIVO y le vuelve la respuesta (§7)"
93 "test-atuq-foco|python3 scripts/test-atuq-foco.py|el navegador MUESTRA el foco del sistema y no lo apaga (§6.5)"
94 "test-atuq-instalacion|python3 scripts/test-atuq-instalacion.py|quién instala de verdad las extensiones: el ESCANEO, no la política"
95 "test-atuq-ruteo|python3 scripts/test-atuq-ruteo.py|el contenedor sale por SU proxy — saludo SOCKS5 (§6.8)"
96 "test-atuq-archivo|python3 scripts/test-atuq-archivo.py|congelar lo leído al CAS y buscarlo; nunca lo privado (§6.3)"
97 "test-atuq-descargas|python3 scripts/test-atuq-descargas.py|la descarga entra al CAS con su BLAKE3 y el fichero no se toca (§6.2)"
98 "test-atuq-medios|python3 scripts/test-atuq-medios.py|el medio lo abre mpv, no una pestaña (§6.6)"
99 "test-atuq-torrent|python3 scripts/test-atuq-torrent.py|magnet → daemon propio que sobrevive al navegador (§6.9)"
100 "test-atuq-sct|python3 scripts/test-atuq-sct.py|la cadena de sct entera: seis cargas y un servidor HTTP real (§6.1)"
101 "test-atuq-codecs|bash scripts/test-atuq-codecs.sh|H.264+AAC · VP9+Opus · AV1 · MP3 · FLAC REPRODUCEN (§4.f)"
102 "test-atuq-ia|python3 scripts/test-atuq-ia.py|la barra lateral le pregunta a un modelo de ESTA máquina (§6.7)"
103 "test-atuq-archivo-semantico|python3 scripts/test-atuq-archivo-semantico.py|el archivo contesta por PARECIDO, no por palabra exacta (§6.3)"
104 "dunst-via-atuq|sh scripts/wlr/dunst-headless.sh --via-atuq|una PÁGINA WEB notifica y la notificación se DIBUJA (§6.10)"
105)
106
107if [ "$LISTAR" = 1 ]; then
108 printf '── los guardianes de atuq, de barato a caro ──\n'
109 for g in "${GUARDIANES[@]}"; do
110 IFS='|' read -r etq cmd afirma <<<"$g"
111 printf ' %-30s %s\n %s\n' "$etq" "$afirma" "$cmd"
112 done
113 exit 0
114fi
115
116# ── el sello, antes que nada ──────────────────────────────────────────────────────────────────
117H=$(./target/release/takana --store "$STORE" hash recipes/atuq.toml --check 2>/dev/null | tail -1)
118case "$H" in
119 *SELLADO*) : ;;
120 *) printf 'no pude preguntar el hash de atuq (¿sin `target/release/takana`?)\n'; exit 2 ;;
121esac
122if [ "${H#* }" != "SELLADO" ]; then
123 printf '✗ el `atuq` VIGENTE no está sellado: %s\n' "${H%% *}"
124 printf ' Ningún guardián puede medir — se niegan, y con razón. Construilo UNA vez y volvé:\n\n'
125 printf ' flock -o work/.farm-build.lock ./target/release/takana --store %s build recipes/atuq.toml\n\n' "$STORE"
126 exit 1
127fi
128printf '── los guardianes de atuq sobre %s ──\n' "${H%% *}"
129mkdir -p "$LOGS"
130
131verdes=0; rojos=0; saltados=0
132RESUMEN=()
133for g in "${GUARDIANES[@]}"; do
134 IFS='|' read -r nombre cmd afirma <<<"$g"
135 case "$nombre" in *"$SOLO"*) : ;; *) continue ;; esac
136 log="$LOGS/$nombre.txt"
137 printf ' %-30s ' "$nombre"
138 t0=$(date +%s)
139 # Cada guardián a SU fichero: dos corridas sobre el mismo log se MEZCLAN y el resultado parece un
140 # pase — ya pasó (se leyó «✓ SALIDA=0» de la corrida vieja con las cargas de la nueva).
141 $cmd >"$log" 2>&1
142 rc=$?
143 t=$(( $(date +%s) - t0 ))
144 # 77 es «no están las condiciones» (falta un rootfs, falta un modelo), no un fallo del producto.
145 case "$rc" in
146 0) veredicto="✓ verde "; verdes=$((verdes+1)) ;;
147 77) veredicto="— saltado"; saltados=$((saltados+1)) ;;
148 *) veredicto="✗ ROJO "; rojos=$((rojos+1)) ;;
149 esac
150 printf '%s %4ds %s\n' "$veredicto" "$t" "$afirma"
151 RESUMEN+=("$veredicto|$nombre|$t|$log")
152done
153
154printf '\n'
155if [ "$rojos" -gt 0 ]; then
156 printf 'los ROJOS, con su salida entera:\n'
157 for r in "${RESUMEN[@]}"; do
158 case "$r" in "✗"*) printf ' %s → %s\n' "$(echo "$r" | cut -d'|' -f2)" "$(echo "$r" | cut -d'|' -f4)" ;; esac
159 done
160 printf '\n'
161fi
162printf '%d verde(s) · %d rojo(s) · %d saltado(s) — salidas en %s/\n' "$verdes" "$rojos" "$saltados" "$LOGS"
163[ "$rojos" -eq 0 ]