Plan Engine — el grafo del flujo de trabajo
El Plan Engine es un grafo Postgres en vivo que es la fuente de verdad de planes y tareas del día a día. Es editable y accionable desde dos clientes — Claude (vía MCP) y el admin — y se mantiene sincronizado bidireccionalmente con los issues/PRs de GitHub y los despliegues de Vercel.
Es el grafo del flujo de trabajo y nunca debe mezclarse con datos de tenant/cliente (ver SoR del producto y la separación de las dos bases de datos en Mapa de la plataforma).
Un grafo, tres vistas, accionable desde dos clientes. El grafo es la fuente de verdad canónica de estado final; GitHub permanece como espejo sincronizado (su eliminación está diferida).
Dónde vive
| Pieza | Ubicación |
|---|---|
| Esquema + migraciones | @tedos/db (packages/db) — Drizzle/Postgres, en un esquema plan dedicado |
| API | La Plan API en @tedos/api, prefijo /plan/* |
| MCP | 9 herramientas plan_* que envuelven el mismo servicio (un motor, dos clientes) |
| Admin | /plan (árbol editable), /roadmap (timeline), /work (dónde-estamos) |
Modelo de datos — 5 tablas (esquema plan)
| Tabla | Propósito |
|---|---|
plan_node | El árbol — kind (effort · sub · milestone · roadmap · note), status, role, priority, timeline_*, outcome_label (positive · negative · gold) |
plan_dependency | El DAG — depends_on / blocks, con guardia anti-ciclos al insertar |
plan_version | Historial append-only — cada escritura añade una fila (before / after jsonb) |
plan_link | Trabajo externo — issue / pr / deploy / branch de github / vercel; el mapa issue#↔nodo |
sync_event | Idempotencia / anti-bucles — unique(source, payload_hash) deduplica la sincronización bidireccional |
El esquema es también el esquema del dataset de trazas de entrenamiento: plan_version es el
almacén de trayectorias, y outcome_label lleva la señal de recompensa auto/gold (ver ADR-006 en
Decisiones).
Superficie API + MCP
- Plan API (
apps/api, Fastify v5, validada con@fastify/type-provider-zod): CRUD de nodos / dependencias / links, más proyeccionestree,roadmapehistory. Protegida por operador. Cada escritura añade una filaplan_versionpor construcción. - Servidor MCP (
apps/api/src/mcp): 9 herramientasplan_*que envuelven la misma capa de servicio, de modo que Claude y el admin atacan un motor, dos clientes — sin segunda implementación.
Puente de sincronización + anti-bucles
Bidireccional, para que el grafo (SoT) y GitHub (espejo) nunca se desincronicen:
- grafo → GitHub (espejo): jobs pg-boss propagan create/update/status de nodos a issues + estado de Projects v2.
- GitHub → grafo + Vercel → grafo: webhooks de ingreso verificados con HMAC actualizan el
grafo; los despliegues de Vercel aterrizan como
plan_link(deploy). - Reconciliación: la última escritura gana, con un centinela de deriva;
sync_eventdeduplica; anti-bucles vía etiquetas de origen + echo-skip (un cambio que hizo el propio puente no se re-emite).
Vistas del admin
| Ruta | Qué |
|---|---|
/plan | Árbol de planes editable + editor de detalle de nodo (crear/enlazar issue, iniciar, estado) |
/roadmap | La proyección timeline del mismo grafo — alineada por construcción, no un documento |
/work | La superficie "dónde estamos", reapuntada de GitHub GraphQL en vivo al grafo |
Topología de despliegue
Desplegado en la app Fly tedos-api (la misma app que la API de producto) como dos grupos de
procesos: web (la API Fastify: /plan/*, webhooks, MCP) y worker (la cola pg-boss que drena los
jobs de espejo grafo→GitHub). Ver Topología de despliegue.
Por qué un grafo en vez de archivos
Los planes solían ser archivos MDX leídos del disco, editables solo vía IDE + PR. El Plan Engine los
mueve a un grafo editable y accionable: plan → subplanes (parent + dependsOn + timeline), con
el roadmap como una proyección que permanece alineada por construcción. Los registros de decisión
son ADR-004 (planes → grafo) y ADR-005 (tareas → grafo, GitHub espejado) — ver
Decisiones.