\n\n```\n\n- `cx` es el centro horizontal, `yFeet` la línea del suelo.\n- `mood`: `idle` | `work` | `ok` | `err`. La nariz y el pecho cambian con el estado.\n- Parpadeo y balanceo de cola salen del tiempo, no hay que gestionarlos fuera.\n\n## Las dos reglas que rompen el sprite si se ignoran\n\n**Escala entera siempre.** 2, 3, 6, 10. Nunca 1.5 ni 2.7. Con factor fraccionario los píxeles\nse reparten mal entre filas y la figura tiembla al moverse. Esta es la razón por la que las\npiezas verticales se diseñan a 360x640: por 3 da 1080x1920 exacto.\n\n**`imageSmoothingEnabled = false` antes de dibujar.** Sin eso el navegador interpola al escalar\ny el contorno se convierte en una mancha.\n\n## Detalle de implementación que merece la pena copiar\n\nEl contorno no está dibujado: se calcula. La figura se define solo con color y después un paso\nautomático pinta de tinta cualquier celda vacía que toque una celda llena. Cambiar la paleta\nentera es cambiar un hex, y no hay que redibujar ninguna línea.\n"}, {"id": "metricas-que-suman-cien", "title": "Métricas que suman cien: cuándo un 97,6% comunica peor que no decir nada", "type": "build-log", "status": "captured", "date": "2026-08-05", "tags": ["marketing", "honestidad", "metricas", "liveknowledge", "diseno-de-producto"], "source": "conversación", "thesis": "Un 97,6% se lee comercialmente como falla, no como casi perfecto. La salida no es redondear: es subir a titular el dato que si es entero y sustituir mediciones por garantias de diseno.", "moves": ["Titular con el 100% real: todas las citas apuntan a una pagina real", "Bajar la metrica con resto a linea secundaria o quitarla", "Sustituir porcentajes por garantias categoricas del sistema"], "condition": "Una garantia tiene que ser cierta por construccion; si el sistema puede fallarla en un caso raro, es una estadistica disfrazada y es peor que el porcentaje original.", "transferable_lesson": "Omitir no es mentir; inflar si. Si un numero medido no comunica, no lo toques: busca que afirmacion entera puedes sostener, que casi siempre es una propiedad del diseno.", "assets": ["flujo-liveknowledge.html"], "target_platforms": ["linkedin", "blog"], "body": "# Métricas que suman cien: cuándo un 97,6% comunica peor que no decir nada\n\n**Tipo:** build-log · **Estado:** captured · **Fecha:** 2026-08-05\n\nMonté una pieza para LiveKnowledge con dos métricas de evals: citas válidas 1.000 y fidelidad al\ndocumento 0.976. Técnicamente son dos buenos números. Comercialmente, el segundo hunde al primero.\n\nLa objeción del cliente fue exacta: **un 97,6% se lee como \"falla\", no como \"casi perfecto\".**\nQuien decide una compra no interpreta un decimal, interpreta si hay resto. Y en cuanto hay resto,\nla pregunta siguiente es qué pasa en ese 2,4%.\n\n## La salida mala\n\nRedondear a 100. Es rápido, se ve bien y destruye lo único que hacía valiosa la pieza, que era\nenseñar una medición real. Además es especialmente estúpido en un producto cuyo argumento es que\ncada afirmación se puede comprobar contra su fuente.\n\n## La salida buena\n\nDos movimientos, ninguno de ellos mentir.\n\n**Primero: subir a titular el número que sí es entero.** El 100% de las citas apuntan a una\npágina real del documento. Ese dato existe, está medido y además es el que sostiene la promesa\ndel producto. La fidelidad no desaparece por mala, desaparece porque no es el titular.\n\n**Segundo, y este es el que importa: cambiar mediciones por garantías.** En vez de tres\nporcentajes, tres afirmaciones categóricas sobre cómo está construido el sistema:\n\n- Ninguna respuesta sale sin cita.\n- Cada cita se abre y se comprueba.\n- El documento completo sigue accesible.\n\nUn porcentaje siempre invita a preguntar por el resto. Una garantía de diseño no tiene resto que\npreguntar. Y comunica más fuerte: no dice \"acertamos casi siempre\", dice \"esto no puede pasar\".\n\n## La condición\n\nUna garantía es una promesa de arquitectura, así que tiene que ser cierta por construcción. Si el\nsistema puede devolver una respuesta sin cita aunque sea en un caso raro, esa línea no es una\ngarantía: es una estadística disfrazada, y es peor que el 97,6% original porque no admite matices.\n\n## La regla que me llevo\n\n**Omitir no es mentir; inflar sí.** Cuando un número medido no comunica, la opción no es tocarlo:\nes no publicarlo y buscar qué afirmación entera puedes sostener en su lugar. Casi siempre existe,\ny casi siempre es una propiedad del diseño y no una media.\n"}, {"id": "oracle-track-record", "title": "El presente abre las cartas: visualizar un track record que no se puede editar", "type": "build-log", "status": "captured", "date": "2026-08-05", "tags": ["oracle", "pixel-art", "canvas", "credibilidad", "verificacion", "kit"], "source": "conversación", "thesis": "Un historial de predicciones solo significa algo si la predicción queda sellada antes y se abre sola cuando toca. La mecánica visual es el argumento.", "mechanic": "Cinco carriles de cartas a velocidad constante cruzando una linea fija HOY: selladas a la derecha, abiertas verde/rojo a la izquierda, giro sobre el eje con scaleX=|cos(k·pi)| en el cruce.", "design_decisions": ["El giro es la metafora: hay un instante de canto ilegible que es el paso de no se sabe a ya se sabe", "La linea del presente va detras de las cartas; delante cortaba el texto en la revelacion", "Fuera los rotulos aforisticos: leyenda del codigo visual y dos lineas tecnicas"], "honesty_constraint": "Temas como categorias, no predicciones concretas. El marcador dice EN PANTALLA y cuenta solo lo animado. Para publicar hacen falta las cifras reales del log.", "transferable_lesson": "Para demostrar rigor no lo cuentes: enseña el mecanismo que te impide hacer trampas.", "assets": ["flujo-oracle.html", "kit-sprite.js"], "target_platforms": ["linkedin", "blog"], "body": "# El presente abre las cartas: visualizar un track record que no se puede editar\n\n**Tipo:** build-log · **Estado:** captured · **Fecha:** 2026-08-05\n\nCualquiera puede contar sus aciertos después. La única forma de que un historial de predicciones\nsignifique algo es que la predicción quede sellada **antes** y se abra sola cuando toque.\n\nMonté esa idea como pieza animada para el Geopolitical Oracle, y la mecánica visual es el\nargumento entero.\n\n## Cómo funciona la pieza\n\nCinco carriles de cartas que se desplazan de derecha a izquierda a velocidad constante. En el\ncentro, una línea vertical fija: **HOY**.\n\n- A la derecha del presente, las cartas están selladas: tema, fecha y candado. El resultado no\n se ve porque todavía no existe.\n- Al cruzar la línea, la carta gira sobre su eje vertical y se abre sola: verde si acertó, roja\n si falló.\n- A la izquierda solo hay resultados. No hay forma de volver a cerrarlas.\n\nDebajo, un marcador que cuenta **las dos cosas**. Un track record sin fallos no es un track\nrecord: es marketing.\n\n## Tres decisiones de diseño\n\n**El giro no es decoración, es la metáfora.** La carta pasa por un momento en el que está de\ncanto y no se lee nada. Ese instante es el paso de \"no se sabe\" a \"ya se sabe\", y dura lo que\ntarda en cruzar la línea. Se resuelve con `scaleX = |cos(k·π)|`, sin librerías.\n\n**La línea del presente va detrás de las cartas.** Probé al revés y el trazo cortaba el texto\njusto en el momento de la revelación. El tiempo pasa por encima del presente, no al contrario.\n\n**Fuera las frases.** La primera versión llevaba rótulos tipo \"el presente la abre, no hay dónde\nesconderse\". Sonaban a cita motivacional y contradecían mi propia regla editorial. Los sustituí\npor leyenda del código visual y dos líneas técnicas: qué se sella, cuándo se abre. La pieza no\ntiene que sentenciar nada; el mecanismo ya lo dice.\n\n## Lo que no me permití\n\nLos temas de las cartas son categorías —tipos del BCE, Brent, control de exportaciones— y no\npredicciones concretas, porque inventar llamadas que no hice sería exactamente lo contrario de\nlo que la pieza defiende. El marcador dice \"en pantalla\" y cuenta solo lo que se ve animado,\nno un histórico real.\n\nPara publicarla, esas cifras tienen que salir del log del Oracle. Si no salen, la pieza se\nqueda como demostración de mecánica y se dice que lo es.\n\n## Lo transferible\n\nCuando quieras demostrar rigor, no lo cuentes: **enseña el mecanismo que te impide hacer\ntrampas.** Un candado con fecha comunica más credibilidad que cualquier adjetivo, y funciona\nigual para predicciones, para métricas de producto o para una definition of done.\n"}, {"id": "puente-telegram-cowork-agent-sdk", "title": "Dispatch no me servía, así que construí un puente Telegram ⇄ Agent SDK", "type": "build-log", "status": "captured", "date": "2026-06-04", "tags": ["claude-agent-sdk", "cowork", "dispatch", "telegram", "bot", "mac-mini", "agentes", "seguridad"], "source": "conversación", "problem": "Hablar con un agente Claude desde el móvil con acceso a archivos/repo/bash en el Mac Mini, conversación fluida y push real. Dispatch (nativo) existía pero funcionaba mal.", "key_finding": "Cowork no expone superficie programática; Dispatch es el único puente oficial y es propietario. La primitiva que da lo mismo sin la caja cerrada es Claude Code headless / Agent SDK como motor.", "architecture": "Telegram long-poll -> bot.py en Mac Mini -> ClaudeSDKClient persistente con cwd acotado, sesión viva (contexto entre turnos) y PreToolUse hook sobre Bash.", "security_layers": ["Allowlist de user_id (ignora a cualquiera que no seas tú)", "PreToolUse hook que bloquea comandos destructivos en Bash", "cwd acotado al repo, no a ~"], "caveats": ["Desde 15-jun-2026 Agent SDK y claude -p en suscripción consumen un crédito separado; uso diario puede agotarlo (fallback: API con key propia).", "Bug reportado (sin confirmar) de pérdida de thinking history en sesiones largas -> /reset frecuente.", "Comprobar message.subtype, no is_error.", "El Mac debe estar despierto (caffeinate)."], "status_detail": "MVP escrito, no ejecutado. Prueba de humo: 'lista los .md en artifacts/captured'.", "next_v2": "Hook que manda botón a Telegram para aprobar acciones externas (push/email/publish) = gate L1 conversacional.", "transferable_lesson": "Cuando una función nativa no llega, la pregunta es qué primitiva por debajo da lo mismo sin la caja cerrada, no cómo forzar la caja.", "format": "build-log", "estimated_read_min": 10, "body": "---\nid: puente-telegram-cowork-agent-sdk\ntitle: \"Dispatch no me servía, así que construí un puente Telegram ⇄ Agent SDK\"\ntype: build-log\nstatus: captured\ndate: 2026-06-04\ntags: [claude-agent-sdk, cowork, dispatch, telegram, bot, mac-mini, agentes, seguridad]\nsource: conversación\n---\n\n# Dispatch no me servía, así que construí un puente Telegram ⇄ Agent SDK\n\n**Objetivo:** hablar con un agente Claude desde el móvil, con acceso a mis archivos/repo/bash en el Mac Mini, conversación fluida con estado y push real. La función nativa (Cowork Dispatch) existía pero la experiencia era mala. Build-log de la alternativa.\n\n## El hallazgo que cambió el diseño\n\nNo se puede \"conectar Telegram a Cowork\". **Cowork no expone superficie programática** — Dispatch es el único puente oficial y es propietario (empareja por QR, va por los servidores de Anthropic). No hay endpoint para inyectar mensajes en una sesión Cowork desde fuera.\n\nLo que sí da lo que quería: **Claude Code en modo headless / Agent SDK como motor**. Funcionalmente es \"Claude gobernado por un script\", con las mismas tools, agent loop y gestión de contexto. No es Cowork la app; es el mismo cerebro por otra puerta.\n\n## Arquitectura (single-user)\n\n```\nTelegram móvil ⇄ long-poll ⇄ bot.py (Mac Mini) ⇄ ClaudeSDKClient persistente\n ├─ cwd acotado al repo\n ├─ sesión viva = contexto entre turnos\n └─ PreToolUse hook = gobierna Bash\n```\n\n- **Fluidez:** un único `ClaudeSDKClient` vivo. No recreo sesión por turno; el contexto se acumula solo. El SDK persiste la conversación a disco (`~/.claude/projects/`) y permite resume/fork.\n- **Push:** nativo de Telegram. Es justo el agujero de Dispatch (no manda notificaciones, tienes que abrir la app a ver si terminó).\n- **Seguridad (el punto crítico):** un bot con Bash en tu Mac es un vector de ataque. Tres capas:\n 1. Allowlist de `user_id` — ignora a cualquiera que no seas tú. No negociable.\n 2. `PreToolUse` hook sobre Bash — bloquea destructivos (`rm -rf`, `curl|sh`…).\n 3. `cwd` acotado al repo, no a `~`.\n\n## Decisiones de arquitectura\n\n| Decisión | Elección | Por qué |\n|---|---|---|\n| Motor | Agent SDK **Python** | Encaja con el stack (Python en todo) |\n| Bot | **Nuevo dedicado**, no el del gate | No chocar con el pipeline de aprobación L1 |\n| Sesión | Cliente persistente global | Single-user → contexto sin resume manual |\n| Respuesta | Bloques completos + typing action | `editMessageText` típo typewriter choca con rate-limits de Telegram; v2 |\n\n## Dos avisos que condicionan la viabilidad\n\n1. **Coste:** desde el **15-jun-2026**, Agent SDK y `claude -p` en planes de suscripción consumen de un **crédito Agent SDK separado** de los límites interactivos. Uso diario puede agotarlo → fallback: API directa con key propia (pago por token).\n2. **Drift en sesiones largas:** reportado (sin confirmar oficialmente) un bug de pérdida de *thinking history* por turno. Mitigación: `/reset` frecuente, no dejar una sola sesión viva días.\n\n## Gotchas del SDK anotados\n\n- Comprobar `message.subtype`, **no** `is_error` — una terminación por max-turns puede colar como éxito.\n- `AgentDefinition` usa camelCase (`disallowedTools`); `ClaudeAgentOptions` usa snake_case (`disallowed_tools`).\n- El Mac debe estar despierto: `caffeinate -dimsu`.\n\n## Estado\n\nMVP escrito, **no ejecutado aún** (requiere Mac Mini + bot token + Claude Code logueado). Prueba de humo definida: \"lista los .md en artifacts/captured\" → si responde con el contenido real del repo, el puente está vivo.\n\n## Siguiente (v2)\n\nHook que, en vez de bloquear/permitir, manda un **botón a Telegram** para aprobar acciones externas (git push, email, publish). Es el gate L1 del pipeline, pero conversacional — cierra el círculo.\n\n## Lección transferible\n\nCuando una función nativa \"no llega\", la pregunta no es *\"¿cómo la fuerzo?\"* sino *\"¿qué primitiva por debajo me da lo mismo sin la caja cerrada?\"*. Dispatch era la caja cerrada; el Agent SDK era la primitiva.\n"}, {"id": "smoothness-pixel-canvas", "title": "Suavidad en pixel art: no es más frames, es coreografía", "type": "build-log", "status": "captured", "date": "2026-08-04", "tags": ["pixel-art", "canvas", "animacion", "easing", "requestanimationframe", "debugging", "playwright"], "source": "conversación", "thesis": "La suavidad en pixel art no viene de la resolución ni del framerate: viene de repartir el tiempo con curvas, escalonar los elementos y no cambiar de estado de golpe.", "techniques": ["Cada beat con duración propia y easeInOutQuint / easeOutQuint", "Stagger por resta en el argumento del easing (k = ease(ciclo*a - i*d))", "Movimiento circular a velocidad angular constante: suavidad barata", "Transición de escena por barrido sólido de 1s con borde lima, sin fundidos"], "bug_teardown": "Una excepción dentro del callback de requestAnimationFrame salta la línea que reprograma el siguiente frame y mata el bucle en silencio; el canvas queda congelado a medio dibujar. Causa raíz secundaria: delta de tiempo negativo hacía retroceder el reloj y el índice a -1.", "transferable_lesson": "En cualquier bucle que se autoreprograma, el reagendado debe estar en el camino que no puede fallar. Si el siguiente paso depende de que el actual termine bien, un fallo no degrada el sistema: lo apaga.", "verification": "Cuatro rondas Playwright 1280x720 y 390x844. Fallos detectados solo mirando: solapes de etiquetas, checks que no aparecían por k>=1 con easing convergente a 0.9997, aire muerto en columnas.", "target_platforms": ["linkedin", "blog"], "body": "# Suavidad en pixel art: no es más frames, es coreografía\n\n**Tipo:** build-log · **Estado:** captured · **Fecha:** 2026-08-04\n\nEn pixel art no puedes mover medio píxel. Cada posición se redondea a entero, así que la\ntentación es pensar que la suavidad está limitada por la rejilla. No lo está: está limitada por\ncómo repartes el tiempo.\n\nConstruí un segundo volumen de escenas animadas —auditoría de citas, radar de intel diaria,\npipeline de vídeo, evaluación en dos capas— con el objetivo explícito de que se movieran mejor\nque las anteriores. Esto es lo que cambió.\n\n## 1. Nada cambia de estado de golpe\n\nEn la primera versión, las etapas de un flujo saltaban: un panel pasaba de \"pendiente\" a\n\"hecho\" en un frame. Se lee como un parpadeo, no como un proceso.\n\nAhora cada beat tiene duración propia y una curva. Una tarjeta que entra al escáner tarda 0,9 s\ncon `easeInOutQuint`; el barrido tarda 0,8 s; el veredicto se revela como un rectángulo cuyo\nancho crece con `easeOutQuint`; la salida hacia su bandeja tarda otros 0,8 s. La misma\ninformación, cuatro veces más legible.\n\n## 2. Escalonar en vez de sincronizar\n\nCinco barras que se llenan a la vez son un bloque. Cinco barras con 0,42 s de desfase entre\nellas son una cascada. El coste es una resta en el argumento del easing:\n\n k = easeOut( clamp( ciclo * 4.2 - i * 0.42 ) )\n\nEse `- i * 0.42` es toda la diferencia entre \"una UI\" y \"algo agradable de mirar\".\n\n## 3. El movimiento circular es gratis\n\nEl radar del Oracle es el elemento más suave de las cuatro escenas y es el que menos código\ntiene: puntos orbitando a velocidad angular constante y una línea de barrido girando. El ojo\npercibe rotación uniforme como suavidad incluso cuando cada píxel salta. La estela del barrido\nson diez líneas con el mismo ángulo decreciente y un aclarado progresivo —sin glow, sin blur,\nsin transparencias.\n\n## 4. Las transiciones también son contenido\n\nAl cambiar de escena, un panel sólido barre la pantalla de izquierda a derecha con un borde\nlima de 2 px, cubre, cambia la escena por debajo y descubre. Un segundo entero. Es más lento que\nun corte y por eso funciona: el corte te obliga a reorientarte, el barrido te lleva.\n\n## 5. El bug que congela el lienzo\n\nA mitad de sesión, una escena se quedaba petrificada sin error visible en pantalla. La causa:\nuna excepción dentro del callback de `requestAnimationFrame`. Como la llamada que reprograma el\nsiguiente frame está al final de la función, cualquier excepción anterior la salta y el bucle\nmuere en silencio. El canvas se queda con el último frame a medio dibujar, que es exactamente lo\nque yo estaba viendo: la cola de tarjetas pintada, el escáner vacío.\n\nDos defensas, ambas de una línea:\n\n- `try { escena() } catch(e) {}` con el `requestAnimationFrame` fuera del try.\n- Clamp del delta de tiempo a `[0, 0.05]`. Un delta negativo hacía retroceder el reloj y el\n índice del array se iba a -1.\n\nLa lección transferible no es sobre canvas: **en cualquier bucle que se autoreprograma, el\nreagendado tiene que estar en el camino que no puede fallar.** Si el \"siguiente paso\" depende\nde que el paso actual termine bien, un fallo no degrada el sistema: lo apaga.\n\n## 6. Verificar mirando, no razonando\n\nCuatro rondas de Playwright a 1280 y 390. Los fallos encontrados: una etiqueta de reloj pisando\nun rótulo, un marcador de puntuación superpuesto a dos líneas de texto, tres checks verdes que\nno aparecían porque `k >= 1` nunca se cumple cuando el easing converge a 0,9997, y una columna\nde cinco filas con 45 px de aire muerto debajo.\n\nNinguno de esos cuatro se detecta leyendo el código.\n"}, {"id": "stack-loop-pixel", "title": "Dibuja tu stack como un sistema, no como un logo wall", "type": "build-log", "status": "captured", "date": "2026-08-04", "tags": ["pixel-art", "canvas", "sistemas", "agentos", "dod", "playwright", "marca-personal"], "source": "conversación", "thesis": "El logo wall enseña qué has instalado; el bucle enseña cómo trabajas. Un flujo con gates y checks de máquina dice más que una rejilla de iconos.", "artifact": "stack-loop.html — canvas 480x224, cero dependencias, loop infinito con 7 estaciones (KIT, CLAUDE, PIPELINE, AGENTOS, PLAYWRIGHT, DOD GATE, SHIP).", "technical_decisions": ["Canvas lógico fijo + escala entera con imageSmoothingEnabled=false para preservar el pixel", "Fuente bitmap 3x5 propia: nada de fillText a 5px", "Auto-outline y specular calculados sobre la rejilla de color, no dibujados a mano"], "editorial_line": "Una misión no se cierra con un juicio, se cierra con una comprobación.", "verification": "Auditoría Playwright a 1280x720 y 390x844, tres rondas de fallos visuales corregidos (solape de título, barra sobre telemetría, pies de panel tapados).", "transferable_lesson": "Si tu diagrama no tiene un gate, no es un sistema: es una lista. Y si el bucle no se cierra, repites el patrón de terminar sin distribuir.", "target_platforms": ["linkedin", "blog"], "body": "# Dibuja tu stack como un sistema, no como un logo wall\n\n**Tipo:** build-log · **Estado:** captured · **Fecha:** 2026-08-04\n\n## La tesis\n\nEl \"logo wall\" —esa rejilla de iconos de herramientas que todos ponemos en la landing— no dice nada.\nEnseña qué has instalado, no cómo trabajas. Dos personas con el mismo logo wall pueden tener\nsistemas opuestos: una ejecuta, la otra colecciona pestañas.\n\nLo que sí dice algo es el **bucle**: qué entra, quién lo toca, qué lo bloquea y qué sale.\nAsí que dibujé el mío como una animación pixel de 480×224 px, sin dependencias, en canvas.\n\n## Qué representa la pieza\n\nSiete estaciones sobre una cinta transportadora, un paquete que las recorre y vuelve arriba\nconvertido en artefacto:\n\n1. **KIT** — el brief. Mi mascota, un zorro pixel verde.\n2. **CLAUDE** — la construcción.\n3. **PIPELINE** — el repo bus: `artifacts/captured/`, `missions/inbox/`.\n4. **AGENTOS** — el daemon en el Mac Mini que recoge misiones y las ejecuta.\n5. **PLAYWRIGHT** — auditoría visual real a 1280 y 390 antes de entregar nada.\n6. **DOD GATE** — la definition of done: al menos un check de máquina.\n7. **SHIP** — URL viva.\n\nEl paquete no desaparece al final: sube, cruza de vuelta etiquetado como BUILD LOG / TEARDOWN /\nSNIPPET / ANTI-THESIS y baja otra vez a KIT. El bucle es el mensaje: si lo que construyes no\nvuelve a alimentar lo que publicas, tienes una fábrica sin salida.\n\n## Tres decisiones técnicas que sostienen la pieza\n\n**1. Canvas lógico fijo + escala entera.**\nTodo se dibuja en una rejilla de 480×224 y luego se escala por un entero según el\n`devicePixelRatio`, con `imageSmoothingEnabled = false`. Es la única forma de que el pixel art\nsiga siendo pixel art en pantallas retina en lugar de convertirse en papilla interpolada.\n\n**2. Fuente bitmap propia de 3×5.**\nNada de `ctx.fillText`. Cualquier tipografía del sistema renderizada a 5 px y escalada se\ndescompone. Escribí los glifos A-Z, 0-9 y símbolos como cadenas de 15 bits. Es la misma regla que\naplico en producción visual: **el texto en una imagen se renderiza con control de píxel, nunca\nse delega a un generador que corrompe los glifos.**\n\n**3. Auto-outline y specular calculados, no dibujados.**\nCada personaje se define como una rejilla de color sin contorno. Después, un paso automático\npinta de tinta cualquier celda vacía adyacente a una llena, y aclara un 28 % el borde superior\nizquierdo de las zonas interiores. Resultado: siete personajes con contorno y luz coherentes sin\nhaber dibujado una sola línea de contorno a mano. Cambiar la paleta de un personaje es cambiar\nun hex.\n\n## La regla que no se negocia\n\nLa animación de la pantalla central rota entre tres escenas, y una de ellas es literalmente mi\nregla de física: sin gravedad, velocidad lineal constante, rebotes en espejo perfectos, objetos\nidénticos, variación solo por el ángulo inicial. No es decoración: es la especificación con la\nque reviso cualquier animación que entrego.\n\nLa otra escena son los machine checks llenándose uno a uno. Ese es el punto editorial de toda la\npieza: **una misión no se cierra con un juicio, se cierra con una comprobación.** El panel\nizquierdo muestra el budget real de mi `mission.yaml` —iteraciones, crédito, no-progress,\nwall clock— porque un sistema autónomo sin presupuesto no es autónomo, es un agujero.\n\n## Verificación antes de entregar\n\nLa pieza se auditó con Playwright a 1280×720 y 390×844, con capturas reales revisadas, antes de\ndarla por buena. Tres rondas: en la primera el título se solapaba con las etiquetas de los\nartefactos; en la segunda la barra de progreso pisaba una fila de la telemetría; en la tercera\nlos pies de foto de los paneles quedaban tapados por una banda blanca que yo mismo había puesto\npara arreglar otra cosa.\n\nNinguno de esos tres fallos se ve pensando. Se ven mirando el screenshot.\n\n## Lo transferible\n\n- Si tu diagrama de arquitectura no tiene un **gate**, no es un sistema: es una lista.\n- Si el bucle no se cierra —si lo construido no vuelve como material publicable— vas a repetir\n el patrón de terminar cosas y no distribuirlas.\n- Dibujar el flujo obliga a nombrarlo. Y al nombrar cada estación te das cuenta de cuáles son\n aspiracionales y cuáles funcionan de verdad.\n\nArchivo único, sin build, sin dependencias. Abre en cualquier navegador.\n"}], "review": [{"id": "2026-05-22T15-40-00-commoditizacion-construccion", "title": "Cuanto más fácil es construir, menos vale construir", "type": "anti-thesis", "status": "review", "created_at": "2026-05-22T15:40:00Z", "target_platforms": ["linkedin"], "tags": ["IA", "mercado-laboral", "consultoría", "posicionamiento", "builder"], "source": {"origin": "claude.ai", "session_summary": "Sesión sobre estrategia de empleo del tiempo y posicionamiento. Discusión sobre commoditización de la capacidad de construir con IA y revalorización del juicio, distribución y medición."}, "notes": "Posible acompañamiento con diagrama de dos curvas cruzándose: precio de construcción bajando vs precio de decisión subiendo. Alternativa: publicar sin imagen (refuerza posicionamiento de builder serio, no creator de contenido). Decisión final pendiente.", "body": "# Cuanto más fácil es construir, menos vale construir\n\nCuanto más fácil es construir, menos vale construir.\n\nEsto suena raro porque el discurso dominante dice lo contrario: la IA democratiza el acceso, cualquiera puede crear, estamos viviendo la edad de oro del builder.\n\nLas dos cosas son ciertas. Y por eso es una paradoja.\n\nCuando una capacidad se vuelve abundante, su precio cae. Es economía básica. La capacidad de construir software con IA está pasando de ser un cuello de botella escaso (años de experiencia técnica) a un commodity barato (suscripción mensual a Claude o Cursor). Lo que antes requería un equipo de 5 personas tres meses, hoy lo hace una persona competente en un fin de semana.\n\nEl resultado obvio: más cosas se construyen.\n\nEl resultado menos obvio: cada cosa construida vale menos.\n\nY aquí viene lo importante. Si la hora de construir va a cero, ¿qué sube de precio? Sube todo lo que la IA no comoditiza:\n\n- Saber qué construir (juicio, criterio, lectura de mercado)\n- Saber para quién (distribución, audiencia, relaciones)\n- Saber si funcionó (medición, iteración, contexto de negocio)\n- Saber por qué importa (tesis defendible, posicionamiento)\n\nNinguna de esas cuatro cosas la resuelve un modelo más grande. Todas requieren contexto humano acumulado, exposición al mercado real, y la capacidad de tomar decisiones bajo incertidumbre con información incompleta.\n\nEl builder que sobrevive a esta transición no es el que construye más rápido. Es el que deja de competir en la capa de construcción y empieza a competir en la capa de decisión.\n"}], "ready": [{"id": "2026-06-21-where-value-went-ch1-en", "title": "Where the Value Went 1/4: AI didn't level the field. It moved it.", "type": "anti-thesis", "status": "ready", "lang": "en", "human_score": 9.2, "series": {"id": "where-the-value-went", "chapter": 1, "of": 4, "refrain": "Value doesn't vanish. It relocates.", "footer": "execution → judgment"}, "target_platforms": ["linkedin"], "visual_system": "S3", "source_artifact": "artifacts/captured/2026-05-22T16-40-00-democratizacion-ejecucion-inteligencia.md", "created_at": "2026-06-21", "notes": "v3: de-ai-ify pass. Removed stacked negation-inversion (kept refrain as sole inversion), arrow-notation cross-ref, varied 'uncomfortable-truth' framing.", "schedule": {"platform": "linkedin", "account_id": "6a1b18bc2b2567671a742a02", "scheduled_for": "2026-06-23T06:30:00Z", "format": "carousel-pdf", "asset": "pb/where-value-went/ch1-carousel", "ig_variant": "animated cover as slide-1", "state": "AWAITING_PUBLICA"}, "asset_status": "carousel built (4 slides) + motion loop", "body": "---\nid: 2026-06-21-where-value-went-ch1-en\ntitle: \"Where the Value Went 1/4: AI didn't level the field. It moved it.\"\ntype: anti-thesis\nstatus: ready\nlang: en\nhuman_score: 9.2\nseries: { id: where-the-value-went, chapter: 1, of: 4, footer: \"execution → judgment\" }\nsource_artifact: artifacts/captured/2026-05-22T16-40-00-democratizacion-ejecucion-inteligencia.md\n---\n\n# AI didn't level the field. It moved it.\n\nEveryone repeats the same line: AI democratized execution. Writing code, drafting a proposal, shipping an MVP used to be a scarce bottleneck. Now anyone with a monthly subscription clears it. True, and only half the sentence.\n\nThe other half: when everyone produces output at the same cost, what separates good from bad stops being technical skill. It becomes everything technical skill never covered. Judgment, knowing which problem is worth solving instead of which one looks promising. Distribution, owning the channel that puts your work in front of the right person. Context, the kind you only earn after years operating in a real market. Measurement, closing the loop between what you ship and what happens next.\n\nNo bigger model hands you any of those four. Same prompt, two people, two completely different results. The difference is what each one brought to the conversation.\n\nSo when execution got cheap, the value didn't disappear. It moved into judgment.\n\nValue doesn't vanish. It relocates.\n\nChapter 1 of 4: Where the Value Went. Next, the value moves again, out of the model and into the system. Most builders own neither.\n\nWhere did your edge go when execution became free?\n"}, {"id": "2026-06-21-where-value-went-ch2-en", "title": "Where the Value Went 2/4: You rent the model. You own the system.", "type": "anti-thesis", "status": "ready", "lang": "en", "human_score": 9.2, "series": {"id": "where-the-value-went", "chapter": 2, "of": 4, "refrain": "Value doesn't vanish. It relocates.", "footer": "model → system"}, "target_platforms": ["linkedin"], "visual_system": "S3", "source_artifact": "artifacts/captured/2026-05-22T16-52-00-modelo-andamio-sistema-edificio.md", "created_at": "2026-06-21", "notes": "v3: de-ai-ify pass. Removed stacked negation-inversion (kept refrain as sole inversion), arrow-notation cross-ref, varied 'uncomfortable-truth' framing.", "schedule": {"platform": "linkedin", "account_id": "6a1b18bc2b2567671a742a02", "scheduled_for": "2026-06-25T06:30:00Z", "format": "carousel-pdf", "asset": "pb/where-value-went/ch2-carousel", "ig_variant": "animated cover as slide-1", "state": "AWAITING_PUBLICA"}, "asset_status": "copy ready · carousel pending (template locked on ch1)", "body": "---\nid: 2026-06-21-where-value-went-ch2-en\ntitle: \"Where the Value Went 2/4: You rent the model. You own the system.\"\ntype: anti-thesis\nstatus: ready\nlang: en\nhuman_score: 9.2\nseries: { id: where-the-value-went, chapter: 2, of: 4, footer: \"model → system\" }\nsource_artifact: artifacts/captured/2026-05-22T16-52-00-modelo-andamio-sistema-edificio.md\n---\n\n# You rent the model. You own the system.\n\nThe model isn't your moat. You rent it. Your moat is everything you'd keep if it vanished tomorrow.\n\nNinety-five percent of my system's calls run through one model I don't own and can't control. That should make me nervous. It doesn't, because the model was never the point.\n\nThe model is scaffolding. You use it to put the building up, then you take it down. The building is the system: your data, your workflows, your client relationships, the knowledge you encoded. That is what stays standing once the scaffolding comes off.\n\nSo use the model hard right now, precisely because it's disposable. Take all the build speed it gives you, and architect so you could swap it tomorrow and nothing falls. This isn't about using less AI. It's about owning what sits on top, so the day the price or the terms or the model change, your building still stands.\n\nLast chapter, the value left execution. This week it moved again, out of the model. And the next cut goes deeper: most of the system is commodity too.\n\nValue doesn't vanish. It relocates.\n\nChapter 2 of 4: Where the Value Went. Next, even the system is mostly clonable. The moat is smaller than you think.\n\nIf your model vanished tomorrow, what would you still own?\n"}, {"id": "2026-06-21-where-value-went-ch3-en", "title": "Where the Value Went 3/4: The skeleton is commodity. The moat is two slots.", "type": "anti-thesis", "status": "ready", "lang": "en", "human_score": 9.2, "series": {"id": "where-the-value-went", "chapter": 3, "of": 4, "refrain": "Value doesn't vanish. It relocates.", "footer": "skeleton → two slots"}, "target_platforms": ["linkedin"], "visual_system": "S3", "source_artifact": "artifacts/captured/2026-05-30T02-38-59-self-installing-agent-scaffold.md", "created_at": "2026-06-21", "notes": "v3: de-ai-ify pass. Removed stacked negation-inversion (kept refrain as sole inversion), arrow-notation cross-ref, varied 'uncomfortable-truth' framing.", "schedule": {"platform": "linkedin", "account_id": "6a1b18bc2b2567671a742a02", "scheduled_for": "2026-06-30T06:30:00Z", "format": "carousel-pdf", "asset": "pb/where-value-went/ch3-carousel", "ig_variant": "animated cover as slide-1", "state": "AWAITING_PUBLICA"}, "asset_status": "copy ready · carousel pending (template locked on ch1)", "body": "---\nid: 2026-06-21-where-value-went-ch3-en\ntitle: \"Where the Value Went 3/4: The skeleton is commodity. The moat is two slots.\"\ntype: anti-thesis\nstatus: ready\nlang: en\nhuman_score: 9.2\nseries: { id: where-the-value-went, chapter: 3, of: 4, footer: \"skeleton → two slots\" }\nsource_artifact: artifacts/captured/2026-05-30T02-38-59-self-installing-agent-scaffold.md\n---\n\n# The skeleton is commodity. The moat is two slots.\n\nI built the same agent twice. 90% was identical. The 10% that wasn't is the entire product.\n\nI built a self-installing agent two ways, once in one runtime, once in another. Put them side by side and the lesson is hard to miss. The skeleton is the same every time: the wizard, the config, the scheduler, the read-propose-apply adapters. All of it clones in a weekend. What you reuse is glue, and glue is commodity.\n\nThe product lives in two slots. The domain playbook, the proprietary knowledge. And the scorer, the judgment that decides what good looks like. Everything else is packaging. If the playbook turns out to be public knowledge, you don't have a product.\n\nThe catch is what the innovation actually was. Auditing a task and scheduling it already existed. What was new was activation packaging, and packaging is clonable by anyone who reads your repo. The two slots are the only part they can't.\n\nI told you to build the system. Now I'm telling you most of the system is commodity too. So where does the value end up? It depends on the domain.\n\nValue doesn't vanish. It relocates.\n\nChapter 3 of 4: Where the Value Went. Final chapter: this stops being true everywhere. The frontier is a map, not a date.\n\nWhat in your system would survive someone cloning it?\n"}, {"id": "2026-06-21-where-value-went-ch4-en", "title": "Where the Value Went 4/4: Don't ask when. Ask where.", "type": "anti-thesis", "status": "ready", "lang": "en", "human_score": 9.2, "series": {"id": "where-the-value-went", "chapter": 4, "of": 4, "refrain": "Value doesn't vanish. It relocates.", "footer": "everywhere → open domains"}, "target_platforms": ["linkedin"], "visual_system": "S3", "source_artifact": "artifacts/captured/alphazero-frontera-dominio.md", "created_at": "2026-06-21", "notes": "v3: de-ai-ify pass. Removed stacked negation-inversion (kept refrain as sole inversion), arrow-notation cross-ref, varied 'uncomfortable-truth' framing.", "schedule": {"platform": "linkedin", "account_id": "6a1b18bc2b2567671a742a02", "scheduled_for": "2026-07-02T06:30:00Z", "format": "carousel-pdf", "asset": "pb/where-value-went/ch4-carousel", "ig_variant": "animated cover as slide-1", "state": "AWAITING_PUBLICA"}, "asset_status": "copy ready · carousel pending (template locked on ch1)", "body": "---\nid: 2026-06-21-where-value-went-ch4-en\ntitle: \"Where the Value Went 4/4: Don't ask when. Ask where.\"\ntype: anti-thesis\nstatus: ready\nlang: en\nhuman_score: 9.2\nseries: { id: where-the-value-went, chapter: 4, of: 4, footer: \"everywhere → open domains\" }\nsource_artifact: artifacts/captured/alphazero-frontera-dominio.md\n---\n\n# Don't ask when. Ask where.\n\nIn 2017 a machine played chess like an alien. It still can't tell you which problem is worth solving.\n\nAlphaZero never studied a single human game. Self-play from the rules alone, and it produced chess so strange that grandmasters called it alien, breaking principles taught for centuries. People remember that and assume the machines are coming for everything. They skip the condition.\n\nThat leap happened in a closed domain: fixed rules, finite states, a clean win-or-lose signal. Under those conditions, self-play has almost no ceiling. Chess, Go, poker, theorem proving, code with tests. All of them fell, for the same reason.\n\nOpen domains have no scoreboard. Which problem is worth solving. Which strategy fits this market. What's worth writing. There's no clean reward signal, so the machine has nothing to self-play against. That gap doesn't close with a bigger model. It's structural.\n\nSo stop asking when AI gets general. Ask where it already went superhuman, and where it never will.\n\nThree chapters got us here. The value left execution for judgment, the model for the system, the skeleton for two slots. And judgment lives where the rules stay open.\n\nValue doesn't vanish. It relocates.\n\nThat's where it went. Four chapters, one map.\n\nWhich side of the frontier is your work on?\n"}], "published": []};
const GENERATED_AT = "2026-10-15T11:45:46.490222Z";
const REPO_OWNER = "Wcoach24";
const REPO_NAME = "alvaro-pipeline";
const BRANCH = "main";
const STORAGE_KEY = "alvaro-pipeline-pat";
const STATUS_ORDER = ['captured', 'review', 'ready', 'published'];
const TYPE_LIST = ['anti-thesis', 'build-log', 'framework', 'snippet', 'teardown', 'dev-note', 'raw-idea'];
// In-memory data store, keyed by id. _status and _shas tracked for ops.
const store = new Map();
STATUS_ORDER.forEach(s => {
(INITIAL_DATA[s] || []).forEach(a => store.set(a.id, { ...a, _status: s, _shas: { md: null, json: null } }));
});
let sortDesc = true;
let activeTypes = new Set();
let searchTerm = '';
let currentViewId = null;
let pendingConfirm = null;
// ==========================================================================
// GITHUB CLIENT
// ==========================================================================
const gh = {
pat: localStorage.getItem(STORAGE_KEY) || null,
isAuthed() { return !!this.pat; },
async _req(method, path, body) {
if (!this.pat) throw new Error('PAT_REQUIRED');
const r = await fetch(`https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}${path}`, {
method,
headers: {
'Authorization': `Bearer ${this.pat}`,
'Accept': 'application/vnd.github+json',
'Content-Type': 'application/json'
},
body: body ? JSON.stringify(body) : undefined,
});
const data = r.status === 204 ? null : await r.json().catch(() => ({}));
if (!r.ok) {
const err = new Error(data.message || `HTTP ${r.status}`);
err.status = r.status;
throw err;
}
return data;
},
async getFile(path) {
try {
return await this._req('GET', `/contents/${path}?ref=${BRANCH}`);
} catch (e) {
if (e.status === 404) return null;
throw e;
}
},
async putFile(path, content, message, sha = null) {
const body = {
message,
content: btoa(unescape(encodeURIComponent(content))),
branch: BRANCH,
};
if (sha) body.sha = sha;
return await this._req('PUT', `/contents/${path}`, body);
},
async deleteFile(path, message, sha) {
return await this._req('DELETE', `/contents/${path}`, { message, sha, branch: BRANCH });
},
async loadShasFor(artifact) {
// Lazy-load SHAs (needed for PUT/DELETE)
const base = `artifacts/${artifact._status}/${artifact.id}`;
const [md, json] = await Promise.all([
this.getFile(`${base}.md`),
this.getFile(`${base}.json`),
]);
artifact._shas = { md: md?.sha || null, json: json?.sha || null };
return artifact._shas;
},
};
// ==========================================================================
// SYNC BADGE
// ==========================================================================
const sync = {
el: null,
setIdle() {
this.el = this.el || document.getElementById('sync-badge');
if (!gh.isAuthed()) {
this.el.textContent = 'read-only';
this.el.className = 'sync-badge';
document.body.classList.remove('authed');
return;
}
document.body.classList.add('authed');
this.el.textContent = 'synced';
this.el.className = 'sync-badge idle';
},
setSyncing(msg = 'syncing…') {
this.el = this.el || document.getElementById('sync-badge');
this.el.textContent = msg;
this.el.className = 'sync-badge syncing';
},
setError(msg = 'error') {
this.el = this.el || document.getElementById('sync-badge');
this.el.textContent = msg;
this.el.className = 'sync-badge error';
},
};
// ==========================================================================
// TOASTS
// ==========================================================================
function toast(msg, kind = 'info', duration = 3500) {
const wrap = document.getElementById('toasts');
const t = document.createElement('div');
t.className = `toast ${kind}`;
t.textContent = msg;
wrap.appendChild(t);
setTimeout(() => {
t.style.transition = 'opacity 0.3s';
t.style.opacity = '0';
setTimeout(() => t.remove(), 300);
}, duration);
}
// ==========================================================================
// HELPERS
// ==========================================================================
function escapeHtml(s) {
return String(s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
}
function relTime(iso) {
const d = new Date(iso);
const diff = (Date.now() - d.getTime()) / 1000;
if (diff < 60) return 'just now';
if (diff < 3600) return Math.floor(diff/60) + 'm ago';
if (diff < 86400) return Math.floor(diff/3600) + 'h ago';
if (diff < 86400*7) return Math.floor(diff/86400) + 'd ago';
if (diff < 86400*30) return Math.floor(diff/86400/7) + 'w ago';
return Math.floor(diff/86400/30) + 'mo ago';
}
function platShort(p) {
return ({linkedin:'LI', twitter:'TW', blog:'BL', newsletter:'NL', internal:'INT'})[p] || p.slice(0,3).toUpperCase();
}
function slugify(s) {
return s.toLowerCase()
.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
.replace(/[^a-z0-9\s-]/g, '')
.trim()
.replace(/\s+/g, '-')
.slice(0, 60);
}
function nowIsoTs() {
return new Date().toISOString().replace(/\.\d+Z$/, 'Z');
}
function nowIdTs() {
return new Date().toISOString().replace(/:/g, '-').replace(/\.\d+Z$/, '');
}
function buildMd(meta, body) {
// body is already the markdown text
return body || `# ${meta.title}\n\n(sin cuerpo)\n`;
}
function buildJson(meta) {
const ordered = {
id: meta.id,
title: meta.title,
type: meta.type,
status: meta.status,
created_at: meta.created_at,
...(meta.updated_at ? { updated_at: meta.updated_at } : {}),
target_platforms: meta.target_platforms || [],
tags: meta.tags || [],
source: meta.source || { origin: 'claude.ai' },
...(meta.notes ? { notes: meta.notes } : {}),
...(meta.published_url ? { published_url: meta.published_url } : {}),
...(meta.published_at ? { published_at: meta.published_at } : {}),
};
return JSON.stringify(ordered, null, 2) + '\n';
}
function flatAll() { return Array.from(store.values()); }
// ==========================================================================
// RENDER
// ==========================================================================
function renderAll() {
renderStats();
renderTypeFilters();
renderKanban();
}
function renderStats() {
const stats = document.getElementById('stats');
const total = store.size;
let html = `
${total}total
`;
STATUS_ORDER.forEach(s => {
const n = flatAll().filter(a => a._status === s).length;
html += `