¿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
KWen cada comando, o comienza cada uno con uncd /ruta/absoluta/hacia/keepwatching &&explícito. No asumas que$KWsobrevive. npm run setuppuede 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… | Familia | Comienza con |
|---|---|---|
| un gran número | stat-counter | stat-counter-rise |
| un pago que vale la pena esperar | countdown | countdown-clock |
| un conjunto ordenado de elementos | ranking | ranking-suspense (finalización) o ranking-reveal (comparticiones) |
| dos opciones que difieren en un número | comparison | split-compare |
| el mismo sujeto en dos estados | reveal | before-after |
| una falsa creencia extendida | myth-fact | myth-vs-fact |
| una oración contundente | cold-open | cold-open-line |
| un proceso con etapas reales | progress | progress-bar |
| valores que realmente escalan | escalation | escalation-ladder |
| algo circular | reverse | loop-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.durationSecy cada tiempo en la escena pornewDuration / 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:
| campo | 10s | ×3 | enviado |
|---|---|---|---|
canvas.durationSec | 10 | 30 | 30 |
contador endSec | 6.4 | 19.2 | 21.0 |
barra endSec | 6.4 | 19.2 | 21.0 |
unit at | 6.4 | 19.2 | 21.0 |
context at / in.at | 6.5 | 19.5 | 21.6 |
source at | — | — | 25.5 — un tiempo que no existía |
posterSec | 8.4 | 25.2 | 27.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 × fpsdebe ser un número entero de fotogramas.- Incrementa
versionenformat.jsoncada vez que cambies la escena — reajustar el tiempo cuenta. Un andamio nuevo en0.1.0que luego reajustas se convierte en0.2.0. El identificador de variante se deriva deversionmás un hash de la especificación, por lo que esto mantiene una medición futura atribuible. npx kw checkdetecta tiempos más allá del final del clip, ynpx kw frame0detecta 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, abreout/<slug>/contact.pngdespué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:
| Bloque | Pregunta | ¿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-clockretuvo 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.
n: 0significa no probado. Nunca lo redondees a "prometedor" o "probable".- Por debajo de n=5 es una dirección, no un resultado. Di cuál estás dando.
- Los formatos perdedores se quedan listados con sus números. No los elimines sigilosamente.
- Nunca escribas
data.ymla mano. Solokw measure applylo escribe. Los números escritos de memoria son indistinguibles de los números que fueron inventados. - Nunca combines un número de formato con un número de eje. Consulta la tabla de arriba.
- Cada número en pantalla necesita una fuente. Un clip que generes lleva o
sampleContent: sourcedcon una URL, oplaceholdercon texto de relleno obvio. Inventar una estadística plausible para una demostración es el mismo fracaso que inventar una cifra de retención. - 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 deformat.jsonreferences/formats.md— la biblioteca de formatos, por familia, con hipótesisreferences/measuring.md— exportación CSV, mapeo y el ciclo de informesreferences/authoring.md— cómo escribir un nuevo formato que valga la pena medir