fix(short): los textos ilegibles se avisaban con el render ya pagado
Build & Deploy ResearchOwl / build-and-push (push) Successful in 1m18s

La nota que teníamos era falsa: los avisos `severe` de shortsmith SÍ llegan a
un humano — el informe de Telegram los saca en rojo. Lo que pasa es que llegan
colgados del render TERMINADO, así que hacerles caso significa editar el spec a
mano y pagar un segundo. Por eso nadie actuó nunca.

`x-fits` tampoco podía ser la comprobación, y a propósito: es guía blanda que el
propio ejemplo de referencia se salta por uno o tres caracteres viéndose bien.
Parar ahí sería gritar con specs buenos, y un aviso que grita se ignora — que es
justo cómo sobrevivieron los graves. Así que shortsmith publica desde 289d50e un
segundo número medido por campo, `x-fits-hard`, y esto lo comprueba antes de
gastar el render: cuesta un reintento del modelo en vez de un render. El mismo
movimiento que hizo `MAX_CUE_CHARS` con los captions en e32c59f.

Auditado contra los 17 short_en de producción: OCHO llevan al menos un texto que
se dibuja ilegible.

- El ya conocido, Cash-Landrum: `ALL THREE DEVELOPED SYMPTOMS CONSISTENT WITH
  RADIATION EXPOSURE`, 36 px pedidos y 20 dibujados. Salta con 63 caracteres
  contra un presupuesto de 61 — así de vertical es la curva pegada al muro.
- El que nadie había visto es peor y más común: el CARTEL DE CIERRE.
  `counter_close.lines` pide 110 px y se dibujó a 28 en el peor caso, y por
  debajo de 64 en cinco de los ocho. Es la llamada a la acción, y en un tercio
  del catálogo es el texto más pequeño del fotograma.
- Y la tolerancia aguanta sobre datos reales: `STILL UNEXPLAINED` encoge de 110
  a 84 px y no se avisa, que es lo correcto.

El desempate entre intentos pasa a contar averías —gancho y texto ilegible—
antes que segundos, por la misma razón que en 6d9b602: una reescritura que
arregla un rótulo pero se pasa un segundo perdía contra la que no lo arreglaba.

Un campo sin presupuesto propio (`Bar.unit`, que se dibuja dentro de la cadena
de `value_label`) se salta a conciencia: reconstruir esa cadena aquí pediría
conocer el formato de la plantilla, que es lo que este repo no sabe ni debe.

Y contra un shortsmith anterior a 289d50e esto se calla — no puede inventarse el
número —, así que de que el contrato traiga el campo se encarga
test_shortsmith_live.py, que es quien habla con el servicio.

Suite: 274 pasan.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
ChemaVX
2026-09-01 15:31:05 +00:00
co-authored by Claude Opus 5
parent 6d9b6025ba
commit 198b0e6238
6 changed files with 330 additions and 38 deletions
+29
View File
@@ -116,6 +116,18 @@ def test_the_prompt_states_what_the_opening_shot_has_to_do():
assert "never the date" in prompt and "subline" in prompt
def test_the_prompt_states_both_text_budgets():
"""El primero es el objetivo y el segundo la línea roja, y hacen falta los
dos: con sólo "CABE ~16" el modelo lee una sugerencia sin consecuencia y
escribe 58. Con sólo la línea roja, apunta a ella y todo sale encogido."""
w, _ = writer("{}")
prompt = w.build_prompt("Caso X", "material", None, "X.TEST")
assert "CABE ~13 caracteres dibujados" in prompt # del esquema
assert "ILEGIBLE por encima de 21" in prompt
assert "is the line that is actually checked" in prompt
def test_prompt_includes_the_worked_example_in_full():
w, _ = writer("{}")
prompt = w.build_prompt("Caso X", "material", None, "X.TEST")
@@ -426,6 +438,23 @@ async def test_a_fixed_hook_beats_a_closer_duration():
assert result.notes and "objetivo" in result.notes[0]
@pytest.mark.asyncio
async def test_a_shortened_label_also_beats_a_closer_duration():
"""El mismo desempate que el gancho, por la otra avería: un rótulo que se iba
a dibujar ilegible no se arregla durando menos."""
ilegible = json.loads(json.dumps(GOOD))
ilegible["shots"][0]["props"]["subline"] = "X" * 60 # 30 s: en objetivo
corto = json.loads(json.dumps(GOOD))
corto["shots"][0]["props"]["subline"] = "SOCORRO, NEW MEXICO"
for shot in corto["shots"]:
shot["duration"] = 24.0 # 48 s: se pasa
w, _ = writer(json.dumps(ilegible), json.dumps(corto))
result = await w.write("Caso X", "material")
assert result.spec["shots"][0]["props"]["subline"] == "SOCORRO, NEW MEXICO"
@pytest.mark.asyncio
async def test_a_note_does_not_eat_the_attempt_the_contract_needs():
"""Si la reescritura sale malformada, aún queda un intento para arreglarla."""