> 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: no era el perfil — son DOS SALIDAS, y el «3 de 13» medía la cámara
Dos cosas antes de la medición: el MOZ_LOG de las que pintan y las que no termina IDÉNTICO (las dos
commitean WaylandBufferSHM de 1280x696), y el cruce de las 15 corridas está confundido — las 13 de
«perfil del usuario» salieron todas con VGA=1.
`screendump` de QMP fotografía UN dispositivo. Con VGA=1 hay dos, y hasta ahora se miraba uno. Con
`id=` en cada uno y capturando los dos en cada toma, en la misma corrida y el mismo instante:
+312s ██ PRIMERA PINTURA — 704456 px EN vga0
gpu0 = 962675 px de fondo + panel y dock (el escritorio, SIN navegador)
vga0 = 703766 px magenta (la ventana, con la página)
⇒ cosmic-comp maneja las DOS salidas y la ventana cae en una u otra. «Pinta 3 de 13» era cuántas
veces cayó en la pantalla que yo fotografiaba: una tasa de mi cámara, no del producto. El §6.10.ter
tiene la misma explicación (aquel arranque también traía `drm: card0 card1`).
Queda en pie, ya sin confundido: el navegador arranca, mapea y commitea SIEMPRE; cuando la ventana
está en la pantalla que se mira, se ve entre +184 y +312 s; el perfil no era la causa y cosmic-comp
nesteado tampoco.
⚠ Tercera vez en el día con la misma forma: un ✗ de una captura de UNA pantalla no dice «la ventana
no está», dice «no está en ESA» — igual que `mapped 1` no probaba que se viera. El estado anota ahora
en qué pantalla apareció y cuántas se fotografiaron, y el vigía NO cuenta las ciegas: las nombra.
diff --git a/docs/26-atuq-envoltorio-gecko.md b/docs/26-atuq-envoltorio-gecko.md
index 9044e229..9eab1405 100644
--- a/docs/26-atuq-envoltorio-gecko.md
+++ b/docs/26-atuq-envoltorio-gecko.md
@@ -1773,7 +1773,7 @@ buffer no llega a la pantalla. Lo próximo es mirarlo del lado del compositor
recibe su `configure`, en qué workspace y en qué salida quedó la ventana— y correr N veces para tener
una tasa, no una anécdota.
-#### 6.10.septies La TASA: pinta **3 de 13** con el perfil del usuario — y cuando pinta, siempre a ~200 s (2026-09-14)
+#### 6.10.septies La TASA: «3 de 13» — ⚠ **el número medía LA CÁMARA, no el producto (ver §6.10.octies)** (2026-09-14)
Diez corridas seguidas, idénticas, con `scripts/cosmic/tasa-primera-pintura.sh`: `--as-user`, `VGA=1`,
ventana de 480 s, captura cada 30 s. Más las cinco de las secciones anteriores, todas sobre la misma
@@ -1784,6 +1784,12 @@ imagen y anotadas en `docs/state/primera-pintura.json`:
| perfil del usuario (lo que hace un usuario) | **3 de 13** | 184, 199, 204 s |
| perfil nuevo en tmpfs | 2 de 2 | 197, 197 s |
+⚠ **ESTA TABLA NO SIGNIFICA LO QUE PARECE.** Las 13 del perfil del usuario salieron todas con `VGA=1`
+y la de perfil nuevo casi ninguna ⇒ «perfil» y «vídeo» están confundidos; y las capturas miraban UNA
+de las DOS salidas. El §6.10.octies lo mide: la ventana cae en cualquiera de las dos pantallas, así
+que los ✗ son «no estaba en la que fotografié». Lo que sí sobrevive es el tiempo: cuando se la ve,
+se la ve entre +184 y +312 s.
+
**Dos cosas que el número dice y una anécdota no podía:**
1. **cuando pinta, pinta siempre en la misma ventana** —184…204 s— y nunca a los 300, 400 ni 900. O
@@ -1805,6 +1811,52 @@ ventana de observación›»—. **No las invalida** (se verificó una por una q
dejó su `MOZ_LOG` de 1200–5000 líneas), pero una corrida abortada de verdad se vería casi igual: por
eso el guion de la tasa cuenta las **abortadas aparte** en vez de sumarlas a los fallos.
+#### 6.10.octies La causa: **son DOS SALIDAS y la ventana cae en cualquiera** — y el «3 de 13» medía la cámara (2026-09-14)
+
+El §6.10.septies cerró con «con el perfil del usuario pinta 3 de 13, con perfil nuevo 2 de 2» y la
+pregunta «qué hace el primer arranque del perfil». La respuesta es: **nada**. Dos cosas antes de la
+medición que la contestó:
+
+1. **el `MOZ_LOG` de las que pintan y las que no termina IDÉNTICO** — las dos commitean
+ `WaylandBufferSHM` de 1280×696. O sea que el navegador dibuja igual en los dos casos;
+2. **el cruce de las 15 corridas está CONFUNDIDO**: las 13 «perfil del usuario» salieron TODAS con
+ `VGA=1`, y de «perfil nuevo» hay una sola con dos tarjetas. Con esa tabla no se puede separar
+ «perfil» de «vídeo»: comparar una columna que no varía es comparar con nada.
+
+**La medición que lo contesta:** `screendump` de QMP fotografía **un** dispositivo. Con `VGA=1` hay
+dos (`vga0` estándar + `gpu0` virtio-gpu) y hasta ahora se fotografiaba uno. Se le puso `id=` a cada
+uno y se capturan LOS DOS en cada toma. Resultado, en la misma corrida y el mismo instante:
+
+ +312s ██ PRIMERA PINTURA — 704456 px de la página EN vga0
+ gpu0 = 962 675 px de fondo azul + panel y dock (el escritorio, SIN navegador)
+ vga0 = 703 766 px magenta (la ventana, con la página)
+
+⇒ **`cosmic-comp` maneja las DOS salidas y la ventana cae en una u otra.** Lo que se publicó como
+«pinta 3 de 13» era **cuántas veces la ventana cayó en la pantalla que yo fotografiaba**. No es una
+tasa del producto: es una tasa de mi cámara. Y el §6.10.ter —«arranca y no pinta la ventana»— tiene
+la misma explicación: aquel arranque también tenía dos tarjetas (`drm: card0 card1`), y la captura
+miraba una.
+
+**Lo que queda en pie de todo lo anterior**, ahora sí sin confundido:
+· el navegador arranca, mapea su superficie y commitea cuadros **siempre** (eso nunca falló);
+· cuando la ventana está en la pantalla que se mira, se ve entre **+184 y +312 s**;
+· el perfil del usuario **no** era la causa, y `cosmic-comp` nesteado tampoco (§6.10.quater).
+
+⚠ **La lección de método, que es la tercera vez en el mismo día:** con dos salidas, un ✗ de una
+captura de una sola pantalla **no dice «la ventana no está»**, dice «no está en ESA». Igual que
+`mapped 1` no probaba que se viera, una foto no prueba que no exista: prueba dónde no está. Por eso
+`docs/state/primera-pintura.json` anota ahora **en qué pantalla** apareció y **cuántas fotografió**
+cada corrida, y `scripts/vigia-imagen.py` **no cuenta las ciegas** — las nombra aparte:
+
+ ⚠ pintaron 6 de 6 corridas CONCLUYENTES · primera pintura +184…+312s
+ ✓ +312s vga0 perfil del usuario vga+virtio-gpu (dos DRM)
+ ⚠ 10 corridas NO se cuentan: con dos salidas fotografiaron UNA sola, y su ✗ sólo dice que
+ la ventana no estaba en esa pantalla
+
+**Lo que sigue**, y ahora es otra pregunta: no «por qué no pinta» sino **por qué el escritorio abre la
+ventana en la salida que no tiene el foco** —y si con una sola salida (`-vga none`, que es como se va
+a usar la imagen de verdad) eso desaparece—. Es barato: la misma serie con una tarjeta.
+
### 6.11 Y ahora la pregunta que faltaba: ¿REPRODUCE? (2026-09-07)
El §6.10 preguntó «¿qué NO puede hacer?» y contestó leyendo cadenas. Media pregunta. La otra mitad
diff --git a/docs/state/primera-pintura.json b/docs/state/primera-pintura.json
index f0babc4c..c761244c 100644
--- a/docs/state/primera-pintura.json
+++ b/docs/state/primera-pintura.json
@@ -14,7 +14,9 @@
"acel": "tcg",
"guion": "scripts/cosmic/atuq-en-imagen.py",
"capturas": "work/atuq-imagen-1tarjeta",
- "nota": "reconstruida de las capturas guardadas"
+ "nota": "reconstruida de las capturas guardadas",
+ "pantallas_fotografiadas": 1,
+ "pantalla": "desconocida (una sola captura)"
},
{
"medido": "2026-09-14T14:36:00Z",
@@ -30,7 +32,9 @@
"acel": "tcg",
"guion": "scripts/cosmic/atuq-en-imagen.py",
"capturas": "work/atuq-imagen-2tarjetas",
- "nota": "reconstruida de las capturas guardadas"
+ "nota": "reconstruida de las capturas guardadas",
+ "pantallas_fotografiadas": 1,
+ "pantalla": "desconocida (una sola captura)"
},
{
"medido": "2026-09-14T14:47:00Z",
@@ -46,7 +50,8 @@
"acel": "tcg",
"guion": "scripts/cosmic/atuq-en-imagen.py",
"capturas": "work/atuq-imagen-comousuario",
- "nota": "reconstruida; el MOZ_LOG commiteaba buffers a +330 s, DESPUÉS de la última captura"
+ "nota": "reconstruida; el MOZ_LOG commiteaba buffers a +330 s, DESPUÉS de la última captura",
+ "pantallas_fotografiadas": 1
},
{
"medido": "2026-09-14T15:10:00Z",
@@ -62,7 +67,9 @@
"acel": "tcg",
"guion": "scripts/cosmic/atuq-en-imagen.py",
"capturas": "work/atuq-imagen-usuario-largo",
- "nota": "reconstruida de las capturas guardadas"
+ "nota": "reconstruida de las capturas guardadas",
+ "pantallas_fotografiadas": 1,
+ "pantalla": "desconocida (una sola captura)"
},
{
"medido": "2026-09-14T15:58:44Z",
@@ -78,7 +85,8 @@
"acel": "tcg",
"guion": "scripts/cosmic/atuq-en-imagen.py",
"capturas": "work/atuq-imagen-medicion",
- "nota": "NO pintó en 900 s y el MOZ_LOG commiteaba WaylandBufferSHM desde ≤ +377 s: «mapped 1» NO prueba que se vea"
+ "nota": "NO pintó en 900 s y el MOZ_LOG commiteaba WaylandBufferSHM desde ≤ +377 s: «mapped 1» NO prueba que se vea",
+ "pantallas_fotografiadas": 1
},
{
"medido": "2026-09-14T16:17:13Z",
@@ -92,7 +100,8 @@
"mem_mb": 2560,
"vcpus": 2,
"acel": "tcg",
- "guion": "scripts/cosmic/atuq-en-imagen.py"
+ "guion": "scripts/cosmic/atuq-en-imagen.py",
+ "pantallas_fotografiadas": 1
},
{
"medido": "2026-09-14T16:26:43Z",
@@ -106,7 +115,9 @@
"mem_mb": 2560,
"vcpus": 2,
"acel": "tcg",
- "guion": "scripts/cosmic/atuq-en-imagen.py"
+ "guion": "scripts/cosmic/atuq-en-imagen.py",
+ "pantallas_fotografiadas": 1,
+ "pantalla": "desconocida (una sola captura)"
},
{
"medido": "2026-09-14T16:39:48Z",
@@ -120,7 +131,8 @@
"mem_mb": 2560,
"vcpus": 2,
"acel": "tcg",
- "guion": "scripts/cosmic/atuq-en-imagen.py"
+ "guion": "scripts/cosmic/atuq-en-imagen.py",
+ "pantallas_fotografiadas": 1
},
{
"medido": "2026-09-14T16:48:45Z",
@@ -134,7 +146,9 @@
"mem_mb": 2560,
"vcpus": 2,
"acel": "tcg",
- "guion": "scripts/cosmic/atuq-en-imagen.py"
+ "guion": "scripts/cosmic/atuq-en-imagen.py",
+ "pantallas_fotografiadas": 1,
+ "pantalla": "desconocida (una sola captura)"
},
{
"medido": "2026-09-14T17:03:19Z",
@@ -148,7 +162,8 @@
"mem_mb": 2560,
"vcpus": 2,
"acel": "tcg",
- "guion": "scripts/cosmic/atuq-en-imagen.py"
+ "guion": "scripts/cosmic/atuq-en-imagen.py",
+ "pantallas_fotografiadas": 1
},
{
"medido": "2026-09-14T17:18:52Z",
@@ -162,7 +177,8 @@
"mem_mb": 2560,
"vcpus": 2,
"acel": "tcg",
- "guion": "scripts/cosmic/atuq-en-imagen.py"
+ "guion": "scripts/cosmic/atuq-en-imagen.py",
+ "pantallas_fotografiadas": 1
},
{
"medido": "2026-09-14T17:34:16Z",
@@ -176,7 +192,8 @@
"mem_mb": 2560,
"vcpus": 2,
"acel": "tcg",
- "guion": "scripts/cosmic/atuq-en-imagen.py"
+ "guion": "scripts/cosmic/atuq-en-imagen.py",
+ "pantallas_fotografiadas": 1
},
{
"medido": "2026-09-14T17:51:50Z",
@@ -190,7 +207,8 @@
"mem_mb": 2560,
"vcpus": 2,
"acel": "tcg",
- "guion": "scripts/cosmic/atuq-en-imagen.py"
+ "guion": "scripts/cosmic/atuq-en-imagen.py",
+ "pantallas_fotografiadas": 1
},
{
"medido": "2026-09-14T18:10:43Z",
@@ -204,7 +222,8 @@
"mem_mb": 2560,
"vcpus": 2,
"acel": "tcg",
- "guion": "scripts/cosmic/atuq-en-imagen.py"
+ "guion": "scripts/cosmic/atuq-en-imagen.py",
+ "pantallas_fotografiadas": 1
},
{
"medido": "2026-09-14T18:25:53Z",
@@ -218,7 +237,25 @@
"mem_mb": 2560,
"vcpus": 2,
"acel": "tcg",
- "guion": "scripts/cosmic/atuq-en-imagen.py"
+ "guion": "scripts/cosmic/atuq-en-imagen.py",
+ "pantallas_fotografiadas": 1
+ },
+ {
+ "medido": "2026-09-14T18:54:22Z",
+ "imagen": "/mnt/cosecha/takana-cosmic-qemu.img",
+ "segundos_primera_pintura": 312,
+ "presupuesto_s": 420,
+ "ventana_s": 480,
+ "cadencia_s": 40,
+ "lanzamiento": "perfil del usuario",
+ "video": "vga+virtio-gpu (dos DRM)",
+ "mem_mb": 2560,
+ "vcpus": 2,
+ "acel": "tcg",
+ "guion": "scripts/cosmic/atuq-en-imagen.py",
+ "pantallas_fotografiadas": 2,
+ "pantalla": "vga0",
+ "nota": "la ventana apareció en vga0 mientras gpu0 seguía mostrando el escritorio sin navegador"
}
]
}
diff --git a/scripts/cosmic/atuq-en-imagen.py b/scripts/cosmic/atuq-en-imagen.py
index b47ead4f..d715f248 100755
--- a/scripts/cosmic/atuq-en-imagen.py
+++ b/scripts/cosmic/atuq-en-imagen.py
@@ -46,7 +46,13 @@ SMP = os.environ.get("SMP", "2")
# Un compositor con dos tarjetas puede componer en la que el `screendump` NO muestra. Por eso el
# default acá es UNA sola (-vga none) y la reproducción del caso original es una opción explícita:
# son dos mediciones distintas y hay que poder pedir cada una por su nombre.
-VGA_ARGS = [] if os.environ.get("VGA") == "1" else ["-vga", "none"]
+# Las dos pantallas van con ID EXPLÍCITO (`vga0`, `gpu0`) y no por el `-vga std` implícito, porque
+# `screendump` de QMP fotografía UN dispositivo y hay que poder pedir cada uno por su nombre: con dos
+# salidas, el compositor puede poner la ventana en la que la captura por defecto NO muestra, y eso se
+# ve exactamente igual que «la ventana no aparece».
+DOS_PANTALLAS = os.environ.get("VGA") == "1"
+VGA_ARGS = (["-vga", "none", "-device", "VGA,id=vga0"] if DOS_PANTALLAS else ["-vga", "none"])
+PANTALLAS = ["gpu0", "vga0"] if DOS_PANTALLAS else ["gpu0"]
class Serial:
@@ -115,6 +121,18 @@ class Serial:
return self.esperar(marca, segundos, nombre=f"de «{orden[:40]}»")
+def capturar(ruta_qmp, out, nombre):
+ """Fotografía CADA pantalla del guest y devuelve {pantalla: fichero}.
+
+ Con dos salidas hay que mirar las dos: la ventana puede estar viva y dibujando en la que no se
+ fotografía, y eso es indistinguible de «no pinta» si sólo se mira una.
+ """
+ ficheros = {p: os.path.join(out, f"{nombre}-{p}.ppm") for p in PANTALLAS}
+ qmp(ruta_qmp, [{"execute": "screendump", "arguments": {"filename": f, "device": p}}
+ for p, f in ficheros.items()])
+ return ficheros
+
+
def pintado(ppm):
"""¿Cuántos píxeles de la pantalla son la página de prueba?
@@ -132,7 +150,7 @@ def pintado(ppm):
return sum(n for n, (r, g, b) in im.getcolors(1 << 24) if r > 90 and b > 90 and g < 40)
-def anotar_medicion(segundos, a):
+def anotar_medicion(segundos, a, pantalla=None):
"""Deja el tiempo de primera pintura en `docs/state/primera-pintura.json`.
Va al estado del repo y no a un log porque quien pregunta «¿esta imagen se puede usar?» es
@@ -145,11 +163,14 @@ def anotar_medicion(segundos, a):
"medido": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"imagen": IMG,
"segundos_primera_pintura": segundos, # None = no pintó dentro de la ventana
+ "pantalla": pantalla, # EN CUÁL de las salidas apareció (medido 2026-09-14:
+ # con dos, la ventana cae en cualquiera de las dos)
+ "pantallas_fotografiadas": len(PANTALLAS), # con 1 y dos salidas, un ✗ NO es concluyente
"presupuesto_s": a.budget,
"ventana_s": a.wait,
"cadencia_s": a.cadence, # la RESOLUCIÓN del número: ±cadencia
"lanzamiento": "perfil del usuario" if a.as_user else "perfil nuevo en tmpfs",
- "video": "vga+virtio-gpu (dos DRM)" if VGA_ARGS == [] else "virtio-gpu solo (-vga none)",
+ "video": "vga+virtio-gpu (dos DRM)" if DOS_PANTALLAS else "virtio-gpu solo (-vga none)",
"mem_mb": int(MEM), "vcpus": int(SMP), "acel": "tcg",
"guion": "scripts/cosmic/atuq-en-imagen.py",
}
@@ -214,7 +235,7 @@ def main():
qemu = [
"nice", "-n", "10", "qemu-system-x86_64",
"-machine", "q35", "-accel", "tcg", "-cpu", "max", "-m", MEM, "-smp", SMP,
- *VGA_ARGS, "-device", "virtio-gpu-pci", "-display", "none", "-nic", "none",
+ *VGA_ARGS, "-device", "virtio-gpu-pci,id=gpu0", "-display", "none", "-nic", "none",
"-drive", f"if=pflash,format=raw,unit=0,file={OVMF_CODE},readonly=on",
"-drive", f"if=pflash,format=raw,unit=1,file={vars_fd}",
"-drive", f"file={IMG},format=raw,if=virtio",
@@ -244,8 +265,8 @@ def main():
print(ser.cmd("echo ENTORNO: WD=$WAYLAND_DISPLAY XDG=$XDG_RUNTIME_DIR DBUS=$DBUS_SESSION_BUS_ADDRESS").strip())
print(ser.cmd("ls -l $XDG_RUNTIME_DIR; ls /usr/lib/dri").strip())
- qmp(qmp_sock, [{"execute": "screendump", "arguments": {"filename": os.path.join(out, "antes.ppm")}}])
- print("== captura ANTES", flush=True)
+ capturar(qmp_sock, out, "antes")
+ print("== captura ANTES (%s)" % ", ".join(PANTALLAS), flush=True)
if a.only_cosmic:
print("== CONTROL: no se lanza el navegador")
@@ -278,21 +299,23 @@ def main():
primera = None # segundos hasta la PRIMERA captura con la página en pantalla
while time.time() - t0 < a.wait:
time.sleep(a.cadence); n += 1
- shot = os.path.join(out, f"durante-{n}.ppm")
- qmp(qmp_sock, [{"execute": "screendump", "arguments": {"filename": shot}}])
+ tomas = capturar(qmp_sock, out, f"durante-{n}")
t = int(time.time() - t0)
- px = pintado(shot)
+ medidas = {p: pintado(f) for p, f in tomas.items()}
+ px = max((v for v in medidas.values() if v is not None), default=None)
+ donde = next((p for p, v in medidas.items() if v and v > 100_000), None)
if primera is None and px and px > 100_000:
primera = t
- print(f" +{t}s ██ PRIMERA PINTURA — {px} px de la página", flush=True)
+ print(f" +{t}s ██ PRIMERA PINTURA — {px} px de la página EN {donde}", flush=True)
if a.until_paint:
break
else:
vivos = ser.cmd("for c in /proc/[0-9]*/comm; do cat $c 2>/dev/null; done | grep -c atuq")
v = vivos.splitlines()[1].strip() if len(vivos.splitlines()) > 1 else "?"
- print(f" +{t}s px de la página: {px if px is not None else '?'} procesos atuq: {v}", flush=True)
+ detalle = " ".join(f"{p}={medidas[p]}" for p in PANTALLAS)
+ print(f" +{t}s px de la página: {detalle} procesos atuq: {v}", flush=True)
- qmp(qmp_sock, [{"execute": "screendump", "arguments": {"filename": os.path.join(out, "despues.ppm")}}])
+ capturar(qmp_sock, out, "despues")
print("== captura DESPUES", flush=True)
# EL NÚMERO. No «pinta / no pinta»: CUÁNTO TARDA. Medido el 2026-09-14 sobre esta misma
@@ -314,7 +337,7 @@ def main():
# El número se PUBLICA donde lo pueda leer el vigía de la imagen, que es estático y no
# arranca nada: sin esto el dato vive en el scrollback del que corrió la VM y se pierde,
# que es exactamente lo que pasó con la corrida a mano del §6.10.ter.
- anotar_medicion(primera, a)
+ anotar_medicion(primera, a, donde if primera is not None else None)
# La evidencia que no se puede leer de una captura: qué hizo el widget de Wayland.
print("---- atuq.log (cola) ----")
diff --git a/scripts/vigia-imagen.py b/scripts/vigia-imagen.py
index c8fc00e3..cef8c4f4 100644
--- a/scripts/vigia-imagen.py
+++ b/scripts/vigia-imagen.py
@@ -43,6 +43,10 @@
# Ninguno contesta «¿y cuándo se ve?» — y esa pregunta ya cobró: el §6.10.ter del SDD 26 dio por
# hecho que `atuq` «no pinta nunca» en la imagen arrancada CON ESTE VIGÍA EN ✓, y la medición
# repetida mostró que pintaba a los +137…+204 s. Lo medido había sido la paciencia.
+# Y el 2026-09-14, peor: con DOS tarjetas DRM la ventana cae en cualquiera de las dos salidas, y las
+# capturas miraban UNA ⇒ el «3 de 13» que se publicó medía dónde apuntaba la cámara. Por eso el dato
+# que se lee acá trae en qué PANTALLA apareció, y las corridas ciegas (dos salidas, una captura) no
+# se cuentan.
# Arrancar la imagen son ~15 min de QEMU sin KVM, así que no se hace acá: `scripts/cosmic/atuq-en-
# imagen.py` lo mide y deja el número en `docs/state/primera-pintura.json`, y este vigía lo LEE y lo
# dice con sus condiciones (lanzamiento, vídeo, RAM, vcpus) — el tiempo depende de la carga de la
@@ -273,26 +277,38 @@ def informe_primera_pintura():
print(" ⊘ el fichero no tiene corridas")
return False
- # Se informan TODAS las corridas y no la última, porque el fenómeno es INTERMITENTE: el
- # 2026-09-14 dos corridas idénticas dieron «+204 s» y «no pintó en 900 s». Un solo número acá
- # sería el de la corrida que tocó, y el lector lo leería como el del producto.
+ # Se informan TODAS las corridas y no la última, porque el fenómeno es INTERMITENTE. Y se separan
+ # por CUÁNTAS SALIDAS fotografió cada una: medido el 2026-09-14, con dos tarjetas DRM la ventana
+ # cae en cualquiera de las dos salidas, así que un ✗ de una corrida que sólo miró una pantalla NO
+ # dice que la ventana no esté: dice que no estaba EN ESA. Contarlos juntos fue lo que produjo un
+ # «3 de 13» que medía la cámara y no el producto.
+ ciegas = [c for c in corridas if c.get("pantallas_fotografiadas", 1) < 2
+ and "dos DRM" in (c.get("video") or "")
+ and c.get("segundos_primera_pintura") is None]
+ corridas = [c for c in corridas if c not in ciegas]
pintaron = [c["segundos_primera_pintura"] for c in corridas
if c.get("segundos_primera_pintura") is not None]
n, m = len(pintaron), len(corridas)
marca = "✓" if n == m else ("✗" if n == 0 else "⚠")
+ if ciegas:
+ marca = "⚠" # con corridas ciegas de por medio, un 6/6 no es un ✓: es un «lo que se pudo medir»
if pintaron:
- print(" %s pintaron %d de %d corridas · primera pintura %s"
+ print(" %s pintaron %d de %d corridas CONCLUYENTES · primera pintura %s"
% (marca, n, m,
("+%ds" % pintaron[0]) if n == 1 else "+%d…+%ds" % (min(pintaron), max(pintaron))))
else:
print(" %s NINGUNA de %d corridas pintó" % (marca, m))
for c in corridas[-6:]:
seg = c.get("segundos_primera_pintura")
- print(" %s %-9s %-22s %-26s %s"
+ print(" %s %-9s %-10s %-22s %-26s %s"
% ("✓" if seg is not None else "✗",
("+%ds" % seg) if seg is not None else "—",
+ (c.get("pantalla") or "")[:10],
c.get("lanzamiento", "?"), c.get("video", "?"),
(c.get("medido") or "")[:16]))
+ if ciegas:
+ print(" ⚠ %d corridas NO se cuentan: con dos salidas fotografiaron UNA sola, y su ✗ sólo"
+ " dice que la ventana no estaba en esa pantalla" % len(ciegas))
fallidas = [c for c in corridas if c.get("segundos_primera_pintura") is None]
if fallidas:
print(" ⚠ en las que NO pintó, el MOZ_LOG igual decía `mapped 1` + «has buffer» +"