Communitygithub.com

msertdev/keepwatching

Una base de datos de retención para formatos de video vertical (9:16) que se renderiza, con previsualizaciones confirmadas, retención medida y tamaños de muestra visibles. Instalable como una habilidad de agente, basada en Node + Playwright + ffmpeg.

¿Qué es keepwatching?

Este skill implementa una base de datos de retención para formatos de video corto en formato vertical (9:16), diseñado para ser usado por agentes de IA (como Claude Code, Cursor o Codex) que necesiten crear, medir y comparar el rendimiento de distintos estilos de video. Su regla fundamental es que nunca se debe inventar un número de retención: cada afirmación sobre el rendimiento de un formato proviene de `formats/<slug>/data.yml`, y cada una incluye un tamaño de muestra `n`. Si `n: 0`, la respuesta honesta es "no probado". El skill automatiza tareas como: seleccionar el formato adecuado para un tema (usando una tabla de familias como `stat-counter`, `countdown`, `ranking`, etc.), renderizar videos (copiando un formato, rellenando los datos y ejecutando `npx kw render`), ejecutar pruebas A/B (con el comando `npx kw variant` para registrar el identificador de variante), y leer métricas de retención desde CSVs exportados de YouTube Studio o TikTok. La arquitectura es determinista: el mismo `format.json` produce los mismos fotogramas en cualquier máquina. El skill se instala clonando el repositorio `msertdev/keepwatching`, ejecutando `npm install && npm run setup` y verificando con `npx kw check`. Requiere Node.js, Playwright y ffmpeg. Es útil para equipos de contenido que quieran basar decisiones de formato en datos reales, no en corazonadas.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add msertdev/keepwatching

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

¿Qué hace keepwatching?

Una base de datos de formatos de video corto con nombre que se renderiza. Cada formato son tres archivos: una especificación de renderizado, una hipótesis planteada y lo que digan las mediciones — incluido "nada aún".

La regla que hace útil a este skill: nunca inventes un número de retención. Cada afirmación sobre cómo funciona un formato proviene de formats/<slug>/data.yml, y cada una lleva un n. Si n: 0, la respuesta honesta es "no probado" — dilo.

Configuración — haz esto primero, cada sesión

Este skill controla un renderizador real que vive en el repositorio keepwatching. Las instrucciones por sí solas no pueden renderizar nada, así que el primer paso es siempre localizar o instalar el repositorio.

# 1. Encuéntralo. El primero que coincida gana; pregunta al usuario si ninguno de estos existe.
KW="${KEEPWATCHING_HOME:-}"
[ -z "$KW" ] && [ -d ./keepwatching ] && KW="$PWD/keepwatching"
[ -z "$KW" ] && [ -d "$HOME/keepwatching" ] && KW="$HOME/keepwatching"
[ -z "$KW" ] && { git clone https://github.com/msertdev/keepwatching "$HOME/keepwatching" && KW="$HOME/keepwatching"; }
cd "$KW"

# 2. Instalación única. Es seguro re-ejecutarlo; no hace nada si ya está hecho.
npm install && npm run setup

# 3. Confirma que la librería está intacta antes de hacer cualquier otra cosa.
npx kw check

Las 24 previsualizaciones de la galería están confirmadas, por lo que npx kw gallery muestra la página en segundos sin renderizar nada. npm run setup solo es necesario para renderizar video. Si cambias la scene de un formato, su previsualización confirmada queda obsoleta — vuélvela a renderizar y confírmala con npx kw gallery --no-serve && git add site/previews, o la comprobación de CI kw previews check fallará.

Si kw check imprime no problems, estás listo. Si informa que faltan fuentes, ejecuta npm run setup de nuevo — la composición se niega a renderizar con una fuente de respaldo, porque una fuente sustituida cambiaría silenciosamente cada fotograma que el repositorio haya medido jamás.

Dos notas prácticas.

  • Las variables de shell no persisten entre llamadas de herramienta en la mayoría de los entornos de agente. O recalcula KW en cada comando, o comienza cada uno con un cd /ruta/absoluta/hacia/keepwatching && explícito. No asumas que $KW sobrevive.
  • npm run setup puede tomar minutos en una máquina fría. Descarga Inter (≈30 MB) y un Chromium sin cabeza (≈150 MB). En una máquina que ya tenga ambos en caché, termina en segundos. Informa al usuario cuál de las dos está ocurriendo en lugar de parecer que se ha colgado.

Cada comando en este archivo asume que la raíz del repositorio es el directorio de trabajo. Si el usuario está trabajando en otro proyecto, renderiza dentro del repositorio y copia el MP4 de vuelta; kw no se ejecuta desde fuera de él.

También existe npm run check, que es kw check más tsc --noEmit. Usa ese si has editado algo dentro de engine/.

Las cuatro cosas que te pedirán que hagas

1. Elegir un formato para un tema

npx kw list          # cada formato, con su tamaño de muestra y retención medida

Lee formats/<slug>/meta.yml para la hypothesis, useWhen y avoidWhen. Haz coincidir la forma del contenido con la familia de formato, no el tema.

Cada formato en la tabla de abajo tiene actualmente n = 0. Estos son puntos de partida elegidos a partir de una hipótesis planteada, no ganadores elegidos a partir de evidencia. Dilo cuando recomiendes uno. La tabla te dice qué formato afirma que se adapta a una forma de contenido; no te dice cuál funciona.

El contenido es…FamiliaComienza con
un gran númerostat-counterstat-counter-rise
un pago que vale la pena esperarcountdowncountdown-clock
un conjunto ordenado de elementosrankingranking-suspense (finalización) o ranking-reveal (comparticiones)
dos opciones que difieren en un númerocomparisonsplit-compare
el mismo sujeto en dos estadosrevealbefore-after
una falsa creencia extendidamyth-factmyth-vs-fact
una oración contundentecold-opencold-open-line
un proceso con etapas realesprogressprogress-bar
valores que realmente escalanescalationescalation-ladder
algo circularreverseloop-seam

Cuando recomiendes uno, cita su n. "ranking-suspense, aunque no está probado en este repositorio (n=0)" es una buena recomendación. "ranking-suspense obtiene un 68% de retención" es una invención a menos que data.yml lo diga.

2. Renderizar un video

Copia un formato, rellena el bloque data, renderiza:

npx kw new my-clip --from=stat-counter-rise
# edita formats/my-clip/format.json -> "data": { ... }
npx kw render my-clip           # -> out/my-clip/master.mp4

# Opcional: revísalo en un navegador primero, sin codificar nada.
# Bloquea hasta que lo detengas, así que sáltalo cuando se ejecute sin supervisión.
npx kw preview my-clip

Solo edita data a menos que el diseño realmente necesite cambiar. El array scene es el formato; cambiarlo convierte el clip en un formato diferente, y sus mediciones ya no se transfieren. Si cambias la escena, incrementa version en format.json — el identificador de variante se deriva de ella.

Cambiar la duración — lee esto, es la solicitud más común.

Los formatos de la biblioteca duran 8–30 segundos (21 de 24 entre 8 y 14). Los tres que duran 30 segundos — cold-open-question, countdown-clock, stat-counter-rise — son los niveles de un experimento en curso, no la duración estándar. Cualquier duración que un formato no tenga ya requiere editar la escena, por lo que "solo editar data" no se aplica. Usa esta regla:

Multiplica canvas.durationSec y cada tiempo en la escena por newDuration / oldDuration. Luego fíjate en la cola: escalar estira la pausa final por el mismo factor, así que cuanto mayor sea el factor, peor será el final.

Ejemplo práctico — stat-counter-rise de 10s a 30s, factor 3.0. Estos son los números que realmente se enviaron, no una ilustración:

campo10s×3enviado
canvas.durationSec103030
contador endSec6.419.221.0
barra endSec6.419.221.0
unit at6.419.221.0
context at / in.at6.519.521.6
source at——25.5 — un tiempo que no existía
posterSec8.425.227.5

Escalar solo dejó el último tiempo en 19.5s dentro de un clip de 30 segundos: 10.5 segundos de una tarjeta congelada. Empujarlo a 21.6 tampoco fue suficiente. Más allá de un factor de aproximadamente dos, la aritmética deja de ser la respuesta — el clip necesita un tiempo que no tenía. Aquí eso es una línea de cita a los 25.5s, que el formato había estado reclamando en sus sources desde el principio y nunca había puesto en pantalla. Pausa final: 4 segundos.

Una tarjeta final mantenida más de unos 3 segundos es silencio muerto, y el silencio muerto al final es lo que mata la tasa de finalización. kw frame0 falla rotundamente a los 5 segundos de una pantalla inmóvil: 3 es el consejo artesanal, 5 es el límite estricto. Encontró tres formatos que ya se habían enviado con un final congelado de 6 segundos.

Restricciones y comprobaciones:

  • durationSec × fps debe ser un número entero de fotogramas.
  • Incrementa version en format.json cada vez que cambies la escena — reajustar el tiempo cuenta. Un andamio nuevo en 0.1.0 que luego reajustas se convierte en 0.2.0. El identificador de variante se deriva de version más un hash de la especificación, por lo que esto mantiene una medición futura atribuible.
  • npx kw check detecta tiempos más allá del final del clip, y npx kw frame0 detecta un primer fotograma que no pinta nada, además de cualquier tramo inmóvil de más de 5 segundos. Ninguno puede decirte que un clip es aburrido — para eso, abre out/<slug>/contact.png después de renderizar. Muestra seis fotogramas distribuidos uniformemente a lo largo del clip, por lo que las fichas casi idénticas son visibles de un vistazo.

Fuentes. kw new --from=<slug> reinicia deliberadamente el andamio a sampleContent: placeholder y elimina los sources del padre — las citas del padre respaldaban los números del padre, no los tuyos. Si pones un número real en pantalla, establece sampleContent: sourced y agrega una entrada sources: con una URL y la afirmación exacta que respalda.

Un número con fuente solo tiene fuente una vez que aterriza. Si animas un contador hacia una cifra citada, márcalo como claim: "final" y mantén la unidad y cualquier palabra como "exactamente" fuera de la pantalla hasta endSec — muestra "contando…" hasta entonces. Cada otro elemento visible durante el conteo necesita neutralWhileCounting: true. kw check rechaza el formato de lo contrario. Los contadores cuyos valores intermedios son honestamente verdaderos ("fotogramas hasta ahora") se marcan como claim: "running" y están exentos.

Lo que cuenta como fuente: algo que un lector pueda abrir y verificar el número en — un artículo primario o su registro permanente (DOI, PubMed), un organismo de normalización, una agencia estadística, un informe oficial. No una lista, no un blog que resuma una, y no tu propio recuerdo. Indica cualquier redondeo que hayas hecho para la pantalla en el campo claim. kw check verifica que una URL y una afirmación estén presentes; no puede verificar que sean verdaderas, así que esa parte depende de ti. Si no puedes encontrar una fuente, usa texto de relleno en su lugar — una estadística inventada es el mismo fracaso que una cifra de retención inventada.

Consulta references/spec.md para la referencia completa de format.json.

3. Ejecutar una prueba A/B

Dos formatos, mismo contenido, mismo día:

npx kw new test-a --from=cold-open-line
npx kw new test-b --from=redacted-reveal
# pon copia idéntica en ambos bloques de datos
npx kw render test-a && npx kw render test-b
npx kw variant test-a           # registra esta cadena contra la subida

El variantId en out/<slug>/variant.json es lo que hace atribuible el resultado. Regístralo contra el video publicado en el momento de la subida — también está escrito en el comentario de metadatos del MP4 si pierdes tus notas.

4. Leer las mediciones

# exporta CSVs de YouTube Studio / TikTok analytics a measure/inbox/
npx kw measure                  # ingiere + informa -> measure/report.md
npx kw measure apply            # escribe los resultados en formats/*/data.yml
npx kw gallery                  # re-renderiza + reconstruye la galería

Sin API, sin OAuth. Consulta references/measuring.md para los pasos exactos de exportación y el formato mapping.csv.

El determinismo es el contrato

El mismo format.json produce los mismos fotogramas, en cualquier máquina, para siempre. Eso es lo que permite que una curva de retención sea atribuida a un formato en lugar de a un render. Al editar el motor, nunca introduzcas Date.now(), Math.random(), requestAnimationFrame, transiciones CSS, o animaciones CSS en la composición. npx kw render <slug> --check-determinism re-busca fotogramas fuera de orden y falla si algún píxel se movió.

Dos mediciones que nunca deben mezclarse

data.yml tiene dos bloques, y responden preguntas diferentes:

BloquePregunta¿Clasifica la biblioteca?
format¿Cómo funciona esta estructura de escena?Sí
content_axis¿Cómo funciona este tema?No

Un solo video publicado lleva ambas etiquetas — es una cuenta regresiva y es sobre el cuerpo. Hacer un promedio de las dos produce un número que no responde ninguna pregunta, por lo que se almacenan por separado, se informan por separado y se muestran en colores diferentes en la galería.

Cuando informes un resultado, di cuál es:

  • "countdown-clock retuvo el 62% (n=6)" — una afirmación de formato.
  • "Los temas personales/del cuerpo retuvieron el 68% (n=9)" — una afirmación de eje.
  • "Las cuentas regresivas personales/del cuerpo retuvieron el 68%" — ninguna, a menos que también digas cuántos videos, en qué formatos, y que los dos están confundidos.

measure/report.md imprime una columna llevado por para cada eje. Si un eje fue solo llevado por un formato, ese resultado de eje y ese resultado de formato son los mismos videos con dos etiquetas; dilo en lugar de presentarlos como dos hallazgos independientes.

Los ejes de contenido se declaran en data/content-axes.yml y se adjuntan a un video mediante la columna opcional content_axis en measure/mapping.csv. Son los ejes propios del usuario, no los de la biblioteca — agrega cualquier distinción que realmente prueben.

variant_id: not-in-library marca un video publicado cuyo formato no está en este repositorio. Es solo una muestra de eje de contenido: nunca llega al tablero de formatos ni a ningún data.yml, y carried by lo nombra para que la brecha sea visible. Los resultados de ejes a nivel de repositorio viven en data/content-axis-results.yml, que es por lo que un eje puede ser medido mientras que cada formato sigue siendo n = 0. No especules sobre qué es dicho formato — la etiqueta deliberadamente no dice nada al respecto.

Reglas de honestidad

Estas no son preferencias de estilo. El repositorio no vale nada si se incumplen.

  1. n: 0 significa no probado. Nunca lo redondees a "prometedor" o "probable".
  2. Por debajo de n=5 es una dirección, no un resultado. Di cuál estás dando.
  3. Los formatos perdedores se quedan listados con sus números. No los elimines sigilosamente.
  4. Nunca escribas data.yml a mano. Solo kw measure apply lo escribe. Los números escritos de memoria son indistinguibles de los números que fueron inventados.
  5. Nunca combines un número de formato con un número de eje. Consulta la tabla de arriba.
  6. Cada número en pantalla necesita una fuente. Un clip que generes lleva o sampleContent: sourced con una URL, o placeholder con texto de relleno obvio. Inventar una estadística plausible para una demostración es el mismo fracaso que inventar una cifra de retención.
  7. Los datos semilla son el canal de un creador. Es un punto de partida, no una ley de la plataforma. Di de quién es la muestra cuando la distinción importe.

Archivos de referencia

  • references/spec.md — la referencia de elemento y animación de format.json
  • references/formats.md — la biblioteca de formatos, por familia, con hipótesis
  • references/measuring.md — exportación CSV, mapeo y el ciclo de informes
  • references/authoring.md — cómo escribir un nuevo formato que valga la pena medir

Skills relacionados