fix(short): el primer plano gastaba el gancho en la fecha

El gancho de un Short es lo que se DIBUJA, no solo lo que se dice. El prompt
llevaba la mitad hablada desde 4b («the first line is the whole hook») y no
tenía nada sobre la mitad vista. De los diecisiete short_en generados, once
abren mal: diez ponen la fecha en el titular —el texto más grande del vídeo—
mientras el subline de debajo ya lleva el sitio, y el output 131 abre con
document_quote.

Medido sobre el renderizador a 5,5 s de plano: las cinco plantillas con
`headline` lo ponen a tinta plena en 0,33-0,40 s, y shortsmith lo garantiza a
cualquier duración desde db1ac7e. Las tres que no lo tienen dejan la banda
superior del fotograma al nivel del fondo TODO el plano —su texto se escribe a
máquina más abajo y no está entero hasta 2,6-2,8 s—. Con 6,9 s de visionado
medio, abrir con una de esas regala el tercio de la ventana en el que se
decide todo.

La costura es `headline`, preguntada al esquema y no a una lista de nombres:
una plantilla nueva de shortsmith con titular podrá abrir sin tocar este repo,
y una sin él no podrá. El mismo pacto que el resto del contrato.

Y `_closer_to_target` ordenaba SOLO por segundos, lo que hacía decorativo el
aviso nuevo: una reescritura que arreglaba el titular pero se pasaba un
segundo perdía contra el intento que abría con una fecha. Ahora el gancho va
delante — los segundos son un gradiente, el gancho es binario.

Comprobado contra los 17 specs reales de producción: avisa de once y de
ninguno más. Los que pasan son los titulares que se quieren (62 CHILDREN,
23 HELICOPTERS, RELEASE 05).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
ChemaVX
2026-09-01 12:39:08 +00:00
co-authored by Claude Opus 5
parent a17edf43b7
commit 6d9b6025ba
6 changed files with 299 additions and 24 deletions
+32
View File
@@ -105,6 +105,17 @@ def test_prompt_states_the_editorial_constraints():
assert "material" in prompt
def test_the_prompt_states_what_the_opening_shot_has_to_do():
"""Las dos mitades del gancho visual. Sin la primera el modelo abre con una
plantilla que aún se está escribiendo; sin la segunda gasta el texto más
grande del vídeo en la fecha, que es lo que hizo en cinco de once casos."""
w, _ = writer("{}")
prompt = w.build_prompt("Caso X", "material", None, "X.TEST")
assert "`headline`" in prompt and "0.4 seconds" in prompt
assert "never the date" in prompt and "subline" in prompt
def test_prompt_includes_the_worked_example_in_full():
w, _ = writer("{}")
prompt = w.build_prompt("Caso X", "material", None, "X.TEST")
@@ -394,6 +405,27 @@ async def test_a_rewrite_that_makes_it_worse_is_discarded():
assert result.attempts == 2, "se pagaron dos generaciones aunque valga la primera"
@pytest.mark.asyncio
async def test_a_fixed_hook_beats_a_closer_duration():
"""El desempate entre intentos válidos ordenaba SÓLO por segundos, y eso
hacía inútil el aviso de apertura: el modelo obedecía la nota, se pasaba un
poco de largo al reescribir, y se elegía igualmente el intento que abría con
una fecha. Los segundos son un gradiente; el gancho es binario y cuesta más.
"""
fecha = json.loads(json.dumps(GOOD))
fecha["shots"][0]["props"]["headline"] = "8 JAN 1981" # 30 s: en objetivo
arreglado = json.loads(json.dumps(GOOD))
arreglado["shots"][0]["props"]["headline"] = "62 CHILDREN"
for shot in arreglado["shots"]:
shot["duration"] = 24.0 # 48 s: se pasa
w, _ = writer(json.dumps(fecha), json.dumps(arreglado))
result = await w.write("Caso X", "material")
assert result.spec["shots"][0]["props"]["headline"] == "62 CHILDREN"
assert result.notes and "objetivo" in result.notes[0]
@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."""