infografia_brief: guion verificado para la infografía de un post
Las tres infografías falladas del 27-jul no fallaron al dibujar, fallaron al
decidir qué era cierto. ChatGPT tenía el artículo anterior en el contexto de la
conversación y coló la ficha de Vilas-Boas dentro de la infografía de Talavera,
y los 84 expedientes del Ejército del Aire español dentro de la de Vilas-Boas.
En una llegó a inventarse un dominio, bibliotecavirtual.defesa.gov.br, que no
existe: una brasileñización del español, a un clic de publicarse.
La respuesta no es pedirle al modelo que se porte bien, es no dejarle decidir.
El guion se extrae del artículo (claude -p, coste cero) y se VERIFICA contra el
propio artículo antes de que ChatGPT vea nada. A ChatGPT le llega "maqueta este
texto", no "lee esto y decide qué es cierto".
Decisiones que no son de adorno:
- La verificación es regex sobre el artículo, sin modelo de por medio. Un
modelo autoevaluándose es exactamente lo que ya falló tres veces.
- Tres severidades, sacadas de cómo falló esto en la vida real: nombre propio
fuera del artículo se cae, URL fuera del artículo se cae sin excepciones, y
cifra que no aparece literal solo avisa. Lo tercero porque el artículo dice
"mil novecientas páginas" donde el guion escribe "1.900", y tumbar eso
automáticamente daría más falsos positivos que aciertos. Los nombres propios
fueron el 100% de la contaminación observada.
- Los títulos de panel NO se verifican: "DATOS DEL CASO" es una etiqueta
editorial, no una afirmación sobre el caso, y no tiene por qué aparecer en el
artículo. Verificarlos hacía caer todos los paneles y el informe se volvía
inservible.
- Una mayúscula en inicio de frase no cuenta como nombre propio, o cualquier
viñeta que empiece por verbo ("Llegan…", "Comienza…") sale marcada como
forastera. Las siglas (SBEDV, APRO, OVNI) sí se persiguen en cualquier
posición: son marcador de contaminación.
- Se compara plegando minúsculas y tildes. El modelo escribe "Neron" donde el
artículo dice "Nerón" y eso no es contaminación; la de verdad son palabras
que no están en el artículo de ninguna forma.
- El idioma lo fija --site, no se adivina del texto: es un dato duro que no se
equivoca, y el idioma del guion arrastra el de la imagen.
Verificado con la avería real, no con un caso de laboratorio: al guion
contaminado de Talavera se le caen Vilas-Boas, Martins, Fontes, Bühler, SBEDV,
Creighton, São Francisco de Sales y el dominio inventado, y se eliminan enteros
los dos paneles que se quedan sin viñetas, mientras los legítimos sobreviven
sin un solo falso positivo. Sobre los dos artículos publicados hoy, extrayendo
de cero: 0 descartes y 0 avisos en ambos, guion inglés íntegro en inglés y
español íntegro en español.
Solo lectura sobre Ghost: no toca el post, no publica, no genera imágenes.
El wrapper ~/.local/bin/infografia queda fuera del repo, como seo-check.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Executable
+314
@@ -0,0 +1,314 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Guion verificado para la infografía de un post (Tool G).
|
||||||
|
|
||||||
|
Por qué existe: las tres infografías falladas del 2026-07-27 no fallaron al
|
||||||
|
dibujar, fallaron al DECIDIR qué era cierto. ChatGPT tenía el artículo anterior
|
||||||
|
en el contexto de la conversación y coló datos de un caso en la infografía de
|
||||||
|
otro (la ficha de Vilas-Boas dentro de Talavera, los 84 expedientes españoles
|
||||||
|
dentro de Vilas-Boas) y llegó a inventarse un dominio, bibliotecavirtual.defesa
|
||||||
|
.gov.br, que no existe.
|
||||||
|
|
||||||
|
La respuesta no es pedirle al modelo que se porte bien, es no dejarle decidir:
|
||||||
|
aquí se extrae el guion del artículo y se VERIFICA contra el propio artículo
|
||||||
|
antes de que ChatGPT vea nada. Lo que no case, no llega a la imagen.
|
||||||
|
|
||||||
|
Severidades, calcadas de cómo falló esto en la vida real:
|
||||||
|
· nombre propio que no está en el artículo → SE CAE (fue el 100% de la
|
||||||
|
contaminación observada: São Francisco de Sales, Fontes, Bühler, O Cruzeiro)
|
||||||
|
· URL o dominio que no está en el artículo → SE CAE, sin excepciones
|
||||||
|
· cifra que no está en el artículo → AVISA, pero se queda: el
|
||||||
|
artículo puede decir "mil novecientas páginas" y el guion "1.900", y
|
||||||
|
tumbar eso automáticamente daría más falsos positivos que aciertos
|
||||||
|
|
||||||
|
Solo lectura sobre Ghost. No publica, no toca el post, no genera imágenes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import string
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
WORK = os.path.expanduser("~/.cache/infografia")
|
||||||
|
TEMPLATE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "infografia_prompt.md")
|
||||||
|
|
||||||
|
# Mismos dos sitios que el resto de ~/seo-tools. El idioma NO se adivina del
|
||||||
|
# texto: lo fija el sitio, que es un dato duro y no se equivoca nunca.
|
||||||
|
SITES = {
|
||||||
|
"en": {"cli": "ghst-en", "idioma": "inglés",
|
||||||
|
"fecha": "November 12, 1976", "decimal": "punto"},
|
||||||
|
"es": {"cli": "ghst-es", "idioma": "español",
|
||||||
|
"fecha": "12 de noviembre de 1976", "decimal": "coma"},
|
||||||
|
}
|
||||||
|
|
||||||
|
EXTRACT_PROMPT = """Lee el artículo que va al final y devuelve SOLO un objeto JSON
|
||||||
|
(sin ```, sin texto alrededor) con el guion de una infografía.
|
||||||
|
|
||||||
|
Reglas innegociables:
|
||||||
|
- Cada dato debe salir del artículo. No uses tu conocimiento del caso.
|
||||||
|
- Copia los nombres propios, cifras y fechas TAL CUAL aparecen en el artículo.
|
||||||
|
- No inventes horas, referencias de archivo ni URLs.
|
||||||
|
- Si un bloque no tiene material suficiente en el artículo, déjalo con menos
|
||||||
|
viñetas o devuélvelo vacío. Prefiero tres viñetas ciertas que seis dudosas.
|
||||||
|
- Escribe el guion en el mismo idioma que el artículo.
|
||||||
|
|
||||||
|
Estructura exacta:
|
||||||
|
{{
|
||||||
|
"titular": "caso y año, máx 60 car.",
|
||||||
|
"subtitulo": "gancho corto, máx 60 car.",
|
||||||
|
"entradilla": "3-4 líneas resumiendo el artículo",
|
||||||
|
"cronologia": [{{"marca": "hora o duración literal del artículo", "texto": "qué pasó"}}],
|
||||||
|
"paneles": [{{"titulo": "...", "vinetas": ["...", "..."]}}],
|
||||||
|
"franja": [{{"titulo": "...", "vinetas": ["...", "..."]}}]
|
||||||
|
}}
|
||||||
|
|
||||||
|
"paneles" son la secuencia de los hechos (entre 4 y 8).
|
||||||
|
"franja" son los bloques de cierre: datos del caso, lugar, argumentos a favor,
|
||||||
|
argumentos escépticos, legado y lo que el artículo sostenga.
|
||||||
|
|
||||||
|
ARTÍCULO:
|
||||||
|
{articulo}
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Palabras que van en mayúscula por posición, no por ser nombre propio. Solo se
|
||||||
|
# usan para no dar la lata: si una de éstas no está en el artículo, es que el
|
||||||
|
# artículo es muy corto, no que haya contaminación.
|
||||||
|
RUIDO = {"el", "la", "los", "las", "un", "una", "y", "de", "del", "en", "the", "a", "an"}
|
||||||
|
|
||||||
|
|
||||||
|
def sh(cmd, timeout=600):
|
||||||
|
return subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch(slug, cli):
|
||||||
|
"""Baja el post por el wrapper ghst-*, igual que seo_finish.fetch."""
|
||||||
|
fd, path = tempfile.mkstemp(suffix=".json", prefix="infografia_")
|
||||||
|
os.close(fd)
|
||||||
|
try:
|
||||||
|
sh(f"{cli} post list --limit 100 --formats html --json > {path}")
|
||||||
|
with open(path) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
posts = data.get("posts", data) if isinstance(data, dict) else data
|
||||||
|
finally:
|
||||||
|
os.unlink(path)
|
||||||
|
for p in posts:
|
||||||
|
if p.get("slug") == slug:
|
||||||
|
return p
|
||||||
|
sys.exit(f"ERROR: no encuentro el post '{slug}' en {cli}")
|
||||||
|
|
||||||
|
|
||||||
|
def plano(html):
|
||||||
|
return re.sub(r"\s+", " ", re.sub(r"<[^>]+>", " ", html or "")).strip()
|
||||||
|
|
||||||
|
|
||||||
|
# ── verificación ──────────────────────────────────────────────────────────────
|
||||||
|
# Nada de esto usa un modelo: son regex sobre el texto del artículo. Un modelo
|
||||||
|
# comprobándose a sí mismo es justo lo que ya falló tres veces.
|
||||||
|
|
||||||
|
RE_TOKEN = re.compile(r"[A-Za-zÁÉÍÓÚÑÜÀÂÃÇáéíóúñüàâãç][\wÁÉÍÓÚÑÜÀÂÃÇáéíóúñüàâãç'’\-]*")
|
||||||
|
RE_CIFRA = re.compile(r"\d[\d.,:]*")
|
||||||
|
RE_URL = re.compile(r"(?:https?://|www\.)\S+|\b[\w\-]+\.(?:com|es|org|net|gov|br|gob)\b\S*", re.I)
|
||||||
|
# Un token en mayúscula que va detrás de uno de estos signos lo está por
|
||||||
|
# posición, no por ser nombre propio.
|
||||||
|
RE_ARRANQUE = re.compile(r"[.:;!?¡¿—–\-(\[«\"]\s*$")
|
||||||
|
|
||||||
|
|
||||||
|
def pliega(s):
|
||||||
|
"""Minúsculas y sin tildes.
|
||||||
|
|
||||||
|
Comparar con tildes generaba falsos positivos tontos (el modelo escribe
|
||||||
|
«Neron» donde el artículo dice «Nerón») sin aportar nada: la contaminación
|
||||||
|
de verdad son palabras que NO están en el artículo de ninguna forma.
|
||||||
|
"""
|
||||||
|
tabla = str.maketrans("áéíóúüàâãçñÁÉÍÓÚÜÀÂÃÇÑ", "aeiouuaaacnAEIOUUAAACN")
|
||||||
|
return s.translate(tabla).lower()
|
||||||
|
|
||||||
|
|
||||||
|
def _en_articulo(token, articulo):
|
||||||
|
return pliega(token).strip(string.punctuation + "’'") in articulo
|
||||||
|
|
||||||
|
|
||||||
|
def revisa(texto, articulo):
|
||||||
|
"""Devuelve (nombres_fuera, cifras_fuera, urls_fuera) de una cadena del guion.
|
||||||
|
|
||||||
|
Solo se persiguen los tokens que parecen nombre propio de verdad:
|
||||||
|
· mayúscula inicial que NO esté al principio de la frase
|
||||||
|
· siglas en versalitas en cualquier posición (SBEDV, APRO, OVNI)
|
||||||
|
Sin lo primero, cualquier viñeta que empiece por un verbo («Llegan…»,
|
||||||
|
«Comienza…») se marcaba como forastera y el informe se volvía inservible.
|
||||||
|
"""
|
||||||
|
urls = [u for u in RE_URL.findall(texto) if not _en_articulo(u, articulo)]
|
||||||
|
nombres = []
|
||||||
|
for m in RE_TOKEN.finditer(texto):
|
||||||
|
t = m.group()
|
||||||
|
sigla = len(t) > 1 and t.isupper()
|
||||||
|
propio = t[:1].isupper() and not t.isupper()
|
||||||
|
if propio and RE_ARRANQUE.search(texto[:m.start()] or " "):
|
||||||
|
continue # va en mayúscula por ir detrás de punto
|
||||||
|
if propio and m.start() == 0:
|
||||||
|
continue # ídem, principio de la cadena
|
||||||
|
if not (sigla or propio) or t.lower() in RUIDO:
|
||||||
|
continue
|
||||||
|
if not _en_articulo(t, articulo):
|
||||||
|
nombres.append(t)
|
||||||
|
cifras = [c for c in RE_CIFRA.findall(texto)
|
||||||
|
if len(c.strip(".,:")) > 1 and not _en_articulo(c, articulo)]
|
||||||
|
return nombres, cifras, urls
|
||||||
|
|
||||||
|
|
||||||
|
def filtra(guion, articulo_low, informe):
|
||||||
|
"""Poda el guion en sitio. Devuelve el guion limpio.
|
||||||
|
|
||||||
|
Una viñeta con un nombre propio o una URL forastera se cae entera: no se
|
||||||
|
puede "arreglar" media viñeta sin inventar, y ése es justo el pecado que
|
||||||
|
esta herramienta existe para evitar.
|
||||||
|
"""
|
||||||
|
def cadena_ok(texto, donde):
|
||||||
|
nombres, cifras, urls = revisa(texto, articulo_low)
|
||||||
|
for c in cifras:
|
||||||
|
informe["avisos"].append(f"{donde}: cifra «{c}» no aparece literal en el artículo")
|
||||||
|
if urls:
|
||||||
|
informe["caidas"].append(f"{donde}: URL forastera {urls} → «{texto[:70]}»")
|
||||||
|
return False
|
||||||
|
if nombres:
|
||||||
|
informe["caidas"].append(f"{donde}: nombre(s) {nombres} fuera del artículo → «{texto[:70]}»")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
for campo in ("titular", "subtitulo", "entradilla"):
|
||||||
|
if guion.get(campo) and not cadena_ok(guion[campo], campo):
|
||||||
|
guion[campo] = ""
|
||||||
|
|
||||||
|
# Marca y texto se revisan por separado: concatenarlos le quitaba a la
|
||||||
|
# primera palabra del texto su condición de inicio de frase, y cualquier
|
||||||
|
# entrada que empezara por un verbo salía marcada como forastera.
|
||||||
|
guion["cronologia"] = [
|
||||||
|
h for h in guion.get("cronologia", [])
|
||||||
|
if cadena_ok(h.get("marca", ""), "cronología/marca")
|
||||||
|
and cadena_ok(h.get("texto", ""), "cronología")
|
||||||
|
]
|
||||||
|
|
||||||
|
for bloque in ("paneles", "franja"):
|
||||||
|
limpios = []
|
||||||
|
for p in guion.get(bloque, []):
|
||||||
|
# Los títulos de panel NO se verifican: son etiquetas editoriales
|
||||||
|
# («DATOS DEL CASO», «ARGUMENTOS ESCÉPTICOS»), no afirmaciones sobre
|
||||||
|
# el caso, y no tienen por qué aparecer en el artículo.
|
||||||
|
titulo = p.get("titulo", "")
|
||||||
|
p["vinetas"] = [v for v in p.get("vinetas", [])
|
||||||
|
if cadena_ok(v, f"{bloque}/«{titulo[:24]}»")]
|
||||||
|
if p["vinetas"]:
|
||||||
|
limpios.append(p)
|
||||||
|
else:
|
||||||
|
informe["caidas"].append(f"{bloque}: panel «{titulo[:30]}» se queda sin viñetas y se elimina")
|
||||||
|
guion[bloque] = limpios
|
||||||
|
return guion
|
||||||
|
|
||||||
|
|
||||||
|
# ── guion a texto ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def formatea(guion):
|
||||||
|
out = [f"TITULAR: {guion.get('titular','')}",
|
||||||
|
f"SUBTÍTULO: {guion.get('subtitulo','')}",
|
||||||
|
f"ENTRADILLA: {guion.get('entradilla','')}", ""]
|
||||||
|
if guion.get("cronologia"):
|
||||||
|
out.append("CRONOLOGÍA")
|
||||||
|
for h in guion["cronologia"]:
|
||||||
|
out.append(f" {h.get('marca','')} — {h.get('texto','')}")
|
||||||
|
out.append("")
|
||||||
|
for bloque, etiqueta in (("paneles", "PANELES (secuencia)"), ("franja", "FRANJA INFERIOR")):
|
||||||
|
if not guion.get(bloque):
|
||||||
|
continue
|
||||||
|
out.append(etiqueta)
|
||||||
|
for i, p in enumerate(guion[bloque], 1):
|
||||||
|
marca = f"{i:02d}. " if bloque == "paneles" else " · "
|
||||||
|
out.append(f"{marca}{p['titulo']}")
|
||||||
|
for v in p["vinetas"]:
|
||||||
|
out.append(f" - {v}")
|
||||||
|
out.append("")
|
||||||
|
return "\n".join(out).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser(description="Guion verificado para la infografía de un post")
|
||||||
|
ap.add_argument("slug")
|
||||||
|
ap.add_argument("--site", choices=("en", "es"), required=True)
|
||||||
|
ap.add_argument("--out", help="guardar el prompt final aquí (además de imprimirlo)")
|
||||||
|
ap.add_argument("--claude-bin", default=os.environ.get("CLAUDE_BIN", "claude"))
|
||||||
|
ap.add_argument("--guion-json", help="saltarse el modelo y usar este guion ya extraído")
|
||||||
|
a = ap.parse_args()
|
||||||
|
|
||||||
|
cfg = SITES[a.site]
|
||||||
|
post = fetch(a.slug, cfg["cli"])
|
||||||
|
articulo = plano(post.get("html"))
|
||||||
|
if len(articulo.split()) < 200:
|
||||||
|
sys.exit(f"ERROR: el artículo tiene {len(articulo.split())} palabras; ¿seguro que es el slug bueno?")
|
||||||
|
|
||||||
|
print(f"═══ [{a.site.upper()}] {a.slug}")
|
||||||
|
print(f" {post.get('status')} · {len(articulo.split())} palabras · idioma: {cfg['idioma']}")
|
||||||
|
|
||||||
|
if a.guion_json:
|
||||||
|
with open(a.guion_json) as f:
|
||||||
|
guion = json.load(f)
|
||||||
|
else:
|
||||||
|
print(" extrayendo el guion con claude -p (coste cero, plan Pro)…")
|
||||||
|
fd, pfile = tempfile.mkstemp(suffix=".txt", prefix="infografia_prompt_")
|
||||||
|
os.close(fd)
|
||||||
|
with open(pfile, "w") as f:
|
||||||
|
f.write(EXTRACT_PROMPT.format(articulo=articulo))
|
||||||
|
try:
|
||||||
|
r = sh(f'{a.claude_bin} -p < "{pfile}"')
|
||||||
|
finally:
|
||||||
|
os.unlink(pfile)
|
||||||
|
if r.returncode != 0:
|
||||||
|
sys.exit(f"ERROR: claude -p falló ({r.returncode}): {r.stderr[:300]}")
|
||||||
|
crudo = r.stdout.strip()
|
||||||
|
m = re.search(r"\{.*\}", crudo, re.S)
|
||||||
|
if not m:
|
||||||
|
sys.exit(f"ERROR: no encuentro JSON en la respuesta del modelo:\n{crudo[:400]}")
|
||||||
|
try:
|
||||||
|
guion = json.loads(m.group())
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
sys.exit(f"ERROR: JSON inválido del modelo ({e}):\n{m.group()[:400]}")
|
||||||
|
|
||||||
|
os.makedirs(WORK, exist_ok=True)
|
||||||
|
with open(os.path.join(WORK, f"{a.site}-{a.slug[:40]}-crudo.json"), "w") as f:
|
||||||
|
json.dump(guion, f, ensure_ascii=False, indent=1)
|
||||||
|
|
||||||
|
informe = {"caidas": [], "avisos": []}
|
||||||
|
antes = json.dumps(guion, ensure_ascii=False)
|
||||||
|
guion = filtra(guion, pliega(articulo), informe)
|
||||||
|
|
||||||
|
print(f"\n─── verificación contra el artículo ───")
|
||||||
|
print(f" descartado: {len(informe['caidas'])} | avisos: {len(informe['avisos'])}")
|
||||||
|
for c in informe["caidas"]:
|
||||||
|
print(f" ✗ {c}")
|
||||||
|
for v in informe["avisos"]:
|
||||||
|
print(f" ⚠ {v}")
|
||||||
|
if not informe["caidas"] and not informe["avisos"]:
|
||||||
|
print(" ✓ todo el guion está anclado al artículo")
|
||||||
|
if len(json.dumps(guion, ensure_ascii=False)) < len(antes) * 0.5:
|
||||||
|
print("\n ⚠️ se ha caído más de la mitad del guion: revísalo antes de usarlo")
|
||||||
|
|
||||||
|
with open(TEMPLATE) as f:
|
||||||
|
plantilla = string.Template(f.read())
|
||||||
|
prompt = plantilla.safe_substitute(
|
||||||
|
idioma_nombre=cfg["idioma"], formato_fecha=cfg["fecha"],
|
||||||
|
formato_decimal=cfg["decimal"], guion=formatea(guion),
|
||||||
|
)
|
||||||
|
|
||||||
|
destino = a.out or os.path.join(WORK, f"{a.site}-{a.slug[:40]}-prompt.txt")
|
||||||
|
with open(destino, "w") as f:
|
||||||
|
f.write(prompt)
|
||||||
|
print(f"\n─── prompt listo para pegar en ChatGPT ───")
|
||||||
|
print(f" {destino}\n")
|
||||||
|
print(prompt)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
Eres diseñador de infografías editoriales. Vas a maquetar UNA infografía a
|
||||||
|
partir del guion que te doy más abajo.
|
||||||
|
|
||||||
|
════════ REGLA 0 ════════
|
||||||
|
El guion ya está cerrado y verificado contra el artículo original. Tu trabajo es
|
||||||
|
MAQUETARLO, no completarlo ni corregirlo.
|
||||||
|
|
||||||
|
· No añadas ningún dato, nombre, fecha, cifra, institución ni URL que no esté
|
||||||
|
en el guion. Ni de tu conocimiento del caso, ni de mensajes anteriores de
|
||||||
|
esta conversación.
|
||||||
|
· No "mejores" ni "completes" un panel que te parezca corto. Si tiene tres
|
||||||
|
viñetas, lleva tres.
|
||||||
|
· No inventes horas intermedias en la cronología. Si el guion salta de 01:45 al
|
||||||
|
amanecer, la imagen salta igual.
|
||||||
|
· Prohibidas las URLs, los logotipos institucionales y las referencias de
|
||||||
|
archivo que no aparezcan literalmente en el guion.
|
||||||
|
|
||||||
|
════════ IDIOMA ════════
|
||||||
|
Todo el texto de la imagen va en $idioma_nombre, sin una sola palabra del otro
|
||||||
|
idioma: titular, subtítulos, títulos de panel, viñetas, notas y pies.
|
||||||
|
Formatos en $idioma_nombre: $formato_fecha, decimales con $formato_decimal.
|
||||||
|
|
||||||
|
════════ ESTILO ════════
|
||||||
|
· 1200 × 800 px, horizontal.
|
||||||
|
· Fondo oscuro, paneles rectangulares con borde tenue, tipografía de palo seco,
|
||||||
|
un color de acento coherente con el tema. Sobrio y documental: esto acompaña a
|
||||||
|
un artículo de investigación, no a un titular sensacionalista.
|
||||||
|
· Cabecera: titular en dos líneas, entradilla de 3-4 líneas e ilustración de la
|
||||||
|
escena central.
|
||||||
|
· Cuerpo: los paneles numerados en orden, la cronología en una columna lateral,
|
||||||
|
y la franja inferior con el resto de bloques.
|
||||||
|
· Si un panel no cabe con holgura, redúcelo de tamaño o quítalo entero — nunca
|
||||||
|
recortes el texto a mitad de frase.
|
||||||
|
|
||||||
|
════════ COMPROBACIÓN FINAL ════════
|
||||||
|
Antes de entregar, confirma por escrito:
|
||||||
|
1. ¿Todo el texto está en $idioma_nombre?
|
||||||
|
2. ¿Aparece algún dato que NO esté en el guion?
|
||||||
|
3. ¿Se contradice algún panel con otro de la misma imagen?
|
||||||
|
4. ¿Hay alguna URL, logo o referencia oficial que no venga en el guion?
|
||||||
|
|
||||||
|
════════ GUION ════════
|
||||||
|
$guion
|
||||||
Reference in New Issue
Block a user