La semana pasada arranqué agentdeck en público. El plan era simple: npx create-next-app, escribir un CLAUDE.md denso de 200 líneas con todas las reglas del proyecto, y empezar a montar el equipo de agentes encima de eso.

Hice la primera parte. La segunda no la hice. Y eso ha sido la primera lección útil de la semana.

El CLAUDE.md ya estaba escrito

Cuando abrí el directorio recién generado por create-next-app (versión 2026-04), ya había dos archivos que no esperaba:

CLAUDE.md       → 1 línea: @AGENTS.md
AGENTS.md       → bloque protegido con instrucciones del equipo de Next.js

Dentro de AGENTS.md me encontré con esto, literal, dirigido a cualquier agente IA que entre al repo:

"This is NOT the Next.js you know — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ before writing any code."

El equipo de Next.js está delimitando un bloque protegido entre <!-- BEGIN:nextjs-agent-rules --> y <!-- END -->, con la idea de mantenerlo intacto entre actualizaciones del framework. Es su zona. Si la tocas, te la pisan en el siguiente npm install next@latest.

Si yo hubiese seguido el plan original (escribir un CLAUDE.md mío de cero), habría duplicado o contradicho lo que el propio framework ya ponía, y me habría comido un conflicto cada vez que actualizara Next.js.

Por qué CLAUDE.md y AGENTS.md son cosas distintas

Esto antes no estaba claro. Ahora sí:

  • AGENTS.md es la convención emergente cross-tool. La leen Cursor, Aider, Claude Code, Copilot Workspace y cada vez más herramientas. Es el punto único de verdad sobre cómo trabajar en este repo, independientemente del agente que lo abra.

  • CLAUDE.md es específico de Claude Code. Si pones @AGENTS.md dentro, Claude lo importa.

Resultado: escribes las reglas una vez en AGENTS.md, y CLAUDE.md se queda en una línea. Si mañana decides probar otro agente, el archivo de reglas ya está donde toca.

Así quedó el CLAUDE.md de agentdeck:

@AGENTS.md

Una línea. No es minimalismo coqueto: es no duplicar.

Cómo conviven mis reglas con las del framework

El layout que estoy usando es este:

<!-- BEGIN:nextjs-agent-rules -->
... (zona protegida que mantiene Next.js — no tocar) ...
<!-- END:nextjs-agent-rules -->

---

# agentdeck

(aquí mis reglas: stack, server vs client components, naming, DB, auth...)

Mi contenido va debajo del <!-- END -->, separado por ---. Cuando Next.js actualice su bloque, el mío no se ve afectado. Cuando yo cambio mis reglas, el suyo sigue intacto.

Es un patrón pequeño, pero ahorra el problema clásico del "actualicé el framework y ahora las reglas del agente están a medias".

Lo que quería contar esta semana iba a ser otro post

Tenía planeado un post sobre cómo cambié de stack: empecé pensando en Astro, terminé eligiendo Next.js 15 antes de escribir una línea. Pero ese cambio ya lo había decidido en la semana 0, así que lo que importa contar de la semana 1 es lo que pasó al meter las manos en el código, no la planificación previa.

Y lo que pasó fueron dos cosas:

  1. El CLAUDE.md ya estaba escrito (esto).

  2. La conexión a Supabase me robó 20 minutos por una razón que no esperaba.

Bonus: si conectas Supabase desde casa, usa siempre el pooler

Este es para quien vaya a hacer lo mismo en los próximos días.

Configuré Drizzle con dos URLs siguiendo la práctica clásica:

  • DATABASE_URL → pooler (aws-0-eu-central-1.pooler.supabase.com:6543) para runtime

  • DIRECT_URL → host directo (db.xxx.supabase.co:5432) para migraciones DDL

Lancé npx drizzle-kit push y se quedó colgado en Pulling schema from database.... Sin error. Sin timeout. Silencio.

Diagnóstico:

$ dig +short A db.xxx.supabase.co
(vacío — no hay IPv4)

$ dig +short AAAA db.xxx.supabase.co
2a05:d018:...   ← solo IPv6

Supabase migró sus direct connections (puerto 5432) a IPv6-only a finales de 2024 para ahorrar costes de IPs IPv4. La mayoría de ISPs domésticos en España, Andorra y LatAm no dan IPv6 público. Resultado: tu máquina resuelve el dominio, intenta conectar, no encuentra ruta, y el cliente Postgres se queda esperando hasta el timeout TCP del kernel (decenas de minutos).

La regla que aplico ahora:

  • Desde cualquier red sin IPv6 público → usar DATABASE_URL (pooler) tanto para runtime como para drizzle-kit.

  • El pooler nuevo de Supabase (Supavisor) sí soporta DDL en modo session por defecto. La advertencia clásica "el pooler no soporta CREATE TABLE" se refería al modo transaction estricto, que ya no es el default.

  • DIRECT_URL queda como opcional, solo útil desde redes con IPv6 (algunos runners de Vercel, GitHub Actions modernos).

20 minutos perdidos. La pista que me ahorró otros 40: "se cuelga sin error" = problema de routing de red, no de credenciales. Si te pasa, primero dig, luego cualquier otra cosa.

Lo que llevo construido

  • Scaffold Next.js 15 + TypeScript + Tailwind v4 + shadcn/ui

  • Drizzle ORM conectado a Supabase

  • Primera tabla (profiles) creada vía drizzle-kit push

  • Clientes Supabase (browser + servidor) listos para auth

Cero agentes activos todavía. El primero (View Builder) llega en la semana 2 o 3, cuando tenga el modelo de datos completo y haga falta empezar a generar pantallas. La regla que me he puesto: no creo un agente hasta que tenga un trabajo repetitivo real para él.

Próxima semana

La sesión de hoy va al modelo de datos de agentdeck. Hay que decidir cómo se modelan en Postgres las cosas que escanea el producto: repos, agentes, skills, hooks, snapshots. Y si el ejercicio da material editorial, el post de la semana 2 sale de ahí.

Si te interesa cómo se diseña el schema de un producto que nace para "ver agentes", nos leemos el martes que viene.

— Sergio

Sigue leyendo