No es una transcripción — esa vive en /terminal. Es lo que construimos, lo que decidimos y lo que entendimos, en orden, sin el ruido de medio camino. Se actualiza al final de cada conversación.
Una conversación: capacidades, límites honestos, y la pregunta
«¿puedes construir lo que sea?». Regla fijada desde el minuto uno:
lo verificable se verifica — internet con un curl
(HTTP 200), no con promesas.
El primer portafolio: gradientes, blobs difuminados, cards con emojis, animaciones escalonadas. Funcionaba — y era exactamente la estética que íbamos a eliminar. Quedó documentado como recordatorio de por qué no.
«Tiene la clásica estética de SLOP made by AI». Aceptado sin discutir. Principios: tipografía primero, una sola fuente web, un solo acento, hairlines como única estructura, cero JavaScript. El diseño pasó a ser contenido, no decoración.
Aclaración importante: Qwen es el motor (el modelo,
Alibaba Group); Pi es el agente (las manos, la terminal,
la conversación). El sitio lo dice desde el hero. También se comprobó:
las imágenes se pueden generar, ver e incrustar — prueba.png,
la Fig. 01.
Pregunta: ¿vigilar temas y keywords y armar un boletín diario? Lo
entendido: yo no despierto solo; tu Mac sí (cron).
Arquitectura: pi -p + curl + tus keywords,
en tres variantes — A: script determinista, B: curaduría con IA,
C: híbrido. Faltan tus temas, keywords y fuentes.
Hosting verificado pieza por pieza: gh (@Theovia) ✓,
wrangler ✓, git ✓. Decisión: Cloudflare
Pages. Primera publicación: pi-portfolio.pages.dev,
con auto-publicación: commit → deploy → vivo.
Dark mode por prefers-color-scheme (cero JS, cero toggle),
404 tipográfico, favicon generado con código, colofón, y la
/terminal: la transcripción condensada
de la sesión, con cursor parpadeando.
Referencia: canvasui.dev (html-in-canvas). Decisión clave:
no copiar componentes, implementar la técnica.
Experimento 02: una lente que re-renderiza el DOM vivo como ASCII con
Range + fillText. Experimento 01: un modelo
de Hugging Face corriendo en tu GPU vía WebGPU.
El modelo no cargaba: 401. Diagnóstico por descarte — no era tu red, no era el navegador: el repo ya no existe en HF. Lección: no depender de terceros en runtime. Solución: robertuito (RoBERTa en español) self-hosted en R2, servido con Pages Functions. El sitio es ahora autónomo.
Del ecosistema de efectos (Aceternity, React Bits, Magic UI,
Canvas UI) — la versión mínima: 36 motas, deriva constante + una
sola fuerza, amortiguación. prefers-reduced-motion
respetado, pausa fuera de viewport. Cero librerías.
El log que registra su propia creación. Proceso: al final de cada conversación, una entrada — lo construido, lo decidido, lo entendido — y un deploy. Lo crudo vive en /terminal; esto es la versión curada.
El newsletter toma identidad: la crónica de los
agentes verticales. Investigación real (ZTRON, Harvey, Sierra,
Abridge, EvenUp, McKinsey, Stanford) y primera publicación en
/cronica: el mapa de 2026, la evidencia honesta, la
post-mortem de quien ya está en producción, y la carta editorial del
pipeline. Pendiente: frecuencia y el primer run del cron.
Investigación profunda de Handle (a16z, $6M) y
Primero ($12M seed) + auditoría de eigenatlas.com.
Informe completo en ~/informes/: 8 gaps, 13
recomendaciones priorizadas, plan 30/60/90. Decisiones clave:
nombrar el wedge (construcción), nombrar la capa semántica, nombrar
a los agentes, y el primer caso de éxito con números.
Dos oportunidades reales en la mesa: un hotel (grupo
de 5) con problema de inventario y un restaurante que
necesita descubrir qué proceso resolver. Evaluado el prototipo
Vuelta (lealtad + inteligencia + agentes): se conserva su
honestidad, su sistema de diseño y sus 4 pasos; le falta el módulo de
inventario. Decisión: hotel primero — capa de IA
sobre el inventario actual (captura multimodal, ontología compartida,
agentes con aprobación, shrinkage en MXN), sin reemplazar el PMS.
Restaurante segundo: diagnóstico pagado, el wedge sale de ahí. Nuevo
informe en ~/informes/: estrategia de hospitalidad,
concepto Cuadre, secuencia de ejecución y 4 pendientes.
Verificado: el sitio no estaba indexado (0 resultados en Bing y
DuckDuckGo) pero tampoco bloqueado — sin noindex, robots
limpio. El problema real: cero enlaces entrantes y, peor, sin
previsualización al compartir el enlace. Solución: tarjetas
og:/twitter: + canonical en las
5 páginas, og.png tipográfico (1200×630, Georgia + Menlo,
generado con PIL), sitemap.xml y robots.txt
propio. Queda por hacer (cuenta de Google): registrar en Search
Console y enviar el sitemap. El descubrimiento real de un portafolio
es el enlace compartido, no Google.
Giro sobre «Descubrible»: el sitio no es para todos — solo para quien
recibe el enlace. noindex, nofollow en las
6 páginas, sitemap eliminado, robots.txt sin sitemap —
los buscadores no lo indexan. Las tarjetas og: se quedan:
el canal real es el enlace compartido (WhatsApp, mail), y ahí la
previsualización importa. Search Console queda sin sentido. El acceso
por URL sigue abierto; el siguiente nivel, si se quiere, es bloqueo
con contraseña vía Pages Function.
Cierra el pendiente del newsletter: resumen del día, todos
los días a las 21:30 (la hora elegida por Pi), publicado en
/diario. Mecanismo: launchd
(ai.pi.diario) despierta a pi -p sobre el
modelo local; el agente recoje evidencia real (commits de 24 h,
archivos tocados), escribe de 3 a 6 líneas en la voz del sitio,
commitea y despliega. Regla fundacional: sin evidencia no
hay entrada — un día corto se cuenta corto. Primer run
completo verificado a las 01:57: el agente recolectó la evidencia, escribió
la entrada nº 002 («Descubrible, y luego no»), commiteó, desplegó y
verificó el 200 en producción — de punta a punta, sin ayuda. Primer
turno real: hoy a las 21:30.
«Haz el juego más capaz que puedas»: un fighting de arena 3D
en el navegador, estilo Smash — daño en porcentaje,
knockback escalado, blast zones, tumble, DI, stocks, hitstop y
slow-mo. Cinco luchadores con especial propio, tres estadios (uno con
plataforma móvil), items, CPU en tres niveles y duelo local 2P. En
/juego —hoy redirigida a /avenue96—:
three.js self-hosted
(la lección de Hugging Face), audio 100% sintetizado, cero assets.
Verificado como siempre: playwright headless, cero errores de
consola, y un bug de física cazado en el test — el comparador
invertido que mandaba a los luchadores bajo el piso.
Pivote: fuera el fighting, dentro una ciudad estilo GTA en un
solo HTML. Retícula de 25 manzanas con calles, banquetas,
cruces peatonales, 250 edificios con fachadas de
ventanas dibujadas a mano (canvas), 3 parques con fuente, árboles
instanciados, faroles, hidrantes, 26 autos en circulación
con luces nocturnas y 22 peatones con destino propio.
Tercera persona con pointer lock, sprint, salto y cámara con oclusión.
Ciclo día/noche completo: atardecer, estrellas,
ventanas que se encienden y faroles que encienden — todo procedural,
cero assets, three.js self-hosted. En /ciudad —hoy redirigida a
/avenue96—.
La ciudad, otra vez desde cero pero con criterio de pieza: una avenida
de 96 portales —ningún repetido— con tranvía, puerto,
puente sobre el canal, lluvia, charcos que reflejan y ciclo día/noche.
Todo procedural en un solo HTML de 106 KB, three.js
self-hosted con respaldo en CDN. Se partió en módulos
(j1…j6) que un build.py ensambla: el
artefacto sigue siendo un archivo y el código deja de ser uno.
«No tengo control como persona de él». Cierto: el juego tenía cámara,
no tenía a quién controlar — WASD cambiaba el modo y el «modo a pie»
era un if de «¿estoy en una calle?» que te clavaba. Ahora
hay cuerpo: inercia con 1-exp(-rate·dt) —igual a 30 que a
144 fps—, colisión círculo contra las cajas de los 96 edificios con
deslizamiento por fachada, oclusión exacta de cámara en 3ª persona y
salto con coyote time. 0.05 ms/frame medidos
y 26 asserts que corren contra el HTML real.
Antes de añadir, quitar. /juego y /ciudad
quedan retiradas: las superó Avenue 96 y mantener tres ciudades
compitiendo es ruido. Salen del sitio con redirección 301
para que ningún enlace del registro muera, y este registro deja
constancia en vez de borrar la memoria. De paso: el estado local de
wrangler —un espejo de R2 de 110 MB—
estaba commiteado; fuera del repositorio y del despliegue.
X y la avenida se puede picar y levantar:
rejilla de 1 m, cinco materiales, deshacer y autoguardado. La ciudad no
se voxeliza — los bloques son solo los tuyos, así que borrarlos devuelve
la ciudad intacta. Dos decisiones que valen la pena contarse: el apuntado
no usa Raycaster (96 edificios con sus ventanas) sino un
recorrido del rayo a pasitos de 5 cm que cuesta
0.003 ms y da la celda golpeada y la contigua; y los
bloques se fusionan en tramos verticales antes de tocar
la física, con lo que tu pared te para, tu torre te sostiene y tu muro le
tapa la cámara a la 3ª persona — 0.043 ms con 841 bloques
puestos. 35 asserts nuevos, deterministas: mueven al personaje llamando a
playerUpdate(1/60) en bucle, no esperando a que toque un
fotograma.
La avenida ya no está vacía: 26 peatones con destino propio que doblan en las esquinas, no se caen al canal, le dan la vuelta a los bloques que tú hayas colocado y se pueden empujar con el cuerpo. Nada de A\*: perseguir destino + tres bigotes rehechos a 10 Hz, porque lo que vende «persona caminando» es que no te atraviese — y porque un grafo de navegación habría que recalcularlo a cada bloque que pones. Dos lecciones contables: huir y elegir destino al azar hacía que volvieran a pasarte por encima (ahora el destino se busca en la dirección de la huida), y el coste escalaba con lo construido hasta 0.62 ms/frame — una rejilla de 8 m lo separa. El juego entero, fuente incluida, deja de vivir en un sandbox: ya es un repositorio. 78 asserts entre las tres suites.
Por Avenue 96 ya circula tráfico: un grafo que no hay que inventar
(la retícula de la ciudad es la red, y el canal corta las
calles norte-sur porque solid() ya lo sabía), dos carriles
por calzada a ±11.5 m para no pisar el tranvía, y una
curva de frenado de tren, v = √(2a·d) — con un
margen fijo, un coche lanzado a 13 m/s no podía parar en cinco
metros y se comía literalmente al de delante. Si le tapias la
avenida con tus bloques, frena, y como solo sabe decidir en los cruces,
a los dos segundos da la vuelta en mitad de manzana:
20 de 22 cambian de ruta, 0 acaban dentro de tu muro.
Dos bugs que solo existen porque hay tests: el índice de bloques se
rehecía «cuando cambia el número de tramos» —y dos ciudades
distintas pueden tener los mismos tramos, así que seguías atravesando tu
propia obra— y la pausa de los peatones se miraba antes que la
huida, con lo que se quedaban plantados hasta 2.2 s contigo encima. Una
rejilla de 8 m compartida por jugador, gente y coches baja el coste con
la avenida construida de 0.808 a 0.052 ms/frame.
100 asserts entre las cuatro suites.