atuq: la bóveda estaba SELLADA y en ninguna imagen — y estar en la imagen no es poder abrirla

El §7.novies dio la función por cerrada: las seis etapas del guardián de metal en verde, con el
navegador de verdad y el diálogo a la vista. Lo que seguía abierto era la decisión 1 del §7.sexies
—«en qué imágenes se declaran»—, escrita como NO mientras ninguna app llimphi pudiera pintar. Ese
motivo se cayó el 2026-09-18, así que antes de tomarla se volvió a medir en vez de darla por sabida:

    atuq             sealed   perfiles=[cosmic, gnome, kde, sway]
    puriy-costura    sealed   perfiles=[cosmic, gnome, kde, sway]
    boveda           sealed   perfiles=[]
    shuma-pregunta   sealed   perfiles=[]

`sealed` con `perfiles: []` es sellado ≠ instalado: la lección de `foot`, que targets.toml repetía
QUINCE veces antes de hoy y que igual volvió a morder. Las dos entran a los cuatro perfiles de
escritorio, las dos o ninguna —sin el dueño `vault.match` no ofrece nada; sin el diálogo,
`Command::new` falla y TODO `vault.fill` se deniega—: media bóveda es una que niega todo en
silencio. ~43 M por imagen (22 M + 21 M medidos), contra los ~1,25 GiB que ya lleva el §6.7.

Y al declararlas apareció el hueco de una capa más arriba: la receta instalaba `/usr/bin/boveda` y
nada más, y los lanzadores de los cuatro escritorios leen `/usr/share/applications`. La app viajaría
en la imagen sin existir para quien la usa — la misma forma de fallo que esto viene persiguiendo.
Entra `boveda.desktop`, con tres cosas medidas antes de escribirlo:

· el icono existe: `dialog-password` está en breeze-icons (6), adwaita (1) y cosmic-icons (2). El
  cuarto perfil lleva sólo hicolor, que no trae iconos: ahí cae al genérico, que es degradarse;
· lo acepta el `desktop-file-validate` del store, con `atuq.desktop` de control. Deja un hint sobre
  `Security`, y las dos formas de callarlo lo cambian por uno PEOR (dos categorías principales ⇒ la
  app aparece dos veces en el menú). Se queda como está;
· ⚠ y lo que NO puede hacer: emparejar la ventana con el lanzador. `llimphi_ui::run` no llama nunca
  a `with_name` ⇒ winit no manda `set_app_id` y la ventana sale SIN app_id y con el título
  "llimphi". Por eso no hay `StartupWMClass`. Vale para toda app llimphi; se arregla en llimphi.

La receta se reconstruyó en el worker con la guarda del §7.quinquies puesta (`### receta verificada
3f1072cc` antes de compilar nada, porque el latido revierte la receta cada media hora y un acierto
de caché sobre la vieja imprime SELLADA en cero segundos): `b3:b0c6adc4` ⇒ `b3:3f1072cc`, 22 M, con
el árbol mirado por dentro y la entrada dentro del artefacto.

Y el guardián de coherencia pasa de CINCO lugares a SEIS: el sexto es `targets.toml` —quién DECLARA
al dueño en la imagen—, con control positivo (`atuq` tiene que estar, o el chequeo está leyendo el
campo equivocado) y su propio control negativo, el tercero. Probado en los dos sentidos: cuatro
perfiles en verde, y `--negative-control-perfil` en rojo.

Abierto, y dicho como lo que es: quién levanta la app con la sesión (atado a la decisión 2 del
§7.sexies, la raíz de las claves), y que el único proveedor de GL de las cuatro imágenes es iris
—mesa-llvmpipe en ningún perfil—, que la bóveda hereda y no agrega.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-09-21 01:30:57 +00:00
co-authored by Claude Opus 5
parent 35cc75be0c
commit e6ab5a5016
6 changed files with 326 additions and 14 deletions
+102 -1
View File
@@ -3317,6 +3317,107 @@ llimphi de escritorio podía pintar sin Vulkan (§7.septies), y que el dueño at
(§7.octies). Las tres se veían igual desde el navegador: una bóveda que no ofrece nada, sin un solo (§7.octies). Las tres se veían igual desde el navegador: una bóveda que no ofrece nada, sin un solo
error. Es el argumento entero a favor de medir en metal y no leer ficheros. error. Es el argumento entero a favor de medir en metal y no leer ficheros.
#### 7.decies La bóveda estaba SELLADA y en ninguna imagen — el sexto lugar donde una extensión se enchufa (2026-09-21)
El §7.novies cerró la función: las seis etapas en verde, el diálogo a la vista, el control de decir
que no. Lo que quedaba abierto era la **decisión 1 del §7.sexies** —«en qué imágenes se declaran»—
con su respuesta escrita como **NO**, y con el motivo: mientras ninguna app llimphi pudiera pintar
(§7.septies), declararlas era instalar 43 M de binarios que niegan todo sin un error.
Ese motivo se cayó el 2026-09-18. La respuesta de hoy es **SÍ, en las cuatro**, y antes de tomarla
la pregunta se volvió a medir en vez de darla por sabida:
```
atuq sealed perfiles=['escritorio-cosmic','escritorio-gnome','escritorio-kde','escritorio-sway']
puriy-costura sealed perfiles=['escritorio-cosmic','escritorio-gnome','escritorio-kde','escritorio-sway']
boveda sealed perfiles=[]
shuma-pregunta sealed perfiles=[]
```
**`sealed` con `perfiles: []` es la lección de `foot` otra vez** —y no por falta de haberla
escrito: `targets.toml` la repetía **quince veces** antes de hoy—: una receta sellada que ningún
perfil declara NO ESTÁ EN NINGUNA IMAGEN, y la métrica de clausura no lo puede ver porque mide lo
declarado. Las dos entran a los cuatro perfiles de escritorio de
`docs/state/targets.toml`, **las dos o ninguna**: sin el dueño `vault.match` no ofrece nada, y sin
el diálogo `Command::new` falla y TODO `vault.fill` se deniega — media bóveda es una que niega todo
en silencio. Cuestan **~43 M por imagen** (22 M + 21 M medidos sobre los artefactos sellados), contra
los ~1,25 GiB que ya lleva el §6.7.
##### El hueco que apareció al declararlas: estar en la imagen no es poder abrirla
Con las dos raíces puestas, la app viaja en las cuatro imágenes y **sigue sin existir para quien la
usa**: `recipes/boveda.toml` instalaba `/usr/bin/boveda` y nada más, y los lanzadores de los cuatro
escritorios leen `/usr/share/applications`. A un binario que nadie lista sólo se llega escribiendo
`boveda` en una terminal. Es **la misma forma de fallo, una capa más arriba**: la función instalada,
apagada y sin un error — que es lo único que este capítulo entero viene persiguiendo.
Entra `boveda.desktop` en la fase `install` de la receta. Tres cosas se midieron antes de escribirlo:
- **el icono existe.** `Icon=dialog-password` es nombre del icon naming spec, y está en los tres
temas que los perfiles declaran: `breeze-icons` (6 ficheros), `adwaita-icon-theme` (1),
`cosmic-icons` (2). El cuarto perfil (sway) lleva sólo `hicolor`, que por diseño no trae iconos:
ahí cae al genérico, que es degradarse y no romperse;
- **el fichero lo acepta el validador de verdad** —el `desktop-file-validate` del artefacto
`desktop-file-utils`, con `atuq.desktop` de control, que pasa sin una observación—. Deja un hint
sobre `Categories=Utility;Security;`: que `Security` se empareja con `Settings` o `System`. **Las
dos formas que callan ese hint lo cambian por uno peor** —medido: `Utility;Security;System;` y
`Utility;Security;Settings;` traen dos categorías principales ⇒ «application might appear more
than once in the application menu»—. Se queda como está, que es además lo que usa KeePassXC;
- **⚠ y la ventana NO se va a poder emparejar con el lanzador, y no es de acá.** `llimphi_ui::run`
no llama nunca a `with_name`, y winit 0.30.13 sólo manda `set_app_id` `if let Some(name) =
attributes.platform_specific.name` ⇒ **la ventana sale sin `app_id`**, y con el título `"llimphi"`,
que es el default de `App::title` y `BovedaApp` no sobrescribe. Por eso el `.desktop` no lleva
`StartupWMClass`: no habría contra qué emparejarlo. Vale para **toda** app llimphi, no para ésta:
el dock las ve a todas iguales y sin nombre. Es de llimphi —como el muro del §7.septies— y se
arregla allá.
El `.desktop` mueve el hash de la receta: `b3:b0c6adc4` ⇒ `b3:3f1072cc`, reconstruida en el worker
con la guarda del §7.quinquies puesta (`### receta verificada 3f1072cc…` antes de compilar nada,
porque el latido revierte la receta del worker cada media hora y un acierto de caché sobre la receta
vieja imprime `SELLADA` en cero segundos). Y el artefacto se miró por dentro, que es la regla 3:
```
/.hammer/recipe.toml
/usr/bin/boveda
/usr/share/applications/boveda.desktop ← 22 M, y el validador del store lo acepta
```
##### El SEXTO lugar, y su guardián
`test-atuq-boveda-coherente.py` decía —y este documento con él— que una extensión de `atuq` está
enchufada en **cinco** lugares. Son **seis**, y el sexto es el que faltaba:
| lugar | qué decide | cómo se ve cuando falta |
|---|---|---|
| `manifest.json` | el id que la extensión declara | — |
| `distribution/policies.json` | la política que la instala | navegador sin la función |
| `native-messaging/*.json` | el permiso para hablarle al host | `connectNative` falla en silencio |
| `atuq.cfg` | las preferencias que la acompañan | el gestor de Gecko se pelea por el campo |
| `recipes/puriy-costura.toml` | el COMMIT del host: si sus verbos existen | «verbo desconocido» ⇒ se calla |
| **`docs/state/targets.toml`** | **quién DECLARA al dueño y al diálogo en la imagen** | **`ok:true, locked:true`** |
El sexto chequeo mira `targets.toml` —el manifiesto de objetivo, no `build-state.json`, que es su
derivado— y exige las dos raíces en los cuatro perfiles, con **control positivo**: `atuq` tiene que
estar ahí, porque un `in` que no encuentra puede ser una raíz ausente o un campo equivocado y las dos
se ven igual. El tercer control negativo (`--negative-control-perfil`) saca a `boveda` de una copia
en memoria y exige que esto lo vea; sin él, un chequeo que siempre dice que sí se vería idéntico a
uno que funciona. Probado en los dos sentidos: los cuatro perfiles en verde, y el control en rojo.
##### Lo que queda abierto, dicho como lo que es
1. **quién levanta la app.** Hoy: la persona, desde el lanzador. Mientras no esté abierta, la bóveda
del navegador contesta `locked:true` —correcto, y es lo que hace cualquier gestor de contraseñas—,
pero nadie decidió todavía si debe autoarrancar con la sesión. No se decide de paso: autoarrancar
la ata a que la identidad del llavero esté desbloqueada en el login (la decisión 2 del §7.sexies,
que sigue abierta);
2. **el único proveedor de GL de las cuatro imágenes es `iris` (Intel).** `mesa-llvmpipe` no está en
ningún perfil y `mesa-swrast` sólo en `escritorio-mirada` — y softpipe no le alcanza a wgpu, que
pide compute (§7.septies). O sea que en metal sin GPU Intel el diálogo no pinta… igual que no
pintaría el escritorio entero, que también es cliente de GL: **la bóveda no agrega un hueco, lo
hereda**. Lo que taparía el caso de la VM es un proveedor por software en el perfil, y eso es una
decisión de tamaño con dos candidatos (`mesa-llvmpipe`, o lavapipe como receta nueva), no un
arreglo de paso.
## 8. Plan, por unidades de trabajo ## 8. Plan, por unidades de trabajo
Cada una cierra sola, se commitea y se pushea. El orden no es preferencia: cada una destraba a la Cada una cierra sola, se commitea y se pushea. El orden no es preferencia: cada una destraba a la
@@ -3344,7 +3445,7 @@ siguiente.
| 9 | **Medios (6.6) ✅ y torrent (6.9) ✅ — 2026-09-10.** El medio lo abre `mpv`; el torrent lo toma `puriy-costura-torrent`, un daemon **propio, configurable y perezoso** que sobrevive al navegador y cosecha al CAS. Medido antes de decidir: la pila de librqbit compila para musl (226 crates), así que la decisión fue «no ahí» y no «no se puede». **Faltan** el foco (6.5) y la IA local (6.7), ésta bloqueada porque el corpus no tiene modelo ni embeddings | 6.56.7, 6.9 | 5 ✅ | | 9 | **Medios (6.6) ✅ y torrent (6.9) ✅ — 2026-09-10.** El medio lo abre `mpv`; el torrent lo toma `puriy-costura-torrent`, un daemon **propio, configurable y perezoso** que sobrevive al navegador y cosecha al CAS. Medido antes de decidir: la pila de librqbit compila para musl (226 crates), así que la decisión fue «no ahí» y no «no se puede». **Faltan** el foco (6.5) y la IA local (6.7), ésta bloqueada porque el corpus no tiene modelo ni embeddings | 6.56.7, 6.9 | 5 ✅ |
| 10 | **Foco (6.5) — 2026-09-10.** Entra al corpus la cadena `nftables` (libmnl + libnftnl + nft 1.1.6), que el grafo pedía y nadie había puesto, con dos arreglos de fábrica: el sello de tiempo que rompía la reproducción y un bashismo. Medido con root: el cgroup en foco no sale y el de al lado sí. Y del lado del navegador, la extensión `foco` MUESTRA el estado y **no tiene verbo para apagarlo**. **Falta** quién pone a `atuq` en un cgroup (delegación, decisión de arje) y quién aplica la política (root) | 6.5 | 6 ✅ | | 10 | **Foco (6.5) — 2026-09-10.** Entra al corpus la cadena `nftables` (libmnl + libnftnl + nft 1.1.6), que el grafo pedía y nadie había puesto, con dos arreglos de fábrica: el sello de tiempo que rompía la reproducción y un bashismo. Medido con root: el cgroup en foco no sale y el de al lado sí. Y del lado del navegador, la extensión `foco` MUESTRA el estado y **no tiene verbo para apagarlo**. **Falta** quién pone a `atuq` en un cgroup (delegación, decisión de arje) y quién aplica la política (root) | 6.5 | 6 ✅ |
| 11 | **Motor de inferencia local (6.7) — 2026-09-11.** Entra `recipes/llama-cpp.toml` (b10901, estática, 199 M, **REPRODUCE**), que resulta ser **un solo muro para dos pendientes**: el §6.7 y la mitad semántica del §6.3 no eran dos problemas, era que el corpus no tenía con qué correr un modelo (`pluma-llm` sólo tiene backends de nube; `rimay-verbo-fastembed` DESCARGA onnxruntime glibc + el modelo). ⚠ Y dejó medido lo que no se podía deducir: **`SOURCE_DATE_EPOCH` —la variable que nos da reproducibilidad— apagaba las SEIS perillas de ISA de ggml**, y el artefacto sellaba y corría con 0 `%ymm`. Ahora van declaradas y el `install` las comprueba. Guardián `scripts/test-llama-cpp.py` con control negativo vivo. **Faltan** el modelo (fuente pineada, no receta), los verbos del host y quién levanta el servidor | 6.7, y la mitad semántica de 6.3 | 5 ✅ | | 11 | **Motor de inferencia local (6.7) — 2026-09-11.** Entra `recipes/llama-cpp.toml` (b10901, estática, 199 M, **REPRODUCE**), que resulta ser **un solo muro para dos pendientes**: el §6.7 y la mitad semántica del §6.3 no eran dos problemas, era que el corpus no tenía con qué correr un modelo (`pluma-llm` sólo tiene backends de nube; `rimay-verbo-fastembed` DESCARGA onnxruntime glibc + el modelo). ⚠ Y dejó medido lo que no se podía deducir: **`SOURCE_DATE_EPOCH` —la variable que nos da reproducibilidad— apagaba las SEIS perillas de ISA de ggml**, y el artefacto sellaba y corría con 0 `%ymm`. Ahora van declaradas y el `install` las comprueba. Guardián `scripts/test-llama-cpp.py` con control negativo vivo. **Faltan** el modelo (fuente pineada, no receta), los verbos del host y quién levanta el servidor | 6.7, y la mitad semántica de 6.3 | 5 ✅ |
| 12 | **La bóveda (SDD-BOVEDA §8) — a medias, 2026-09-15.** La décima extensión: ofrece la credencial del sitio abierto y no puede sacar una contraseña por su cuenta —`vault.match` contesta títulos y usuarios; la contraseña sale por `vault.fill`, que **pregunta en el escritorio**—. La dirección la pone el chrome, nunca la página. El gestor de Gecko se aparta como VALOR DE ARRANQUE y no como política. **Destrabada el 2026-09-16** (§7.quinquies.bis): el `Cargo.lock` de tawasuyu publicado —y resultó ser byte a byte el que la otra sesión ya tenía sin commitear—, el pin subido a `23a292863` ⇒ `b3:7d63655a`, construido en el worker (el vendoreo son 2,4 G y el hub estaba al 99%) y medido con el mismo `strings` que había diagnosticado el hueco: `vault` de **0 a 10**, con `cas`/`sct` de control. **Falta**, y no era lo que este renglón decía (§7.sexies, 2026-09-18): el host sellado contesta `vault.status → locked:true` porque **el dueño de la bóveda y el diálogo de consentimiento no estaban en el corpus** — la función está apagada de fábrica en las cuatro imágenes. Entran `recipes/boveda.toml` y `recipes/shuma-pregunta.toml`, **las dos SELLADAS el 2026-09-18** (`b3:59ffd74b` y `b3:99763eca`, construidas en el worker). ⚠ Y con eso apareció el muro de verdad, que no es de la bóveda: **ninguna app llimphi de escritorio abre ventana en esta distro** porque no hay Vulkan en ninguna imagen y el camino GL de llimphi arma la instancia sin display handle (§7.septies). **CERRADA el 2026-09-18** (§7.novies): el guardián de metal —`scripts/test-atuq-boveda-metal.py`— pasa sus **seis etapas**, con el navegador de verdad, el diálogo a la vista y el control de decir que NO. Para llegar hubo que arreglar **tres piezas ajenas al navegador**: el dueño y el diálogo no estaban en el corpus (§7.sexies), ninguna ventana llimphi podía pintar sin Vulkan (§7.septies, arreglado en llimphi) y el dueño atendía de a UN cliente, lo que con el navegador abierto dejaba la bóveda muda (§7.octies, arreglado en pacha con su test de regresión) | el gestor de contraseñas de la suite dentro del navegador | 5 ✅ | | 12 | **La bóveda (SDD-BOVEDA §8) — a medias, 2026-09-15.** La décima extensión: ofrece la credencial del sitio abierto y no puede sacar una contraseña por su cuenta —`vault.match` contesta títulos y usuarios; la contraseña sale por `vault.fill`, que **pregunta en el escritorio**—. La dirección la pone el chrome, nunca la página. El gestor de Gecko se aparta como VALOR DE ARRANQUE y no como política. **Destrabada el 2026-09-16** (§7.quinquies.bis): el `Cargo.lock` de tawasuyu publicado —y resultó ser byte a byte el que la otra sesión ya tenía sin commitear—, el pin subido a `23a292863` ⇒ `b3:7d63655a`, construido en el worker (el vendoreo son 2,4 G y el hub estaba al 99%) y medido con el mismo `strings` que había diagnosticado el hueco: `vault` de **0 a 10**, con `cas`/`sct` de control. **Falta**, y no era lo que este renglón decía (§7.sexies, 2026-09-18): el host sellado contesta `vault.status → locked:true` porque **el dueño de la bóveda y el diálogo de consentimiento no estaban en el corpus** — la función está apagada de fábrica en las cuatro imágenes. Entran `recipes/boveda.toml` y `recipes/shuma-pregunta.toml`, **las dos SELLADAS el 2026-09-18** (`b3:59ffd74b` y `b3:99763eca`, construidas en el worker). ⚠ Y con eso apareció el muro de verdad, que no es de la bóveda: **ninguna app llimphi de escritorio abre ventana en esta distro** porque no hay Vulkan en ninguna imagen y el camino GL de llimphi arma la instancia sin display handle (§7.septies). **CERRADA el 2026-09-18** (§7.novies): el guardián de metal —`scripts/test-atuq-boveda-metal.py`— pasa sus **seis etapas**, con el navegador de verdad, el diálogo a la vista y el control de decir que NO. Para llegar hubo que arreglar **tres piezas ajenas al navegador**: el dueño y el diálogo no estaban en el corpus (§7.sexies), ninguna ventana llimphi podía pintar sin Vulkan (§7.septies, arreglado en llimphi) y el dueño atendía de a UN cliente, lo que con el navegador abierto dejaba la bóveda muda (§7.octies, arreglado en pacha con su test de regresión). **Y ENTRA A LAS IMÁGENES el 2026-09-21** (§7.decies): hasta ese día las dos estaban `sealed` con `perfiles: []` —o sea selladas y en ninguna imagen, la lección de `foot` que ese mismo fichero repetía quince veces—, así que la función seguía apagada de fábrica aunque anduviera. Se declaran en los cuatro perfiles de escritorio (~43 M por imagen) y la receta aprende a instalar `boveda.desktop`, porque estar en la imagen no es poder abrirla: sin entrada en `/usr/share/applications` a la app sólo se llega escribiendo su nombre en una terminal. El guardián de coherencia pasa de CINCO lugares a SEIS —el sexto es `targets.toml`— con su tercer control negativo. **Abierto**: quién la levanta con la sesión, y de dónde sale la raíz de las claves (decisión 2 del §7.sexies) | el gestor de contraseñas de la suite dentro del navegador | 5 ✅ |
| 9.a | **Proxy por contenedor ✅ v0.5** — contenedores por política + extensión con `proxy.onRequest` | 6.8, y NO dependía de 5: es API de Firefox | — | | 9.a | **Proxy por contenedor ✅ v0.5** — contenedores por política + extensión con `proxy.onRequest` | 6.8, y NO dependía de 5: es API de Firefox | — |
Las unidades 2 y 4 son **paralelizables**: la toolchain no toca el chrome y el chrome no toca la Las unidades 2 y 4 son **paralelizables**: la toolchain no toca el chrome y el chrome no toca la
+96
View File
@@ -328,6 +328,30 @@ paquetes = [
# trampa, porque el panel está en todas. Bajarlo de acá es bajarlo de la imagen, no de la receta. # trampa, porque el panel está en todas. Bajarlo de acá es bajarlo de la imagen, no de la receta.
"llama-cpp", "llama-cpp",
"ia-modelo-chat", "ia-modelo-chat",
# ── LA BÓVEDA DEL NAVEGADOR (SDD 26 §7.sexies, decisión 1 — 2026-09-21) ──────────────────────
# `atuq` trae la décima extensión desde el 2026-09-15 y el host pineado atiende `vault.*` desde
# el 2026-09-16; aun así la función venía **APAGADA DE FÁBRICA en las cuatro imágenes**, sin un
# solo error: `vault.status` contesta `{"ok":true,"locked":true}` porque el host NUNCA abre la
# base (sled toma lock exclusivo y el proceso que lanza Gecko muere y revive con cada pestaña);
# le habla a un DUEÑO por un socket, y el dueño no estaba declarado en ningún perfil. Las dos
# recetas estaban selladas desde el 2026-09-18 con `perfiles: []` — sellado ≠ instalado, que es
# la lección de `foot`, escrita QUINCE veces en este fichero antes de hoy (`grep 'lección de
# .foot.'`) y aun así vuelta a pasar. Y «cerrada» es una respuesta EXITOSA:
# ni un log, ni un reintento, ni la insignia la distinguen de un usuario que no desbloqueó la suya.
# `boveda` la app DUEÑA de la base: la abre para su ventana y levanta el socket del
# navegador en un hilo. Sin ella, `vault.match` no ofrece nada, nunca.
# `shuma-pregunta` el diálogo de consentimiento que `PorDialogo` lanza por PATH. Sin él,
# `Command::new` falla ⇒ TODO `vault.fill` se deniega, indistinguible de que
# la persona haya dicho que no.
# **Las dos o ninguna**: media bóveda es una que niega todo en silencio. Cuestan ~43 M por imagen
# (22 M + 21 M medidos sobre los artefactos sellados), contra los ~1,25 GiB del §6.7 de acá arriba.
# ⚠ La respuesta fue NO hasta el 2026-09-18, y no por el tamaño: ninguna app llimphi de escritorio
# podía abrir ventana sin Vulkan (§7.septies), así que declararlas habría sido instalar una bóveda
# que niega todo. Eso se arregló en llimphi y se MIDIÓ en metal: las seis etapas de
# `scripts/test-atuq-boveda-metal.py` en verde —con el navegador de verdad, el diálogo a la vista
# y el control de contestar que NO (§7.novies)—. Por eso entran hoy y no antes.
"boveda",
"shuma-pregunta",
# ── CAPTURA Y STREAMING ────────────────────────────────────────────────────────────────────── # ── CAPTURA Y STREAMING ──────────────────────────────────────────────────────────────────────
# `obs-studio` (2026-09-04). A diferencia de mpv, ésta vive en ESTA COLA y no en el corpus, y no # `obs-studio` (2026-09-04). A diferencia de mpv, ésta vive en ESTA COLA y no en el corpus, y no
# es preferencia: su frontend es Qt6 y las 13 recetas Qt viven sólo en `incoming-kde`. Un qtbase # es preferencia: su frontend es Qt6 y las 13 recetas Qt viven sólo en `incoming-kde`. Un qtbase
@@ -566,6 +590,30 @@ paquetes = [
# trampa, porque el panel está en todas. Bajarlo de acá es bajarlo de la imagen, no de la receta. # trampa, porque el panel está en todas. Bajarlo de acá es bajarlo de la imagen, no de la receta.
"llama-cpp", "llama-cpp",
"ia-modelo-chat", "ia-modelo-chat",
# ── LA BÓVEDA DEL NAVEGADOR (SDD 26 §7.sexies, decisión 1 — 2026-09-21) ──────────────────────
# `atuq` trae la décima extensión desde el 2026-09-15 y el host pineado atiende `vault.*` desde
# el 2026-09-16; aun así la función venía **APAGADA DE FÁBRICA en las cuatro imágenes**, sin un
# solo error: `vault.status` contesta `{"ok":true,"locked":true}` porque el host NUNCA abre la
# base (sled toma lock exclusivo y el proceso que lanza Gecko muere y revive con cada pestaña);
# le habla a un DUEÑO por un socket, y el dueño no estaba declarado en ningún perfil. Las dos
# recetas estaban selladas desde el 2026-09-18 con `perfiles: []` — sellado ≠ instalado, que es
# la lección de `foot`, escrita QUINCE veces en este fichero antes de hoy (`grep 'lección de
# .foot.'`) y aun así vuelta a pasar. Y «cerrada» es una respuesta EXITOSA:
# ni un log, ni un reintento, ni la insignia la distinguen de un usuario que no desbloqueó la suya.
# `boveda` la app DUEÑA de la base: la abre para su ventana y levanta el socket del
# navegador en un hilo. Sin ella, `vault.match` no ofrece nada, nunca.
# `shuma-pregunta` el diálogo de consentimiento que `PorDialogo` lanza por PATH. Sin él,
# `Command::new` falla ⇒ TODO `vault.fill` se deniega, indistinguible de que
# la persona haya dicho que no.
# **Las dos o ninguna**: media bóveda es una que niega todo en silencio. Cuestan ~43 M por imagen
# (22 M + 21 M medidos sobre los artefactos sellados), contra los ~1,25 GiB del §6.7 de acá arriba.
# ⚠ La respuesta fue NO hasta el 2026-09-18, y no por el tamaño: ninguna app llimphi de escritorio
# podía abrir ventana sin Vulkan (§7.septies), así que declararlas habría sido instalar una bóveda
# que niega todo. Eso se arregló en llimphi y se MIDIÓ en metal: las seis etapas de
# `scripts/test-atuq-boveda-metal.py` en verde —con el navegador de verdad, el diálogo a la vista
# y el control de contestar que NO (§7.novies)—. Por eso entran hoy y no antes.
"boveda",
"shuma-pregunta",
# ── VISOR DE IMÁGENES, TAMBIÉN EN LAS CUATRO ───────────────────────────────────────────────── # ── VISOR DE IMÁGENES, TAMBIÉN EN LAS CUATRO ─────────────────────────────────────────────────
# `swayimg` y no `imv`, que era lo que proponía `docs/plan-apps-usuario-final.md`: imv dibuja con # `swayimg` y no `imv`, que era lo que proponía `docs/plan-apps-usuario-final.md`: imv dibuja con
# OpenGL de función fija (`glBegin`/`glOrtho`) y esta distro NO tiene proveedor de GL de # OpenGL de función fija (`glBegin`/`glOrtho`) y esta distro NO tiene proveedor de GL de
@@ -941,6 +989,30 @@ paquetes = [
# trampa, porque el panel está en todas. Bajarlo de acá es bajarlo de la imagen, no de la receta. # trampa, porque el panel está en todas. Bajarlo de acá es bajarlo de la imagen, no de la receta.
"llama-cpp", "llama-cpp",
"ia-modelo-chat", "ia-modelo-chat",
# ── LA BÓVEDA DEL NAVEGADOR (SDD 26 §7.sexies, decisión 1 — 2026-09-21) ──────────────────────
# `atuq` trae la décima extensión desde el 2026-09-15 y el host pineado atiende `vault.*` desde
# el 2026-09-16; aun así la función venía **APAGADA DE FÁBRICA en las cuatro imágenes**, sin un
# solo error: `vault.status` contesta `{"ok":true,"locked":true}` porque el host NUNCA abre la
# base (sled toma lock exclusivo y el proceso que lanza Gecko muere y revive con cada pestaña);
# le habla a un DUEÑO por un socket, y el dueño no estaba declarado en ningún perfil. Las dos
# recetas estaban selladas desde el 2026-09-18 con `perfiles: []` — sellado ≠ instalado, que es
# la lección de `foot`, escrita QUINCE veces en este fichero antes de hoy (`grep 'lección de
# .foot.'`) y aun así vuelta a pasar. Y «cerrada» es una respuesta EXITOSA:
# ni un log, ni un reintento, ni la insignia la distinguen de un usuario que no desbloqueó la suya.
# `boveda` la app DUEÑA de la base: la abre para su ventana y levanta el socket del
# navegador en un hilo. Sin ella, `vault.match` no ofrece nada, nunca.
# `shuma-pregunta` el diálogo de consentimiento que `PorDialogo` lanza por PATH. Sin él,
# `Command::new` falla ⇒ TODO `vault.fill` se deniega, indistinguible de que
# la persona haya dicho que no.
# **Las dos o ninguna**: media bóveda es una que niega todo en silencio. Cuestan ~43 M por imagen
# (22 M + 21 M medidos sobre los artefactos sellados), contra los ~1,25 GiB del §6.7 de acá arriba.
# ⚠ La respuesta fue NO hasta el 2026-09-18, y no por el tamaño: ninguna app llimphi de escritorio
# podía abrir ventana sin Vulkan (§7.septies), así que declararlas habría sido instalar una bóveda
# que niega todo. Eso se arregló en llimphi y se MIDIÓ en metal: las seis etapas de
# `scripts/test-atuq-boveda-metal.py` en verde —con el navegador de verdad, el diálogo a la vista
# y el control de contestar que NO (§7.novies)—. Por eso entran hoy y no antes.
"boveda",
"shuma-pregunta",
# ── VISOR DE IMÁGENES, TAMBIÉN EN LAS CUATRO ───────────────────────────────────────────────── # ── VISOR DE IMÁGENES, TAMBIÉN EN LAS CUATRO ─────────────────────────────────────────────────
# `swayimg` y no `imv`, que era lo que proponía `docs/plan-apps-usuario-final.md`: imv dibuja con # `swayimg` y no `imv`, que era lo que proponía `docs/plan-apps-usuario-final.md`: imv dibuja con
# OpenGL de función fija (`glBegin`/`glOrtho`) y esta distro NO tiene proveedor de GL de # OpenGL de función fija (`glBegin`/`glOrtho`) y esta distro NO tiene proveedor de GL de
@@ -1179,6 +1251,30 @@ paquetes = [
# trampa, porque el panel está en todas. Bajarlo de acá es bajarlo de la imagen, no de la receta. # trampa, porque el panel está en todas. Bajarlo de acá es bajarlo de la imagen, no de la receta.
"llama-cpp", "llama-cpp",
"ia-modelo-chat", "ia-modelo-chat",
# ── LA BÓVEDA DEL NAVEGADOR (SDD 26 §7.sexies, decisión 1 — 2026-09-21) ──────────────────────
# `atuq` trae la décima extensión desde el 2026-09-15 y el host pineado atiende `vault.*` desde
# el 2026-09-16; aun así la función venía **APAGADA DE FÁBRICA en las cuatro imágenes**, sin un
# solo error: `vault.status` contesta `{"ok":true,"locked":true}` porque el host NUNCA abre la
# base (sled toma lock exclusivo y el proceso que lanza Gecko muere y revive con cada pestaña);
# le habla a un DUEÑO por un socket, y el dueño no estaba declarado en ningún perfil. Las dos
# recetas estaban selladas desde el 2026-09-18 con `perfiles: []` — sellado ≠ instalado, que es
# la lección de `foot`, escrita QUINCE veces en este fichero antes de hoy (`grep 'lección de
# .foot.'`) y aun así vuelta a pasar. Y «cerrada» es una respuesta EXITOSA:
# ni un log, ni un reintento, ni la insignia la distinguen de un usuario que no desbloqueó la suya.
# `boveda` la app DUEÑA de la base: la abre para su ventana y levanta el socket del
# navegador en un hilo. Sin ella, `vault.match` no ofrece nada, nunca.
# `shuma-pregunta` el diálogo de consentimiento que `PorDialogo` lanza por PATH. Sin él,
# `Command::new` falla ⇒ TODO `vault.fill` se deniega, indistinguible de que
# la persona haya dicho que no.
# **Las dos o ninguna**: media bóveda es una que niega todo en silencio. Cuestan ~43 M por imagen
# (22 M + 21 M medidos sobre los artefactos sellados), contra los ~1,25 GiB del §6.7 de acá arriba.
# ⚠ La respuesta fue NO hasta el 2026-09-18, y no por el tamaño: ninguna app llimphi de escritorio
# podía abrir ventana sin Vulkan (§7.septies), así que declararlas habría sido instalar una bóveda
# que niega todo. Eso se arregló en llimphi y se MIDIÓ en metal: las seis etapas de
# `scripts/test-atuq-boveda-metal.py` en verde —con el navegador de verdad, el diálogo a la vista
# y el control de contestar que NO (§7.novies)—. Por eso entran hoy y no antes.
"boveda",
"shuma-pregunta",
# `dunst` es LA OTRA MITAD, y sólo hace falta acá: KDE la atiende con plasma-workspace, GNOME con # `dunst` es LA OTRA MITAD, y sólo hace falta acá: KDE la atiende con plasma-workspace, GNOME con
# gnome-shell y COSMIC con cosmic-notifications; sway no tenía a NADIE escuchando # gnome-shell y COSMIC con cosmic-notifications; sway no tenía a NADIE escuchando
# `org.freedesktop.Notifications`, así que una página que pedía notificar mandaba el mensaje al bus # `org.freedesktop.Notifications`, así que una página que pedía notificar mandaba el mensaje al bus
+38
View File
@@ -92,6 +92,44 @@ install = '''
set -e set -e
test -x target/release/boveda || { echo "no hay target/release/boveda — ¿cambió el nombre del bin en pacha-boveda-llimphi?" >&2; exit 1; } test -x target/release/boveda || { echo "no hay target/release/boveda — ¿cambió el nombre del bin en pacha-boveda-llimphi?" >&2; exit 1; }
install -Dm755 target/release/boveda /out/usr/bin/boveda install -Dm755 target/release/boveda /out/usr/bin/boveda
# ── EL LANZADOR, Y POR QUÉ ES PARTE DE LA FUNCIÓN Y NO UN ADORNO (2026-09-21) ─────────────────
# Sin esta entrada la app viaja en la imagen y **no existe para quien la usa**: los lanzadores de
# los cuatro escritorios leen `/usr/share/applications`, y a un binario que nadie lista sólo se
# llega escribiendo `boveda` en una terminal. Sería exactamente la forma de fallo que esta receta
# existe para cerrar —la función instalada, apagada y en silencio (SDD 26 §7.sexies)— una capa más
# arriba: la bóveda declarada en el perfil, la insignia del navegador vacía, y nada que falle.
#
# `Icon=dialog-password` es nombre del icon naming spec y está MEDIDO en los tres temas que los
# perfiles declaran, no supuesto: `breeze-icons` 6 ficheros, `adwaita-icon-theme` 1, `cosmic-icons`
# 2. El cuarto perfil (sway) lleva sólo `hicolor`, que por diseño no trae iconos: ahí el lanzador
# cae al genérico, que es degradarse, no romperse.
#
# ⚠ Lo que esta entrada NO puede hacer, y conviene saberlo antes de buscarlo acá: emparejar la
# VENTANA con el lanzador. `llimphi_ui::run` no llama nunca a `with_name`, así que winit 0.30.13
# sólo manda `set_app_id` `if let Some(name) = attributes.platform_specific.name` ⇒ la ventana sale
# **sin app_id**, y con el título `"llimphi"`, que es el default de `App::title` y `BovedaApp` no
# sobrescribe. Por eso acá no hay `StartupWMClass`: no habría contra qué emparejarlo. Es de llimphi
# —como el muro del §7.septies— y se arregla allá, no acá.
mkdir -p /out/usr/share/applications
cat > /out/usr/share/applications/boveda.desktop <<'DESKTOP'
[Desktop Entry]
Type=Application
Name=Bóveda
Name[en]=Vault
GenericName=Gestor de contraseñas
GenericName[en]=Password Manager
Comment=Tus contraseñas, y quien las pide tiene que preguntarte
Comment[en]=Your passwords, and whoever asks has to ask you
Exec=boveda
Icon=dialog-password
Terminal=false
Categories=Utility;Security;
Keywords=contraseñas;claves;bóveda;passwords;
StartupNotify=true
DESKTOP
chmod 0644 /out/usr/share/applications/boveda.desktop
test -s /out/usr/share/applications/boveda.desktop || { echo "boveda.desktop quedó vacío" >&2; exit 1; }
''' '''
[deps] [deps]
+81 -7
View File
@@ -3,31 +3,38 @@
python3 scripts/test-atuq-boveda-coherente.py python3 scripts/test-atuq-boveda-coherente.py
python3 scripts/test-atuq-boveda-coherente.py --negative-control python3 scripts/test-atuq-boveda-coherente.py --negative-control
python3 scripts/test-atuq-boveda-coherente.py --negative-control-perfil
── QUÉ MIRA, Y POR QUÉ ESTE GUARDIÁN EXISTE APARTE ─────────────────────────────────────────── ── QUÉ MIRA, Y POR QUÉ ESTE GUARDIÁN EXISTE APARTE ───────────────────────────────────────────
Una extensión de `atuq` está enchufada en CINCO lugares distintos, y ninguno da error si falta: Una extensión de `atuq` está enchufada en SEIS lugares distintos, y ninguno da error si falta:
manifest.json el id que la extensión declara manifest.json el id que la extensión declara
distribution/policies.json la política que la instala distribution/policies.json la política que la instala
native-messaging/*.json el permiso para hablarle al host native-messaging/*.json el permiso para hablarle al host
atuq.cfg las preferencias que la acompañan atuq.cfg las preferencias que la acompañan
recipes/puriy-costura.toml el COMMIT del host, que decide si sus verbos existen recipes/puriy-costura.toml el COMMIT del host, que decide si sus verbos existen
docs/state/targets.toml quién DECLARA al dueño y al diálogo en las imágenes
Si falta la política, la extensión no se instala y el usuario ve un navegador sin la función. Si Si falta la política, la extensión no se instala y el usuario ve un navegador sin la función. Si
falta el permiso del host, la extensión se instala y **se conecta a nada** —`connectNative` falla falta el permiso del host, la extensión se instala y **se conecta a nada** —`connectNative` falla
en silencio y la insignia no aparece nunca—. Si faltan las preferencias, la bóveda y el gestor de en silencio y la insignia no aparece nunca—. Si faltan las preferencias, la bóveda y el gestor de
Gecko se pelean por el mismo campo. Y si el commit pineado del host es ANTERIOR a los verbos, la Gecko se pelean por el mismo campo. Y si el commit pineado del host es ANTERIOR a los verbos, la
extensión conecta, manda `vault.match` y recibe «verbo desconocido» —que `fondo.js` lee como «no hay extensión conecta, manda `vault.match` y recibe «verbo desconocido» —que `fondo.js` lee como «no hay
bóveda» y se calla—. Ninguno de los cuatro se ve como un error: se ven como «no anda». bóveda» y se calla—. Y si `boveda` y `shuma-pregunta` no están declaradas en el perfil, la
extensión se instala, conecta, manda `vault.match` y el host contesta `{"ok":true,"locked":true}`
—«cerrada», que es una respuesta EXITOSA— porque del otro lado del socket no hay nadie: el dueño
está sellado y en ninguna imagen. Ninguno de los cinco se ve como un error: se ven como «no anda».
Éste NO reemplaza al guardián de metal (servidor HTTP, navegador real, login real, el diálogo de Éste NO reemplaza al guardián de metal (servidor HTTP, navegador real, login real, el diálogo de
consentimiento a la vista). Lo precede: mide lo que se puede medir sin construir nada, en un consentimiento a la vista). Lo precede: mide lo que se puede medir sin construir nada, en un
segundo, y ahorra descubrir en metal un renglón que faltaba en un JSON. segundo, y ahorra descubrir en metal un renglón que faltaba en un JSON.
── LOS DOS CONTROLES NEGATIVOS, UNO POR AFIRMACIÓN ────────────────────────────────────────── ── LOS TRES CONTROLES NEGATIVOS, UNO POR AFIRMACIÓN ──────────────────────────────────────────
`--negative-control` saca el permiso del host de una copia en memoria y exige que esto lo detecte. `--negative-control` saca el permiso del host de una copia en memoria y exige que esto lo detecte.
`--negative-control-verbo` le manda al quinto chequeo un verbo inventado y exige lo mismo. Sin `--negative-control-verbo` le manda al quinto chequeo un verbo inventado y exige lo mismo.
ellos, un guardián que siempre dice que sí se vería idéntico a uno que funciona. `--negative-control-perfil` saca a `boveda` de los cuatro perfiles de escritorio, también en
memoria, y exige que el sexto lo vea. Sin ellos, un guardián que siempre dice que sí se vería
idéntico a uno que funciona.
Y el quinto chequeo trae además un control POSITIVO —`sct.observe`, que el host pineado tiene que Y el quinto chequeo trae además un control POSITIVO —`sct.observe`, que el host pineado tiene que
tener sí o sí—, porque es un `grep` que espera cero y un cero puede ser una ausencia o un sitio tener sí o sí—, porque es un `grep` que espera cero y un cero puede ser una ausencia o un sitio
@@ -38,6 +45,7 @@ import os
import re import re
import subprocess import subprocess
import sys import sys
import tomllib
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ATUQ = os.path.join(ROOT, "recipes/atuq") ATUQ = os.path.join(ROOT, "recipes/atuq")
@@ -151,6 +159,56 @@ def revisar_el_host(verbo_extra=None):
mal(f"el host pineado ({commit[:9]}) NO atiende {v}: la extensión lo manda y recibe " mal(f"el host pineado ({commit[:9]}) NO atiende {v}: la extensión lo manda y recibe "
f"«verbo desconocido», que fondo.js lee como «no hay bóveda» y se calla") f"«verbo desconocido», que fondo.js lee como «no hay bóveda» y se calla")
# ── 9. EL SEXTO LUGAR: ¿alguien DECLARA al dueño y al diálogo en las imágenes? ───────────────
# El quinto lugar pregunta si el host SABE los verbos. Éste pregunta lo de una capa más abajo, que
# es lo que costó tres semanas de no ver (SDD 26 §7.sexies): quién CONTESTA del otro lado del
# socket. El host no abre la bóveda nunca, a propósito —`sled` toma un lock exclusivo y el proceso
# que lanza Gecko muere con cada pestaña—, así que le habla a un DUEÑO; y el 2026-09-18 el dueño
# estaba SELLADO y en `perfiles: []`, o sea en ninguna imagen. Sellado ≠ instalado.
#
# Se mira `targets.toml` —el manifiesto de objetivo— y no `build-state.json`, que es su derivado:
# lo que decide qué va en la imagen es la declaración, y el estado se recalcula solo.
PERFILES_ESCRITORIO = ["escritorio-kde", "escritorio-gnome", "escritorio-cosmic", "escritorio-sway"]
# Las DOS o ninguna: media bóveda es una que niega todo en silencio. Sin el dueño, `vault.match` no
# ofrece nada; sin el diálogo, `Command::new` falla y TODO `vault.fill` se deniega.
LA_BOVEDA_EN_LA_IMAGEN = ["boveda", "shuma-pregunta"]
# El control POSITIVO, por lo mismo que el del §quinto lugar: un `in` que espera encontrar puede
# fallar porque la raíz falta o porque estoy leyendo el campo equivocado, y las dos se ven igual.
# `atuq` TIENE que estar en los cuatro — si no está, este chequeo no está midiendo el fichero.
RAIZ_CONTROL = "atuq"
TARGETS = os.path.join(ROOT, "docs/state/targets.toml")
def revisar_los_perfiles(sin_esta=None):
try:
with open(TARGETS, "rb") as f:
perfiles = tomllib.load(f)["perfil"]
except FileNotFoundError:
mal(f"no existe {os.path.relpath(TARGETS, ROOT)}: el sexto lugar queda SIN COMPROBAR")
return
except Exception as e: # TOML roto es un fallo ruidoso, no un «está bien»
mal(f"no se pudo leer {os.path.relpath(TARGETS, ROOT)} ({e})")
return
for perfil in PERFILES_ESCRITORIO:
if perfil not in perfiles:
mal(f"{perfil} no existe en targets.toml: ¿se renombró un perfil de escritorio?")
continue
raices = list(perfiles[perfil].get("paquetes", []))
if sin_esta:
raices = [r for r in raices if r != sin_esta]
if RAIZ_CONTROL not in raices:
mal(f"CONTROL ROTO: {perfil} no declara {RAIZ_CONTROL}. O el navegador se cayó de la "
f"imagen, o estoy leyendo el campo equivocado — y entonces lo de abajo no prueba nada")
continue
faltan = [r for r in LA_BOVEDA_EN_LA_IMAGEN if r not in raices]
if faltan:
mal(f"{perfil} NO declara {', '.join(faltan)}: la extensión se instala igual y la "
f"bóveda contesta `locked:true` con `ok:true` — apagada, y sin un solo error")
else:
bien(f"{perfil} declara el dueño y el diálogo ({RAIZ_CONTROL} de control)")
fallas = [] fallas = []
@@ -168,7 +226,7 @@ def leer(p):
return f.read() return f.read()
def revisar(permisos_host, verbo_extra=None): def revisar(permisos_host, verbo_extra=None, sin_perfil=None):
# 1. La extensión existe y declara el id que todos los demás nombran. # 1. La extensión existe y declara el id que todos los demás nombran.
try: try:
m = json.loads(leer("extensions/boveda/manifest.json")) m = json.loads(leer("extensions/boveda/manifest.json"))
@@ -232,10 +290,14 @@ def revisar(permisos_host, verbo_extra=None):
# 8. Y el quinto lugar, que vive en otra receta y en otro repo. # 8. Y el quinto lugar, que vive en otra receta y en otro repo.
revisar_el_host(verbo_extra) revisar_el_host(verbo_extra)
# 9. Y el sexto, que vive en el manifiesto de objetivo: quién la pone en la imagen.
revisar_los_perfiles(sin_perfil)
def main(): def main():
control = "--negative-control" in sys.argv control = "--negative-control" in sys.argv
control_verbo = "--negative-control-verbo" in sys.argv control_verbo = "--negative-control-verbo" in sys.argv
control_perfil = "--negative-control-perfil" in sys.argv
host = json.loads(leer("native-messaging/puriy_costura.json")) host = json.loads(leer("native-messaging/puriy_costura.json"))
permisos = list(host.get("allowed_extensions", [])) permisos = list(host.get("allowed_extensions", []))
verbo_extra = None verbo_extra = None
@@ -247,9 +309,15 @@ def main():
# mirando el host sino contestando que sí. # mirando el host sino contestando que sí.
verbo_extra = "vault.este_verbo_no_existe" verbo_extra = "vault.este_verbo_no_existe"
print(f"⚠ control negativo: se le agrega el verbo inventado {verbo_extra}") print(f"⚠ control negativo: se le agrega el verbo inventado {verbo_extra}")
sin_perfil = None
if control_perfil:
# El dueño, sacado de una copia en memoria de los cuatro perfiles: si el sexto chequeo no
# lo ve faltar, está contestando que sí en vez de mirar `targets.toml`.
sin_perfil = "boveda"
print(f"⚠ control negativo: se saca {sin_perfil} de los perfiles de escritorio")
print(f"── la bóveda en atuq ──") print(f"── la bóveda en atuq ──")
revisar(permisos, verbo_extra) revisar(permisos, verbo_extra, sin_perfil)
if control: if control:
if any("allowed_extensions" in f for f in fallas): if any("allowed_extensions" in f for f in fallas):
@@ -263,6 +331,12 @@ def main():
return 0 return 0
print("\n✗ el control del verbo NO lo detectó: el quinto chequeo no mide nada") print("\n✗ el control del verbo NO lo detectó: el quinto chequeo no mide nada")
return 1 return 1
if control_perfil:
if any(sin_perfil in f and "NO declara" in f for f in fallas):
print("\n✓ el control negativo del perfil lo detectó")
return 0
print("\n✗ el control del perfil NO lo detectó: el sexto chequeo no mide nada")
return 1
if fallas: if fallas:
print(f"\n{len(fallas)} problema(s)") print(f"\n{len(fallas)} problema(s)")
return 1 return 1
+8 -5
View File
@@ -6,12 +6,15 @@
python3 scripts/test-atuq-boveda-metal.py --list # qué afirma cada etapa, sin correr nada python3 scripts/test-atuq-boveda-metal.py --list # qué afirma cada etapa, sin correr nada
── POR QUÉ ESTE GUARDIÁN, Y POR QUÉ NO ALCANZA CON EL DE COHERENCIA ────────────────────────── ── POR QUÉ ESTE GUARDIÁN, Y POR QUÉ NO ALCANZA CON EL DE COHERENCIA ──────────────────────────
`test-atuq-boveda-coherente.py` mira los CINCO lugares donde una extensión se enchufa y tarda un `test-atuq-boveda-coherente.py` mira los SEIS lugares donde una extensión se enchufa y tarda un
segundo. Es lo que hay que correr primero y no reemplaza a nada. Lo que no puede contestar es si del segundo. Es lo que hay que correr primero y no reemplaza a nada. Lo que no puede contestar es si del
otro lado del cable hay alguien: el 2026-09-18 los cinco lugares estaban en orden —manifiesto, otro lado del cable hay alguien: el 2026-09-18 los cinco lugares que entonces se miraban estaban
política, permiso, prefs y el commit del host con sus verbos— y la bóveda contestaba **cerrada** en en orden —manifiesto, política, permiso, prefs y el commit del host con sus verbos— y la bóveda
las cuatro imágenes, porque el DUEÑO de la base y el DIÁLOGO de consentimiento no estaban en el contestaba **cerrada** en las cuatro imágenes, porque el DUEÑO de la base y el DIÁLOGO de
corpus. Ningún guardián de ficheros podía verlo; éste empieza justamente por ahí. consentimiento no estaban en el corpus. Ningún guardián de FICHEROS podía verlo; éste empieza
justamente por ahí. (El sexto lugar nació de eso: desde el 2026-09-21 aquél mira además quién
DECLARA al dueño en la imagen, que es la mitad del §7.sexies que sí se puede ver sin construir
nada — y la que volvió a morder el 2026-09-21, con las dos recetas selladas y en `perfiles: []`.)
── LAS SEIS ETAPAS, Y QUÉ AFIRMA CADA UNA ──────────────────────────────────────────────────── ── LAS SEIS ETAPAS, Y QUÉ AFIRMA CADA UNA ────────────────────────────────────────────────────
A sin la app `boveda`, el host contesta `locked:true` ← el control, y es el bug del §7.sexies A sin la app `boveda`, el host contesta `locked:true` ← el control, y es el bug del §7.sexies
+1 -1
View File
@@ -77,7 +77,7 @@ done
# se descubre en el primer minuto y no después de media hora de navegadores. # se descubre en el primer minuto y no después de media hora de navegadores.
GUARDIANES=( GUARDIANES=(
"vigia-atuq-verbos|python3 scripts/vigia-atuq-verbos.py|el host PINEADO atiende los verbos que mandan las diez extensiones (§7)" "vigia-atuq-verbos|python3 scripts/vigia-atuq-verbos.py|el host PINEADO atiende los verbos que mandan las diez extensiones (§7)"
"test-atuq-boveda-coherente|python3 scripts/test-atuq-boveda-coherente.py|la bóveda está enchufada en los CINCO lugares (SDD-BOVEDA §8)" "test-atuq-boveda-coherente|python3 scripts/test-atuq-boveda-coherente.py|la bóveda está enchufada en los SEIS lugares (SDD-BOVEDA §8); el sexto es quién la DECLARA en la imagen (SDD 26 §7.decies)"
# Corría truncado (`--hasta A`) mientras el muro del §7.septies tapaba las otras cinco etapas. # Corría truncado (`--hasta A`) mientras el muro del §7.septies tapaba las otras cinco etapas.
# Desde el 2026-09-18 corre ENTERO: seis etapas, ~6 min, con el navegador de verdad, el diálogo de # Desde el 2026-09-18 corre ENTERO: seis etapas, ~6 min, con el navegador de verdad, el diálogo de
# consentimiento a la vista y el control de decir que NO. Es el único de la suite que mide la # consentimiento a la vista y el control de decir que NO. Es el único de la suite que mide la