Cómo construí OMLA dirigiendo agentes de IA en lugar de escribir el código
- Categoría
- IA y LLM Local
- Publicado
- 11 julio 2026
- Por
- Jacob Lloyd — escrito con ayuda de IA, después del proyecto
- Tiempo de lectura
- 12 min de lectura
En palabras simples: La historia real de cómo una persona, sin equipo de programación, dirigió asistentes de IA para construir una plataforma real de licencias en línea para una organización sin fines de lucro. Explica el método de trabajo: planes escritos con claridad, capas de verificación cruzada, y una regla de que nada sale a producción sin aprobación humana. Un plano práctico para construir software serio con ayuda de IA.
Este es el bucle exacto que usé para lograr que agentes de codificación de IA construyeran OMLA, el backend de licencias por regalías de una organización sin fines de lucro, casi en solitario. Róbate el proceso. El backend terminado es solo la prueba de que funciona.
tl;dr
- Qué es: el flujo de trabajo que logró que Claude Code y un enjambre local de agentes de IA construyeran el backend real de una organización sin fines de lucro, conmigo como arquitecto y guardián, no como mecanógrafo.
- Qué cuesta: básicamente nada nuevo. Una suscripción a un agente de codificación, tal vez una GPU de sobra para segundas opiniones. El costo real es tu propio tiempo de revisión.
- Qué necesitas: un agente de codificación, especificaciones escritas de verdad en lugar de intuiciones, y una regla estricta de que nada se despliega hasta que un humano escriba la palabra.
- Qué obtienes: un backend funcional con rastro documental. Auditorías en capas, pruebas contra una pila local real, y un script de despliegue que se niega a salir sin un "GO" humano.
Lo que obtienes
Antes de cualquier tutorial, esto es lo que salió al otro lado: OMLA, la Open Model Licensing Association. Un backend de licencias por regalías y un sitio público para modelos de IA abiertos.
| Qué | El resultado |
|---|---|
| Estado | En proceso de constituirse como organización sin fines de lucro en el estado de Washington, 501(c)(3) pendiente. El sitio lleva la etiqueta "Beta 0.9.0" a propósito. |
| El trato | Gratis para uso personal, de investigación y educativo. El uso comercial debe el 30% de lo que sea mayor: los ingresos atribuibles o el costo de ejecutar el modelo. |
| Base de datos | 12 tablas centrales, Postgres 17 en Supabase, seguridad a nivel de fila en todas partes, matemática monetaria en centavos enteros (nunca flotantes) |
| Identidad | Firmas híbridas ed25519 + ML-DSA-65, clásicas más poscuánticas, para que una futura ruptura cuántica no derribe el sistema. Sin firma verificada, no hay declaración de regalías. |
| Rastro de auditoría | Solo de append, encadenado con hash SHA-256, verificado cada hora, con un "ancla" diaria de la punta de la cadena guardada por completo fuera de la base de datos |
| Frontend | Unas 30 páginas HTML estáticas simples, sin framework, en 13 idiomas, en hospedaje compartido común |
| Quién lo construyó | Un humano dirigiendo el tráfico, Claude Code escribiendo el código, un enjambre local de agentes de modelos abiertos haciendo pasadas de revisión y trabajo pesado |
| Dónde está ahora | Totalmente en staging y probado. El despliegue es solo dry-run hasta que yo escriba personalmente "GO". |
Cronología aproximada, por si crees que esto pasó en un fin de semana:
- Finales de mayo de 2026 — giro desde un proyecto personal de chatbot hacia una organización sin fines de lucro de licencias, más el esquema inicial de 12 tablas
- En cuestión de días — endurecimiento de firmas poscuánticas, las edge functions de la ruta del dinero, y la reescritura "sin custodia" (más abajo)
- Principios de junio — el backend se llevó a producción (la base de datos, no el frontend público)
- 16 de junio — terminada la reescritura legal, entra en vigor la License v1.0
- 2 de julio — actualización completa del sitio y el backend, más una retraducción a 13 locales, en staging y probada, todavía esperando mi "GO" al momento de escribir esto
Nota curiosa sobre el origen: OMLA originalmente significaba "Open Machine Learning Assistant", un proyecto personal de chatbot. Las decisiones de infraestructura se trasladaron directamente al giro hacia licencias; el chatbot fue degradado a un ayudante de documentación con alcance fijo, deliberadamente apagado en producción. Convertir un chatbot casero en infraestructura de licencias es una cantidad normal de scope creep, ¿no?
La regla que hizo posible todo lo demás
Una decisión de diseño hizo la mayor parte del trabajo pesado, antes de escribir una sola línea de código: OMLA nunca mueve, retiene ni transmite dinero. Calcula lo que se debe y publica la billetera o los datos de pago que el beneficiario mismo proporcionó. El usuario comercial le paga al creador directamente, entre pares. Sin procesamiento de pagos, sin depósito en garantía, sin "saldo de OMLA". Los identificadores de billetera son información de enrutamiento, no cuentas.
El texto de la licencia lo dice sin rodeos: "OMLA publica; OMLA no paga."
¿Por qué molestarse? Ser un transmisor de dinero es su propia pesadilla regulatoria, encima de la de licencias. Si evitas la custodia, la carga de KYC/sanciones/impuestos se traslada a las dos partes que realmente hacen la transacción, que es donde legalmente pertenece de todos modos. Esta única decisión es la razón por la que un aficionado en solitario pudo siquiera intentar este proyecto.
Ni siquiera fue el diseño original. A mitad de la construcción, el esquema todavía implicaba custodia, así que una tabla literalmente llamada payments tuvo que renombrarse a royalty_statements — toda una migración de columnas, índices, triggers, políticas de RLS y enums, solo para que la base de datos dejara de mentir sobre lo que hace. El texto viejo todavía volvió a morderme después (ver los tropiezos).
Este es el camino que recorre un dólar de regalías sin que OMLA lo toque jamás.
El paso 4 es la puerta: sin firma verificada allá en el paso 1, jamás hay declaración de regalías. OMLA publica el número y la billetera, y de ahí en adelante es entre otras dos partes — lo mismo que si yo te dijera que un amigo te debe veinte dólares y los dejara arreglárselas entre ustedes.
El bucle real: humano, agentes, verificar, desplegar
El flujo de trabajo no es complicado. Solo es estricto sobre quién hace qué, y se repite cada vez que algo cambia.
Lo importante no son las casillas, sino que ninguna se salta. Cada cambio empieza conmigo escribiendo qué debería existir y por qué, no "mejóralo". Los agentes redactan a partir de esa especificación, otros agentes auditan el borrador antes de que yo lo vea siquiera, y las pruebas corren contra infraestructura que se comporta como producción. Solo entonces leo el diff.
Todo el backend corre local-first: una pila real de Supabase, Postgres 17 más edge functions, en mi propia máquina, en contenedores rootless. Producción es un destino de despliegue, nunca la fuente de la verdad. Si lo local y lo real no coinciden, lo real está equivocado hasta que yo lo publico.
El estándar de calidad vive en la configuración del proyecto, no solo en mi cabeza: "Audit-ready by default: ship work that can withstand external audit" (listo para auditoría por defecto: publica trabajo que resista una auditoría externa). Todo se hace bajo el supuesto de que un revisor independiente, sea IA o humano, va a intentar encontrarle agujeros después.
Probar contra una pila real, no un simulacro
Nada de lo anterior importa si las pruebas mienten, así que aquí nada se despliega contra una base de datos simulada. Todo corre contra una pila local real y desechable. Tres capas, todas en verde antes de que nada se mueva hacia el despliegue:
deno test # 13 unit tests for the edge functions
./test/run.sh # isolated scratch DB per run, migrations w/ ON_ERROR_STOP,
# smoke tests (PQ signatures, wrong-key rejection, audit
# hash-chain, payout gate), RLS cross-tenant isolation,
# an adversarial suite, then rollback + reapply
./test/integration.sh # the full money path against the live local stack:
# register → tampered payload rejected → wallet verify →
# splits → usage report → statement published → replay
# (asserts zero duplicate writes) → admin gate → compliance
Esa línea sobre "reenviar y verificar cero escrituras duplicadas" importa más de lo que suena. Los informes de uso vienen de sistemas ajenos, así que tarde o temprano alguien reenvía uno por accidente; la prueba verifica que reenviar el mismo informe produce cero declaraciones nuevas, no solo que nada se rompió. La suite se endureció después para verificar resultados en lugar de solo registrarlos — suena quisquilloso hasta que te toca depurar una prueba que "pasó" sin hacer nada.
Auditorías que califican otras auditorías
Una IA revisando su propio trabajo es un sello de goma. Por eso las auditorías están deliberadamente en capas: los agentes locales del enjambre hacen una primera pasada de corrección, y luego un modelo más fuerte corre como puerta de auditoría final e independiente.
Esa puerta final no es ceremonial. En una pasada verificó las correcciones de los agentes locales y aun así encontró un bypass de search-path más brechas de columnas, firmas y linaje que la primera pasada había pasado por alto. Eso se convirtió en su propia migración de endurecimiento: doce hallazgos numerados, cada uno enviado con una prueba de regresión para que no pueda volver silenciosamente.
La escala se ajusta al cambio. Lo rutinario recibe un panel de revisión de 3 agentes. La revisión completa del frontend y el i18n recibió una pasada de un enjambre de 24 agentes, que detectó que el sitio se había desalineado (seguía describiendo el viejo producto de chatbot, no el backend de licencias en que se había convertido) y forzó una reconstrucción para ajustarse a la realidad.
La puerta de despliegue: dry-run hasta que yo lo escriba
El script de despliegue tiene un solo trabajo: asegurarse de que nada de lo anterior llegue a producción por accidente. Dry-run por defecto, siempre; producción solo se toca con una bandera explícita:
./deploy.sh # dry run (default) — shows exactly what WOULD happen
./deploy.sh --go # only a human runs this, only when ready
Un envoltorio guiado va un paso más allá: literalmente tienes que escribir "GO" antes de cualquier paso que modifique producción. Y la regla permanente para agentes, escrita para que deje de ser una sugerencia: nunca agregar --go sin una instrucción humana explícita.
Otras garantías incorporadas al script, no solo política en papel:
- La sincronización del frontend nunca usa
--delete, así que no puede borrar archivos en el host que no gestiona - Se toma una copia de seguridad del lado del servidor antes de cualquier sobrescritura del frontend
- El backend se niega a actuar a menos que el proyecto vinculado coincida con el proyecto de producción esperado — una protección dura contra un push a la base de datos equivocada
- Contra el entorno remoto solo corren migraciones aditivas, nunca un reset
- La función del chatbot de documentación queda excluida de todo despliegue, por principio
- Los secretos viven solo en un archivo con permisos 600 fuera del repositorio; una verificación en texto plano confirma que la clave de service-role nunca aparece en lo que se publica
Honestamente, el registro de despliegues de este proyecto es sobre todo un diario de mí acobardándome: dry run tras dry run mientras un rediseño totalmente en staging esperaba la palabra. Eso no es un error. Así es exactamente como se supone que se debe sentir "con puerta de control".
Tropiezos
Las partes que duelen:
- El byte de ancho cero. El SQL de las migraciones tiene que ser ASCII puro. Un solo byte no-ASCII invisible puede partir un token de comillas de dólar
$$y fusionar en silencio dos sentencias SQL. La solución se volvió ritual: buscar bytes no-ASCII con grep (se quiere cero) y contar tokens$$(se quiere un número par), cada vez. - Los signos de porcentaje muerden. En una cadena de formato de
RAISE EXCEPTION,%%es un porcentaje literal y cada valor necesita exactamente un%. Esto me mordió una vez, que es la única razón por la que ahora está escrito. - SECURITY DEFINER necesita un search_path fijado. Toda función definer y de trigger fija su search path para que nadie pueda suplantar una tabla en
pg_tempy alimentarla con datos falsos. La prueba adversarial cuenta las funciones fijadas y falla si el número baja. - El superusuario inestable. Tras un reinicio no limpio, el rol de Postgres local a veces vuelve sin privilegios de superusuario. La solución es un paro y arranque limpio de la pila, no depuración. Está escrito para que los agentes dejen de redescubrirlo.
- El i18n sobrescribe el HTML en silencio. La capa de traducción sobrescribe por completo el texto de la página, así que "arreglar" el HTML puede seguir sirviendo texto obsoleto si el archivo de locale no se arregló también. Así fue exactamente como el texto de la era de custodia siguió reapareciendo después de supuestamente eliminado.
- La cadena de auditoría se puede engañar, a propósito, para saberlo. Un atacante con control total de la base de datos podría eliminar el trigger de la cadena de hash y reconstruir un historial falso autoconsistente; la verificación interna por sí sola no lo detectaría. Por eso un trabajo diario ancla el hash de la punta de la cadena por completo fuera de la base de datos, y la suite de pruebas demuestra ambas mitades en una sola ejecución: la verificación interna es engañada, el ancla externa sí detecta la reescritura.
- El sitio en vivo no es el sitio en staging. Al momento de escribir esto, el sitio público todavía muestra algo de texto previo al realineamiento (un borrador viejo de licencia, una tarjeta obsoleta sobre las vías de pago) porque el rediseño está en staging, no desplegado. Además, la URL pública
/aboutes un índice de directorio simple; la página de divulgación de IA vive un nivel más abajo.