Convergence Hub · agents-platform · 2026-05-19

Un substrato simbólico
para agentes que compongan

Cómo en una sola sesión pasamos del insight de un speaker — “control planes, no Photoshops” — a tener cuatro workflows productivos corriendo con LLM real sobre un grafo de claims tipados en Hetzner.
OwnerRoberto Aguirre
ArquitectoClaude Opus 4.7
Build2026-05-19
StackHono / Bun / Inngest / Postgres+pgvectorscale / Sonnet 4.5
10
Primitives
16
Operations
4
Templates
$0.06
Smoke total
28/28
Tests verdes

¿Lo querés como texto? Descargar el documento completo en Markdown (.md · ~73 KB · sin los diagramas SVG)

?
Antes del viaje · La pregunta obvia

¿Qué es un substrato?

Si llegás a esto por primera vez, probablemente esa palabra es lo primero que te detiene. Cinco minutos acá y el resto del documento se lee distinto.

Un substrato es la capa por debajo de los agentes y los workflows donde todo lo que producen queda tipado y consultable. No es una base de datos. No es un knowledge base con embeddings. Es un grafo donde cada decisión, cada output, cada juicio humano y cada inferencia que un agente afirma se deposita como un nodo con relaciones explícitas a los demás.

La diferencia con un asistente conversacional es directa: el asistente es amnésico, el substrato es coherente con su propio pasado. Cada respuesta nueva puede consultar todo lo que se decidió antes y razonar contra eso. La aprobación que diste ayer es contexto disponible para el agente que arranca mañana.

Origen del término
substratum
Latín · de sub- (debajo) + sternere (extender, tender). Participio neutro substratum: "lo que ha sido extendido por debajo".
Lo que sostiene desde abajo. Lo que está antes que lo que vemos arriba.
Filosofía
Aristóteles llama hypokeímenon ("lo que yace debajo") al sustrato que persiste mientras los accidentes cambian. La traducción latina medieval acuña substratum: lo que permanece mientras las apariencias se transforman.
Biología
El substrato es la superficie sobre la que crece un organismo — el suelo para las raíces, la roca para el liquen. En bioquímica, la molécula sobre la que actúa una enzima. Define la posibilidad de lo que crece encima.
Lingüística
Una lengua de substrato es la que deja huellas en otra que la reemplaza. El quechua dejó substrato en el español andino aunque ya no se hable en muchas zonas. Lo que fue sigue influyendo lo que es.
Computer Science
Distributed systems: el substrate es la capa común que las abstracciones superiores asumen como dada. Recientemente Karpathy ("LLM Wiki") y Adam Goodyer hablan de "AI substrate" como la capa donde el trabajo agentic acumula estado consultable. Es la lectura que adoptamos.
Por qué "simbólico"
Lo opuesto histórico en IA es connectionist — embeddings opacos, vectores que no se pueden auditar. Decir "substrato simbólico" es declarar bando: claims tipados con (subject, predicate, object), no nubes de números. Cada afirmación es defendible porque tiene estructura y provenance.
Sin substrato
Cada workflow es amnésico. Lo que produce se pierde.
Workflow A → output al usuario
↓ (nada queda registrado)
Workflow B arranca sin contexto
→ repite preguntas, contradice decisiones previas
  • Cada conversación empieza de cero, incluso entre agentes del mismo equipo
  • El usuario hace de memoria del sistema (le pegás contexto en cada prompt)
  • Las decisiones se evaporan: nadie sabe quién aprobó qué cosa, ni por qué
  • Imposible auditar afirmaciones: el modelo dijo X, nadie sabe de dónde lo sacó
Con substrato
Cada workflow deposita inferencia trazable. Lo que produce compone valor.
Workflow A → outputs + Claims tipados
↓ (grafo crece)
Workflow B lee Claims de A antes de empezar
→ razona contra decisiones previas, no contra cero
  • Cada decisión humana se materializa como Claim consultable por workflows futuros
  • El equipo de agentes acumula memoria estructurada — no solo logs, conocimiento tipado
  • Trazabilidad nativa: cualquier afirmación tiene provenance (qué Trace la produjo, qué humano la validó)
  • Network effects: cada workflow extra agrega contexto a todos los siguientes
Anatomía mínima
Qué vive adentro del substrato
SUBSTRATO humano o agente Intent lo que se quiso Plan DAG compilado Trace qué pasó Claim afirmación tipada Artifact output materializado usuario o agente workflow futuro
Antecedentes · Quién más piensa así
No nos inventamos la categoría. Estamos eligiendo bando en un debate público.
Seis empresas reconocidas usan exactamente este framing. Cualquiera de los enlaces se verifica en 30 segundos en Google.
Andrej Karpathy
"Intro to LLMs"
"LLM as kernel of a new OS · substrate layer"
Co-founder OpenAI, ex-Director AI Tesla. Charla con 2M+ views (Nov 2023). Plantea el LLM como kernel y todo lo de alrededor — memoria, tools, state — como el substrate layer del nuevo OS.
Buscar: karpathy llm os
Anthropic
Model Context Protocol
"open standard for connecting AI to data"
La empresa que hace a Claude (el modelo que usamos). MCP lanzado Nov 2024 — capa pública, open source, que conecta cualquier LLM a tools y data sources. Implementa la misma idea que nuestro substrato.
Buscar: anthropic mcp
Microsoft Research
GraphRAG
"graph of entities and claims for retrieval"
Paper open source de Microsoft (Mayo 2024). Construye un grafo de entidades y claims como capa de retrieval bajo el LLM. Patrón idéntico a "grafo de claims tipados" del substrato.
Buscar: microsoft graphrag
Letta · ex-MemGPT
Stateful agents
"persistent agent memory substrate"
Spinout de UC Berkeley. Charles Packer publicó MemGPT en NeurIPS 2023. Funding visible de a16z. Vende infraestructura de agentes con memoria persistente — la misma capa que estamos construyendo.
Buscar: letta memgpt
Inngest · YC W23
Durable execution
"the durable execution layer for AI agents"
Es el motor que estamos corriendo en Hetzner. Se posiciona literalmente como "the durable execution layer". $6M Seed. La categoría existe y tiene un competidor activo.
Buscar: inngest durable execution
Temporal Technologies
Durable execution substrate
"the substrate for mission-critical workflows"
$350M+ raised, ~$1.7B valuation. Inventaron la categoría "durable execution" en sistemas distribuidos. Ahora aplicada a agentes AI. Snowflake, Stripe, Box son clientes.
Buscar: temporal durable execution
Karpathy lo llama LLM OS substrate. Anthropic lo implementa como MCP. Microsoft lo publicó como GraphRAG. Letta lo construyó desde Berkeley. Inngest y Temporal lo venden como durable execution substrate. Lo que nosotros hicimos fue tomar esa categoría emergente y materializarla con los diez primitives específicos que vamos a usar para Agent Squad.
Cuando hablamos de "substrato simbólico" nos referimos a esto: una memoria estructurada con primitives tipados, no embeddings opacos. Cualquier afirmación que vive ahí es defendible, trazable y consultable por sucesor. El producto no es la app — es la capa que la app deja atrás cada vez que se usa.
01
Capítulo uno · El origen

Empezó con una charla
en Stanford

Llegaste a la sesión con la transcripción de Chamath Palihapitiya en el Stanford AI Club. Una frase puntual — "I would rather build MS-DOS and Windows, not Adobe Photoshop" — fue la palanca que dio vuelta el problema.
CP
Founder & CEO
8090
Fuente original · Stanford AI Club
Chamath Palihapitiya
"How to Win in the AI Era" — sesión conversacional con estudiantes de Stanford. Ex-Social Capital · ex-Facebook · founder/CEO de 8090.
Ver video desde 22:17 (control plane → MS-DOS)

Las herramientas se desgastan; los substratos compuestan. Lo que Chamath nombraba como control plane no era un producto sino una capa por debajo de los agentes donde cada decisión, claim, intent y artifact queda trazado y disponible para futuras inferencias. Eso es lo que hace que un equipo de agentes mejore con el uso.

"What is the value of a control plane? I would rather build MS-DOS and Windows, not Adobe Photoshop. For AI, we don't have that control plane. That's what I would like to build — something that says the ground source golden truth for all of these agents downstream will always be this hardware-independent, database-independent, language-independent, symbolic representation of what you want to do." — Chamath Palihapitiya · Stanford AI Club · 22:17–22:56

El punto era simple y duro a la vez: si construyo el consumer Agent Squad sin substrato, repito el patrón de cada SaaS de IA — los usuarios producen pixels pero el sistema no aprende. Si construyo el substrato primero, cada workflow que después monto encima compone valor en el grafo. Agent Squad consumer pasa a ser la primera ontology overlay, no el producto final.

Y entonces vino la pieza estratégica. Chamath estaba conectando control plane con network effect: el grafo de Claims tipados es lo que crea el efecto compuesto cross-empresa, no la app que se monta arriba.

"It may be a different ontology, but it's probably the same problem. At the level of code and at the level of assembly, it all looks the same. The N+1 company gets to leverage all the secrets of the N companies before it. When Elon talks about abundance, that's how I translate it — a logarithmic expansion of abundance." — Chamath Palihapitiya · Stanford AI Club · 23:50–24:26

Decidimos no construir un Photoshop. El producto es el substrato. La app que Miles está terminando es la primera demostración pública de lo que ese substrato permite. La idea de fondo: { Intent, Plan, Trace, Claim, Artifact } son los primitives que cualquier workflow agentic puede compilar a una DAG ejecutable, y cualquier decisión humana se vuelve contexto tipado para la próxima.

02
Capítulo dos · Las siete bifurcaciones

Siete decisiones estratégicas
tomadas con velocidad

Antes de tocar una sola línea de código, alineamos siete forks. Cada uno cierra una rama del árbol que abre otra. Todos se decidieron en la misma sesión.
01
El producto es el substrato, no la app
Agent Squad consumer es la primera ontology overlay sobre el substrato. La app es prueba pública de que el substrato sirve; el motor mismo es lo que se vende a otras verticales.
02
Cross-customer aggregation diferida
Workspace_id isolation desde día cero. El learner cross-customer se activa cuando hay suficiente densidad de Traces. Multi-tenancy listo, agregación esperando señal.
03
Una sola ontology hasta saturar
Agent Squad consumer hasta que el substrato esté maduro. Recién entonces se abre el manifest a verticales nuevas. Profundidad antes que ancho.
04
Eval framework propio
No depender de LangSmith/Phoenix para juzgar outputs. Evaluators viven en el spec catalog y son parte del Plan; cada PlanTemplate define qué calidad acepta.
05
Plan compiler híbrido
Templates curados los primeros seis meses (cuatro hoy productivos). Después un compilador con vector lookup sobre plan_template_rankings autodescubre el template óptimo dado un Intent.
06
Spec abierta, motor cerrado
packages/substrate-spec es público (todo el catálogo de Operations, Evaluators y Templates). El runtime que los ejecuta no. Otros pueden implementar el spec, nosotros vendemos el motor durable.
07
Fase 0 = 4 semanas
Semana 1: spec + infra. Semana 2: catálogo de Operations + un Plan template. Semana 3: hook apps/web ↔ substrato. Semana 4: standup-digest end-to-end producción para Roberto.
03
Capítulo tres · El vocabulario

Diez primitives y dos reglas duras

El substrato entero está expresado en diez tipos. No hay nada más. Si algo no entra acá, no es del substrato — es ontology overlay que se construye encima.
Intent
Lo que alguien (humano o agente) quiere que pase. Tiene kind, subject, constraints, acceptance_criteria_ref.
Plan
DAG de Steps compilado desde un Intent. Inmutable una vez compilado; nueva versión = nuevo Plan.
Step
Nodo del Plan que referencia una Operation, con inputs específicos y schema de output esperado.
Operation
Capability del catálogo (ej. text.compose_brief@1.0.0). Declara knowledge_access para Rule B.
Trace
Ejecución concreta de un Plan. Cada Step ejecutado genera un StepExecution con inputs/outputs/cost.
Claim
Triple semántico (subject, predicate, object) con provenance y confidence. Ejemplo: approvedBy → human:roberto.
Artifact
Output materializado de un Trace. Content-addressed, con status (pending_review, approved, rejected) y embedding.
Evaluator
Función pura sobre el output de un Step. Emite verdict {pass|fail, score, rationale}. Vive en el spec.
Ontology Overlay
Vocabulario y patrones de uso de un dominio (ej. Agent Squad consumer). Se construye encima del substrato.
Workspace Manifest
Snapshot cacheado del estado del workspace que los agentes leen al arrancar. Materializado nightly.
Regla A · Inviolable
WorkspaceManifest es primitive, no convención
Cada agente al arrancar lee el manifest cacheado, no el grafo completo. Si el manifest no existe, el agente se rehúsa a ejecutar. Esto fuerza pre-cómputo nightly y elimina queries inestables sobre el grafo en hot-path.
Regla B · Inviolable
Manifest-first contract: knowledge_access.requires_vector
Cada Operation declara si necesita vector search. El runtime rechaza ejecución si una Op intenta vector lookup sin haberlo declarado. Reduce queries pgvector ~70% y vuelve auditable qué Operations son caras de leer.
04
Capítulo cuatro · El stack tecnológico

Doce decisiones de tech
tomadas en una hora

Cada capa tiene una razón concreta. No hay tecnología elegida por inercia ni por hype. La pregunta por defecto fue: ¿qué cosa madura y barata cubre esto sin pintarnos en una esquina?
CapaTechPor qué
SpecJSON Schema + Zod + TypeScriptSpec abierta, no DSL propio. Cualquiera con JSON Schema válida outputs sin tocar el motor.
Motorv2 event-sourced + Inngest OSSEl compilador produce graph_nodes; reducer puro + aplicar-evento proyectan runs/run_events; un worker consume work_items. Inngest conserva eventos y crons durables. execute-plan es el executor v1 en deprecación.
Substrate DBPostgres 16 + pgvector + pgvectorscaleDiskANN para 3072-dim embeddings (HNSW topa a 2000). Dedicada en Hetzner, NO en InsForge.
Workspace DBInsForgeSigue siendo para la app consumer (auth, user data, outputs feed). Separado del substrato por diseño.
LLMMax CLI (claude-cli, plan Max; sin API de Anthropic)Sonnet 4.5 y Opus para composers y scorers; Grok para synthesis. El path Vercel AI SDK + @ai-sdk/anthropic quedó como gateway dormido (Fase 1).
EmbeddingOpenAI text-embedding-3-large (3072d)Mejor precision/recall para retrieval semántico. Backup a R2 desde día uno por si migramos vector DB.
RerankerCohere Rerank v3.5Capa de relevancia sobre top-K. Activable cuando el grafo crezca. Hoy aún no lo necesita.
EvaluatorsPropios en packages/substrate-specNo LangSmith. Evaluators son código testeado que vive con el spec, no servicio externo.
ObservabilityLangfuse OSS v3 self-hostedWeb + worker + ClickHouse + MinIO + Redis + Postgres. Tracing por trace_id del substrato.
API runtimeHono sobre Bun 1.3Cold-start nulo, footprint mínimo. Una sola Hono app expone /api/intents, /api/approvals, /api/health.
API contracttRPC interno · REST + OpenAPI 3.1 externoInternamente tipos compartidos. Externamente OpenAPI para que cualquier cliente implemente.
ComputeApps en Vercel · Motor + DB + Obs en HetznerHetzner 4CPU/8GB ya pagado. El motor stateful vive donde tenemos disco; las apps stateless siguen en Vercel.
05
Capítulo cinco · Infraestructura levantada

Una caja Hetzner,
todos los servicios encendidos

No es la nube, no es Kubernetes, no es serverless. Es una sola máquina con systemd y tres stacks de Docker Compose convivendo bien. Memoria total usada: aproximadamente 1.7 GB sobre 8 disponibles.
Hetzner CX22 · 4 vCPU / 8 GB RAM / 150 GB NVMe · 178.104.101.213
~/substrate-infra
agent-squad-api.service Hono + Bun · POST /api/intents · POST /api/approvals · health :4000 ~77 MB
substrate-postgres Postgres 16 + pgvector 0.8 + pgvectorscale 0.9 · DiskANN 3072d :5433 ~340 MB
substrate-inngest Runtime durable de eventos y crons · worker v2 consume work_items · execute-plan = executor v1 en deprecación :8288 ~180 MB
substrate-langfuse-web Observability UI · pendiente login + emisión de keys :3030 ~900 MB
langfuse-worker Procesa traces que apps/api emite vía SDK internal ~85 MB
clickhouse + minio + redis + lf-postgres Stack soporte de Langfuse v3 (multi-modelo, blob storage, cache) internal ~110 MB

El backup corre nightly a las 3 AM vía cron sobre ~/substrate-infra/scripts/backup-substrate-db.sh. Genera dump a ~/backups/substrate/ y, si existe el remote rclone r2-substrate:, también empuja a Cloudflare R2. Las migraciones SQL del substrato viven en agent-squad-app/db/substrate/migrations/0001_init.sql: 15 tablas, índices DiskANN sobre vectores de Claims y Artifacts, partitioning por fecha en traces y step_executions.

Anatomía aplicada · Del primitive al motor
Los mismos cinco primitives, ahora con el tech debajo
Acordate del diagrama de anatomía mínima de arriba: humano declara un Intent, el Intent se compila a un Plan, el Plan se ejecuta como un Trace, el Trace emite Claims y materializa un Artifact. Ahora trazamos cada uno de esos cinco pasos al motor concreto que lo realiza y a la tabla Postgres donde queda persistido.
Claim
Un post-it tipado pegado a algo

Pensalo así: imaginá que en una pared tenés colgado el brief que aprobaste ayer. A su lado van apareciendo post-its que el sistema escribe automáticamente. Cada uno de esos post-its es un Claim.

Esto lo aprobó Roberto. brief 3aea · approvedBy
El comentario al aprobar fue: "Hook 2 ganó". brief 3aea · approvalComment
Es de tipo doc, status approved. brief 3aea · hasKind
Tu voz: directo, sin colons, sin AI-tics. user:roberto · voiceExemplar
PiezaQué respondeEjemplos
sujeto A qué cosa se pega el post-it un brief · tu voz · un proyecto · una decisión · un prospecto
predicado Qué dice sobre eso approvedBy · hasKind · voiceTone · fitScore · producedByOp
objeto Qué valor lleva "human:roberto" · "doc" · "directo sin rodeos" · 0.75
provenance De dónde vino (auditoría nativa) qué Trace lo emitió · qué Step · qué humano lo confirmó
Formato
Qué guarda
Problema
Log
"User Roberto approved at 22:46 — message: Hook 2 ganó"
oración suelta en un archivo, difícil de buscar y razonar después
Embedding
3072 números que representan "este texto"
similar matemáticamente, no podés leerlo, no podés auditar de dónde vino
Claim
(brief 3aea, approvedBy, human:roberto) + provenance
estructurado · leíble · consultable · trazable a quién lo puso
Por qué importa para vos. Mañana arrancás otro brief sobre el mismo topic. El motor antes de llamar a Sonnet hace claim.recall_decisions sobre el grafo y le pasa al modelo: "hace 1 día, sobre un brief similar, Roberto aprobó la variante Hook 2 con el comentario 'gana sobre los otros 3'". El LLM no adivina ni recuerda — hereda esa decisión como input. Después de 100 workflows no tenés 100 logs ni 100 vectores opacos: tenés un grafo de 500–1000 afirmaciones tipadas sobre tu voz, tus criterios, tus decisiones pasadas. Y cualquier workflow futuro puede consultarlo antes de actuar. Eso es el efecto compuesto.
POSTGRES SUBSTRATE · 16 tablas · pgvectorscale DiskANN 3072d Intent declared POST /api/intents Hono + Zod validation on apps/api :4000 (systemd-managed) ▸ intents (workspace_id isolated) Plan compiled Inngest function handle-intent-declared selects template + substitutes constraints ▸ plans · steps ▸ plan_edges (DAG) Trace executes Inngest function execute-plan topo-sort + dispatch 16 Ops · LLM · vector ▸ traces (partitioned) ▸ step_executions Claim emitted × N Operations runtime claim.recall_* (read) + emitClaim factory durante Steps 1-N ▸ claims (subject, predicate, object) + vec Artifact published Operation artifact.publish@1.0.0 + lineage edges + 3 provenance Claims ▸ artifacts + embedding ▸ lineage_edges approved human verdict POST /api/approvals → step.waitForEvent match → resume (state vive en Redis) ▸ UPDATE artifact ▸ + 2 Claims (approved/comment) CICLO COMPLETO Intent ingresa por HTTP · Plan se compila en memoria · Trace ejecuta el DAG con retries durables · Claims se emiten al grafo conforme avanzan los Steps · Artifact se publica una vez · humano cierra el ciclo con approved.
Cada primitive arriba corresponde a un grupo de tablas en Postgres y a una pieza concreta del runtime. El motor — Inngest + Hono + Operations runtime — es el verbo entre primitives; Postgres es el sustantivo donde quedan. Esto es lo que el speaker llamaba "control plane simbólico": cada cambio de estado del sistema es una fila tipada que persiste, no una mutación en memoria.
Arquitectura · Cómo se conectan las piezas
El recorrido completo de un Intent
Cliente declara un Intent vía HTTP. apps/api lo persiste y emite evento. Inngest compila el Plan, ejecuta el DAG paso a paso contra Postgres + LLMs externos, y suspende durablemente en el human-gate hasta que llegue la aprobación. Cuando llega, reanuda y deposita Claims tipados al grafo.
Cliente Hetzner CX22 · 4 vCPU / 8 GB · self-hosted External apps/web SvelteKit · Vercel (Miles team) curl / HTTP Roberto, scripts, future integrations apps/api · Hono + Bun systemd :4000 ▸ POST /api/intents ▸ POST /api/approvals ▸ GET /api/inngest (sync) ▸ Operations runtime: 16 ops · LLM · DB · vector ▸ env: ANTHROPIC + OPENAI Inngest :8288 durable workflow engine ▸ handle-intent-declared compila Plan + crea Trace ▸ execute-plan topo-sort + dispatch Ops ▸ step.waitForEvent suspende human-gate 24h Redis queue + waits AOF + snapshot 60s SQLite config + history mounted volume Postgres 16 + pgvector + pgvectorscale :5433 ▸ intents · plans · steps · plan_edges ▸ traces · step_executions (partitioned by date) ▸ claims · artifacts (DiskANN 3072d embeddings) Langfuse :3030 + ClickHouse + MinIO observability stack (pending keys → tracing live) Anthropic Sonnet 4.5 composers, scorers OpenAI embedding- 3-large Cohere Rerank v3.5 (when needed) 1. POST /intents 2. event intent.declared 3. exec step poll-interval 5s 4. CRUD intents/traces/claims 5. LLM call (cost-tracked) 6. POST /approvals → resume wait RECORRIDO COMPLETO DE UN INTENT 1. Cliente POSTea Intent → apps/api persiste row + emite "intent.declared". 2-5. Inngest compila Plan + ejecuta Steps en topo-order. Cada Step llama de vuelta a apps/api por HTTP, que ejecuta la Operation (DB + LLM externo) y persiste outputs. 6. En human_gate, Inngest suspende durablemente. POST /approvals → emite "approval.received" → match async.data.artifact_id → reanuda → emite Claims tipados al grafo.
control plane (apps/api)
workflow engine
persistencia (DB + cache)
APIs externos (LLM)
approval resume (durable)
La objeción honesta · "¿Por qué no simplemente RAG con LangChain?"

Cuatro alternativas conocidas
y dónde rompen para este caso

Si ya entendiste los 5 primitives y la arquitectura, viene la pregunta natural: ¿no se puede armar algo parecido con las herramientas que ya existen? Sí — hasta cierto punto. Cada una sirve para un pedazo. Ninguna combina los cuatro requisitos que el substrato resuelve a la vez.
Vanilla RAG
retrieval-augmented generation
Da bien
Recuperar chunks de documentos que se parecen a tu query usando similitud vectorial. Sirve para Q&A sobre un corpus estático (manuales, knowledge bases).
Rompe acá
No puede responder "¿qué decidió Roberto sobre este topic?" — solo encuentra documentos que mencionan el topic. Cero auditoría: el LLM cita un chunk pero no sabés si fue una decisión, una opinión rechazada, o una hipótesis vieja. Sin estructura, cada respuesta puede alucinar sobre afirmaciones que nunca se hicieron.
RAG te dice qué se parece. El substrato te dice qué fue afirmado, por quién, y cuándo.
LangChain · LlamaIndex
orchestration libraries
Da bien
Componer cadenas de prompts y llamadas a LLM con una API consistente. Útil para prototipar pipelines lineales rápidamente en un notebook.
Rompe acá
El estado vive en memoria del proceso. Si el server crashea a mitad de la cadena, arrancás de cero. No tiene human-in-the-loop durable, no tiene retries por step con backoff persistido, no tiene grafo de afirmaciones — solo es pegamento entre prompts. Para producción con flujos de >2 minutos se vuelve frágil.
LangChain es el pegamento entre prompts. El substrato es el motor durable + la memoria estructurada.
AutoGPT · BabyAGI · CrewAI
autonomous agent frameworks
Da bien
Demos virales: agentes que se prompten a sí mismos en loop, eligen tools, descomponen tareas. Espectaculares en YouTube.
Rompe acá
El LLM decide el próximo paso en cada iteración → costo y latencia impredecibles, runs infinitos cuando se confunde. Sin DAG explícito no podés auditar qué va a hacer ni acotar el blast radius. Sin memoria compartida entre runs, cada conversación empieza de cero. Funciona en demos, no en operación.
Los agent frameworks son improvisación. El substrato es partitura con margen de improvisación dentro de cada Step.
Workflow engine solo
Temporal · Inngest · Step Functions
Da bien
Ejecución durable, retries, human-in-the-loop, observabilidad. Es la base de lo que estamos usando — Inngest mismo cae en esta categoría.
Rompe acá
Es solo orquestación. No tiene noción semántica de qué se está afirmando. Cada Step ejecuta lógica suelta; el output queda como blob en una tabla genérica. Sin la capa de Claims tipados arriba, dos workflows del mismo dominio no pueden compartir afirmaciones de manera estructurada — solo eventos.
El workflow engine es el verbo. El substrato agrega el sustantivo (Claims tipados) arriba. Inngest + Postgres + spec = substrato.
El punto del substrato
El substrato combina los cuatro requisitos a la vez: (1) recuperación semántica con embeddings cuando hace falta (heredado de RAG), (2) composición de pasos en DAGs reusables (lo que prometía LangChain pero estructurado), (3) durabilidad en cada paso con human-gate (workflow engine), y sobre eso (4) un grafo de Claims tipados que ninguna de las cuatro alternativas tiene. Las primeras tres son features que el substrato adopta; la cuarta es el moat que ninguna alternativa pública te da.
06
Capítulo seis · El catálogo

Una librería de capabilities
de la que cualquier Plan tira

El catálogo es público (packages/substrate-spec). Cualquier PlanTemplate nuevo agrega entre cero y cuatro Operations específicas del dominio; el resto del motor (durable execution, human-gate, lineage, claims, cost tracking) viene gratis.
73
Operations
Capabilities atómicas registradas en el motor. Cada una declara inputs, outputs, knowledge_access y cost model. El dominio más grande es research (17); la última en sumarse, research.audience_intelligence.
12
Evaluators
Funciones puras que juzgan outputs. Emiten verdict {pass|fail, score, rationale}.
23
PlanTemplates
DAGs reusables que compilan un Intent a un Plan. Los 4 canónicos (abajo) más document-extract/query, junta-yt-review, scene-production y el pipeline de video (script/assembly/publish/reel).
Gate bloqueante
Sin verde no se mergea ni despliega: check + unit (web/api) + e2e-criticos + e2e-fast/3d en el CI soberano. Cada Operation y PlanTemplate gatea ahí.
Research y oportunidad · 17
  • research.synthesize@1.0.0
  • research.keyword_expand@1.0.0
  • research.reddit_search@1.0.0
  • research.youtube_comments@1.0.0
  • research.rank_opportunities@1.0.0
  • research.audience_intelligence@1.0.0
Documentos (RAG)
  • document.ingest@1.0.0
  • document.chunk@1.0.0
  • document.extract@1.0.0
  • document.query@1.0.0
Media y transcripción
  • media.ingest@1.0.0
  • media.transcribe@1.0.0
  • url.fetch_transcript@1.0.0
Video y escena
  • video.script_draft@1.0.0
  • broll.generate@1.0.0
  • heygen.generate@1.0.0
  • voice.tts@1.0.0
Junta (board de agentes)
  • junta.panel@1.0.0
  • junta.compose_verdict@1.0.0
  • junta.load_report@1.0.0
Text y compose
  • text.compose_narrative@2.0.0
  • text.compose_brief@1.0.0
Trace, artifact y memoria
  • trace.query@1.0.0
  • artifact.publish@1.0.0
  • claim.recall_decisions@1.0.0
  • claim.recall_voice@1.0.0
Evaluación
  • evaluator.run@1.0.0
Ventas, métricas y varios
  • prospect.search@1.0.0
  • pricing.observe@1.0.0
  • metrics.digest@1.0.0
  • audience.load@1.0.0

Muestra representativa. El catálogo completo son 73 Operations registradas en el motor (inngest/operations), con research como el dominio más denso.

07
Capítulo siete · Los cuatro Plan Templates

Cuatro DAGs productivos
cada uno con su propio agente

Cada PlanTemplate es un DAG topo-sorteado que se compila desde un Intent. Los colores marcan el tipo de Step: amber LLM, cian evaluator, verde publish, rosa human-gate, gris system.
DAG
Directed Acyclic Graph — grafo de nodos con flechas direccionales y sin ciclos. "Qué paso depende de qué paso", sin morderse la cola. Mismo modelo que usan Git (historial de commits), Excel (referencias entre celdas), Airflow y Temporal (data pipelines). El motor le hace topo-sort al DAG para arrancar cada Step solo cuando todos sus inputs terminaron — lo que permite ejecutar varias ramas en paralelo.
system
LLM
evaluator
publish
human-gate
standup-digest-v1
$0.0222 / run
Karina · PMO Lead · 8 steps · 0 nuevas Ops
s1 trace.query s2 artifact.list s3 claim.dec. s4 claim.voice s5 · LLM compose s6 evaluator s7 publish s8 human-gate → ✓
lead-research-v1
$0.0143 / run
Alexa · Sales · 8 steps · 2 nuevas Ops
s1 claim.dec. s2 claim.voice s3 prospect.search s4 · LLM score_batch s5 · LLM compose s6 evaluator s7 publish s8 human-gate
brief-synthesis-v1
$0.0147 / run
Sofia · Content · 7 steps · 1 nueva Op
s1 claim.dec. s2 claim.voice s3 artifact.list s4 · LLM compose_brief s5 evaluator s6 publish s7 human-gate
video-render-v1
$0.0124 / run
Mae + Sofia + Marcus · 9 steps · 4 nuevas Ops · TTS/compose aún mock
s1 url.transcript s2 claim.voice s3 claim.dec. s4 · LLM script_draft s5 voice.tts s6 video.compose s7 evaluator s8 publish s9 human-gate
08
Capítulo ocho · Human-gate suspension

El gate durable
donde la persona es autoridad final

En v2 el gate no es un step.waitForEvent: ACTIVAR_GATE crea un flow durable en artifact_approval_flows. Revisión, aprobación/elección, acuse del executor y cierre son estados persistidos; deadline_at + el barrido aplican el fallback con CAS, y required_role define quién puede decidir.
Secuencia histórica · así lo hacía el motor v1 (step.waitForEvent) — hoy reemplazada por el flow durable de arriba
Executor
Inngest
API
Roberto
step.run(s7 human_gate)
step.waitForEvent('approval.received', if: async.data.artifact_id == X)
trace.status → awaiting_human · intent.status → awaiting_human
— flujo suspendido hasta 24 horas —
POST /api/approvals { artifact_id, decision: approve, comment }
inngest.send({ name: 'approval.received', data: { artifact_id, decision, ... } })
match async.data.artifact_id → resume step
artifact.status → approved · emit 5 Claims · trace.verdict → pass

Bug crítico aprendido: el match en step.waitForEvent debe usar async.data.X, no event.data.X. event referencia el trigger original (plan.compiled) — usarlo ahí evalúa al tiempo de la suspension, no del evento futuro, y matchea con null. async.data referencia el evento que se está esperando. Sin esto, todos los gates se quedan colgados indefinidamente.

Lo que esto desbloquea: el humano puede aprobar/rechazar/agregar comment, y la decisión queda como Claim tipado consultable por futuros workflows. La próxima vez que armemos un brief sobre el mismo topic, claim.recall_decisions ya tiene "este angle ganó" como contexto. Esto es el efecto compuesto del substrato — no es retrieval, es construcción de memoria estructurada.

09
Capítulo nueve · Smoke test con LLM real

Cuatro templates activados
con Sonnet 4.5 verdadero

Wireamos ANTHROPIC_API_KEY + OPENAI_API_KEY a apps/api/.env, restart systemd, y disparamos un Intent por cada template. Los cuatro alcanzaron awaiting_human con outputs reales. Costo total para los cuatro runs: seis centavos.
standup-digest
$0.0222
content-brief
$0.0147
lead-list
$0.0143
video-reel
$0.0124
Total · 4 templates end-to-end
$0.064
standup-digest · narrative real
"Shipped hoy: video reel 9:16 listo para Fase 1, brief LinkedIn substrato vs RAG aprobado, lead list ICP corrió. En cola hay 5 digests sin resolver desde las 16:32. Mi recomendación es aprobar o rechazar los pendientes antes de cerrar el día."
content-brief · angle generado
"RAG te da respuestas parecidas. Un grafo de claims tipados te da respuestas defendibles. Cuando tu negocio depende de que la IA no invente, la arquitectura importa más que el prompt."
video-reel · script real, hook
"¿Cansado de que ChatGPT te invente respuestas que después no sirven para nada? El problema no es que los agentes sean malos. Es que sus salidas se pierden. Nada se acumula."
lead-list · prospect score con rationale
"Sofía Ramírez (Helix Comms, Founder & CEO). Fit 0.75. Beta testing 3 LLM tools señala experimentación activa con IA. Interés público en marketplaces de skills se alinea con la propuesta."

Lo no trivial: Sonnet razonó sobre el propio backlog del workspace. El narrative de standup-digest mencionó textualmente cuántos digests anteriores quedaron queued y awaiting_human, porque trace.query + artifact.list_recent + claim.recall le feedearon esos datos. La cadena retrieval → composer está validada end-to-end con datos reales del substrato.

10
Capítulo diez · El ciclo cerrado

Aprobamos el brief
y nacieron cinco Claims

El último paso de la sesión fue cerrar el ciclo del content-brief: POST /api/approvals con decision approve y un comment. El gate se reactivó, el artifact pasó a approved, y el substrato materializó cinco Claims tipados que viven para siempre como contexto consultable.
Artifact
Brief LinkedIn
"Substrato vs RAG"
approved
approvedBy →
human:roberto
approvalComment →
"angle es exactamente el frame que quiero. Hook 2 gana sobre los otros 3"
hasKind →
doc
hasStatus →
pending_review (antes de aprobar)
producedByOp →
artifact.publish@1.0.0

Este es el momento donde el substrato deja de ser teoría y empieza a comportarse como capital. La próxima vez que armemos un brief sobre el mismo topic, claim.recall_decisions va a recuperar "Hook 2 ganó sobre los otros 3" como contexto del composer. Sonnet no necesita recordarlo — el grafo se lo entrega. Eso es lo que distingue un substrato de un RAG: no busca documentos parecidos, sirve afirmaciones tipadas con provenance.

Después de una sola sesión, el workspace de Roberto tiene cuatro PlanTemplates productivos, diecisiete Operations atómicas, sesenta y pico de Claims acumulados como contexto compuesto, y un costo real de operación de aproximadamente dos centavos por workflow ejecutado. La pieza importante no es eso. La pieza importante es que cada uno de esos Claims va a estar acá mañana, y dentro de un año, alimentando los próximos workflows que armemos.

11
Extensión · 2026-05-20 · primer estándar externo absorbido

El substrato absorbe
un estándar de la comunidad

Apareció audience.md — un estándar Markdown abierto, escrito en 2025, para definir audiencias de modo que humanos y agentes la consuman igual. La pregunta no era si reemplazaba al substrato. La pregunta era si las primitivas que ya teníamos podían absorberlo sin reescribirlo. La respuesta corta es sí. Lo que sigue es la evidencia.
Qué es audience.md (resumen)

Archivo Markdown plano (AUDIENCE.md) versionable en git con doce secciones canónicas: nombre, summary, primary audiences, jobs-to-be-done, pains, motivations, decision criteria, language and tone, anti-goals, evidence vs assumptions, open questions. Frontmatter YAML opcional con status, owners, last_reviewed. Pensado para que cualquier agente LLM lo lea como contexto base antes de operar sobre la audiencia.

Mapping a las primitivas que ya teníamos

Cero primitivas nuevas. Cuatro alineaciones limpias.

audience.md
El archivo en sí
substrato
Artifact (kind=workspace_doc)

Conceptualmente equivalente a cualquier otro Artifact publicado, solo que su lifecycle es "siempre presente" en lugar de "producido por una Op". Vive en filesystem, no en R2.

audience.md
Las 12 secciones canónicas
substrato
OntologyOverlay (schema scoped al workspace)

Define qué predicados son válidos para hablar de la audiencia (hasJob, hasPain, hasDecisionCriterion). Otros workspaces pueden tener overlays distintos.

audience.md
Evidence vs Assumptions (sección requerida)
substrato
Claim.provenance.source_refs + Claim.confidence

Esta es la coincidencia perfecta. Evidence con source verificable = Claim confidence alta + source_refs poblado. Assumption = confidence baja + source_refs vacío. El estándar y el substrato describen exactamente la misma distinción.

audience.md
YAML frontmatter (status, owners, last_reviewed)
substrato
WorkspaceManifest metadata

Cumple Rule A: declarativo, versionable, durable. Idéntica filosofía al manifest del workspace.

El wiring · brief-synthesis-v1 antes y después

Un step nuevo. Cero refactors al resto del DAG.

ANTES (5 days ago)
s1 recall_decisions vector s2 recall_voice vector s3 list_recent db query s4 compose_brief LLM Sonnet 4.5 s5 evaluator → s6 publish → s7 gate audiencia = solo string corto target_audience tesis Roberto / voz / pillars no llegan al prompt
DESPUÉS (2026-05-20)
s0_audience NEW fs read · pure s1 decisions s2 voice s3 recent s4 compose_brief LLM + AUDIENCE block s5 evaluator → s6 publish → s7 gate Sofia recibe pillars + voz + prohibited terms audiencia ya no es un string suelto, es un contrato
El archivo concreto · workspaces/ai4managers/AUDIENCE.md

El primer AUDIENCE.md derivado del brand blueprint de Roberto.

---
audience_id: ai4managers
status: stable
owners: [roberto@aguirre]
last_reviewed: 2026-05-20
source_doc: basic-memory:/sistema/reference_ai4m_brand_blueprint.md
---

# Audience: AI4Managers

## Summary
Spanish-speaking middle-to-senior managers (40–55) who built consolidated
careers before the AI shift and now sense the rules of their profession changed...

## Jobs To Be Done
- Understand what AI changes in MY specific role this quarter
- Operate AI tools and agents without becoming an engineer
- Preserve professional standing against AI-fluent juniors

## Language And Tone
**Core terms**: operar, desplegar, agentes en producción, manager, battle-tested
**Prohibited terms**: "estamos en la era de…", "imaginá si…", "lab", emojis,
  jerga ingenieril sin traducción (LLM, RAG, embeddings, fine-tuning)

## Anti-Goals
- Beginner-tutorial content
- Engineering deep-dives that require coding fluency
- English-only or US-only case studies

## Evidence
- Roberto's professional transition (Manager Senior → Head of AI Ops Engineering)
- Corporate training engagements (only currently validated monetization)
- LinkedIn outreach with ~207 unique authors, 33 batches

## Assumptions
- LATAM and Spain share enough vocabulary to be a single audience
- Status preservation > upside ambition (untested at scale)

... 4 secciones más: Motivations, Decision Criteria, Open Questions, Content Pillars
Lo que cambia en el prompt de Sofia

El prompt ahora tiene una regla de precedencia y un guard de pillar.

AUDIENCE DEFINITION (precedence rule):
- If an AUDIENCE DEFINITION block is provided in the user message, it is the
  authoritative source of audience truth. The short "Target audience" label is
  only a hint.
- audience_profile MUST reflect the canonical document: pains, motivations,
  decision criteria, jobs-to-be-done.
- angle, hook_patterns and key_message MUST respect Language guidance:
  use core terms, avoid prohibited terms verbatim.
- If the topic does not fit any allowed content pillar declared in the document,
  set ready_for_production=false and put a one-line reason in key_message
  starting with "PILLAR_MISMATCH:"

El guard de pillar es la pieza interesante. Si alguien dispatcha un Intent para escribir, por ejemplo, un tutorial básico de prompt engineering — que no encaja en ninguno de los cuatro pillars del brand blueprint — Sofia no produce el brief: lo marca PILLAR_MISMATCH con razón. El human-gate lo recibe y Roberto decide si ampliar los pillars o descartar la pieza.

Backward compatibility · cero breakage

Workspaces sin AUDIENCE.md siguen funcionando idéntico al día anterior. El handler retorna { present: false, doc: null } y Sofia hace fallback al string target_audience como siempre lo hizo. Los smoke tests previos (standup, lead, brief sin audience, video) pasan sin tocar nada. La extensión es opt-in al nivel de workspace, no un cambio de comportamiento global.

El delta exacto · qué pesa esta extensión

120 líneas insertadas. 6 archivos modificados. 5 archivos nuevos. 6 tests más.

1
primitive nueva
AudienceDoc
1
op agregada
audience.load@1.0.0
1
template modificado
brief-synthesis-v1
34/34
tests verdes
+6 audience

La lectura arquitectural. Esta es la primera vez que un estándar comunitario externo mapea limpio a las primitivas del substrato sin forzar. Eso confirma una hipótesis que llevábamos asumiendo: el modelo simbólico tiene poder expresivo suficiente para absorber convenciones de la comunidad sin reescribirlas. La próxima extensión candidata es llms.txt (estándar de Jeremy Howard para que crawlers AI consuman docs) — si vuelve a mapear a Artifact + WorkspaceManifest, el patrón "absorb don't reinvent" se vuelve doctrina.

La lectura operativa. El próximo Intent produce_artifact / content-brief dispatched contra el workspace AI4Managers va a generar briefs alineados al brand blueprint sin que nadie tenga que pegarle el documento al prompt cada vez. La tesis, los pillars, las palabras prohibidas, los anti-goals — todo está en el grafo, y se aplica automáticamente.

12
Capítulo doce · As-built · fotografía 2026-06-11 · escrito para estudiantes de ingeniería

La arquitectura hoy,
pieza por pieza

Los capítulos anteriores cuentan el origen (mayo 2026: el substrato nació en una sesión). Este capítulo fotografía lo que existe hoy, construido y verificado en producción: qué es cada componente, para qué sirve, dónde vive y cómo se conecta. Lo que en mayo eran 16 operations y 28 tests, hoy son 73 operations, 13.776 tests y un manual de 34 pasos que un navegador re-verifica contra producción. Si vas a robarte algo de este caso, robate el criterio: cada pieza existe por una razón operativa, ninguna por hype.

El sistema en una frase: una app stateless en Vercel le declara intenciones a un motor en una caja Hetzner; el motor las compila a planes tipados, los ejecuta de forma durable con un LLM cuyo costo se contabiliza paso a paso, fuerza una aprobación humana al final de cada plan, y convierte cada decisión humana en memoria estructurada consultable con SQL.

Antes del mapa — la confusión que conviene desarmar primero

Visto de afuera, un plan en ejecución parece un grafo de pasos con un nodo que consulta una base vectorial. De ahí sale la crítica fácil: "esto es una plantilla de n8n con un nodo de vector, y ya." Confunde el artefacto (un DAG en runtime) con el proceso que lo generó. n8n es un flujo que un humano dibuja a mano. Aquí el flujo no se dibuja: se compone y se valida. Tres diferencias de categoría:

1 · Composición validada
Nova (un LLM) genera el plan desde lenguaje natural; el servidor lo valida formalmente contra el catálogo — refs y edges (validatePlanAgainstCatalog), grafo sin ciclos por Kahn, tope de 10 pasos. Un humano no dibujó este grafo; el sistema lo derivó y lo probó.
2 · Las personas siguen al catálogo
Karina, Alexa, Sofía no son procesos ni tienen capacidad: son etiquetas de identidad. La capacidad vive en las operations del catálogo, y Nova asigna el actor desde ahí — nunca lee el squad. Es la inversión que ningún flujo de nodos tiene: el equipo se deriva del trabajo, no al revés.
3 · Determinismo donde importa
De 73 operations, 55 son handlers de código deterministas; el LLM solo entra en las 18 que componen o juzgan texto. Y todo plan termina en un gate humano con fallback:'fail': el modelo no tiene camino para saltárselo. No es "el LLM hace todo" ni "todo hardcoded".

La sofisticación no está donde la critican (que exista un DAG, que exista un vector) sino donde no miran: la composición-validada, la inversión catálogo↔personas y el gate estructural. Cuatro arquitectos reales — Cherny, Embiricos, Chase, Majors — sometieron este as-built a revisión: acta de la sesión Master-Arq →

El mapa de capas — quién habla con quién
CLIENTE
BrowserSvelteKit 5 (runes) hidratado · oficina 3D Threlte/Three.js · parser SSE propio
↓ HTTPS · cookie httpOnly (el browser jamás ve tokens de servicio)
APP stateless
SvelteKit server · Vercel13 páginas de producto + 15 endpoints · gates fail-closed por request · auth server-first
InsForge (BaaS)identidad: auth.users + perfil JSONB app_state · user_access (gate) · magic_links (one-time)
ResendSMTP transaccional (magic links)
↓ server-to-server · bearer timing-safe · superficie mínima en nginx
BORDE + MOTOR una caja Hetzner
Cloudflare → nginxTLS · expone SOLO health, workspaces/*, approvals, intents · proxy_buffering off para SSE · Inngest NO es público
Hono sobre Bun · systemd :400010 rutas · rate limits · validación de planes contra el catálogo · Nova · idleTimeout: 120
Inngest OSSejecución durable · human-gate step.waitForEvent ≤24h · sobrevive reinicios
Adapter LLMClaude Code headless (CLI, $0 marginal) · fallback API · solo text_delta sale al usuario
↓ SQL · embeddings como columna · trazas con costo por paso
DATOS + OBSERVABILIDAD
Postgres 16 + pgvector/pgvectorscale77 tablas del substrato · DiskANN 3072d en claims y artifacts · 32 particiones mensuales
Langfuse v3web + worker + ClickHouse + MinIO + Redis · traza y costo por trace_id
Crons + backupspg_dump nightly 03:00 UTC → ~/backups/substrate/ (+R2 opcional) · standup digest 07:30 con preflight del CLI

Tres dominios de cómputo, cada uno donde le conviene: lo stateless en Vercel (escala solo, no guarda nada), lo stateful en una caja con disco (Postgres, Inngest, Langfuse: 3 stacks de Docker Compose + un systemd), y la identidad en un BaaS aparte — desacoplada del substrato por diseño. Memoria total observada del lado motor: ~1.7 GB de 15 disponibles.

Las piezas, una por una
A Lo que toca el usuario
SvelteKit 5 + Vercel app consumer

Qué es. Una app full-stack con runes (reactividad compilada) servida serverless. 13 páginas de producto (welcome, onboarding, office, briefing, library, activity, outputs, squads, hire, squad-proposal, discover, deep-dive, share — aparte quedan la demo pública, el callback de OAuth y las legales) cada una con su +page.server.ts que carga datos reales del motor — y degrada a contenido de ejemplo etiquetado si el motor no responde.

Para qué sirve. Es la cara del producto: todo lo que el founder ve y toca. Stateless a propósito: no guarda nada, así Vercel puede crear y destruir instancias sin que se pierda estado.

apps/web/src/routes/ · adapter-vercel · maxDuration 90s en chat y compose
La oficina 3D Threlte + Three.js

Qué es. Una escena voxel renderizada en el browser: 16 agentes en 3 zonas de squad, 12 escritorios, layout calculado por una función pura (testeada con vitest, sin tocar WebGL).

Para qué sirve. Convierte "tenés agentes trabajando" en algo que se ve. El recap de la oficina ("N shipped · M await review") viene de un endpoint del motor que agrega trazas reales de las últimas 24h.

apps/web/src/lib/scenes/ · OfficeScene, VoxelAgent, SquadZones, CameraRig
ChatDrawer + parser SSE streaming

Qué es. El chat con cada agente. Un parser SSE propio (~60 líneas, puro, testeado) pinta cada fragmento de la respuesta en una burbuja transitoria; al abrir el drawer con sesión vacía, hidrata el hilo persistido en el motor.

Para qué sirve. Conversar con un agente sin proceso esperando: la "continuidad" del agente vive en su memoria (briefing + trabajos + hilo), que el motor arma en el instante del mensaje. Si el stream se corta antes del primer fragmento, degrada al camino clásico con un único retry.

lib/components/chat/ChatDrawer.svelte · lib/chat/sse.ts · lib/chat/session.ts
NovaModal + SkillLaunchModal puerta única

Qué es. La UI de "pedile a Nova": un textarea en lenguaje natural y un modal que muestra el desenlace — match con un SuperSkill existente, plan nuevo compuesto (con costo estimado y pasos en humano), o un "no puedo" honesto. Desde ahí el plan se guarda con nombre propio.

Para qué sirve. El usuario nunca navega un catálogo técnico: pide. Y lo que Nova compone puede promoverse a SuperSkill relanzable desde la library.

lib/components/library/NovaModal.svelte · SkillLaunchModal.svelte
B Identidad y sesión
InsForge BaaS de identidad

Qué es. Un backend-as-a-service que aporta auth.users con perfil JSONB passthrough, más dos tablas propias: user_access (gate booleano de beta privada, fail-closed) y magic_links (tokens CSPRNG de un solo uso, 15 min, consumo atómico con UPDATE … WHERE consumed_at IS NULL).

Para qué sirve. Toda la identidad, separada del substrato por diseño. El estado de la app del usuario (squad, onboarding, SuperSkills instalados) vive en profile.app_state — el perfil ES la base de datos del consumer.

migrations/2026…create-user-access.sql · create-magic-links.sql · SDK @insforge/sdk
hooks.server.ts el guardián por request

Qué es. El hook de SvelteKit que corre en CADA request del server: lee la cookie httpOnly, renueva el access token con el refresh si expiró (~15 min), y consulta user_access con la service key.

Para qué sirve. Fail-closed de verdad: error leyendo el gate = no autorizado. El bypass para tests E2E existe pero está doblemente gateado (dev && CI) — en build de producción esa rama es código muerto.

apps/web/src/hooks.server.ts
Auth server-first la lección del refresh token

Qué es. Login, OAuth de Google y magic links se intercambian en el server (client_type=server), nunca en el browser. Razón: el SDK de browser hace el intercambio PKCE en su constructor y jamás expone el refresh token — sin él, las sesiones morían a los 15 minutos.

Para qué sirve. Sesiones de 30 días con renovación silenciosa, y tokens que viven solo en cookie httpOnly/secure.

lib/server/auth.ts · routes/api/auth/* · routes/auth/callback
/api/user/state estado con disciplina

Qué es. El único endpoint que escribe app_state: hace read-merge-write del estado completo, re-sanitiza campos con pick explícito (el junk no persiste) y rechaza bodies >64KB. Del lado cliente, una write-queue serializa los POSTs (dos escrituras en vuelo se pisarían) con update optimista y revert compare-and-swap.

Para qué sirve. Multi-dispositivo sin servidor de estado: creás un squad en un browser, lo ves en otro. localStorage queda como cache write-through, con precedencia perfil > cache > derivación.

routes/api/user/state/+server.ts · lib/stores/userState.ts · lib/squads/store.ts
C El borde
Cloudflare → nginx superficie mínima

Qué es. TLS con certificado origin de Cloudflare y un solo site de nginx que proxya 443 → 127.0.0.1:4000 exponiendo únicamente /health, /api/workspaces/*, /api/approvals y /api/intents. El endpoint de Inngest no existe para internet. Para el SSE del chat: proxy_buffering off y read timeout de 120s.

Para qué sirve. Que el motor tenga exactamente la superficie que la app necesita y ni un path más. El puerto 4000 está cerrado al público por firewall; solo loopback y el bridge de Docker llegan.

substrate-infra/nginx/api-substrate.digitalhubassist.ai.conf
Bearer + rate limiting middleware

Qué es. Un token bearer server-to-server comparado en tiempo constante (anti timing-attack), y un rate limiter de ventana deslizante en memoria: chat 10/min, compose 4/min, intents 10/min — montado después del bearer (un 401 no consume cupo), respondiendo 429 con Retry-After.

Para qué sirve. El LLM es el recurso caro; los límites protegen la sesión de un loop accidental o un script ansioso. El browser jamás ve este token: solo la app server-side lo usa.

apps/api/src/middleware/bearer-auth.ts · rate-limit.ts
D El motor
Hono sobre Bun systemd :4000

Qué es. Una sola app HTTP con 10 archivos de ruta: POST /api/intents, POST /api/approvals, y bajo /api/workspaces/:id/ — outputs, activity, brief (GET/PUT), manifest, chat (+history), compose (+launch/discard), superskills (+launch). Corre como unidad systemd con restart on-failure y logs a archivo.

Para qué sirve. Es el único punto de entrada al substrato. Detalle con cicatriz: idleTimeout: 120 en el export del server — el default de Bun (10s) mataba el SSE cuando el modelo razonaba más de 10s en silencio. Lo cazó el manual vivo, no un test unitario.

apps/api/src/index.ts · routes/ · .runtime/{stdout,stderr}.log
El catálogo: 73 Operations la costura de sustitución

Qué es. Setenta y tres refs tipadas y versionadas repartidas en 33 dominios (trace.query, claim.recall_decisions, text.compose_brief, video.compose…); son 70 ids, porque tres conviven en dos versiones vivas. El dominio más grande es research, con 17. Todas tienen handler de runtime salvo dos — human_gate.approve y human_task — que no lo tienen a propósito: las ejecuta el propio executor suspendiendo el workflow.

Para qué sirve. La operation es la unidad con contrato (inputs tipados → outputs + costo) y la costura donde se pondría en cuarentena cualquier dependencia volátil — un framework de agentes cabe adentro de una operation; la inversa no.

packages/substrate-spec/src/catalog/ · apps/api/src/inngest/operations/
Compiler + 23 PlanTemplates + 12 evaluators Intent → Plan

Qué es. Un Intent declarado se compila a un Plan: DAG topo-sorteado de steps donde cada step referencia una operation del catálogo. Los planes que compone Nova pasan una validación server-side de 4 capas (cada operación existe en el catálogo, el grafo no tiene ciclos — Kahn —, tope de 10 steps, y la aprobación final la agrega el sistema); todo lanzamiento — de fábrica, compuesto o guardado — re-valida contra el catálogo y exige estructuralmente el gate humano terminal. Los evaluators emiten verdicts por paso.

Para qué sirve. Que "el modelo decidió" nunca sea la explicación de una ejecución: lo que corre es un artefacto tipado que el servidor validó, con costo estimado calculado server-side (el modelo no puede mentirlo).

apps/api/src/substrate/plans.ts · packages/substrate-spec/
Nova (compose) una llamada, tres desenlaces

Qué es. Una sola consulta al modelo decide: MATCH (el pedido corresponde a un SuperSkill existente — validado server-side contra la lista real, el modelo no puede inventar ids), COMPOSE (arma un plan nuevo usando SOLO operations del catálogo), o CANNOT (lo dice honesto y el pedido queda registrado como demanda).

Para qué sirve. Es el único lugar del sistema donde el LLM "diseña" — y por eso es el más vigilado: lo que propone entra por su validación de 4 capas y por el mismo gate estructural que todo lo demás. Cada pedido queda en plan_drafts como telemetría: los "no pude" priorizan qué construir.

apps/api/src/substrate/nova-compose.ts · routes/compose.ts
SuperSkills + launch-plan planes promovidos

Qué es. Un plan compuesto que gustó se promueve con nombre propio al catálogo del workspace: se re-valida contra el catálogo ANTES del insert, y un unique parcial en DB hace imposible el doble-promote (gana exactamente uno, el otro recibe 409). El lanzamiento usa un helper compartido con los planes de fábrica: assertHumanGatePresent verifica estructuralmente ≥1 gate humano terminal con fallback 'fail'.

Para qué sirve. Que lo que el usuario crea pidiendo tenga las mismas garantías que lo que construyó el equipo. El formulario de relanzado se deriva mecánicamente de las variables que el propio plan declara.

apps/api/src/substrate/superskills.ts · launch-plan.ts · routes/superskills.ts
Adapter LLM CLI Max + fallback API

Qué es. Un adapter propio con dos backends: Claude Code headless por CLI (sesión de suscripción Max — costo marginal $0) y la API oficial como fallback. Para streaming parsea NDJSON línea a línea y emite SOLO text_delta — el razonamiento interno del modelo jamás llega al usuario; la línea result final gana sobre lo acumulado.

Para qué sirve. Separar arquitectura de etapa: la beta corre a costo cero, y como cada paso registra su costo notional en dólares, la economía unitaria del switch a API se conoce HOY ($0.013–0.022 por digest). Prender la API es configuración, no re-arquitectura.

apps/api/src/inngest/llm.ts · LLM_PROVIDER=claude-cli
E Orquestación y memoria
Inngest OSS ejecución durable

Qué es. El runtime durable self-hosted para eventos, crons y reintentos operativos. handle-intent-declared selecciona v2 y arranca una corrida sobre el grafo compilado; worker-orquestacion da cuerda al worker que consume work_items; los barridos recuperan leases y gates vencidos; materialize-manifest conserva su cron. execute-plan permanece únicamente como executor v1 en deprecación y canario anti-zombie.

Para qué sirve. Inngest hace durable la entrega y el housekeeping, pero ya no es la autoridad sobre el avance del DAG. Esa verdad vive en Postgres: runs + run_events, plegados por el reducer, y la cola transaccional que procesa el worker v2. Un deploy puede interrumpir un tick; no puede borrar lo que la corrida ya decidió.

apps/api/src/inngest/functions/ · apps/api/src/orquestacion/ · substrate-infra/inngest/ · :8288 (no público)
Postgres 16 + pgvector/pgvectorscale el substrato físico

Qué es. Una instancia dedicada (Docker, :5433) con 77 tablas lógicas y 32 particiones mensuales. Las dos de mayor volumen — traces y step_executions — particionadas por mes. Los embeddings (3072d, text-embedding-3-large) son una columna en claims y artifacts con índice StreamingDiskANN — HNSW topa en 2000 dimensiones, por eso pgvectorscale.

Para qué sirve. Memoria semántica y hechos tipados en el MISMO store: un JOIN entre similitud vectorial y claims estructurados, sin dual-write contra un vector DB externo. La consistencia es gratis porque hay una sola base.

db/substrate/migrations/0001–0007 · substrate-infra/postgres/
El grafo de provenance lineage obligatorio

Qué es. La cadena intents → plans (steps, edges) → traces → step_executions → artifacts / claims, más lineage_edges con una vista recursiva (profundidad máx 12, guard de ciclos). Regla dura: todo claim y artifact DEBE tener provenance {trace_id, step_id}.

Para qué sirve. Auditoría nativa: de cualquier afirmación del sistema se puede responder "¿de dónde salió esto?" caminando el grafo — qué trace la emitió, qué step, qué humano la confirmó.

db/substrate/migrations/0001_init.sql · lineage_upstream view
workspace_manifests Regla A

Qué es. Un snapshot navegable por workspace (squad, intents activos, artifacts recientes, decisiones, índice de navegación) materializado cada noche y on-demand por evento. Desde la migración 0006, la columna manifest JSONB es la fuente de verdad.

Para qué sirve. Que un agente arranque sabiendo dónde está parado sin listar media base: lee UN registro. Es cache derivado — se puede reconstruir entero desde el grafo.

apps/api/src/substrate/manifests.ts · inngest/functions/materialize-manifest.ts
F Operación y verificación
Langfuse v3 observabilidad LLM

Qué es. Stack self-hosted de 6 servicios (web, worker, ClickHouse, MinIO, Redis, Postgres propio) que traza cada llamada LLM con latencia, tokens y costo, correlacionada por trace_id del substrato. Con cicatriz propia: ClickHouse capado a 2 CPUs después de un runaway que clavó 7.7 cores durante 19 horas.

Para qué sirve. Responder "¿qué hizo exactamente el modelo en este paso y cuánto costó?" con un click. El único Redis del sistema vive adentro de este stack — no está en el camino de la app.

substrate-infra/langfuse/ · :3030
Crons + backups la rutina nocturna

Qué es. Dos crons: backup nightly (03:00 UTC, pg_dump gzip a ~/backups/substrate/, retención 30 días, push opcional a R2) y standup digest diario (07:30 UTC, declara un Intent real vía la API pública con bearer — con preflight que verifica la salud del CLI antes de disparar).

Para qué sirve. El backup es la respuesta al dominio de falla catastrófico (RPO ≤24h). El digest es el latido del producto: el sistema se usa a sí mismo todos los días. Los dumps crecen con el sistema: 680K en mayo, 1.8M hoy.

substrate-infra/scripts/backup-substrate-db.sh · standup-digest-daily.sh
La verificación como producto 13.776 tests + manual vivo

Qué es. Tres anillos: 13.333 tests unit y de integración (4.102 de la app, 9.058 del motor contra Postgres real, 173 del spec), 443 E2E/visuales con Playwright repartidos en 81 archivos (journeys completos + regresión de screenshots), y el anillo exterior — un manual de 34 pasos que un navegador real re-ejecuta contra producción antes de cada demo, con usuario efímero y limpieza quirúrgica del workspace.

Para qué sirve. El anillo exterior caza lo que los internos no ven: el bug del idleTimeout (SSE muerto a los 10s de silencio) pasó todos los tests unitarios y E2E — lo encontró el manual. Si un paso se rompe en producción, su sello deja de decir VERIFICADO.

tools/manual-walkthrough/ · apps/web/tests/ · apps/api/src/**/*.test.ts
Anatomía de tres requests — el sistema en movimiento

La arquitectura estática es la mitad de la historia. Estos son los tres caminos que más enseñan, paso por paso:

Flujo 1 · Ejecutar un SuperSkill (el ciclo completo)
01
El founder toca "Ejecutar" en la library. El browser manda SOLO {workflowId, input} — nada más viaja.
browser
02
El proxy de la app valida sesión + gate, valida el input contra el catálogo cerrado y construye el Intent completo server-side: declared_by = human:<email>. POST al motor con bearer.
app · vercel
03
El motor compila el Intent a un Plan (DAG tipado) y lo valida contra el catálogo; assertHumanGatePresent verifica estructuralmente el gate humano terminal. Si el plan no termina en aprobación humana, no existe ejecución posible.
motor
04
Inngest camina el DAG: cada step despacha su operation, registra inputs/outputs snapshot, verdict del evaluator y costo en dólares. Si el proceso se reinicia a mitad, la ejecución continúa donde estaba.
inngest
05
El último step produce un Artifact pending_review y el workflow se SUSPENDE (step.waitForEvent, hasta 24h). No hay polling: el proceso duerme.
human-gate
06
El founder aprueba con comentario en /outputs. El evento resume el workflow: artifact → approved y la decisión se materializa como Claims tipados (approvedBy, approvalComment…) que los próximos planes consultarán vía claim.recall_decisions. Si nadie decide en 24h: estado terminal expired, sin huérfanos.
memoria
Flujo 2 · Una pregunta al chat, en streaming
01
El drawer se abre con sesión vacía → hidrata el último hilo desde el motor (fail-soft: si no responde, el chat abre vacío e igual funciona). Enviar continúa el mismo conversation_id.
browser
02
El motor persiste el mensaje del usuario, arma al agente EN ESE INSTANTE (persona + briefing + últimos trabajos con comentarios de aprobación + hilo) y lanza el CLI en modo stream.
motor
03
El parser NDJSON re-emite SOLO text_delta como eventos SSE. nginx no bufferea (proxy_buffering off); la función de Vercel hace pipe del body sin acumular; el browser pinta cada delta en la burbuja.
sse end-to-end
04
La cicatriz didáctica: el default de Bun cortaba toda conexión muda >10s — y el modelo razona en silencio (sus thinking deltas se filtran). Preguntas cortas pasaban; las "de trabajo real" morían y degradaban al camino clásico. Fix: idleTimeout: 120. Lo cazó el manual vivo en producción.
lección
05
El evento done trae la reply canónica; la burbuja transitoria desaparece y la reply se persiste — SOLO si el stream completó. No quedan respuestas a medias en el historial.
persistencia
Flujo 3 · Nova compone, el usuario lo guarda
01
El pedido en lenguaje natural va al motor. UNA llamada al modelo decide: ¿match con un SuperSkill existente, plan nuevo, o "no puedo"? El match se valida contra la lista real del workspace — un id inventado no existe.
nova
02
Si compone: el plan usa SOLO operations del catálogo y pasa las 4 capas de validación de Nova (existencia, sin ciclos, tope de steps, aprobación final). El costo estimado lo calcula el servidor desde el catálogo. El usuario ve los pasos en humano y el precio.
validación
03
"Guardar como SuperSkill": el plan se re-valida y se inserta en el catálogo del workspace. Doble-guardado imposible: unique parcial en DB, la carrera la gana exactamente uno.
promote
04
La card aparece en "Tus SuperSkills" — leída del motor, fail-soft. Nova la ve en pedidos futuros (≤20 customs en su prompt) y puede ofrecerla en vez de componer de cero.
library
05
Relanzar pasa por el MISMO helper que los planes de fábrica: assertHumanGatePresent — sin gate humano terminal, no hay launch. Las garantías no dependen de quién creó el plan.
launch
El modelo de datos — 15 tablas, cada una con su porqué
TablaQué guardaEl porqué
intentsQué quiere lograr quién, en qué workspaceEl punto de partida auditado de TODA ejecución; soporta árbol (parent_intent_id)
plans + steps + plan_edgesEl DAG ejecutable compilado del IntentVersionado por intent; cada step lleva operation_ref, retry policy, timeout y human_gate opcional
operationsEl catálogo: metadatos de cada operación, versionadosDeprecar sin borrar; el plan referencia id@version
tracesCada ejecución concreta de un plan (estado, costo real, verdict)Particionada por mes; entrada al lineage
step_executionsCada step ejecutado: inputs/outputs snapshot, error, verdict, costoLa auditoría fina; particionada igual que traces
claimsHechos tipados (sujeto · predicado · objeto) + provenance + confidence + embedding 3072dLa memoria del equipo; recallable por SQL y por similitud (DiskANN)
artifactsLo producido (docs, videos, digests…) content-addressable (sha256), con status machinepending_review → approved/rejected/expired; dedup por contenido
lineage_edgesAristas de provenance entre claims y artifactsVista recursiva (profundidad 12, cycle guard): "¿de dónde salió esto?"
evaluatorsCatálogo de cómo evaluar (deterministic / llm_judge / human) con thresholdLos verdicts por paso salen de acá
ontology_overlaysRelabels por vertical sobre el mismo grafoWorkspace A dice "video reel", workspace B "blueprint" — mismos primitives
workspace_manifestsEl snapshot navegable por workspace (Regla A)Cache derivado, reconstruible; columna manifest JSONB como SSOT
chat_messagesEl hilo founder ↔ agente por conversaciónEl motor lee los últimos 20 para armar el prompt; la app hidrata al recargar
plan_draftsCada pedido a Nova con su desenlace (proposed/matched/rejected/launched)Telemetría de demanda: los "no pude" priorizan el roadmap
superskillsPlanes promovidos con nombre, por workspaceUnique parcial anti doble-promote; archive en vez de delete
+ en InsForge (identidad, separada del substrato): user_access (gate fail-closed de beta privada) · magic_links (tokens one-time con consumo atómico) · profile.app_state (el estado del consumer)

Detalle que un estudiante debería notar: workspace_id está en todas las tablas de datos del workspace desde la migración 0001 (intents, traces, claims, artifacts, lineage_edges, manifests, chat, drafts, superskills); las de definición (plans, steps) lo heredan vía su intent, y las de catálogo (operations, evaluators, overlays) son globales por diseño. El modelo de datos es multi-tenant desde el día uno — el aislamiento pendiente está en la capa de routing, no en los datos.

Siete decisiones para robarse
1
Columna vertebral aburrida, costura para lo volátil. El spine es Postgres + un executor durable + SQL: tecnología que no rompe APIs entre versiones. Lo que cambia rápido (frameworks de agentes, modelos) queda en cuarentena adentro de una operation. La innovación va en el modelo de memoria, no en la infraestructura.
2
La gobernanza vive AFUERA del LLM. El servidor valida los planes (4 capas), fuerza el gate humano estructuralmente y calcula los costos. Al modelo no se le confía nada que se pueda verificar. "Trust the LLM" es una filosofía válida para tooling de developers; para entregables de negocio, la inversa.
3
Dominios de falla explícitos. Contra lo frecuente (deploys, crashes): ejecución durable que sobrevive reinicios. Contra lo catastrófico (la caja): backup nightly con RPO ≤24h. Saber qué mecanismo cubre qué — y decirlo — vale más que prometer multi-región.
4
Fail-soft en toda lectura. Si el motor no responde en 2.5s, la página carga igual (con contenido de ejemplo etiquetado); si el historial no llega, el chat abre vacío. Recuperar datos nunca bloquea usar el producto. Las escrituras, en cambio, fallan ruidosamente.
5
Costo por paso desde el día uno. Cada step registra su costo en dólares aunque hoy el LLM cueste $0 marginal. Resultado: la economía unitaria del switch a API se conoce ANTES de necesitarla. Instrumentar costos es barato al principio y carísimo después.
6
El identificador de tenant va en los datos antes que en el routing. workspace_id en cada tabla de datos desde la 0001 (las de catálogo son globales por diseño). Cuando llegue multi-tenancy real, es trabajo de capa de routing — no una migración de datos con el producto andando.
7
La verificación es parte del producto. El anillo exterior — un manual que un navegador re-ejecuta contra producción — cazó el bug que 800+ tests internos no podían ver, porque solo existe en el camino completo real. Si tu sistema le habla a usuarios, algo tiene que recorrerlo como un usuario.
Las cifras as-built

Medidas contra el repo el 2026-09-03, no estimadas: el catálogo se cuenta importando OPERATION_CATALOG, y los tests corriendo las suites. Cualquiera de estos números que quede viejo es un bug del documento.

73
Operations
23
PlanTemplates
12
Evaluators
77
tablas (+32 particiones)
13.776
tests (13.333 unit + 443 E2E)
34
pasos verificados en prod
~1.7GB
RAM del motor (de 15)
$0.013–0.022
por digest (notional)
Lo que NO hay todavía — la otra mitad de la honestidad

Routing multi-tenant (el modelo de datos está listo; la beta sirve un workspace con gate de founder) · alta disponibilidad y RTO formalizado (hay durabilidad de runs y backup nightly, no hay réplica ni drill de restore documentado) · alerting proactivo con SLOs (hay observabilidad, nadie despierta a las 3am) · regresión offline de evals (el human gate ya acumula el dataset etiquetado; falta el harness en CI). Las cuatro están asumidas, clasificadas y comprometidas — el detalle vive en el red-team de arquitectura.

Para cerrar el caso de estudio. La base de este sistema son marcas que cualquier estudiante reconoce: Postgres, nginx, Vercel, Cloudflare, Anthropic, Inngest. Lo custom es una capa fina, tipada y testeada. La pregunta que ordenó cada decisión no fue "¿qué tecnología está de moda?" sino "¿qué cosa madura y barata cubre esto sin pintarnos en una esquina?" — y la única apuesta original de verdad, la memoria estructurada de decisiones humanas, es exactamente la pieza que ningún framework vendía. El recorrido completo del producto, con cada paso verificado contra producción, vive en el manual del operador.

13
Capítulo trece · La Junta de Arquitectos responde

La Junta responde

Cuatro arquitectos reales de agentes de coding tomaron las preguntas que dejaron los estudiantes y un padre de familia, y las contestan desde su lente. Boris Cherny (Claude Code · Anthropic), Alexander Embiricos (Codex · OpenAI), Harrison Chase (LangChain/LangGraph) y Charity Majors (Honeycomb). Las citas marcadas ⚠ son recaps de charlas públicas (no transcripción literal); las ✓ son verbatim de fuente propia.
Estudiante
«¿Por qué no usaron Redis IRIS? El video lo muestra como el "context engine": espeja los datos a RAM por change-data-capture, retriever semántico que auto-descubre entidades como tools, y caché de respuestas del LLM.»
Boris Cherny — do the simple thing

IRIS es andamiaje para un cuello de botella que este sistema no tiene. Espejar todo a RAM resuelve latencia de alto tráfico sincrónico — cientos de miles de usuarios, 10-15 tool calls por pregunta. La carga de Agent Squad es la opuesta: pocos workflows profundos y asíncronos, un digest tarda minutos por diseño. Cuando agregás una capa, preguntate si sobrevive al próximo modelo o si la estás poniendo porque el de hoy no llega.

⚠ recap: "scaffolding gains get wiped out by the next model"
Charity Majors — ¿qué se rompe a las 3am?

El costo que IRIS te cobra es el que más duele en producción: una segunda copia de tus datos por CDC es un segundo sistema que mantener consistente — y la consistencia entre dos stores es exactamente lo que falla un domingo a las 3am, sin que lo hayas anticipado. No operes lo que no necesitás operar.

Harrison Chase — context engineering

IRIS es una solución de contexto para alto tráfico. Acá el contexto se controla por el catálogo y los claims tipados — sabés exactamente qué entra al LLM en cada paso, que es el problema difícil de verdad. Matiz honesto: si algún día aparece un agente sincrónico de alto tráfico, el patrón de IRIS cabe adentro de una operation. Hoy no.

✓ blog: "make sure the LLM has the appropriate context at each step"
En una línea: no es que IRIS sea malo — resuelve un problema (latencia de alto tráfico) que Agent Squad no tiene, y agrega uno (mantener dos copias de los datos) que hoy no tenemos. La herramienta correcta para la escala correcta.
Estudiante
«¿Por qué los squads siempre se arman de tres personas? Parece hardcodeado. El LLM debería conversar a fondo al inicio para componer el equipo, y en la última milla para producir los entregables.»
Alexander Embiricos — tenías razón

La intuición es correcta y ya se actuó sobre ella: hay una conversación inicial donde el sistema descubre qué trabajo necesitás, propone las capacidades y deriva el equipo de ahí. El "3" era un default de presentación, nunca una restricción. Lo que sigue es tu segunda mitad — la última milla: que el agente produzca con más autonomía, no que el humano conduzca paso a paso.

⚠ recap: "you're delegating to it rather than pairing with it"
Boris Cherny — las personas siguen al catálogo

El squad nunca fue el centro del diseño. Las personas son etiquetas de identidad; la capacidad vive en el catálogo de operations. El planner compone desde el catálogo y nunca lee el squad — así que el número de personas emerge del trabajo, no se fija de antemano. Hardcodear el tamaño habría sido el error que el estudiante temía; el sistema lo evita por construcción.

Harrison Chase — composición validada

Lo que ves no es hardcode ni autonomía total: es un router/state-machine que genera el plan desde lenguaje natural y lo valida contra un catálogo, con guardrails. El equipo es un subproducto de qué operations entraron al plan. Esa es la diferencia entre dibujar un flujo a mano y derivarlo.

En una línea: tenías razón en que no debe ser fijo — y ya no lo es. El equipo nace del trabajo: una conversación inicial descubre qué hace falta, y las personas salen del catálogo, no de un número escrito a mano.
Padre de familia
«Mi hijo no sabe qué estudiar de ahora en adelante. Con todo esto de la IA, ¿qué debería profundizar?»
Boris Cherny

Que entienda el modelo y cómo darle herramientas y un objetivo — no que se case con un framework de moda que el próximo modelo deja obsoleto. La habilidad durable es saber cuánto andamiaje poner y cuánto borrar.

Alexander Embiricos

Que aprenda a dirigir y delegar a agentes. El cuello de botella del futuro no es la capacidad del modelo, es la velocidad del humano para encargar y revisar. Quien sepa orquestar varios agentes a la vez rinde por diez.

Harrison Chase

Sigue siendo ingeniería de sistemas. El trabajo difícil de los agentes no es el modelo: es controlar qué información entra a cada paso. Fundamentos de sistemas + "context engineering" no caducan con el próximo release.

Charity Majors

Que aprenda a operar sistemas en producción — observabilidad, "¿cómo sé que esto funciona y cómo lo arreglo cuando falle?". Los sistemas con IA son no-deterministas; entender cómo se comportan cuando se rompen es una habilidad que la IA no automatiza pronto.

En una línea: fundamentos de sistemas (no recetas de un framework) + cómo trabajar con la IA — darle herramientas, orquestarla y operarla. Lo que dura es el criterio, no la sintaxis del momento.
Padre de familia
«Si emplearan a mi hijo como practicante, ¿en qué pieza lo pondrían a trabajar para que aprenda de verdad?»
Charity Majors — el mejor asiento

En el anillo de verificación: el robot que recorre el producto entero contra producción cada noche. Para verificar un solo paso tenés que entender TODAS las capas que lo atraviesan — pantalla, servidor, motor, base. Es el único asiento desde donde se ve el mapa completo, y con riesgo cero: usa usuarios de prueba que se autodestruyen. Test in prod, desde el día uno.

✓ "Nines don't matter if users aren't happy"
Alexander Embiricos

El practicante es el caso de uso del sistema: un trabajador con energía y sin criterio formado todavía — igual que el modelo. Las mismas barandas que contienen a la IA lo contienen a él, así que puede hacer trabajo real desde la semana uno, que es la única forma de aprender.

Harrison Chase

Después del anillo, dale una Operation propia del catálogo: una pieza determinista de punta a punta. Ahí aprende a controlar exactamente qué entra y qué sale de un paso — el músculo central.

En una línea: primero el anillo de verificación (ve todo el mapa, riesgo cero), después su propia Operation del catálogo. El recorrido paso a paso ya está escrito en la Guía del practicante →
Estudiante
«¿Esta plataforma es mejor que Hermes Agent?»
Primero, el dato — qué es Hermes Agent (verificado)

Framework open-source de Nous Research (feb 2026): un agente personal, self-hosted, que vive en tus mensajeros (CLI, Telegram, Discord, Slack), recuerda entre sesiones y se auto-mejora — tras una tarea compleja escribe una "skill" reutilizable, sin pedir permiso — y se conecta a 200+ modelos. Sus barandas son de infraestructura (container hardening, root de solo lectura), no de aprobación de las acciones. La pregunta no es ociosa: es de las herramientas que más crecen en 2026. hermes-agent.org

Alexander Embiricos — es mi tesis al máximo

Hermes lleva la autonomía hasta el final: se auto-mejora, corre largo, saca al humano del loop. Para un asistente personal es exactamente lo que querés — el humano es el cuello de botella, quitalo. Agent Squad pone una compuerta humana a propósito, porque sus efectos caen sobre un negocio ajeno: un brief que se manda a un cliente, no un script que corrés en tu máquina. Si tu caso es personal y tolera autonomía, Hermes encaja. Si alguien tiene que firmar cada entregable, esa compuerta no es burocracia.

⚠ recap: "the bottleneck is the human, not the model"
Charity Majors — ¿dónde está la baranda?

La diferencia más honesta: Hermes pone la baranda en el contenedor (que el agente no te rompa la máquina); Agent Squad la pone en la acción (que no haga nada con efecto sin firma) y suma provenance y SLOs. Un agente que aprende solo y actúa solo en tus mensajeros: ¿cómo sabés qué aprendió, cómo lo auditás cuando hace algo raro a las 3am? Para uso personal, esa opacidad es tu riesgo — aceptable. Para trabajo que cae sobre clientes, no.

✓ "Nines don't matter if users aren't happy"
Harrison Chase — misma idea, otra governance

Hermes auto-crea skills (orquestación emergente): flexible para tareas personales variadas. Agent Squad deriva un grafo tipado validado contra un catálogo (orquestación explícita): controlable para flujos de negocio repetibles y auditables. Y un punto de humildad: el "closed learning loop" de Hermes y nuestros SuperSkills promovidos son la misma idea — capturar un procedimiento para reusarlo. La diferencia es quién aprieta el gatillo: Hermes lo hace solo; nosotros, con un humano que promueve.

✓ blog: "control exactly what goes into the LLM at each step"
Boris Cherny — apuestas opuestas

Hermes es model-agnostic (200+ modelos) y trae harness: memoria, learning loop, 40+ tools. Eso es opcionalidad y features — y también andamiaje que el próximo modelo puede volver innecesario. Agent Squad apuesta a un solo modelo frontera y pone andamiaje mínimo + barandas duras. Ninguna es "mejor": son la apuesta opuesta. La pregunta que importa no es cuál tiene más, sino cuál de las dos apuestas le sirve a tu problema.

⚠ recap: "bet on the model, not the scaffolding"
En una línea: "mejor" es la pregunta de novato; "mejor para qué" es la de arquitecto. Hermes es un copiloto personal autónomo que aprende solo — brillante si eso necesitás. Agent Squad es un equipo que produce trabajo organizacional que alguien firma, con trazabilidad total — necesario cuando los efectos caen sobre un negocio o un cliente. No compiten en el mismo eje: uno optimiza autonomía personal, el otro governance organizacional. Elegí por tu problema, no por la demo.
14
Capítulo catorce · La sustitución del motor

De dos motores
a uno

Durante semanas convivieron dos autoridades sobre una corrida. Hoy no: producción corre v2 al 100%. Inngest conserva la durabilidad de eventos y crons; el avance del trabajo pertenece al motor event-sourced.

El motor original (v1) vivía en inngest/functions/execute-plan.ts. Recibía plan.compiled, caminaba el DAG de plans/steps y mantenía un workflow suspendido con step.waitForEvent cuando encontraba un human gate. Funcionó, dejó trazas útiles y probó la tesis. También concentró demasiada autoridad dentro de una función difícil de reconstruir, inspeccionar y reemplazar.

v2 cambió la unidad de verdad. El compilador convierte el template en un grafo versionado de graph_nodes. Una corrida vive en runs; lo que le ocurrió vive, en orden, en run_events. Un reducer puro decide transiciones y efectos; aplicar-evento los persiste transaccionalmente; el worker reclama leases de work_items, ejecuta operaciones y devuelve nuevos eventos. Estado derivado, log reproducible, cola explícita. Esa arquitectura ganó porque se puede explicar después de un crash, no solamente mientras todo está sano.

A El cambio de autoridad
Motor v1 executor en deprecación

Qué hacía. execute-plan recorría el Plan, escribía steps y traces, reintentaba operaciones y suspendía el workflow en los gates con step.waitForEvent.

Por qué sale. Su estado efectivo estaba repartido entre el historial de Inngest y las proyecciones del substrato. Reanudar, reconstruir o cambiar el motor exigía entender una ejecución viva, no plegar un log estable.

apps/api/src/inngest/functions/execute-plan.ts · v1, inalcanzable para lanzamientos nuevos
Motor v2 100% de producción

Qué hace. Compila templates a grafos inmutables, registra cada transición como evento y mueve trabajo mediante una cola con leases. El reducer no hace I/O: declara. aplicar-evento persiste. El worker ejecuta.

Qué conserva Inngest. Entrada durable de eventos, crons, retries operativos y el tick que da cuerda al worker. Inngest sigue en el sistema; dejó de ser el lugar donde se esconde la máquina de estados de una corrida.

apps/api/src/orquestacion/ · graph_nodes · runs · run_events · work_items
B El gate dejó de ser una espera
Gate bifásico durable flow propio

Ya no existe un waitForEvent sosteniendo el Plan. El efecto ACTIVAR_GATE crea transaccionalmente un registro en artifact_approval_flows: el entregable aparece para revisión, la persona aprueba y, cuando el protocolo lo requiere, elige una opción; el executor acusa recibo y recién entonces el flow completa.

consumirHandoffV2 aplica la decisión a la corrida y encadena los sucesores. El orden de locks es una invariante, no una recomendación: runs → artifact_approval_flows → work_items. Evita que aprobación, timeout y worker cierren el mismo gate en órdenes incompatibles.

migraciones 0073–0075 · reducer.ts · aplicar-evento.ts · aprobar-gate.ts
Timeout, autoridad y calidad fail closed

Cada flow congela deadline_at, política de fallback y required_role. El cron barrido-gates-vencidos reclama solamente el estado que observó mediante CAS: state = fase AND deadline_at <= now(). Una aprobación que ganó la carrera no puede ser pisada por el timeout.

El gate no es la única barrera. Un evaluator configurado como bloqueante emite un veredicto hard-stop: si falla, el downstream no se despacha. La corrida no convierte una mala salida en trabajo posterior solamente porque el worker siga vivo.

artifact_approval_flows.deadline_at · barrido-gates-vencidos · evaluator gating
C Deprecar sin confiar en la memoria
W1
v1 quedó inalcanzable. seleccionarMotor nunca devuelve v1: una bandera distinta de active + v2 falla cerrado. El handler y launchCompiledPlan tampoco hacen fallback. Una guarda de arquitectura exige cero emisores de plan.compiled. Si el executor viejo recibe uno, emite el canario orquestacion.v1_zombie_awakened y dispara alerta SLO.
prod · cerrado
W2
El kill-switch pasó a decir la verdad. orchestration_state = active|paused controla admisión y está separado de orchestration_engine. Pausar no cambia de motor. Además, WORKER_ORQUESTACION quedó encendido por default y está en on en producción.
prod · cerrado
W3
v2 dejó de depender del archivo que vamos a borrar. Los helpers compartidos salieron de execute-plan.ts hacia substrate/. El executor viejo ya no es una biblioteca accidental del motor nuevo.
prod · cerrado
Lo que sigue es aburrido a propósito: período de soak observando cero actividad v1, borrar execute-plan.ts y después limpiar eventos, columnas y tablas legacy del schema. Primero demostrar que el cadáver no respira; después retirarlo.
siguiente wave
D Las dos cicatrices
El escape hatch que era mentira

“Volver a v1” sonaba reversible, pero no lo era. v1 no podía reanudar una corrida v2, no se ejercitaba con tráfico real y ejecutaba el template, no el grafo compilado que v2 había autorizado. Cambiar la bandera no recuperaba una corrida: creaba otra interpretación del trabajo.

El worker apagado que parecía seguridad

Con dos motores, apagar el worker v2 podía parecer una precaución. Con v2 como único motor es un footgun: acepta corridas, deja work_items en ready y nada avanza. Por eso el default ahora es on; apagarlo exige una decisión explícita y observable.

Los tres resets operativos obedecen la misma conclusión: normalizan el ruteo a orchestration_state='active' y orchestration_engine='v2' sin tocar enabled. Resetear el progreso no revoca permiso ni resucita un motor retirado.

🎧
El viaje continúa · Agosto 2026

Del ruido a la audiencia

El motor de research empezó midiendo demanda: qué se busca, qué se pregunta, qué hueco queda entre lo que la gente pide y lo que hay. Terminó entendiendo a la persona que mira: quién es, qué la mueve, qué la frena, y las palabras exactas de su dolor.

La investigación de mercado del substrato ya era profunda. Medía la demanda compuesta de cada oportunidad por tres dimensiones separadas, nunca una disfrazada de otra: la búsqueda del ángulo, el mercado del tema, y el respaldo social de Reddit y de los comentarios de YouTube, atribuido contra una tabla cerrada para que el modelo nunca escriba un número que no vio. Un pase dedicado ataba la evidencia, y un ranking por frentes de Pareto ordenaba las oportunidades sin sumar peras con manzanas.

Y entonces un fundador dudó de un cero. El informe decía que no había comentarios de valor en YouTube, y él no se lo creyó: "yo manualmente encuentro casos que no son elogios". La duda, insistida cuatro veces, destapó capas reales. Primero, cuatro defectos en cómo el motor descubría los videos: buscaba consejos genéricos ("cómo crecer"), no los videos de problema ("por qué mi canal no crece") donde la audiencia pregunta. Y un hallazgo de fondo: el clasificador léxico no podía distinguir un problema de una curiosidad, porque la diferencia era semántica; la señal limpia estaba en otra capa, en la procedencia de la búsqueda, no en el título.

Pero lo mayor llegó con un video que el fundador trajo a mano: una masterclass de instructora, cuyos comentarios eran historias personales. Ahí el valor no era demanda. Era inteligencia de audiencia: el perfil de quién mira, sus motivaciones, sus barreras, y los huecos que revela contando su historia. El motor lo perseguía sin verlo. Así nació un pase nuevo, r14, que lee los comentarios que el research ya muestreó y extrae cinco cosas honestas contra una tabla cerrada: perfil, motivaciones, barreras, huecos, y frases de dolor como cita textual literal, lista para el guion.

Honesto por diseño, como todo el substrato: cada frase de dolor se valida como fragmento literal exacto del comentario, nunca reescrita por el modelo. Nunca infiere edad ni género por el nombre. Una sola voz dice "una persona declara", jamás "la audiencia es". Y si no hay señal, la sección no se escribe. La demanda dice qué falta en el mercado; la audiencia dice quién está del otro lado. El motor aprendió a escuchar las dos.
3
Waves nuevas
5
Idiomas leídos
100
Comentarios por corrida
r14
Pase de audiencia
v10
Template en prod

En una corrida real de producción, en cinco idiomas, el informe escribió lo que antes se perdía: un creador de 70 años que usa IA como asistente, uno que se identifica como neurodiverso y pierde el hilo si preproduce varios videos, uno que escribe en francés "j'espère que je vais y arriver avant que la maladie m'emporte". Palabras reales de personas reales, escritas en el brief, listas para volverse contenido. El cero que el fundador no se creyó nunca fue el final: era la punta del hilo.

🛡️
Soberanía y guardián · Agosto 2026

El guardián entre el motor y producción

Un motor que evoluciona rápido necesita algo que le impida llevar un cambio roto hasta los usuarios. No confianza en la intención: un guardián que solo deja pasar lo que probó que funciona.

El substrato dejó de depender de terceros para su propia integración. El código y el CI se volvieron soberanos: viven en una caja propia, sin pedirle permiso a nadie. Y con esa mudanza llegó la pregunta que importa de verdad: cuando el motor cambia, ¿qué es lo que decide si ese cambio sale a producción o no?

La suite de pruebas de extremo a extremo llevaba días marcada como "en rojo" y fuera de esa decisión. Al mirarla de cerca apareció algo distinto: no estaba rota, llevaba cinco noches seguidas en verde. El semáforo mentía. Se corrigió lo que el comentario del gate declaraba, y quedó a la vista la deuda real, la única que faltaba pagar: que esa suite volviera a frenar un deploy malo, no solo a mirarlo pasar.

El hallazgo que ordenó todo fue este: no hay un despliegue, hay dos. El de la cara visible y el del motor, cada uno por su camino. Y no se validan igual. Las pruebas de extremo a extremo corren contra un simulador del substrato, así que dicen la verdad sobre la interfaz pero no sobre el motor, que tiene su propia suite. Tratarlos como uno solo habría hecho que un tropiezo de la cara frenara al motor sin motivo. Cada guardián cuida su propia puerta.

Y entonces se probó de verdad, no en el papel. Se rompió un test a propósito: el deploy quedó bloqueado, tal como debía. Se restauró: el deploy corrió. La mordida. El guardián no cree en la promesa de que algo anda; exige la evidencia de que anda, y se comprueba rompiéndolo y viendo que se planta. Rojo bloquea, verde despliega, y ninguno de los dos es una opinión.
2
Despliegues distintos
6
Shards de prueba
5
Noches en verde
Mordida: rojo bloquea
0
Dependencia externa

Con la puerta clara, la suite de la cara pasó a bloquear su deploy: las pruebas livianas de interfaz en cada cambio, y las pesadas de la escena viva cuando el cambio toca esa escena. El motor sigue su camino, validado por lo suyo. El substrato ya no solo se construye rápido: se publica sin que un cambio roto se cuele, y esa garantía está probada a mano, mordida por mordida.

Y el guardián se afinó dos veces más. Dejó de exponer su llave: el token con el que publica ya no viaja en la línea de comando, donde cualquier proceso del servidor podía leerlo, sino en el entorno, fuera de la vista. Y aprendió a no congelarse: una vez la herramienta de publicación se colgó en una llamada de red y se quedó clavada hasta el tope del turno, cuarenta y cinco minutos, hasta que hubo que matarla a mano. Ahora cada paso de publicación tiene un límite de tiempo duro y se reintenta solo, así un cuelgue se corta a los pocos minutos en vez de trabar todo. Un guardián que además no deja su llave a la vista ni se duerme en la puerta.

El substrato, en tus manos · demo guiado

Asígnale una tarea a tu equipo
y míralo trabajar

Tres actos contra el motor real. Le das un objetivo en lenguaje natural; el equipo lo planifica y lo ejecuta; Control de calidad lo revisa antes de entregarlo; tú apruebas o pides cambios. Después le pides algo que tu equipo todavía no sabe hacer — y lo construye.
🧭
Nova
planifica el trabajo
⚙️
El equipo
ejecuta los pasos
🔍
Control de calidad
revisa antes de entregar
Tu aprobación
la última palabra es tuya
Entregable
Acto 3 — algo que tu equipo todavía no sabe hacer
Tu equipo construyó esto