tawasuyu

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…

TOML 1569Shell 203Rust 109Markdown 993728 Sutichaykuna

atuq: los dieciocho guardianes en UN comando — y el sello llevaba nueve horas atrás

`atuq` es `source.dir` entero, asi que el commit de la boveda de esta manana (`6f0ba974`) movio el
ArtifactHash y dejo el artefacto vigente SIN SELLAR. Durante esas nueve horas ningun guardian podia
medir: se niegan a correr contra uno que no sea el vigente, que es lo correcto. Pero un guardian que
se niega solo grita CUANDO alguien lo corre, y nadie lo corria — el repo se veia sano y la unica
senal era un `hash --check` que nadie tenia motivo para teclear.

Resellado en 3,7 s (es derivado y el firefox vigente estaba en el store) y corridos los dieciocho
sobre `b3:e556024b`: 16 en verde y 2 en ROJO, los dos por la MISMA causa —los tres verbos de la
boveda del §7.quinquies—. O sea que la unidad 12 no rompio nada de al lado, que es lo que habia que
saber antes de seguir: toco `atuq.cfg`, la politica y el empaquetado de extensiones.

`scripts/test-atuq-suite.sh` deja eso en un comando, en serie (cuatro nucleos sin swap; dos
navegadores a la vez es un OOM) y cada guardian a SU fichero, porque dos corridas sobre el mismo log
se mezclan y el resultado parece un pase. Lo primero que mira es el sello y NO corre nada si falta:
verificado contra un store vacio, sale por ahi y devuelve 1.

Dos cosas quedan escritas como control y no como adorno:

· el numero de rojos esperados va en la cabecera. Un cuadro entero en verde puede ser un producto
  sano o un runner que no corre nada, y se ven igual;
· esa cabecera decia «un rojo» —prediccion escrita antes de correr— y la primera corrida dijo dos:
  el guardian de coherencia de la boveda lleva el mismo chequeo del cable adentro.

Coste medido en gioser: ~29 min. Los cuatro primeros salen en 10 s y no tocan el navegador.

ba202d7826a3ec5821e97bfb11329482f204c425 · ruwaq Sergio · (1 killa ñawpaq) · tayta 4de3f2b52 · kay commitpi sach'ata qhaway

Semántico sayk'uy

Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.

2 qillqakuna · +182 −0

diff --git a/docs/26-atuq-envoltorio-gecko.md b/docs/26-atuq-envoltorio-gecko.md
index b7979540..5b565795 100644
--- a/docs/26-atuq-envoltorio-gecko.md
+++ b/docs/26-atuq-envoltorio-gecko.md
@@ -2629,6 +2629,44 @@ el `BuildID` sea el que `SOURCE_DATE_EPOCH` obliga.
 **La regla que sale de todo el día:** cuando la duda es «¿llegó al artefacto?», la respuesta no está
 en el log del build ni en la receta — está **dentro del artefacto**, y hay que ir a buscarla ahí.
 
+### 2.septies Los dieciocho guardianes, en una corrida — y el sello que se había quedado atrás (2026-09-15)
+
+Cada afirmación de este documento tiene quién la mida, y son **dieciocho guardianes**. Hasta hoy
+ninguno corría solo: se corrían de a uno, el día que alguien tocaba esa parte. Ya se sabe cómo
+termina eso —el de descargas estuvo ROJO dos días y nadie lo corrió (`29831e62`)—, así que la corrida
+entera es ahora **un comando**: `scripts/test-atuq-suite.sh`.
+
+**Y lo primero que encontró fue un hueco que no era de ningún guardián, sino del ciclo.** Todo
+`recipes/atuq/` es `source.dir`, así que el commit de la bóveda de esta mañana (unidad 12,
+`6f0ba974`) movió el `ArtifactHash` — y **`atuq` quedó NO-SELLADO durante las nueve horas
+siguientes**. En ese lapso ningún guardián podía medir nada: se niegan a correr contra un artefacto
+que no sea el vigente, que es lo correcto. Pero un guardián que se niega **sólo grita cuando alguien
+lo corre**, y nadie lo corría: el repo se veía sano, el commit estaba pusheado, y la única señal era
+un `hash --check` que nadie tenía motivo para teclear. Por eso lo primero que hace el runner es mirar
+el sello y NO correr nada si falta, diciendo el comando exacto para construirlo. Verificado con
+control: contra un store vacío sale por ahí y devuelve 1.
+
+El re-sello costó **3,7 s** —`atuq` es derivado y el `firefox` vigente estaba en el store— y el ciclo
+que manda es el de siempre: **editar todo → resellar UNA vez → medir**.
+
+**El cuadro, sobre `b3:e556024b` (gioser, en serie, 29 min):**
+
+| | |
+|---|---|
+| **16 en verde** | política, rootfs, inicio, chrome, nativo, foco, instalación, ruteo, archivo, descargas, medios, torrent, `sct`, códecs, IA y archivo semántico |
+| **2 en ROJO** | `vigia-atuq-verbos` y `test-atuq-boveda-coherente`, **los dos por la misma causa**: los tres verbos de la bóveda (§7.quinquies) |
+
+O sea: **el commit de la bóveda no rompió nada más**. Los quince guardianes que no tienen nada que
+ver con ella siguen verdes sobre el artefacto que la trae adentro, y eso es lo que hacía falta saber
+antes de seguir — porque la unidad 12 tocó `atuq.cfg`, la política y el empaquetado de extensiones,
+que es exactamente donde un cambio se lleva puesto algo de al lado sin decirlo.
+
+⚠ **Y el control del runner se corrigió solo en la primera corrida.** La cabecera anunciaba «17 en
+verde y UN rojo» —predicción escrita antes de correr— y el cuadro dijo dos: el guardián de coherencia
+de la bóveda lleva el mismo chequeo del cable adentro, que es justamente lo que el §7.quinquies le
+pidió. El número quedó escrito en la cabecera **como control**: un cuadro entero en verde puede ser
+un producto sano o un runner que no corre nada, y se ven igual.
+
 ## 7. La costura: un host de native messaging en Rust
 
 Todo lo del §6 que no es CSS pasa por **un solo mecanismo**: un proceso Rust que habla native
diff --git a/scripts/test-atuq-suite.sh b/scripts/test-atuq-suite.sh
new file mode 100755
index 00000000..d60f10b3
--- /dev/null
+++ b/scripts/test-atuq-suite.sh
@@ -0,0 +1,144 @@
+#!/usr/bin/env bash
+# test-atuq-suite.sh — corre TODOS los guardianes de `atuq`, en orden de coste, y da un cuadro.
+#
+# ══ POR QUÉ EXISTE ════════════════════════════════════════════════════════════════════════════
+# Los guardianes de atuq son dieciocho y ninguno corre solo. Ya pasó lo que tenía que pasar: el de
+# descargas estuvo ROJO dos días y nadie lo corrió (commit `29831e62`), y el 2026-09-15 el artefacto
+# vigente estuvo SIN SELLAR desde el commit de la bóveda hasta que alguien miró — durante esas horas
+# ningún guardián podía medir nada y el repo se veía igual de sano. Un guardián que nadie corre es
+# documentación, no una medición.
+#
+# ══ LO PRIMERO QUE MIRA ES EL SELLO, Y NO ES UN DETALLE ═══════════════════════════════════════
+# Todo `recipes/atuq/` es `source.dir` ⇒ tocar una extensión, `atuq.cfg`, la política o el README
+# cambia el ArtifactHash, y los guardianes se NIEGAN a medir un artefacto que no sea el vigente. El
+# ciclo es **editar todo → resellar UNA vez → medir**; al revés se pagan 340 M de store por edición.
+# Si falta el sello, esto NO corre nada: dice cómo construirlo y sale.
+#
+# ══ EL CONTROL DE ESTA CORRIDA ════════════════════════════════════════════════════════════════
+# Hoy (2026-09-15) el resultado CORRECTO es **16 en verde y DOS en rojo**: `vigia-atuq-verbos` y
+# `test-atuq-boveda-coherente`, los dos por la MISMA causa —la receta de `puriy-costura` pinea un
+# host anterior a los verbos de la bóveda, abierto y medido en el SDD 26 §7.quinquies—. **Si esto
+# sale todo verde, lo roto es esto**: un cuadro entero en verde puede ser un producto sano o un
+# runner que no corre nada, y se ven igual.
+#
+# ⚠ Esta cabecera decía «un rojo» hasta que la primera corrida la desmintió: el segundo guardián
+# lleva el mismo chequeo del cable ADENTRO. Queda escrito porque es la trampa de siempre —predecir
+# el resultado y no volver a mirarlo—, y porque el número es el control: si mañana hay UN rojo, o
+# tres, cambió algo que hay que ir a ver.
+#
+# ══ LO QUE CUESTA ═════════════════════════════════════════════════════════════════════════════
+# **~29 min** en gioser, en serie (medido 2026-09-15). Los cuatro primeros salen en 10 s y no tocan
+# el navegador: para «¿rompí algo del cruce política↔XPI?» alcanza con `--only` y no hace falta la
+# media hora. Los caros son `sct` (241 s, seis cargas), `archivo-semantico` (227 s, embeddings),
+# `instalacion` (180 s, tres sesiones) y `ia` (180 s, levanta `llama-server`).
+#
+# ══ QUÉ NO ESTÁ ACÁ ═══════════════════════════════════════════════════════════════════════════
+# `scripts/wlr/dunst-headless.sh --via-atuq` (una página web notifica y se DIBUJA): vive en el arnés
+# de sway y se corre desde ahí. Se nombra para que su ausencia no se lea como cobertura.
+#
+# NO CONSTRUYE NADA y NO TOMA EL LOCK de la granja: sólo lee artefactos ya sellados, así que se puede
+# correr con la granja a pleno. Va en SERIE a propósito — cada guardián levanta un navegador y esta
+# máquina tiene cuatro núcleos sin swap; dos a la vez es un OOM. Y cada uno escribe en SU fichero:
+# dos corridas sobre el mismo log se MEZCLAN y el resultado parece un pase.
+#
+#   scripts/test-atuq-suite.sh                  # los dieciocho, en orden de coste
+#   scripts/test-atuq-suite.sh --list           # qué afirma cada uno, sin correr nada
+#   scripts/test-atuq-suite.sh --only sct       # sólo los que contengan «sct» en el nombre
+#   scripts/test-atuq-suite.sh --store DIR --logs DIR
+set -u
+
+STORE="./store"
+LOGS="work/atuq-suite"
+SOLO=""
+LISTAR=0
+
+while [ $# -gt 0 ]; do
+  case "$1" in
+    --store) STORE="$2"; shift 2 ;;
+    --logs)  LOGS="$2";  shift 2 ;;
+    --only)  SOLO="$2";  shift 2 ;;
+    --list)  LISTAR=1;   shift ;;
+    -h|--help) sed -n '2,40p' "$0"; exit 0 ;;
+    *) printf 'no entiendo «%s» — ver --help\n' "$1" >&2; exit 2 ;;
+  esac
+done
+
+# nombre|orden de coste implícito|qué AFIRMA. El orden es de barato a caro: lo que se rompe gratis
+# se descubre en el primer minuto y no después de media hora de navegadores.
+GUARDIANES=(
+  "vigia-atuq-verbos.py|el host PINEADO atiende los verbos que mandan las diez extensiones (§7)"
+  "test-atuq-boveda-coherente.py|la bóveda está enchufada en los CINCO lugares (SDD-BOVEDA §8)"
+  "test-atuq-politica.py|el cruce XPI empaquetado ↔ política, con cinco roturas a propósito"
+  "test-atuq-rootfs.py|el rootfs resuelve todos los NEEDED y nada se cae al LAB"
+  "test-atuq-inicio.py|la página de inicio y la pestaña nueva son las NUESTRAS (§4.e)"
+  "test-atuq-chrome.py|el JS de atuq.cfg llega al gBrowser, y la vista dividida (§4.g)"
+  "test-atuq-nativo.py|una extensión habla con un proceso NATIVO y le vuelve la respuesta (§7)"
+  "test-atuq-foco.py|el navegador MUESTRA el foco del sistema y no lo apaga (§6.5)"
+  "test-atuq-instalacion.py|quién instala de verdad las extensiones: el ESCANEO, no la política"
+  "test-atuq-ruteo.py|el contenedor sale por SU proxy — saludo SOCKS5 (§6.8)"
+  "test-atuq-archivo.py|congelar lo leído al CAS y buscarlo; nunca lo privado (§6.3)"
+  "test-atuq-descargas.py|la descarga entra al CAS con su BLAKE3 y el fichero no se toca (§6.2)"
+  "test-atuq-medios.py|el medio lo abre mpv, no una pestaña (§6.6)"
+  "test-atuq-torrent.py|magnet → daemon propio que sobrevive al navegador (§6.9)"
+  "test-atuq-sct.py|la cadena de sct entera: seis cargas y un servidor HTTP real (§6.1)"
+  "test-atuq-codecs.sh|H.264+AAC · VP9+Opus · AV1 · MP3 · FLAC REPRODUCEN (§4.f)"
+  "test-atuq-ia.py|la barra lateral le pregunta a un modelo de ESTA máquina (§6.7)"
+  "test-atuq-archivo-semantico.py|el archivo contesta por PARECIDO, no por palabra exacta (§6.3)"
+)
+
+if [ "$LISTAR" = 1 ]; then
+  printf '── los guardianes de atuq, de barato a caro ──\n'
+  for g in "${GUARDIANES[@]}"; do printf '  %-32s %s\n' "${g%%|*}" "${g#*|}"; done
+  printf '\n  no está acá: scripts/wlr/dunst-headless.sh --via-atuq (vive en el arnés de sway)\n'
+  exit 0
+fi
+
+# ── el sello, antes que nada ──────────────────────────────────────────────────────────────────
+H=$(./target/release/takana --store "$STORE" hash recipes/atuq.toml --check 2>/dev/null | tail -1)
+case "$H" in
+  *SELLADO*) : ;;
+  *) printf 'no pude preguntar el hash de atuq (¿sin `target/release/takana`?)\n'; exit 2 ;;
+esac
+if [ "${H#*	}" != "SELLADO" ]; then
+  printf '✗ el `atuq` VIGENTE no está sellado: %s\n' "${H%%	*}"
+  printf '  Ningún guardián puede medir — se niegan, y con razón. Construilo UNA vez y volvé:\n\n'
+  printf '    flock -o work/.farm-build.lock ./target/release/takana --store %s build recipes/atuq.toml\n\n' "$STORE"
+  exit 1
+fi
+printf '── los guardianes de atuq sobre %s ──\n' "${H%%	*}"
+mkdir -p "$LOGS"
+
+verdes=0; rojos=0; saltados=0
+RESUMEN=()
+for g in "${GUARDIANES[@]}"; do
+  nombre="${g%%|*}"; afirma="${g#*|}"
+  case "$nombre" in *"$SOLO"*) : ;; *) continue ;; esac
+  log="$LOGS/${nombre%.*}.txt"
+  printf '  %-32s ' "$nombre"
+  t0=$(date +%s)
+  case "$nombre" in
+    *.sh) bash "scripts/$nombre" >"$log" 2>&1 ;;
+    *)    python3 "scripts/$nombre" >"$log" 2>&1 ;;
+  esac
+  rc=$?
+  t=$(( $(date +%s) - t0 ))
+  # 77 es «no están las condiciones» (falta un rootfs, falta un modelo), no un fallo del producto.
+  case "$rc" in
+    0)  veredicto="✓ verde  "; verdes=$((verdes+1)) ;;
+    77) veredicto="— saltado"; saltados=$((saltados+1)) ;;
+    *)  veredicto="✗ ROJO   "; rojos=$((rojos+1)) ;;
+  esac
+  printf '%s %4ds   %s\n' "$veredicto" "$t" "$afirma"
+  RESUMEN+=("$veredicto|$nombre|$t|$log")
+done
+
+printf '\n'
+if [ "$rojos" -gt 0 ]; then
+  printf 'los ROJOS, con su salida entera:\n'
+  for r in "${RESUMEN[@]}"; do
+    case "$r" in "✗"*) printf '  %s → %s\n' "$(echo "$r" | cut -d'|' -f2)" "$(echo "$r" | cut -d'|' -f4)" ;; esac
+  done
+  printf '\n'
+fi
+printf '%d verde(s) · %d rojo(s) · %d saltado(s) — salidas en %s/\n' "$verdes" "$rojos" "$saltados" "$LOGS"
+[ "$rojos" -eq 0 ]