El underlayer: AgentManifest v2 + mapa de migración
Documento de diseño (pre-código). Convierte el north starworker-manifest-northstar
(“declarar un Worker = componer un manifiesto de datos, jamás escribir un servicio”)
en un contrato concreto y un plan de migración, diseñado contra el código real y
validado contra los cuatro agentes existentes como casos de prueba.
Regla de diseño: si Auditor, Librarian, Steward o Chat no caben en el schema
sin pérdida, el schema está mal — no el agente.
El candado que este diseño sirve (invariante en worker-manifest-northstar):
ningún agente nuevo se implementa como servicio bespoke: o es expresable como
manifiesto/datos sobre el runtime común, o espera.
1. Lo que ya existe y se conserva (no partimos de cero)
| Pieza | Estado | Rol en el diseño |
|---|---|---|
AgentRuntime (clase abstracta, cognitive/contracts.ts:130) | Existe — ThinLoopRuntime es impl #1; el comentario ya nombra MastraRuntime como impl #2 | El seam de ejecución. No se toca; se cumple |
AgentManifest/Registry/IntegrationManifest/TriggerKind (contracts.ts:150-235) | Tipos muertos (nada los implementa) | La v1 del contrato — se evoluciona, no se descarta (ver §3) |
Contrato de adopción de Mastra (topic mastra-adoption-contract, reviewed) | Decidido | Mastra = mecánica del loop (suspend/resume, approvals); Driftless = auth, gateway, tools, gobernanza, ledger. Sin semántica de producto en estado de Mastra. Gates y rollback definidos ahí |
Model Gateway v2 + mastra-model.adapter.ts | Existe | Todo modelo pasa por el gateway (BYOM); el manifiesto solo selecciona |
Cola pg-boss + disciplina (topic agent-queue-discipline, reviewed) | Existe | Invariantes: un solo budget de tiempo con tres enforcers; singletonKey como leader election; nada de compute de agente en el web tier; drops ruidosos |
| Policy engine sobre tools canónicas + observador T3 | Existe | El único camino de resolución de tools (§5.3) |
Trace único (RunTraceEvent, topic run-trace-enrichment, reviewed) | Existe | Un solo stream enriquecido; el merge depende de que el runtime pase EL MISMO objeto a onStep y al trace |
El ledger de la ronda 1 (branch claude/driftless-research-features-5srdnn) | Sin mergear — capa de datos | record_events, context_deliveries, invokes con run/record, dossier. El runtime escribe en estas tablas (§5.6) |
AgentRunnerService (hardcodea auditor/architect),
LibrarianRunnerService, StewardService (el cuarto bespoke), y el turno del
Chat — cuatro cableados, tres composiciones de tools, dos-tres loops.
2. Huecos de la v1 (por qué se evoluciona)
La v1 (contracts.ts:195) acertó en lo estructural: el agente ES un objeto, los
Action son una superficie cerrada (“un action que no toca el grafo no
pertenece”), Skill es artefacto de primera clase fuera del loop, Gate decide
si corre, y IntegrationManifest monta en el mismo bus. Se conserva todo eso.
Lo que la v1 no contempla y el north star exige:
- Es código, no datos —
contextProviders/tools/actionsson objetos con funciones. Un Worker declarado desde el chat necesita que el manifiesto sea serializable y que las funciones se resuelvan por NOMBRE contra registros. - Sin identidad de ledger (
actor, acts-as) — el Steward hoy resuelve identidad a mano (owner del workspace); debe ser campo. - Sin cognición declarada — el Steward es determinista (cero LLM). Un manifiesto no debe presuponer modelo.
- Sin budget, autonomía, envelope ni escalamiento — los campos que hacen a la autonomía legible, acotada y reversible (Art. VIII).
- Sin criterion — el juicio versionado que se entrega pre-run (y que la ronda 1 ya sabe registrar como delivery).
- Registry en memoria — los Workers de usuario viven en DB con gobernanza, no en un array de código.
- Sin clase de cola — la disciplina distingue clase-agente (sandbox, watchdog 15/25min) de clase-batch (determinista acotado, estilo Steward).
3. El contrato: AgentManifest v2
Serializable (jsonb). Las capacidades se referencian por nombre; tres registros de código las resuelven: ToolSets, Skills, ContextProviders (los objetos-con-funciones de la v1 se convierten en entradas nombradas de registro).agent_manifests), con
status draft → proposed → approved, versión y events — la misma máquina que
los topics, porque un Worker es autoridad en pie (verdad ejecutable). Los 4 de
plataforma se siembran system:true, approved. Cambiar un manifiesto = un diff
que un owner aprueba. Todo run estampa manifest_name@version en agent_runs.
4. La prueba del contrato: los cuatro agentes sin pérdida
| Campo | Auditor | Librarian | Steward | Chat |
|---|---|---|---|---|
| triggers | push / pull_request / drift_event | schedule (nightly) + manual | schedule (cadencia) + integration_event (sync) | human_message |
| cognition | llm | llm | deterministic | llm |
| model | byom (gate actual) | byom | — | byom/driftless |
| skill | auditor.skill | librarian.skill | — | chat.skill + titler |
| tools | code.investigate + graph.read | graph.curate | broker.read + records.write | surface.read (las 16) |
| criterion | topics matcheados por anchors | — | — | según thread |
| envelope | — (no invoca broker) | — | — | — |
| actions | createProposal, postPRComment | writeEvidence, vouch, archive | updateRecord(drift), shareNote | reply, citations |
| autonomy | propose | vouch: auto_reversible (undo existente) | flag: auto_reversible (drifted es reversible) | n/a (humano presente) |
| budget | 15/25 min, maxSteps 16 | 15/25 min | batch corto, sin watchdog LLM | por turno |
| runtime | sandbox, queue agent | none, queue agent | none, queue batch | none, interactive |
cognition,
queue_class) y Chat (interactive) — exactamente los casos que un diseño
hecho solo contra el Auditor habría roto después.
Y el quinto caso, el que valida el futuro — el Worker de campaña:
triggers: schedule diario · cognition llm · tools: broker.invoke:gmail + broker.invoke:exa + web.browse · surface: collection outbound · criterion: outbound-mx-criterion + gmail-connection-criterion · envelope: el mapa stage→ops · autonomy: draft auto, send propose · escalation: review_queue.
Cabe sin añadir un campo. El contrato generaliza.
5. Decisiones de diseño
- El manifiesto es datos; las capacidades son registros de código. El chat/
CLI/MCP componen jsonb;
ToolSets/Skills/ContextProvidersse resuelven por nombre. Añadir una capability nueva sí es código (revisado); añadir un agente jamás. - Un solo loop bajo el seam existente.
AgentRuntimeya es la abstracción; la migración consolidaagentic-loop+ThinLoopRuntimeen una impl (yMastraRuntimeentra por el mismo seam según su contrato de adopción). Se preserva el invariante del trace (mismo objeto a onStep y al store). - Un solo camino de tools. Los ToolSets nombrados componen las tools canónicas (surface-tools E2 + investigation + graph + broker) SIEMPRE a través del policy engine y el observador T3. Mueren las tres composiciones bespoke.
- Un dispatcher. Tabla de triggers derivada de los manifiestos aprobados →
agentsForTrigger(la firma v1, ahora sobre DB) → enqueue. La disciplina de cola no cambia: singletonKey por (agente, workspace), budget único, drops ruidosos.agent-schedule.servicese vuelve el dispatcher genérico. - Dos clases de cola, un runtime. La distinción que el Steward estrenó
(clase webhook-processing) se legitima DENTRO del runtime (
queue_class), no como excusa para otro servicio bespoke. - El ledger de la ronda 1 es la salida del runtime. El loader de criterion
escribe
context_deliveries(source nuevos:collection_criterion/connection_criterion); el contexto del run inyectarun_iden cada invoke y cadarecord_eventautomáticamente — lo que en la ronda 1 se pasa a mano, el runtime lo estampa solo. El dossier (evidence) se vuelve el insumo del campoautonomy(WS5). IntegrationManifestv1 se conserva como está (inbound→TriggerEvent, outbound→Actions) — es la otra mitad del bus y no estorba; se materializa cuando un caso real lo pida (no en esta migración).
6. Mapa de migración
Principio: cada fase deja el sistema funcionando idéntico o mejor, con gate medible y rollback. Nunca big-bang; los silos mueren uno a uno, el más simple primero. (Gates de release del contrato Mastra + pass^k del plan SOTA aplican.)Fase 0 — Contratos sin cambio de comportamiento
- Materializar
AgentManifestV2+ tablaagent_manifests+ seed de los 4 (system:true, describiendo la realidad ACTUAL de cada uno). - Los runners existentes LEEN su config del manifiesto (triggers, budget, tools nombrados) pero conservan su ejecución bespoke.
- Registros nombrados de ToolSets/Skills/ContextProviders envolviendo lo que ya existe (cero lógica nueva).
- Gate: comportamiento byte-idéntico (evals de loop existentes verdes); los manifiestos sembrados describen fielmente cada agente (revisión humana).
- Rollback: trivial — los runners ignoran el manifiesto.
Fase 1 — Un loop; primer inquilino: Librarian
- Consolidar la ejecución bajo el seam
AgentRuntime(ThinLoop como impl canónica; decisión Mastra-vs-ThinLoop se toma AQUÍ con el spike del contrato de adopción — es intercambiable por diseño del seam). - Migrar Librarian (el más simple: sin sandbox, tools de grafo, safe-lane
con undo ya probada): su service se reduce a
run(manifest, trigger). - Gate: mismo output en corpus golden; autovouch conserva undo; approval rate del Librarian no cae.
- Rollback: el service bespoke sigue en el árbol una release; flag por workspace.
Fase 2 — Steward y Auditor
- Steward: prueba
cognition:'deterministic'+queue_class:'batch'dentro del runtime (sin modelo, sin skill — el loop degenera a pipeline determinista). Su lógica pura (digest, cadencia, notas) ya son funciones con tests: se re-usa intacta; solo muere el cableado. Aquí se paga la ironía de haberlo construido bespoke — y se paga barato precisamente porque su lógica quedó pura. - Auditor (el más difícil): sandbox Daytona + BYOM + land loop + comment. El punto de enchufe del refuter (plan SOTA WS3) queda como paso del runtime, no del runner.
- Gate por agente: pass^k en corpus sembrado ≥ baseline; approval rate sostenido; cero dobles-runs (singleton respetado).
- Rollback: igual que F1, por agente.
Fase 3 — Chat entra al registro
- Trigger
human_message,interactive:true(streaming + suspend/resume — si Mastra ganó en F1, sus approvals mecánicos entran aquí). El turno del chat se vuelverun(manifest,…)con stream. - Gate: paridad de latencia p95 y de citations; los threads no pierden
invariantes (topic
chat-turn-lifecycle-invariants).
Fase 4 — Muerte de los silos + candado mecánico
- Borrar los runners bespoke.
- El candado se vuelve CI: un guard de arquitectura (patrón
architecture.spec.ts) que falla si aparece un*RunnerServicenuevo fuera del runtime, o un dispatch que no pase poragentsForTrigger. El invariante deworker-manifest-northstardeja de depender de la memoria de nadie.
Fase 5 — Worker v1 interno (dogfooding)
- El agente de campaña de outbound como primer manifiesto
system:false(redactado a mano, aprobado por owner) — el primer inquilino de negocio. - Corre semanas fabricando evidencia en el ledger. La declaración conversacional (el “compilador” del chat) se construye DESPUÉS de que esto demuestre que un manifiesto acotado no puede hacer daño fuera de su envelope.
- Gate de apertura a usuarios: invariante 3 del north star — dogfooding primero, apertura después.
Dónde enchufa la branch de la ronda 1
La capa de datos (migraciones 102–108, servicios de lectura, corpus A/B, skill trailer) se cherry-pickea en F0–F1 renumerando migraciones: es la salida que el runtime necesita y no opina sobre ejecución. El único conflicto real es el cableado del Steward, que en F2 se re-expresa como manifiesto (su lógica pura sobrevive intacta).7. Riesgos nombrados
- Mastra: que su estado no respete “sin semántica de producto” — el contrato de adopción ya define blockers/rollback; la decisión se toma en F1 con spike, no por fe.
- Chat streaming: la paridad interactiva es el gate más frágil (F3 va al final a propósito).
- Sandbox lifecycle dentro del loop consolidado (Auditor): el warm-pool y
los 3 sitios de
daytona.create(gotcha conocida) se unifican en F2. - Scope creep del manifiesto: la tentación de añadir campos “por si acaso”. Regla: un campo entra solo si uno de los 5 casos de prueba lo exige.
8. Qué NO decide este documento
- Mastra vs ThinLoop como impl definitiva (se decide en F1 con el spike y los gates del contrato de adopción — el seam hace la decisión reversible).
- El schema del “compilador” conversacional (F5+; depende de aprender del primer Worker manual).
- Multi-workspace / marketplace de manifiestos (parked bets; señales propias).
