# growth-engine

La plataforma de diagnóstico de nicetry con sus clientes. Funciona como una
LLM wiki con estructura fija:

- **Fuentes crudas** (el inbox): todo el material de un cliente
  (entrevistas, grabaciones, chats, documentos, planillas, URLs). Son
  inmutables.
- **Wiki**: el modelo del negocio en tres capas (capa → subcapa →
  componente → ítems, con estado, anotaciones, relaciones y evidencia) y su
  mapeo contra lo que nicetry puede construir. La mantiene la IA y las
  personas la corrigen a mano.
- **Esquema**: las tres capas, las subcapas y los componentes por defecto,
  y el catálogo de destinos del mapeo. Son datos versionados por tenant.

Cada fuente que entra se procesa sola (ver "Pipeline"). Lo que la IA
cambia se aplica directo, como un commit de autor `ia` que se puede
revertir. Cada cambio, de la IA o de una persona, queda en un historial
como el de git. API REST multi-tenant; también expone MCP.

Cumple el contrato común de engines de nicetry, **v1**
(`.development/about/engine-contract.md`).

## Autenticación

- `X-Engine-Key: <api_key>`: en toda ruta `/{tenant}/...`. Cada tenant
  tiene su key; el slug del tenant es el mismo que en el resto de los
  engines de nicetry. Sin key válida, 401 exista o no el tenant; una key
  válida de otro tenant, 403; con key válida y un tenant que no existe, 404.
- `X-Actor: <sistema>:<id>` en toda escritura (`user:ana@x.com`,
  `agent:<rutina>`, `panel:<email>`…; patrón
  `^[a-z][a-z0-9-]*:\S{1,200}$`, 400 si no cumple). Queda en `actor` de cada
  commit. Si falta, se registra `unknown:` (va a ser obligatorio). Los
  commits del pipeline llevan `ia:pipeline`. `author` y `message` siguen
  siendo el texto del commit.
- `X-Request-Id`: si viene, se respeta; si no, se genera. Vuelve siempre en
  la respuesta, va en los logs y en las llamadas salientes.
- `x-admin-api-key: <ADMIN_API_KEY>`: solo para `/admin/tenants` (alta,
  baja, rotación de keys y vaciado). Un tenant nuevo arranca con las
  subcapas y el esquema por defecto. `POST /admin/tenants/{slug}/reset` con
  `{"confirm": "<slug>"}` lo vacía sin borrarlo: se va todo, historial
  incluido, y quedan las subcapas por defecto y la configuración (key,
  esquema, conexión a ops), además del registro de gasto en IA.
  `DELETE /admin/tenants/{slug}` borra el tenant con todo; si tiene datos de
  un diagnóstico, responde 409 con lo que tiene salvo que el cuerpo traiga
  `{"confirm": "<slug>"}`.

Rate limit por minuto (429 + Retry-After): 600 por key, 120 por IP sin
credencial, 30 por IP en /admin/tenants.

Públicas: `GET /health` (`{status, engine, version, db}`, con un ping a la
base; 503 si no responde), `GET /` (los puntos de entrada en JSON),
`/docs`, `/openapi.json`, `/llms.txt`.

## Modelo

Cuatro niveles: **capa → subcapa → componente → ítems**.

- **Capas**, fijas: `direccion` (hacia dónde va el negocio, quién hace qué
  y bajo qué reglas), `conocimiento` (cómo trabaja y qué sabe) y
  `plataforma` (con qué sistemas opera y por dónde se comunica).
  `GET /{tenant}/layers`.
- **Subcapas** (`/{tenant}/sublayers`): no son fijas. Arrancan con un set
  por defecto (Estrategia, Organización, Gobierno · Procesos, Capacidades,
  Información · Aplicaciones, Integraciones, Canales) y se editan.
- **Componentes** (`/{tenant}/components`): lo propio de cada negocio,
  dentro de una subcapa. `status`: `hoy` (existe hoy), `propuesto` (sale
  de sus objetivos o de un problema) o `relevar` (falta preguntarlo).
  Tienen `tags` libres.
- **Ítems** (`/{tenant}/items`): los N ítems de un componente. Su
  `status` es opcional; sin estado propio heredan el del componente.
- **Anotaciones** (`/{tenant}/annotations`): sobre un componente o uno de
  sus ítems. `kind`: `nota`, `brecha`, `pregunta` o `decision`; se marcan
  `resolved`.
- **Relaciones** (`/{tenant}/relations`): entre dos componentes, con una
  etiqueta.
- **Recorridos** (`/{tenant}/flows`): secuencias ordenadas de relaciones,
  cada paso con su texto.

- **Mapeos** (`/{tenant}/mappings`): qué puede construir nicetry para un
  componente; puede tener varios. `targetType` sale del catálogo del
  esquema (`GET /{tenant}/mapping-targets`): entidades de ops (rol, skill,
  politica, conexion, canal, rutina), engines (hub, data, loops, bus) o
  `integracion`. `state`: `existe` (apunta a una entidad real del
  cliente, `targetRef`) o `a_crear` (`targetLabel` y
  `proposedDescription` propuestos). Llevan `why` y `evidenceIds`.
  Growth no crea nada en ops ni en otros engines.

`GET /{tenant}/model` devuelve todo el modelo de una vez (lo que usa la
UI), con los mapeos y la cantidad de afirmaciones vigentes de cada
componente. `GET /{tenant}/agenda` devuelve lo que falta relevar:
componentes e ítems en `relevar` y preguntas abiertas.

## Esquema

- `GET/PATCH /{tenant}/schema`: `components` (los componentes por defecto
  por subcapa: el vocabulario preferido de la IA, que los crea cuando
  aparece evidencia; no se instancian solos), `targets` (el catálogo de
  destinos del mapeo) y `guidance` (indicaciones para la IA sobre este
  cliente: rubro, vocabulario, qué priorizar). `version` es la versión del
  esquema por defecto del que partió. Cada cambio es un commit.
- Ops del cliente, solo lectura: `PUT /{tenant}/connections/ops` con
  `{url, apiKey}` (la X-Engine-Key del cliente en ops, mismo slug; se
  guarda cifrada), `GET` y `DELETE` en la misma ruta.
  `GET /{tenant}/ops-catalog` lista lo que tiene (`{type, ref, label}`)
  para vincular mapeos `existe`. Sin conexión, todo queda `a_crear`.

## Pipeline

Al crear una fuente con `content` o `url` (`POST /{tenant}/sources`), o al
subir su archivo (`POST` con `expectsFile: true` y después
`PUT /{tenant}/sources/{id}/file`), entra a la cola. Sin `kind` ni
`title`, el engine los infiere. Pasos:

1. **Extraer** el texto: pegado, PDF (los escaneados, con visión), docx,
   pptx, xlsx y csv, chats exportados de WhatsApp (.txt o .zip), emails
   (.eml), páginas web (se guarda una copia como archivo de la fuente),
   imágenes (visión) y audio o video (transcripción con marcas [mm:ss] y
   hablantes). El texto queda en `text`.
2. **Afirmaciones atómicas**: una afirmación, una fuente, con la cita
   textual, verificada contra el texto.
3. **Curar**: con las afirmaciones nuevas y el modelo actual, la IA crea o
   ajusta componentes e ítems, cambia estados, suma evidencia, anotaciones,
   relaciones, brechas y preguntas, y marca las afirmaciones que las nuevas
   reemplazan. De a una fuente por tenant.
4. **Mapear** los componentes que se tocaron.
5. Todo se aplica en **un commit** de autor `ia`, con `sourceId`. Revertirlo
   deshace la ingesta entera. La fuente termina `procesada`,
   `sin_novedades` o `requiere_intervencion` (con el motivo en
   `statusNote`).

`processing` en cada fuente: `state` (en_cola, extrayendo,
transcribiendo, afirmando, esperando, curando, mapeando, listo, error),
`attempts`, `error`, `commitId` y `costUsd`. `GET /{tenant}/sources?active=true`
lista las que están en curso. Se reintenta hasta 3 veces;
`POST /{tenant}/sources/{id}/reprocess` la vuelve a encolar. Una fuente
creada con `status` distinto de `pendiente` no se procesa.

Al vaciarse la cola después de un lote (3 fuentes o más desde la última revisión) corre una revisión, como mucho una vez por hora por tenant (ver abajo); para una fuente suelta, la revisión es a pedido.

## Consultar, revisar y costo

- `POST /{tenant}/query` `{question}` → `{answer, citations}`: respuesta en
  markdown con marcadores [n] que indexan `citations` (desde 1;
  `null` si no se pudo resolver). Para guardarla en el modelo,
  `POST /{tenant}/annotations` con kind `nota`.
- `POST /{tenant}/lint` → `{findings, commit}`: busca contradicciones,
  afirmaciones reemplazadas, componentes sin evidencia y mapeos colgados, y
  los deja en la agenda como preguntas o brechas. Tarda de 30 a 60 s.
- `GET /{tenant}/usage?from=&to=` → `{totalUsd, byStep, byDay}`: gasto en
  IA (AI Gateway, etiquetado por tenant y por paso).

Sin IA configurada en el despliegue, estas rutas responden 503 y las
fuentes esperan en la cola.

## Inbox y evidencia

- **Fuentes** (`/{tenant}/sources`): todo lo que entra. `kind`: nota,
  entrevista, grabacion, documento, planilla, chat, url, otro. El contenido
  original (`content`, `url`, el archivo) no cambia: si cambió, es otra
  fuente. `text` es la versión en texto (la extracción) y sí se
  actualiza. `status`: pendiente → procesada, sin_novedades,
  requiere_intervencion o descartada (todo ítem del inbox tiene que
  terminar en un estado final). `consent` registra quién consintió.
  Archivo: `PUT /{tenant}/sources/{id}/file` con el archivo como cuerpo
  (`?name=` para el nombre); `GET` en la misma ruta lo descarga.
- **Afirmaciones** (`/{tenant}/claims`, filtros `sourceId`, `componentId`,
  `current=true`): `text`, `quote`, `locator`, `speaker`, `verified`,
  `observedAt` (cuándo fue cierto), `learnedAt` (cuándo nos enteramos),
  `componentIds`. Nada se borra en silencio: la que una más nueva
  reemplaza queda con `supersededById` y `supersededAt`.
- **Evidencia** (`/{tenant}/evidence`): una cita textual de una fuente,
  con `locator` (dónde está: {"char":[a,b]}, {"page":n}, {"seconds":n},
  {"sheet":"Ventas"}) y `speaker`; `claimId` si sale de una afirmación. `verified` lo calcula el engine: la cita
  aparece literal en `text` o `content` de la fuente (sin distinguir
  mayúsculas, tildes, espacios ni comillas tipográficas). Se recalcula si cambia el
  `text` de la fuente.
- **Vínculos** (`/{tenant}/evidence-links`): qué cita `respalda` o
  `contradice` qué componente, ítem o anotación. Las contradicciones son
  hallazgos: se guardan las dos versiones.

## Épicas, iteraciones y retrospectivas

- **Épicas** (`/{tenant}/epics`): una necesidad del cliente con un
  objetivo. `kind`: diagnostico u otra. `appetite` (tiempo que se decide
  invertir), `exitCriteria`, y al cerrar `outcome`: seguir, cambiar_rumbo o
  frenar. `componentIds`: qué componentes del mapa toca (hay un solo mapa
  por negocio).
- **Iteraciones** (`/{tenant}/iterations`): dentro de una épica, con un
  objetivo. `POST /{tenant}/iterations/{id}/close` la cierra y pone el tag
  `<épica>/iteracion-<n>` en ese momento del historial.
- **Retrospectiva** (`/{tenant}/retro-items`): ítems de una iteración:
  `aprendizaje` (lo aprendido del negocio, va al modelo), `mejora` (de la
  forma de trabajar) y `accion` (con `targetIterationId`).

## CRUD

Cada entidad tiene: `GET /{tenant}/<entidad>` (con filtros por query y
paginado: `?limit` de 1 a 500, 100 por defecto, y `?cursor`; el cuerpo es
`{data: [...]}` y el cursor siguiente viene en el header `X-Next-Cursor`,
que falta en la última página; el listado de fuentes trae
`textPreview`/`textLength` y `contentPreview`/`contentLength` en vez de
`text` y `content`, completos en `GET /{tenant}/sources/{id}`),
`GET/PATCH/DELETE /{tenant}/<entidad>/{id}` (sublayers, components, flows y
epics aceptan también el slug) y `GET /{tenant}/<entidad>/{id}/history`.
Las escrituras aceptan `author` y `message` (en el body; en DELETE, por
query) y responden `{ data, commit }`. Todo `POST` acepta
`Idempotency-Key`: con la misma key y el mismo cuerpo dentro de 24 h
devuelve la respuesta original (header `Idempotent-Replayed: true`); con
otro cuerpo, 409 `idempotency_conflict`. Un `slug` que se manda explícito y
ya existe da 409; si no se manda, se deriva del nombre.

Borrar algo borra también lo que depende de eso (los ítems y anotaciones
de un componente, sus relaciones, sus vínculos de evidencia…), todo en el
mismo commit: se puede revertir.

`POST /{tenant}/batch` aplica varias operaciones en un solo commit, todas o
ninguna. Un `ref` en una operación de alta permite usar su id en las
siguientes como `"$ref:<nombre>"`.

## Historial (como git)

Cada escritura es un commit (id corto, autor, mensaje, iteración opcional;
`sourceId` en los de la IA) con la foto completa de cada entidad que tocó.
Los commits del pipeline tienen autor `ia`. Un "ref" es `head`, el id
de un commit o el nombre de un tag (un "/" en un tag va como %2F en la ruta).

- `GET /{tenant}/log`: la historia (filtrable por entidad o iteración),
  paginada con `?limit` y `?cursor` / `X-Next-Cursor` (`before=<ref>`
  sigue andando para arrancar desde un momento).
- `GET /{tenant}/commits/{ref}`: qué cambió en ese commit (antes/después).
- `GET /{tenant}/diff?from=<ref>&to=<ref>`: qué cambió entre dos momentos
  (`from=root`: desde el tenant vacío).
- `GET /{tenant}/model?at=<ref>`: el modelo tal como estaba.
- `POST /{tenant}/commits/{ref}/revert`: deshace un commit con uno nuevo.
  Lo que creó se borra con la misma cascada que un DELETE (deshacer una
  ingesta `ia` se lleva todo lo que creó) y lo que cambió vuelve a como
  estaba. Si algo posterior está en el camino, 409 sin aplicar nada, con
  `conflicts`: `[{kind, id, name?, commit, commitMessage, reason}]`, donde
  `reason` es `changed-after` (una entidad del commit cambió después) o
  `depends` (una posterior depende de lo que creó). `force: true` revierte
  también eso, en cascada.
- `GET/POST /{tenant}/tags`, `DELETE /{tenant}/tags/{name}`: marcas con
  nombre sobre un commit.

## Errores

`{"error": "para una persona", "code": "snake_case_estable", "details"?: [...], "requestId": "..."}`.
Códigos: 400 `invalid_json`, `validation_error` (con `details`),
`invalid_actor` · 401 `unauthorized` · 403 `forbidden` · 404 `not_found`
(también rutas inexistentes) · 409 `conflict` (duplicado, referencia rota,
revert con cambios posteriores, fuente ya en proceso),
`idempotency_conflict`, `idempotency_in_progress` · 413
`payload_too_large` · 422 `unprocessable` (ops no aceptó la key) · 429
`rate_limited` · 502 `upstream_error` (la IA falló o no respondió) · 503
`unavailable` (IA o cifrado sin configurar, base caída) · 500 `internal`.

URLs que carga un tenant (fuentes `url`, conexión a ops): solo https, sin
direcciones privadas, con la resolución DNS fijada para la conexión.

## MCP

`POST /{tenant}/mcp` (mismo X-Engine-Key): herramientas `help` (este texto) y
`api` (cualquier llamada REST de este tenant, con el mismo X-Actor y el
mismo X-Request-Id; el path no puede salir del tenant). La respuesta de
`api` empieza con el status y, si vienen, las líneas `next-cursor: <c>` (hay
más páginas: se sigue con `query.cursor`) y `deprecation: …`; después, el
cuerpo.

Spec completo en `/openapi.json` · Swagger UI en `/docs`.
