# F2 · E3 · Relevo de turno al atascarse

Diseño y plan de E3 cerrados el 20/10.

## Antes de correr

### Pruebas en CPU

- **Tests:** la suite completa pasa (193 tests, más los 3 del ensayo en seco). Incluyen:
  - la función de decisión: umbral, zancada, salto grande, cambio de nivel, variables no válidas;
  - el parche: con E3 apagado o encendido, el bootstrap y el prompt son idénticos a M2. Con E3, `_maybe_handover` llama al planificador a partir de 60.000 tokens; sin E3, o sin planificador, no lo llama.
- **nbcheck** del brazo frente a M2 (`88a379f1…`): 8 cambios declarados, JSON válido. ipynb `172d3cf3…`. Metadatos idénticos a los de E1B.
- **Contrato en CPU** con el servidor simulado (`ft09 vc33 wa30`, umbral 100 y zancada 50 para cruzarlo enseguida):
  - E3: OK en 8 s, 8 marcadores `ECHO E3 stall handover`, 0 tracebacks;
  - base, con las mismas variables puestas: OK en 7 s, 0 marcadores, 0 tracebacks.

### Ensayo en seco sobre las grabaciones

`tools/e3_dryrun.py`, con 60.000 y 15.000:

| Corrida | Relevos E3 | Con 10 o más partidas vivas no atascadas (alguien esperaba) |
|---|---|---|
| base v3 | 111 | 66 (59 %) |
| A/A v6 | 114 | 62 (54 %) |
| E2 v5 | 127 | 78 (61 %) |
| E1b v7 | 124 | 76 (61 %) |

Esto sirve de referencia para la corrida real: se esperan del orden de 110–130 marcadores `ECHO E3 stall handover` en el log. Si hay muchos menos, la pieza no actuó como se esperaba.

## La corrida (kernel v8)

- **Push** a las 22:58:38 UTC del 19/10; partidas de 23:09:27 a 03:15:43 UTC (4 h 6 min); COMPLETE hacia las 03:19 UTC. Unas 4,3 h de RTX.
- Salida en `artifacts/campaign/f2-e3-v8/`: 50 partidas, 0 `Traceback`, `submission.parquet`.
- **La pieza actuó como se esperaba:** 110 marcadores `ECHO E3 stall handover` en el log, dentro de los 110–130 del ensayo en seco. El ensayo sobre la propia corrida reconstruye 105 relevos, con alguien esperando en 49 (47 %).
  - Por nivel: 8 en el nivel 1, 33 en el 2, 11 en el 3, 9 en el 4, 14 en el 5, 11 en el 6, 14 en el 7, 1 en el 8 y 9 en el 9.

## Comparación pareada

`tools/compare.py f1-base-v3 f2-e3-v8 --arm E3 --also-base f1-base-aa-v6`, contra la base agrupada de 4 pasadas, con el criterio declarado en el LOG antes del push.

| | base agrupada (v3 + A/A v6) | E3 | diferencia | banda de ruido | juegos mejor / peor / igual | p (signos) |
|---|---|---|---|---|---|---|
| cap 1,15 | 48,00 (47,45 · 51,88 · 41,85 · 50,84) | **48,80** (47,50 · 50,09) | +0,79 | 10,03 | 12 / 10 / 3 | 0,83 |
| cap 1 | 46,09 | 47,39 (46,21 · 48,57) | +1,29 | 9,88 | 13 / 11 / 1 | 0,84 |

- **Veredicto: no pasa.** 48,80 no supera 58,03. Por la regla declarada, no hay segunda corrida.
- **Niveles por pasada:** E3 106 y 106; base 104, 108, 99 y 109.
- **Acciones por nivel completado:** 37,8 frente a 37,4.
- **Movimientos por juego:** ft09 +39, ls20 +18, cd82 +17, m0r0 −32, su15 −13, tr87 −11. Son del mismo tamaño que los del A/A, que no cambió nada (cn04 −67, cd82 −46, m0r0 +35).

## Lecturas secundarias

| Corrida | tokens generados | tokens del último nivel no superado por encima del P90 | niveles superados (2 pasadas) | caché del prompt | prompt sin caché (prefill) |
|---|---|---|---|---|---|
| base v3 | 9.093.810 | 13,4 % | 211 | 93,8 % | 21,0 M |
| A/A v6 | 9.028.551 | 17,9 % | 207 | 93,7 % | 20,7 M |
| E1b v7 | 9.202.071 | 17,5 % | 215 | — | — |
| **E3 v8** | 8.898.756 | **14,6 %** | 212 | **92,0 %** | **25,9 M** |

(`data/wsp-walls/gate1.py` para el P90; la caché sale de `usage.prompt_tokens_details.cached_tokens` en los `*_requests.jsonl`.)

- **El gasto en niveles atascados no bajó:** 14,6 %, dentro del 13–18 % de las corridas sin E3.
- **Los relevos costaron recargas de contexto:** el prompt sin caché sube de ~21 M a 25,9 M tokens (+4,9 M, +23 %). Son ~45 k tokens por relevo: cada relevo hizo que la partida perdiera su caché y tuviera que recargar el contexto entero al volver.
- **Se generó un poco menos:** 4,30 M tokens por pasada frente a 4,34 M de media en la base (−1 %).
- **El modelo se comportó igual:** `call_profile.py` da 44,5 % de llamadas que actúan (base 43,0–44,2 %) y 7,8 % que leen `transitions` (base 6,8–6,9 %); ninguna diferencia es significativa (p ≥ 0,31). Es lo esperado: E3 no cambia lo que ve el modelo.
- **No hay `handover_ms` ni tiempos de espera:** el DIAG de M2 no se imprime con esta configuración. La caché es la mejor medida disponible del coste.

## Lectura

- **E3 es neutral:** +0,79 con cap 1,15, dentro del ruido. Coincide con la expectativa declarada (0 a +3).
- **Por qué no ayudó (mi lectura, no medida directamente):**
  - el aumento del prompt sin caché (+4,9 M, ~45 k por relevo) cuadra con que cada relevo hiciera perder la caché a la partida atascada, que paga la recarga entera al volver. Lo que gana quien esperaba se va en recargas;
  - en la mitad de los relevos (53 %) no había 10 partidas no atascadas vivas: al final de la corrida quedan pocas partidas que puedan aprovechar el turno.
- **Conclusión:** M2 cede el turno justo cuando la caché se pierde de todas formas (al recortar el contexto), y E3 rompía esa regla. Con esta corrida, ceder más a menudo no compensa. Encaja con lo que Franzen vio al cambiar los valores de prioridad.
- **Para F3:** el envío final es la base (M2 con `n_passes` de Franzen y entradas fijadas). El negativo va al informe del 9/11.
- **RTX:** ~4,3 h. Gastadas ≈ 25,9 h de 30; quedan ≈ 4,1 h, para la corrida corta de F3 (~2,5 h).
