This is the abridged developer documentation for PoktaCare Docs
# Inicio
> Hub técnico del estudio Grader — arquitectura, onboarding y enlaces a cada repo involucrado.
Este sitio es el hub técnico para quienes colaboran en el **estudio comparativo de extracción clínica** y su producto central, el **Grader**. Si acabas de unirte, empieza en [Primeros pasos](/getting-started/). Acceso restringido: si estás viendo esto, ya tienes acceso autorizado. El contenido aquí es tan sensible como los repos privados que enlaza — trátalo igual, no lo reenvíes ni lo publiques fuera de este grupo. Ver [Reglas](/guardrails/) para el detalle. **Agentes y LLMs:** empieza en [`/llms.txt`](/llms.txt). Usa [`/llms-small.txt`](/llms-small.txt) para contexto reducido o [`/llms-full.txt`](/llms-full.txt) para el sitio completo. ## El proyecto, en corto [Sección titulada «El proyecto, en corto»](#el-proyecto-en-corto) Automatizamos la preparación de registros clínicos para un registro de biológicos en reumatología (nombre del registro y de las entidades colaboradoras en modo *stealth* — ver [Reglas](/guardrails/)). Los médicos tomaban su nota de consulta y tenían que volver a capturarla manualmente en la plataforma del registro; el pago por hacerlo bajó de \~$30 a \~$12 USD por paciente y varios médicos ya no lo hacen. La apuesta: un motor que lea la nota clínica, prepare un borrador estructurado y ayude al médico a corregirlo y aprobarlo antes de cualquier envío controlado. Para justificar esa apuesta con números corre un estudio comparativo — varios “harnesses” (arneses de extracción) sobre el mismo corpus de notas reales: 1. **LLM plano** — sin prompt especializado, línea base. Vive en `pokta-care-monorepo`, no es un servicio separado. 2. **RheumaAI** — agente ya entrenado en reumatología. Repo propio, servicio propio. 3. **Grader** — el producto en evolución. Repo propio, servicio propio, fork de RheumaAI. 4. Un cuarto brazo opcional (LLM ajustado por otro reumatólogo) — registrado en código como placeholder, no corre todavía. Cómo se conectan estas piezas — quién llama a quién, dónde caen los resultados — está en [Arquitectura](/architecture/), no aquí; esta página es solo el mapa de “qué es qué.” **North Star:** el objetivo final no es “leer un Word y llenar un formulario” — es un copiloto en tiempo real que, durante la consulta, va extrayendo la información clínica en vivo y le dice al médico “te faltó preguntar X” antes de que termine. La extracción retrospectiva de notas es el entrenamiento y validación de ese motor, no el producto final. ## Los repos [Sección titulada «Los repos»](#los-repos) | Repo | Qué es | Servicio | | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------- | | [`pokta-grader-bioagent`](https://github.com/poktalabs/pokta-grader-bioagent) | El Grader — arm 3 del estudio | Railway, `grader.poktacare.com` | | [`rheuma-ai-bioagent`](https://github.com/poktalabs/rheuma-ai-bioagent) | RheumaAI — arm 2 del estudio | Railway, `rheumai.xyz` | | [`pokta-care-monorepo`](https://github.com/poktalabs/pokta-care-monorepo) | Orquestador del estudio: arm 1, scoring, persistencia, y la app de producto (`app.poktacare.com`) | Vercel + Railway | Detalle completo, con links directos a README/CONTRIBUTING de cada uno, en [Repos y guías](/repos/). ## Equipo [Sección titulada «Equipo»](#equipo) * **Dr. Erick Zamora Tehozol** — Reumatología y validación clínica * **Ing. Ángel Meléndez Córdoba** — Ingeniería ## Dónde seguir [Sección titulada «Dónde seguir»](#dónde-seguir) * **[Primeros pasos](/getting-started/)** — checklist de acceso, por dónde empezar según en qué repo vas a trabajar. * **[BiobadamexAI](/biobadamexai/)** — workflow actual: ingesta, extracción, revisión clínica y envío controlado. * **[Arquitectura](/architecture/)** — cómo se conectan los tres repos: quién llama a quién, cómo se mide, dónde caen los resultados. * **[Repos y guías](/repos/)** — directorio de enlaces. * **[Reglas](/guardrails/)** — las reglas que no se negocian. Léelas antes de tu primer cambio.
# BiobadamexAI
> Cómo una nota clínica pasa de la ingesta a extracción, revisión médica y registro controlado en BIOBADAMEX.
BiobadamexAI es el flujo institucional que convierte notas clínicas en borradores estructurados para BIOBADAMEX. Separa la automatización de la decisión médica: el sistema extrae y calcula; el médico revisa, corrige y aprueba; un paso posterior y explícito puede enviar el registro. ## En una frase [Sección titulada «En una frase»](#en-una-frase)
```
flowchart LR
A["Nota clínica"] --> B["Inventario cifrado"]
B --> C["Extracción solicitada"]
C --> D["LLM + reglas deterministas"]
D --> E["Borrador por revisar"]
E --> F{"Decisión médica"}
F -->|corregir| E
F -->|rechazar| R["Rechazada"]
F -->|aprobar| G["Aprobada"]
G --> H{"Confirmar envío"}
H --> I["Cola serial"]
I --> J["Sandbox Tenki"]
J --> K["BIOBADAMEX crdA–crdE"]
K --> L["idpac + resumen"]
```
## Estados que ve el médico [Sección titulada «Estados que ve el médico»](#estados-que-ve-el-médico) | Estado | Significado | Acción disponible | | ------------------- | --------------------------------------------------------------- | ---------------------------------- | | Sin extraer | El archivo está guardado, pero no se ha enviado al extractor | Extraer | | En proceso | Hay un trabajo de extracción activo o en reintento | Esperar | | Error de extracción | Se agotaron los intentos y existe un fallo visible | Reintentar | | Por revisar | Existe un borrador estructurado | Revisar y corregir | | Aprobada | El médico aprobó el borrador | Enviar mediante un paso separado | | Rechazada | El médico descartó esa extracción | Conservar el historial o reextraer | | Registrada | El trabajador obtuvo un `idpac` y confirmó la página de resumen | Consultar el recibo | Estos estados se derivan de archivos, trabajos y borradores. No existe una columna única que pueda desincronizarse del flujo. ## Superficies actuales [Sección titulada «Superficies actuales»](#superficies-actuales) * Consola médica: carga, inventario, extracción manual, revisión, corrección, aprobación, rechazo y envío. * Ingesta por servicio: carga por lotes autenticada con clave de servicio. Solo guarda archivos; no prueba extracción. * Auditoría desde RheumAI: califica una nota sin persistirla o crea un borrador `review_pending` para revisión. * Agente clínico dedicado: inventario y extracción limitados al médico configurado; no aprueba ni registra. * Panel de flujos: proyección operativa con estados, tiempos, modelo, número de correcciones e `idpac`, sin devolver el cuerpo clínico. ## Reglas centrales [Sección titulada «Reglas centrales»](#reglas-centrales) 1. Un archivo cargado no equivale a un registro extraído. 2. Un borrador extraído no equivale a un registro aprobado. 3. Aprobar no envía automáticamente. 4. Enviar requiere propiedad, estado aprobado, campos requeridos resueltos y una bandera de entorno explícita. 5. El valor `unknown` se conserva. Un cero real o `false` documentado no se confunde con ausencia. 6. Las comorbilidades no documentadas solo se convierten a `No` después de confirmación explícita del médico. 7. Nada de esto demuestra paridad completa con todos los controles de la plataforma externa. ## Alcance de esta documentación [Sección titulada «Alcance de esta documentación»](#alcance-de-esta-documentación) Las páginas describen el código de `poktalabs/pokta-care-monorepo` en el commit `b852f056d527d5bcec72b67256e5af98d386b323`. No inspeccionan variables del entorno desplegado, datos clínicos, credenciales ni la plataforma externa en vivo. ## Evidencia en código [Sección titulada «Evidencia en código»](#evidencia-en-código) * [Rutas montadas por la API](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/index.ts) * [Inventario de notas](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/web/src/pages/BiobadamexNotes.tsx) * [Contratos de trabajos](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/packages/biobadamex-registry/src/jobs.ts) * [Estado derivado de una nota](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/services/biobadamex-note-status.ts)
# RheumAI
> Qué es RheumAI, cómo procesa una consulta clínica y dónde encaja con BiobadamexAI.
RheumAI es un sistema especializado de apoyo a decisiones clínicas en reumatología. Combina conversación clínica, recuperación de literatura, herramientas médicas y una revisión posterior de la respuesta. Su propósito es ayudar al médico a organizar información, detectar vacíos y revisar hipótesis. Esta descripción explica su función; no afirma validación prospectiva, aprobación regulatoria ni condición de dispositivo médico. No sustituye el juicio clínico ni aprueba registros de forma autónoma. ## En una frase [Sección titulada «En una frase»](#en-una-frase) RheumAI convierte una pregunta o nota clínica en una respuesta estructurada y revisada por varias capas de software:
```
flowchart LR
A["Pregunta o nota"] --> B["Planificador"]
B --> C["Fuentes y herramientas"]
C --> D["Respuesta clínica"]
D --> E["ORVS y control de citas"]
E --> F["Revisión ética"]
F --> G["Respuesta para el médico"]
```
## Superficies actuales [Sección titulada «Superficies actuales»](#superficies-actuales) | Superficie | Uso | Estado observado | | -------------------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------- | | Aplicación web | Conversación, archivos y seguimiento de sesiones | Activa en el código principal | | `POST /api/chat` | Flujo conversacional persistente | Activo; usa base de datos y puede usar archivos | | `POST /v1/study/rheumaai-reply` | Evaluación controlada de una nota desidentificada | Activo; stateless, autenticado y sin escritura en las tablas de RheumAI | | Modos `vanilla`, `rag`, `dag`, `quick-orvs`, `full-orvs` | Comparar variantes del pipeline | Experimentales; se activan explícitamente | | Investigación profunda | Búsqueda y síntesis de evidencia más larga | Activa como flujo separado | | x402 | Cobro opcional para clientes de API | Integración opcional, controlada por configuración | ## Qué sucede con una consulta clínica [Sección titulada «Qué sucede con una consulta clínica»](#qué-sucede-con-una-consulta-clínica) 1. La capa de acceso aplica autenticación, límites y, si está habilitado, el control x402. 2. RheumAI crea o recupera la conversación y prepara el estado de la solicitud. 3. Si hay archivos, los analiza antes de planear la respuesta. 4. El planificador elige herramientas y fuentes. Entre ellas puede haber PubMed, Semantic Scholar, conocimiento local, grafo de conocimiento o interpretación de laboratorio. 5. Las fuentes recuperadas se reordenan según su relación con la consulta. 6. El generador produce la respuesta con la persona clínica de RheumAI. 7. ORVS revisa la respuesta cuando corresponde. El sistema puede regenerarla si no pasa. 8. Un control separado revisa PMID recuperados y elimina identificadores no verificados. 9. La revisión ética puede bloquear una salida rechazada. Si la revisión falla técnicamente, el flujo actual deja pasar la respuesta y registra el fallo. 10. El resultado vuelve al médico con el contexto disponible en ese turno. ## Qué no hace todavía [Sección titulada «Qué no hace todavía»](#qué-no-hace-todavía) * No produce por sí solo un registro BIOBADAMEX estructurado y listo para enviar. * No calcula una puntuación de completitud de la **nota de entrada** contra todos los campos obligatorios del registro. * No distingue en un contrato estructurado entre dato ausente, no aplicable y desconocido. * No reemplaza la revisión y aprobación del médico. La integración propuesta con BiobadamexAI usa RheumAI como capa de razonamiento y explicación. El esquema del registro, la procedencia campo por campo, la evaluación de completitud y la aprobación clínica permanecen en el flujo institucional controlado. Consulta [Evaluación de notas y completitud](/rheumai/clinical-notes/) y el [workflow actual de BiobadamexAI](/biobadamexai/). ## Límites de interpretación [Sección titulada «Límites de interpretación»](#límites-de-interpretación) La documentación describe el código en el commit `149508b3ab79da0b88c56deb63f6fb66e35d0e68`. Que una herramienta esté registrada no demuestra que esté configurada en todos los entornos ni que haya sido validada clínicamente. Las rutas experimentales y los fallbacks están etiquetados en las páginas siguientes. ## Evidencia en código [Sección titulada «Evidencia en código»](#evidencia-en-código) * [Entrada del servidor y rutas](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/index.ts) * [Flujo principal de chat](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/routes/chat.ts) * [Registro dinámico de herramientas](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/tools/index.ts) * [Ruta stateless para el estudio](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/study/rheumaai-reply-route.ts)
# Arquitectura
> Cómo los tres repos se conectan para producir un resultado del estudio — arms, request flow, scoring, persistencia.
> Esta página describe el estudio comparativo y sus arms. Para el producto clínico actual, consulta el [workflow de BiobadamexAI](/biobadamexai/). Esta página no existe en ningún repo individual porque ninguno de los tres tiene el panorama completo — cada uno documenta su propio servicio, no cómo encaja con los otros dos. Este es ese mapa. ## Los tres arms, quién los corre [Sección titulada «Los tres arms, quién los corre»](#los-tres-arms-quién-los-corre) El código define tres “harnesses” (`STUDY_HARNESSES = ["plain", "rheumaai", "grader"]`, `pokta-care-monorepo/apps/api/src/services/biobadamex-study-harness.ts`). Un “arm” del estudio es en realidad `(modelo × harness × prompt opcional)` — no hay una lista fija de modelos hardcodeada, se configura en runtime vía la variable de entorno `STUDY_ARMS`. | Harness | Cómo se invoca | Dónde vive el código | | ---------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- | | `plain` | Llamada LLM **en proceso**, dentro del monorepo — sin repo externo involucrado | `pokta-care-monorepo` (`plainInvoker`) | | `rheumaai` | HTTP hacia `RHEUMAI_STRUCTURED_URL` | servicio externo: `rheuma-ai-bioagent`, ruta `POST /v1/study/rheumaai-reply` | | `grader` | HTTP hacia `GRADER_STRUCTURED_URL` | servicio externo: `pokta-grader-bioagent`, ruta `POST /v1/chat/completions` | **El cuarto arm (LLM ajustado por otro reumatólogo) no es un harness separado.** Es el harness `plain` corriendo con un `promptId` distinto (`rheum-v1`), registrado en código pero con el prompt vacío/reservado — si se invoca, el código lanza un error en vez de correr silenciosamente con contenido incorrecto. No está “casi listo”, está sin construir todavía. ## Request flow [Sección titulada «Request flow»](#request-flow)
```
flowchart TD
M["pokta-care-monorepo
(orquestador del estudio)"]
P["plain
(llamada LLM en proceso)"]
R["rheumaai
HTTP POST → rheuma-ai-bioagent
/v1/study/rheumaai-reply"]
G["grader
HTTP POST → pokta-grader-bioagent
/v1/chat/completions"]
O[("biobadamex_study_arm_outputs
una fila por nota × arm")]
S["scoring
(biobadamex-eval.ts)"]
A[("biobadamex_study_analysis
agregado, sin llave por nota")]
M --> P
M --> R
M --> G
P --> O
R --> O
G --> O
O --> S
S --> A
```
`rheumaai` y `grader` nunca se llaman entre sí ni comparten proceso — cada uno es un servicio HTTP independiente, desplegado por separado (Railway), con su propia autenticación por API key. El monorepo es el único que sabe que existen los tres; ninguno de los servicios sabe del otro (con una excepción cosmética: el comentario en `rheuma-ai-bioagent`’s `rheumaai-reply-route.ts` reconoce explícitamente que su técnica de auth fue copiada de `pokta-grader-bioagent`’s `structured.ts` — copiada, no importada, los repos no tienen dependencia en runtime). ## Sweep flow — de principio a fin [Sección titulada «Sweep flow — de principio a fin»](#sweep-flow--de-principio-a-fin) Secuencia real de `pokta-care-monorepo/apps/api/scripts/biobadamex-sweep-run.ts`: 1. **Cargar el corpus** — lista los `.docx` en `CORPUS_DIR` (ruta de filesystem, nunca dentro de un repo git). Deriva un `note_id = sha256(filename)[:12]`; el nombre real del archivo (que es PHI — nombre del paciente) nunca se loguea ni persiste. 2. **Resolver los arms** — lee `STUDY_ARMS` del entorno. Si está vacío, termina sin tocar nada. 3. **Modo dry-run opcional** — con `PLAN_ONLY=1`, imprime el plan (notas × arms) y termina — cero llamadas a proveedores, cero escritura a DB. 4. **Guard contra corridas previas sin drenar**, luego crea un `study_run`. 5. **Por cada nota**: la lee/parsea y arma un job por cada `(nota, arm)` — se encolan en `pg-boss`. 6. **Workers** toman los jobs y llaman al invoker correcto por arm, con un semáforo de concurrencia por proveedor. 7. **Poll cada 5s** hasta que todas las celdas estén “settled” (hay fila de salida, o se agotaron los reintentos), hasta 45 min. 8. **Reporte final**: cuenta esperado/settled/output/fallas, agrupado por causa de falla. ## Scoring — qué significa “completeness” [Sección titulada «Scoring — qué significa “completeness”»](#scoring--qué-significa-completeness) `compareField` (`biobadamex-eval.ts`) compara cada campo con un veredicto de cuatro estados, siempre usando un chequeo explícito de “desconocido” (nunca un chequeo falsy — así un `0` real nunca se confunde con “faltante”): * **match** — ambos desconocidos, o los valores coinciden (para `patient.sex` específicamente, vía un catálogo tipo HL7 AdministrativeGender, no comparación exacta de string). * **mismatch** — ambos conocidos, valores distintos. * **missing** — el gold tiene valor, la extracción no. * **unexpected** — la extracción tiene valor, el gold no. **“Completeness” es una tasa de presencia, no una tasa de acierto.** Es `presente / total` sobre los campos requeridos que no son UNKNOWN — no factoriza si el valor es correcto contra el gold. La exactitud/acierto es una métrica separada (`variant_accuracy`, `delta_grader_minus_plain`). Esta distinción ya causó un bug real en el estudio (arm-2 estructurando la prosa del modelo en vez de la nota original inflaba completeness sin mejorar exactitud) — si vas a reportar un número, sé explícito sobre cuál de las dos métricas es. ## Dónde caen los resultados [Sección titulada «Dónde caen los resultados»](#dónde-caen-los-resultados) * **`biobadamex_study_arm_outputs`** — una fila por `(run_id, note_id, arm_id)`. Columnas relevantes: `model_id`, `harness`, `record` (el JSON extraído), `das28`, `missing_required`, `latency_ms`, tokens. Sin FK sobre `note_id` — a propósito. * **`biobadamex_study_analysis`** — solo agregado, formato largo, **sin llave a nivel de nota** (a propósito, para prevenir re-identificación). Columnas: `run_id`, `arm_id`, `model_id`, `harness`, `scope`, `field_key`, `metric`, `numerator`, `n`, `value`, intervalos de confianza. Aquí es donde filtras por `scope="field"`, `metric="field_present_rate"` para ver el desglose campo por campo (ej. el campo `sex`). ## Corpus real de notas [Sección titulada «Corpus real de notas»](#corpus-real-de-notas) Vive fuera de todo repo, en una ruta de filesystem pasada por `CORPUS_DIR`. El script de censo (`biobadamex-instrument-census.ts`) verifica explícitamente que esa ruta **no** esté dentro de un working tree de git — “Real patient notes must never be committable” es un comentario literal en el código, no solo una convención. Ver [Reglas](/guardrails/) para las reglas de trabajo con datos sintéticos en su lugar.
# Arquitectura de BiobadamexAI
> Componentes, almacenes, colas y límites de confianza del flujo actual.
## Mapa del sistema [Sección titulada «Mapa del sistema»](#mapa-del-sistema)
```
flowchart TB
UI["Consola React"] --> API["API Hono"]
M2M["Carga por servicio"] --> API
RAI["RheumAI audit proxy"] --> AUD["API de auditoría"]
AG["Agente clínico dedicado"] --> AGA["API de agente"]
API --> R2[("R2 · originales cifrados")]
API --> DB[("PostgreSQL · metadatos y borradores")]
API --> Q["pg-boss"]
AUD --> EXT["Extractor"]
AGA --> Q
Q --> EXT
EXT --> NEB["Nebius · nota desidentificada"]
EXT --> CORE["Esquema tri-state · DAS-28 · completitud"]
CORE --> DB
UI --> REVIEW["Revisión y corrección"]
REVIEW --> DB
DB --> SUB["Solicitud de envío"]
SUB --> Q2["Cola registry-submit"]
Q2 --> TENKI["MicroVM Tenki"]
TENKI --> REG["BIOBADAMEX ASP.NET"]
REG --> RECEIPT["idpac + resumen"]
RECEIPT --> DB
```
## Componentes [Sección titulada «Componentes»](#componentes) ### Aplicación web [Sección titulada «Aplicación web»](#aplicación-web) La consola React concentra el flujo diario en una lista de notas. Los filtros muestran qué necesita atención y cada fila ofrece una acción contextual: extraer, revisar o ver. Las pantallas de revisión muestran el documento fuente, el registro estructurado, la corrección y la confirmación previa al envío. ### API [Sección titulada «API»](#api) La API Hono monta superficies separadas: * `/api/biobadamex/intake`: texto pegado, archivos y carga por servicio. * `/api/biobadamex/uploads`: inventario, extracción bajo demanda y recuperación del original. * `/api/biobadamex/drafts`: cola de revisión, detalle, corrección, aprobación, rechazo y envío. * `/api/biobadamex/audit`: calificación y envío para revisión desde RheumAI. * `/api/biobadamex/agent`: acceso acotado para un agente que actúa por un médico. * `/api/flujos`: línea de tiempo operativa con una proyección reducida. Las rutas de consola exigen sesión, usuario resuelto y acceso de investigación. Las superficies máquina-a-máquina usan claves de servicio distintas. ### Almacenamiento [Sección titulada «Almacenamiento»](#almacenamiento) * R2 conserva el archivo original cifrado con AES-256-GCM. * PostgreSQL guarda metadatos del archivo, trabajos, borradores, auditoría y resultados del envío. * La copia de trabajo del registro preserva valores tri-state. * `agentRecord` conserva la extracción original; `record` es la copia que el médico puede corregir. * El texto fuente del borrador se almacena cifrado cuando existe una clave maestra válida. ### Cola [Sección titulada «Cola»](#cola) `pg-boss` separa extracción y registro. La API solo registra trabajadores cuando `BIOBADAMEX_QUEUE_ENABLED=1`. Extracción tiene reintentos y dead letter. Registro tiene otra cola y se procesa uno por trabajador, pero una instalación con varias instancias necesita coordinación adicional para garantizar serialización global. ### Paquete de dominio [Sección titulada «Paquete de dominio»](#paquete-de-dominio) `@pokta/biobadamex-registry` contiene el esquema, valores `unknown`, reglas por enfermedad, DAS-28, contratos de trabajos, mapa de controles externos y reglas de corrección y completitud. ### Trabajador de registro [Sección titulada «Trabajador de registro»](#trabajador-de-registro) El trabajador Playwright corre dentro de una microVM desechable. Inicia sesión, recorre `crdA` a `crdE`, obtiene el `idpac` después de guardar `crdA` y exige que el resumen final muestre ese identificador. ## Tres límites distintos [Sección titulada «Tres límites distintos»](#tres-límites-distintos) | Límite | Qué controla | Qué no demuestra | | --------------------------- | ---------------------------------------- | -------------------------------------------- | | Extracción | Convierte una nota en un registro tipado | Que todos los valores sean correctos | | Revisión médica | Permite corregir y aprobar | Que la plataforma externa recibió cada campo | | Confirmación del trabajador | Obtiene `idpac` y ve el resumen | Lectura campo por campo después del envío | ## Evidencia en código [Sección titulada «Evidencia en código»](#evidencia-en-código) * [Montaje de la API](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/index.ts) * [Esquema del borrador](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/packages/db/src/schema/biobadamex-drafts.ts) * [Configuración del pipeline](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/config/pipeline-config.ts) * [Ensamblador de ciclo de vida](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/dal/biobadamex-lifecycle.ts) * [Trabajador Playwright](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/biobadamex-registry-worker/src/driver.ts)
# Completitud y revisión médica
> Reglas por enfermedad, correcciones, aprobación, rechazo y confirmaciones clínicas.
## Dos barras que no deben mezclarse [Sección titulada «Dos barras que no deben mezclarse»](#dos-barras-que-no-deben-mezclarse) BiobadamexAI distingue: 1. **Campos que el registro externo exige:** confirmados por el comportamiento del formulario. 2. **Campos que el equipo clínico exige para una nota útil:** una barra clínica propia y dependiente de enfermedad. La documentación y los análisis deben nombrar cada barra. Llamar “estándar del registro” a la segunda atribuiría a BIOBADAMEX una regla que no impone. ## Completitud por enfermedad [Sección titulada «Completitud por enfermedad»](#completitud-por-enfermedad) `requiredFieldsFor(record)` selecciona la variante antes de calcular faltantes: * Artritis reumatoide: usa componentes clínicos confirmados para esa variante. * Espondiloartritis: excluye TJC28, SJC28 y DAS-28; no fabrica BASDAI o ASDAS. * Vasculitis: puede exigir BVAS según la regla confirmada. * LES y esclerosis sistémica: conservan índices candidatos, pero no eligen uno mientras exista ambigüedad. * Sjögren y variante desconocida mantienen una postura conservadora. Algunos comentarios históricos en `extraction.ts` todavía describen una barra plana. El código ejecutable llama `requiredFieldsFor(record)` y esta documentación sigue la implementación probada. ## Borrador y procedencia [Sección titulada «Borrador y procedencia»](#borrador-y-procedencia) Cada borrador contiene `record` como copia corregible, `agentRecord` como snapshot inmutable, DAS-28 reconciliado, faltantes, fuente cifrada, modelo, huella del prompt, propietario, revisor y estado del envío. La vista operativa reduce la diferencia agente-humano a conteos. No devuelve valores clínicos. ## Revisión [Sección titulada «Revisión»](#revisión) La pantalla permite ver la fuente, revisar filas específicas de enfermedad, corregir campos permitidos, recomputar DAS-28 y faltantes, revisar episodios biológicos y aprobar o rechazar. Una corrección modifica `record`, nunca `agentRecord`, y genera auditoría. Cargar otro borrador o corregir reinicia confirmaciones. ## Qué bloquea cada paso [Sección titulada «Qué bloquea cada paso»](#qué-bloquea-cada-paso) | Paso | UI | Backend | | -------- | --------------------------------------------- | ------------------------------------------------------------------------- | | Aprobar | Exige confirmaciones, diagnóstico y episodios | Exige propiedad y estado `review_pending`; no recibe los ticks de UI | | Corregir | Solo campos definidos | Valida path, tri-state, propiedad y estado; recomputa | | Rechazar | Acción explícita | Exige propiedad y estado `review_pending` | | Enviar | Drawer de confirmación | Exige propietario, `approved`, faltantes resueltos, flag y payload válido | La checklist de aprobación vive principalmente en el cliente. El endpoint de aprobación no vuelve a validar esos ticks. El endpoint de envío sí vuelve a comprobar las condiciones que protegen la mutación externa. La acción de UI dice “Aprobar y enviar al registro”, pero el endpoint solo cambia el estado a `approved`. El envío ocurre después desde la pantalla terminal. La copia no debe interpretarse como una mutación externa inmediata. `No realizado` también es una exención cliente-side: desbloquea la aprobación, pero no cambia `unknown`, no limpia `missingRequired` y no deja auditoría. Por eso el envío posterior sigue bloqueado. ## Comorbilidades no documentadas [Sección titulada «Comorbilidades no documentadas»](#comorbilidades-no-documentadas) Cuando alguna permanece `unknown`, el drawer exige confirmar que lo no documentado debe registrarse como `No`. El backend exige la misma confirmación. Solo los `unknown` del bloque de comorbilidades se convierten a `false`; fechas, actividad, tratamientos y otros desconocidos no cambian. La normalización se persiste y audita. El preview es completo para el **registro tipado interno**, no para todos los controles del formulario externo. Puede mostrar índices o episodios `requested/current` que el trabajador no envía; el trabajador solo coloca episodios `previous` en la cuadrícula de tratamientos previos. ## Interrupciones y aprendizaje [Sección titulada «Interrupciones y aprendizaje»](#interrupciones-y-aprendizaje) La calificación guarda `acted` o `dismissed`. Discrepar con la recomendación es un motivo de dismissal, no un tercer estado. La combinación borrador, prompt y actor se actualiza en vez de duplicarse. Este ledger mide qué avisos valen una interrupción. No autoriza el envío ni sustituye la corrección clínica. ## Evidencia en código [Sección titulada «Evidencia en código»](#evidencia-en-código) * [Reglas por enfermedad](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/packages/biobadamex-registry/src/record.ts) * [Completitud](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/packages/biobadamex-registry/src/completeness.ts) * [Resultado de extracción](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/packages/biobadamex-registry/src/extraction.ts) * [Rutas de borradores](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/routes/biobadamex-drafts.ts) * [Pantalla de revisión](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/web/src/pages/BiobadamexReviewGate.tsx) * [Drawer de envío](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/web/src/components/BiobadamexSubmitDrawer.tsx)
# Ingesta y extracción
> Cómo entran las notas, cómo se protegen y cómo se convierten en borradores.
## Formas de ingreso [Sección titulada «Formas de ingreso»](#formas-de-ingreso) | Entrada | Persistencia del original | Momento de extracción | | --------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------- | | Texto pegado en consola | No crea archivo original | Inmediata, se encola al recibirlo | | PDF, Word o texto en consola | Original cifrado en R2 + fila de metadatos | Bajo demanda desde el inventario | | Carga por lotes con clave de servicio | Original cifrado por nota | Solo almacena; el médico extrae después | | RheumAI “submit for review” | No crea fila de upload | Reextrae en servidor y crea un borrador retenido | | Agente clínico dedicado | Usa solo uploads del médico configurado | Bajo demanda, con clave de agente | | Documento del canal de estudio WhatsApp | Original cifrado en R2 | Inmediata cuando el canal, consentimiento y médico están configurados | La respuesta de carga por lotes prueba almacenamiento, no extracción. Cada nota devuelve su propio resultado y una nota inválida no cancela sus hermanas. ## Archivos [Sección titulada «Archivos»](#archivos) 1. El servidor valida formato, nombre y tamaño antes de decodificar por completo. 2. Extrae texto localmente. PDF usa `unpdf`, Word usa el parser de documentos y texto se decodifica directamente. 3. Cifra los bytes originales con AES-256-GCM. 4. Guarda el ciphertext en R2 y metadatos en PostgreSQL. 5. El inventario expone estado y nombre derivado, no el cuerpo de la nota. 6. Al solicitar extracción, la API comprueba alcance antes de obtener o descifrar el archivo. El límite actual del archivo original es 20 MiB. Los nombres se limpian para impedir controles y separadores de ruta. ## Trabajo de extracción [Sección titulada «Trabajo de extracción»](#trabajo-de-extracción) El trabajador obtiene el texto, aplica desidentificación de mejor esfuerzo, ejecuta el modelo de `registro-extraction`, analiza JSON, convierte nulos a `unknown`, valida el esquema, calcula DAS-28, calcula faltantes por enfermedad y crea un borrador `review_pending` con fuente cifrada y atribución. ## Integridad [Sección titulada «Integridad»](#integridad) * El modelo no decide la semántica tri-state. * Un cero real se conserva. * `false` solo representa negación explícita, salvo comorbilidades confirmadas al enviar. * DAS-28 reportado se compara; el valor autoritativo se calcula desde componentes cuando es posible. * Episodios biológicos mantienen rol, fármaco, fechas y motivo separados. * El solicitado sin fecha propia puede usar la fecha de solicitud o nota; no aplica a episodios actuales o previos. ## Privacidad y riesgo residual [Sección titulada «Privacidad y riesgo residual»](#privacidad-y-riesgo-residual) La desidentificación elimina identificadores genéricos y tokens del nombre cuando puede localizarlos. No es garantía de cumplimiento: un nombre libre sin etiqueta puede sobrevivir. Fechas clínicas, sexo, centro y médico pueden conservarse porque forman parte del registro. El payload clínico de la cola de extracción puede contener texto en claro dentro de PostgreSQL. El camino de estudio cifra su nota antes de la cola, pero el camino clínico todavía no comparte esa protección. ## Reintentos y fallos [Sección titulada «Reintentos y fallos»](#reintentos-y-fallos) * Extracción permite cinco intentos con backoff. * Al agotarlos, un dead-letter crea un fallo visible sin guardar el texto clínico. * Una nota fallida o rechazada puede volver a extraerse. * Un borrador pendiente o aprobado evita duplicados. ## Huecos operativos [Sección titulada «Huecos operativos»](#huecos-operativos) * Un fallo entre guardar el objeto R2 y crear la fila puede dejar un objeto cifrado huérfano. * Un fallo de cola después de guardar el upload produce éxito parcial: la fuente existe aunque no haya trabajo. * Si el borrador se crea y luego falla la auditoría, el reintento puede crear otro borrador porque la inserción no tiene llave de idempotencia por intento. * Una nota pegada que termina en dead letter no tiene archivo original recuperable desde el inventario. * Completar desde fuente llena solo escalares `unknown`; no fusiona arrays de episodios y no tiene UI clínica. ## Evidencia en código [Sección titulada «Evidencia en código»](#evidencia-en-código) * [Ingesta](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/routes/biobadamex-intake.ts) * [Inventario y extracción](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/routes/biobadamex-uploads.ts) * [Extractor](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/services/biobadamex-extract.ts) * [Trabajos](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/packages/biobadamex-registry/src/jobs.ts) * [Handlers de cola](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/services/biobadamex-jobs.ts)
# Integraciones y cambios seguros
> Autenticación, servicios externos, banderas y pruebas antes de modificar el workflow.
## Integraciones [Sección titulada «Integraciones»](#integraciones) | Integración | Uso | Límite | | ------------------- | ---------------------------------------- | -------------------------------------------- | | Privy | Sesión y atribución opcional | La clave de servicio autoriza rutas M2M | | PostgreSQL/Drizzle | Metadatos, borradores, auditoría y colas | Contiene datos clínicos protegidos | | R2 | Originales cifrados | Descifrado solo después de comprobar alcance | | pg-boss | Extracción, dead letter y envío | Trabajadores solo si la cola está habilitada | | Nebius | Extracción estructurada | Desidentificación no es garantía | | RheumAI audit proxy | Calificar o enviar para revisión | No aprueba ni registra | | Agente clínico | Inventario y extracción por médico | No aprueba ni envía | | Tenki | MicroVM para Playwright | Requiere configuración y salida a internet | | BIOBADAMEX | Formulario crdA–crdE | El mapa no cubre todos los controles | ## Configuración [Sección titulada «Configuración»](#configuración) Nombres únicamente, nunca valores: * `BIOBADAMEX_QUEUE_ENABLED` * `BIOBADAMEX_REGISTRY_SUBMIT_ENABLED` * `NEBIUS_API_KEY`, `NEBIUS_BASE_URL` * clave de ingesta M2M y `BIOBADAMEX_INGEST_MEDIC_ID` * `BIOBADAMEX_AUDIT_API_KEY` * clave del agente y médico `onBehalfOf` * variables R2 y clave maestra de cifrado * token, imagen y workspace de Tenki * URL base y credenciales del registro No copies valores a archivos, documentación, comandos, logs o PRs. ## Invariantes [Sección titulada «Invariantes»](#invariantes) 1. Comprobar propietario antes de descifrar R2. 2. Mantener `agentRecord` inmutable. 3. Recomputar DAS-28 y faltantes tras corrección. 4. No convertir `unknown` a cero o `false` de forma general. 5. Separar aprobar de enviar. 6. Volver a comprobar propiedad, estado, faltantes, confirmación y flag al enviar. 7. Usar la versión del mapa del payload. 8. No equiparar `idpac` y resumen con read-back campo por campo. 9. Separar datos del estudio y workflow clínico. 10. Documentación no autoriza deploy, migración ni escritura externa. ## Mapa de cambio y pruebas [Sección titulada «Mapa de cambio y pruebas»](#mapa-de-cambio-y-pruebas) | Cambio | Archivos | Verificación mínima | | --------------------- | -------------------------------- | ----------------------------------------- | | Ingesta | intake/uploads | tests de intake, batch y uploads | | Extracción | extract/config | extractor, fallos y esquema | | Tri-state/completitud | paquete registry | suite completa del paquete | | Revisión/corrección | drafts + UI | tests de drafts, preview y typecheck web | | Cola | queue/jobs | jobs, dead-letter y lifecycle | | Mapa/worker | field-map/resolver/filler/driver | suites registry y worker; bundle | | Envío | submit + Tenki | mocks únicamente; nunca envío real | | Auditoría/agentes | audit/agent | claves fail-closed, alcance y no mutación | ## Verificación observada [Sección titulada «Verificación observada»](#verificación-observada)
```bash
pnpm --filter @pokta/biobadamex-registry test
pnpm --filter @pokta/biobadamex-registry-worker test
pnpm --filter @pokta/api test --
pnpm exec turbo run typecheck --force
```
En el commit documentado: registry 190 pruebas, worker 41, API enfocada 156 y typecheck forzado 12 tareas, todas pasaron. No se ejecutaron migraciones, despliegues, consultas clínicas ni escrituras al registro. ## Riesgos que deben permanecer visibles [Sección titulada «Riesgos que deben permanecer visibles»](#riesgos-que-deben-permanecer-visibles) * Texto clínico en claro dentro del payload de extracción. * Desidentificación de mejor esfuerzo. * Checklist de aprobación principalmente cliente-side. * Worker best-effort para campos no fatales. * Mapa incompleto y con ambigüedades. * Sin read-back campo por campo. * Registry-submit sin política específica de reintento. * Descripciones médico-facing pendientes en pipeline config. ## Evidencia en código [Sección titulada «Evidencia en código»](#evidencia-en-código) * [Auditoría RheumAI](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/routes/biobadamex-audit.ts) * [Agente clínico](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/routes/biobadamex-agent.ts) * [Pipeline config](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/config/pipeline-config.ts) * [Colas](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/lib/queue.ts) * [Lifecycle](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/dal/biobadamex-lifecycle.ts)
# Envío al registro
> Condiciones, cola, sandbox, llenado de crdA–crdE y comprobación del resultado.
El envío es un proceso separado de la aprobación. No se dispara al aprobar y permanece deshabilitado salvo configuración explícita. ## Condiciones antes de encolar [Sección titulada «Condiciones antes de encolar»](#condiciones-antes-de-encolar) `POST /api/biobadamex/drafts/:id/submit` exige sesión, acceso de investigación, propiedad, estado `approved`, faltantes resueltos, `BIOBADAMEX_REGISTRY_SUBMIT_ENABLED=1`, confirmación de comorbilidades cuando aplica y payload válido con versión del mapa. Un administrador puede leer borradores de otro médico con auditoría, pero no enviarlos. Un borrador sin propietario tampoco es enviable. ## Trabajo en cola [Sección titulada «Trabajo en cola»](#trabajo-en-cola) La API crea un trabajo con id del borrador, registro aprobado y versión del mapa. La cola usa lote de uno por trabajador, pero no configura reintentos específicos. Varias instancias necesitan coordinación adicional para garantizar una sola sesión externa global. ## Sandbox [Sección titulada «Sandbox»](#sandbox) Por cada trabajo, el servicio valida configuración, carga el bundle, crea una microVM Tenki, inyecta payload y credenciales mediante variables de proceso, ejecuta Playwright, analiza el resultado JSON y destruye la microVM al salir. ## Recorrido externo [Sección titulada «Recorrido externo»](#recorrido-externo) 1. Login. 2. Llenar y guardar `crdA`. 3. Leer el `idpac` asignado. 4. Visitar y guardar `crdB`, `crdC`, `crdD` y `crdE`. 5. Abrir resumen. 6. Exigir el texto del mismo `idpac`. `idpac` y resumen son fatales. Muchos campos individuales son best-effort: un control ausente o una opción no reconocida se registra y se omite. `succeeded` confirma paciente y resumen, no paridad campo por campo. ## Mapa externo [Sección titulada «Mapa externo»](#mapa-externo) El mapa está versionado. Cambiar selectores exige subir `REGISTRY_MAP_VERSION` y revalidar. Existen huecos conocidos: controles no modelados, radios no confirmados con valores distintos, ambigüedad BASDAI, marcas biológicas pendientes y campos de detalle/fecha no cubiertos. No se debe describir como cobertura total del formulario. ## Tratamientos biológicos [Sección titulada «Tratamientos biológicos»](#tratamientos-biológicos) `crdE` acepta episodios con rol. Solo `previous` entra al bloque previo. El trabajador rechaza rol desconocido con fármaco, conflicto marca-sustancia, episodios duplicados que sobreescribirían fechas y fármacos sin mapeo confirmado. ## Resultado y errores [Sección titulada «Resultado y errores»](#resultado-y-errores) * Éxito: persiste `idpac`, `submittedAt`, estado `submitted` y auditoría. * Rechazo del formulario: persiste `submitError`, audita y no repite el mismo envío. * Fallo de infraestructura: lanza error; la cola actual no tiene política específica de reintento. * La lectura posterior verifica resumen, no cada campo. * Hay contratos de lectura acotada, pero no están montados en el flujo de submit de este commit. ## Riesgos de idempotencia y estado [Sección titulada «Riesgos de idempotencia y estado»](#riesgos-de-idempotencia-y-estado) * El endpoint descarta el id del trabajo y no usa singleton por borrador. Dos solicitudes antes del primer resultado pueden encolar dos mutaciones. * Si `crdA` asigna `idpac` y falla una página posterior, el resultado fallido puede contenerlo en memoria, pero la persistencia guarda solo `submitError`. Un reintento puede volver a crear identidad. * `summaryUrl` no se persiste. * Un fallo de infraestructura antes de un resultado estructurado deja el borrador `approved` sin `submitError`; la UI no tiene un estado durable `queued/running`. ## Evidencia en código [Sección titulada «Evidencia en código»](#evidencia-en-código) * [Endpoint de envío](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/routes/biobadamex-drafts.ts) * [Servicio Tenki](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/services/biobadamex-registry-submit.ts) * [Handler de resultado](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/api/src/services/biobadamex-jobs.ts) * [Mapa](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/packages/biobadamex-registry/src/field-map.ts) * [Driver](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/biobadamex-registry-worker/src/driver.ts) * [Llenado](https://github.com/poktalabs/pokta-care-monorepo/blob/b852f056d527d5bcec72b67256e5af98d386b323/apps/biobadamex-registry-worker/src/field-filler.ts)
# Primeros pasos
> Checklist de acceso y por dónde empezar según en qué repo vas a trabajar.
## Acceso — checklist [Sección titulada «Acceso — checklist»](#acceso--checklist) * [ ] Acceso al repo en el que vas a trabajar (ver tabla abajo). * [ ] Acceso a `poktalabs/pokta-care-monorepo` — ahí viven los scripts del benchmark y el scoring, aunque tu trabajo principal sea en otro repo. * [ ] Alta en la app (`app.poktacare.com`) — el registro está abierto, solo necesitas entrar con tu Gmail, no hay lista blanca para el acceso básico. El acceso a las superficies de investigación (dashboard del estudio y consola BIOBADAMEX) sí se controla por una lista blanca de correos aparte — si tu trabajo la requiere, pídesela a quien te dio de alta. * [ ] Acceso al documento “grader persona” en Google Drive (relevante si trabajas en extracción/retrieval del Grader). * [ ] Acceso al grupo de chat con el Dr. Erick Zamora, para coordinar handoffs técnicos con quien hizo la mayoría de las modificaciones de reumatología. * [ ] Acceso a este sitio (`docs.poktacare.com`) — ya lo tienes si estás leyendo esto. Si alguno sigue pendiente, dilo directamente a quien te dio de alta — no es algo que debas resolver rodeando el acceso. ## Por dónde empezar [Sección titulada «Por dónde empezar»](#por-dónde-empezar) **Vas a trabajar en el Grader (extracción, prompts, retrieval, arm 3):** Empieza en [`pokta-grader-bioagent/CONTRIBUTING.md`](https://github.com/poktalabs/pokta-grader-bioagent/blob/main/CONTRIBUTING.md). Ese archivo tiene el contexto específico del repo, tu primera misión concreta y el setup de entorno (`dev/SETUP.md`). Vuelve a [Arquitectura](/architecture/) aquí para entender cómo tu trabajo encaja en el estudio completo. **Vas a trabajar en RheumaAI (arm 2):** Empieza en el `README.md` de `rheuma-ai-bioagent`. El repo del Grader es un fork de este — si ya conoces uno, el otro te va a resultar familiar (mismo runtime Bun, misma librería LLM, misma arquitectura de tools). **Vas a trabajar en el harness del estudio, scoring, o la app de producto:** Empieza en `pokta-care-monorepo`, específicamente `apps/api/scripts/biobadamex-*.ts` para el benchmark y `apps/api/src/services/biobadamex-eval.ts` para el scoring. Lee [Arquitectura](/architecture/) primero — ahí está el mapa completo de cómo este repo llama a los otros dos. **No estás seguro / tu trabajo cruza varios repos:** Lee [Arquitectura](/architecture/) completo antes de tocar código. Es la única página que explica el sistema de punta a punta. ## El punto de partida honesto [Sección titulada «El punto de partida honesto»](#el-punto-de-partida-honesto) El Grader y RheumaAI son forks de un framework llamado BioAgents (equipo Bio/Dezy) que ya no tiene mantenimiento activo upstream. La infraestructura se montó y se le entregó al Dr. Erick Zamora, quien hizo las modificaciones de reumatología (encriptación homomórfica completa, artículos especializados) encima. **Nadie en el equipo entiende hoy el 100% de este código internamente** — parte de tu trabajo real, en cualquiera de los dos repos, es ingeniería inversa de piezas heredadas. Es normal, no una señal de mala documentación previa. No estás entrenando ni afinando un modelo — todo lo que hay hoy es orquestación de LLMs ya entrenados con prompts, schemas de extracción y (opcionalmente) recuperación de contexto vía embeddings.
# Reglas no negociables
> Las reglas que no se negocian — stealth, PHI, integridad del estudio, push/merge. Copia canónica; los repos individuales enlazan aquí.
Esta es la **copia canónica** de estas reglas — los `CONTRIBUTING.md` de cada repo enlazan aquí en vez de repetir el texto completo. Si vas a citarlas o actualizarlas, hazlo en esta página. Estas reglas no son burocracia: son las que evitan que un cambio bien intencionado cause un problema real (legal, de confianza médica, o de validez del estudio). Léelas antes de tu primera línea de código, no después. ## 1. Stealth / trato como confidencial [Sección titulada «1. Stealth / trato como confidencial»](#1-stealth--trato-como-confidencial) El registro y los médicos/entidades colaboradoras están en modo *stealth*. No hay NDA firmado, pero el trato es como si lo hubiera: **ninguna superficie pública puede nombrar el registro, a los médicos colaboradores, ni a las entidades involucradas, ni insinuar su respaldo/participación.** Esto incluye repos públicos, posts, demos a terceros, portafolio, LinkedIn. Este sitio está gateado por acceso, no es público — pero el contenido es igual de sensible que los repos privados que enlaza. No lo reenvíes, no tomes screenshots para compartir fuera del grupo autorizado, no asumas que “gateado” significa “casual”. Si tienes duda sobre si algo cuenta como superficie pública, pregunta antes de publicar. ## 2. Los datos de pacientes reales nunca entran a un repo [Sección titulada «2. Los datos de pacientes reales nunca entran a un repo»](#2-los-datos-de-pacientes-reales-nunca-entran-a-un-repo) El corpus de 110 notas es información real de pacientes (los nombres de archivo son nombres de pacientes) y está fuera de git a propósito — ni siquiera en un repo privado, ni “solo para pruebas”. Trabaja con notas sintéticas, nunca con el corpus real. Ya existen notas sintéticas de ejemplo: `src/routes/persona-isolation.test.ts` y `persona-runtime-guard.test.ts` en `pokta-grader-bioagent` (una nota a la vez), y los ejemplos hardcodeados en `biobadamex-sweep-dry-run.ts` del monorepo. Ninguno es a escala de 110 — si necesitas más variedad sintética, genera notas inventadas, nunca pidas las reales para desarrollo local. ## 3. Integridad del estudio — la más fácil de romper sin querer [Sección titulada «3. Integridad del estudio — la más fácil de romper sin querer»](#3-integridad-del-estudio--la-más-fácil-de-romper-sin-querer) **Cualquier mejora cuyo número vaya a respaldar el estudio se mide en datos held-out, nunca en las mismas 110 notas contra las que el estudio reporta resultados.** Por qué importa: si ajustas un cambio mirando qué tan bien le va contra esas 110 notas, y luego reportas ese mismo número como resultado del estudio, estás entrenando contra el set de prueba. El número sube porque ajustaste específicamente para ese conjunto — no porque el sistema extraiga mejor en general — y eso invalida la comparación entre arms, que es todo el punto del ejercicio. Esto es fácil de violar sin mala intención: “mejoré el score” se siente bien y es la métrica más a la mano. La regla existe justamente porque es la trampa más natural en la que caer. Mejorar cualquiera de los tres sistemas como producto es el objetivo, y está perfecto. El matiz es dónde mides el número que vas a mostrar como resultado del estudio. ## 4. Push y merge [Sección titulada «4. Push y merge»](#4-push-y-merge) Los tres repos de código (`pokta-grader-bioagent`, `rheuma-ai-bioagent`, `pokta-care-monorepo`) prohíben trabajar directo en `main` o hacer push directo — ramas + PR + revisión humana, siempre. Este sitio de documentación sigue la misma regla. Los forks de terceros (como BioAgents) nunca se empujan de vuelta a remotos upstream/terceros. ## Lo que todavía no está decidido [Sección titulada «Lo que todavía no está decidido»](#lo-que-todavía-no-está-decidido) * Compensación / términos formales de colaboración para contribuidores externos. * Coordinación de handoffs pendientes con el Dr. Erick Zamora. * Si el cuarto arm opcional del estudio se llega a construir. Si alguno de estos te bloquea, dilo directamente a quien te haya dado de alta — no improvises una solución para un punto que no es tuyo.
# Flujo del MVP
> El recorrido de principio a fin del clínico en RheumAI — pantalla por pantalla, con cada compuerta, punto de decisión y estado de construcción (enviado, tras bandera, o sin construir).
Mapa del recorrido completo del clínico, derivado del código (`apps/web`) — no de la memoria. Un clínico, una sesión autenticada, **dos carriles de producto** desde la consola. **El carril B es el MVP**: la cuña del registro calificado por completitud, donde se construye el foso (*moat*). La extracción y el envío al registro son las dos bisagras que dependen de una bandera de configuración. ## El recorrido [Sección titulada «El recorrido»](#el-recorrido)
```
flowchart TD
Landing["Landing · poktacare.com
capta al clínico"]:::xrepo --> SignIn
subgraph AUTH["Entrar"]
direction TB
SignIn["/iniciar-sesion
login Privy"]:::ship --> Me{"GET /api/users/me"}
Me -->|"403 access_required"| Req["/solicitar-acceso
none · pending · approved · rejected"]:::ship
Me -->|"sin onboarding"| Perfil["/consola/perfil
perfil + cédula"]:::ship
Me -->|"ok"| Home
Req -->|"aprobado · Entrar"| Home
Perfil --> Home
end
Home["/consola · ConsoleHome
inicio RheumAI"]:::ship
Home --> NC
Home --> Inv
subgraph LANEA["Carril A · Consulta → Plan de paciente"]
direction TB
NC["/consola/nueva-consulta"]:::ship --> AE["/consola/borradores/:id
Editor de aprobación"]:::ship
AE -->|"Compartir · magic link"| PP["/p/plan
plan del paciente (token, sin login)"]:::ship
end
subgraph LANEB["Carril B · Cuña BiobadamexAI — el MVP"]
direction TB
Up["/consola/biobadamex/subir
subir .docx/.doc/.pdf/.txt · ≤25"]:::ship
Wa["WhatsApp file-drop
consent → auth → acuse → ingesta"]:::gate
Up --> Inv
Wa --> Inv
Inv["/consola/biobadamex
inventario · filtros de estado + canal"]:::ship
Inv -->|"Extraer"| Ex{"extraer + calificar
QUEUE_ENABLED"}:::gate
Ex --> Rev["/consola/biobadamex/:id
Compuerta de revisión · checklist por variante"]:::ship
Rev --> Gr["/…/calificacion
la calificación — dato del foso"]:::ship
Rev -->|"Aprobar · dueño + checks"| Sub{"Enviar
solo dueño · SUBMIT_ENABLED · Charlson"}:::gate
Sub -->|"pg-boss → Tenki"| Reg[("Registro BIOBADAMEX")]:::ship
end
subgraph OUT["Un expediente · tres salidas"]
direction LR
O1["Completar
el expediente enviado"]:::ship
O2["Preguntar · Segunda opinión
rheumai.xyz — NO cableado aquí"]:::xrepo
O3["Plan de paciente
/p/plan · 'pregunta a RheumAI' pendiente"]:::stub
end
Reg --> O1
AE --> O3
Home -.->|"halo de profundidad"| O2
classDef ship fill:#e4eef8,stroke:#2b6cb0,stroke-width:1px,color:#1b3a57;
classDef gate fill:#f6ecd6,stroke:#9a6a10,stroke-width:1px,color:#5c3f08;
classDef stub fill:#e9ebef,stroke:#6b7280,stroke-width:1px,color:#3b3f48;
classDef xrepo fill:#ece6fb,stroke:#6d45d9,stroke-width:1px,color:#3d2680;
```
**Leyenda** (colores del diagrama): **azul** = enviado y accesible · **ámbar** = tras bandera / condicional · **gris** = pendiente / sin construir · **violeta** = app aparte (rheumai.xyz). ## Cada pantalla, cada punto de decisión [Sección titulada «Cada pantalla, cada punto de decisión»](#cada-pantalla-cada-punto-de-decisión) Los “puntos de decisión” son las bifurcaciones, compuertas y estados condicionales dentro de cada pantalla — donde el flujo se ramifica y donde tomamos decisiones de producto. | Pantalla | Propósito | Puntos de decisión y compuertas | Estado | | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | | `/iniciar-sesion` — Ingresar | Login alojado por Privy. | Al autenticar, retorna a `?next=` (p. ej. un enlace de revisión abierto desde WhatsApp aterriza ahí). | Enviado | | `/solicitar-acceso` — Solicitar acceso | Compuerta *fail-closed*: un médico autenticado pero no aprobado pide acceso. | 4 estados de `useAccessStatus`: **none** → formulario (nombre + cédula + especialidad + consentimiento) · **pending** · **approved** → “Entrar” · **rejected**. Se renderiza en el shell lateral con logout. El correo se sella del lado servidor. | Enviado | | `/consola/perfil` — Onboarding | Perfil de primera vez (nombre, especialidad, cédula). | Compuerta en `Shell`: \`canUseApp = onboarded | | | `/consola` — Inicio | Inicio RheumAI — “Nueva consulta” + consultas recientes. | Estados carga / vacío / lista; filas con `draftId` enlazan al Editor de aprobación. Es la entrada del **Carril A** — el producto de consulta, distinto de la cuña. | Enviado | | `/consola/biobadamex` — Inventario | Una lista para toda la cuña; el estado es un filtro, no una pantalla. | Filtro de estado (revisar → sin extraer → en proceso → aprobadas → enviadas → rechazadas → todas) con default inteligente. **Filtro de canal** ortogonal (whatsapp/consola/masiva) sólo aparece con >1 canal. “Extraer” en lote sólo en el filtro “sin extraer”. | Enviado | | `/consola/biobadamex/subir` — Subir | Ingesta desde consola. | Dos modos: **pegar** (extrae ya) vs **archivo** (queda en inventario, se extrae después). Acepta `.docx .doc .pdf .txt`, ≤ 25 archivos. La extracción depende de `QUEUE_ENABLED`. | Enviado | | WhatsApp file-drop — *(servidor, sin pantalla)* | El carril de ingesta nativo del chat. | Whitelist o *pass-through* a triage. consentimiento (ACEPTO/BAJA) → clasifica documento por extensión → **auth clínica fail-closed** → acuse exactly-once → ingesta (núcleo compartido) → hitos sin PHI. El coach es otra bandera (`COACH_ENABLED`, off por defecto). | Tras bandera | | `/consola/biobadamex/:id` — Compuerta de revisión | Revisión de completitud + aprobación no evitable. No edita el expediente en silencio — sólo aprobar/rechazar/corregir. | **Checklist por variante**: AR → NAD/NAT/DAS-28/VSG/PCR; vasculitis → BVAS; LES → SLEDAI (no obligatorio, pendiente de dueño clínico); ES → Valentini/EUSTAR; EspA/Sjögren → sin instrumento forzado. **Compuerta de aprobación** = diagnóstico resuelto Y cada fila confirmada Y episodios de biológico confirmados. Corregir reinicia todas las confirmaciones. **Aquí no hay “Segunda opinión”.** | Enviado | | `…/:id → Enviar` — Envío al registro | Empuja el expediente aprobado a BIOBADAMEX (pg-boss → Tenki). | Sólo en la pantalla terminal (aprobada). **Sólo dueño** (`draft.medicId === me.id`; los admin ven texto, sin botón) · compuerta dura `SUBMIT_ENABLED=1` · comorbilidades Charlson no documentadas exigen un checkbox de “confirmar ausencia”. | Tras bandera | | `/…/calificacion` — La calificación | Primera impresión del médico — y la fuente del foso. El veredicto se muestra antes de cualquier pregunta. | Un *prompt* por campo faltante; cada uno resuelve a **acted** (→ compuerta de revisión) o **dismissed** (con motivo). Alimenta el registro de calidad de interrupción. | Enviado | | `/consola/borradores/:id` — Editor de aprobación (Carril A) | La pantalla original de aprobación de consulta — diagnóstico + decisiones de tratamiento. | No evitable: todas las filas confirmadas antes de aprobar; códigos de baja confianza marcados. Ya aprobada, “Compartir” genera un magic link del paciente. | Enviado | | `/p/plan` — Plan del paciente | Vista de sólo lectura por token (sin login) — el pago del Carril A. | Implementada completa (tratamiento vs lenguaje simple, procedencia). Un stub dentro: “Pregúntale a RheumAI · próximamente”. | Enviado (+ stub) | ## Dónde muerden las compuertas de lanzamiento [Sección titulada «Dónde muerden las compuertas de lanzamiento»](#dónde-muerden-las-compuertas-de-lanzamiento) Del registro de compuertas (`workstreams/biobadamexai/LAUNCH-GATES.md`), mapeadas sobre el flujo. **BLOCKER** = antes del lanzamiento público · **CDSS** = antes de anunciar “Preguntar” como apoyo a decisiones citado · **BUILD** = una superficie prometida aún sin cablear. | Compuerta | Severidad | Dónde muerde | | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------- | | **G8 · G9** — orígenes Privy + smoke test de auth | Blocker | Paso de ingreso (login no funciona fuera de dominio hasta permitir orígenes) | | **G4** — `GET /api/registry/queue` sin proteger | Blocker | rheumai.xyz (app aparte) | | **G5 · G5c** — ingesta WhatsApp + coach | Blocker (ya **en producción**; la fila del registro está desactualizada) | Carril WhatsApp file-drop | | **G5b** — bypass de auto-respuesta del triage de paciente | Blocker\* (aislado del piloto por la whitelist) | Ruta *pass-through* | | **G1 · G2 · G3** — barandal recomienda-no-diagnostica, estadísticas sin verificar, unificar citas | CDSS | Salida “Preguntar” (se puede enviar acotado sin esto) | | **G6** — cableado de Planes de paciente | Build | Salida Planes (el Carril A ya genera /p/plan; el chat in-plan es el stub) | | **G10** — chequeo de deriva del diccionario de campos | Build | Compuerta de revisión | | **G7** — bloque “Preguntar” en la landing (diseño) | Build | Landing | ## Decisiones por fijar [Sección titulada «Decisiones por fijar»](#decisiones-por-fijar) Lo que el mapa deja abierto para el flujo del MVP — las decisiones a tomar mientras lo recorremos. 1. **¿“Preguntar / Segunda opinión” está en el MVP, y desde dónde?** No está cableado en la consola — la compuerta de revisión no tiene UI de segunda opinión, y `consultRheumaSupport` es código muerto. La capacidad vive sólo en la app aparte rheumai.xyz. Decidir: enlazar hacia ella, cablearla, o sacarla del encuadre del MVP. 2. **¿Dos carriles o uno?** La consola lleva el producto original consulta→plan (Carril A) y la cuña BiobadamexAI (Carril B). El rebrand mantiene ambos como nav de nivel superior. Para el MVP, ¿el Carril A está en alcance, o el lanzamiento es estrictamente la cuña? 3. **¿Dónde aterriza *primero* el clínico — y sobre qué?** El inicio de consola es la superficie de consulta original, no la cuña. Para un clínico captado *para* BIOBADAMEX, ¿`/consola` debería abrir con la cuña (notas por revisar) en lugar de “Nueva consulta”? 4. **SLEDAI y los conjuntos obligatorios por variante.** Los instrumentos de LES se renderizan pero no son obligatorios (pendiente de dueño clínico); EspA/Sjögren no fuerzan instrumento. El conjunto de campos obligatorios por variante *es* la definición de completitud — necesita el visto bueno clínico antes de calificar notas reales. 5. **Envío al registro — ¿activo para el piloto?** El envío es sólo-dueño y con compuerta dura `SUBMIT_ENABLED`. Confirmar la postura de la bandera para el piloto de 20 médicos: ¿envían al registro en vivo, o paran en “aprobado” mientras se valida el ciclo?
# Repos y guías
> Directorio de enlaces — cada repo, su README/CONTRIBUTING, y dónde está desplegado.
## `pokta-grader-bioagent` [Sección titulada «pokta-grader-bioagent»](#pokta-grader-bioagent) El Grader — arm 3 del estudio, el producto en evolución. * Repo: [github.com/poktalabs/pokta-grader-bioagent](https://github.com/poktalabs/pokta-grader-bioagent) * Onboarding: [`CONTRIBUTING.md`](https://github.com/poktalabs/pokta-grader-bioagent/blob/main/CONTRIBUTING.md) * Setup de entorno: [`dev/SETUP.md`](https://github.com/poktalabs/pokta-grader-bioagent/blob/main/dev/SETUP.md), [`dev/getting-started.md`](https://github.com/poktalabs/pokta-grader-bioagent/blob/main/dev/getting-started.md) (genéricos del framework BioAgents) * Convenciones de ingeniería: [`AGENTS.md`](https://github.com/poktalabs/pokta-grader-bioagent/blob/main/AGENTS.md) * Desplegado: Railway, `grader.poktacare.com` * Runtime: Bun + Elysia ## `rheuma-ai-bioagent` [Sección titulada «rheuma-ai-bioagent»](#rheuma-ai-bioagent) RheumaAI — arm 2 del estudio, agente ya entrenado en reumatología. Origen del fork del Grader. * Repo: [`poktalabs/rheuma-ai-bioagent`](https://github.com/poktalabs/rheuma-ai-bioagent) (privado) * Onboarding: `README.md` (explica routes, tools, state model, LLM library) * Desplegado: Railway, `rheumai.xyz` * Runtime: Bun + Elysia — mismo stack que el Grader ## `pokta-care-monorepo` [Sección titulada «pokta-care-monorepo»](#pokta-care-monorepo) Orquestador del estudio (arm 1 en proceso, invoca los otros dos arms por HTTP, scoring, persistencia) y la app de producto real. * Repo: [`poktalabs/pokta-care-monorepo`](https://github.com/poktalabs/pokta-care-monorepo) * Scripts del benchmark: `apps/api/scripts/biobadamex-*.ts` * Scoring: `apps/api/src/services/biobadamex-eval.ts` * Invocación de arms: `apps/api/src/services/biobadamex-study-arms.ts` y `biobadamex-study-harness.ts` * Desplegado: Vercel (`app.poktacare.com` / `api.poktacare.com`) + Railway * Stack: TypeScript, Turbo, Drizzle, React ## Este sitio [Sección titulada «Este sitio»](#este-sitio) * Repo: [`poktalabs/pokta-care-docs`](https://github.com/poktalabs/pokta-care-docs) * Stack: Astro + Starlight, desplegado en Cloudflare Pages bajo `docs.poktacare.com` * Acceso: gateado — ver [Reglas](/guardrails/)
# Arquitectura de RheumAI
> Componentes, flujo de datos y puntos de integración del sistema actual.
## Mapa del sistema [Sección titulada «Mapa del sistema»](#mapa-del-sistema)
```
flowchart TB
UI["Preact web app"] --> API["Elysia API"]
EXT["Cliente externo"] --> API
STUDY["BiobadamexAI study harness"] --> SR["Ruta stateless de estudio"]
API --> ACCESS["Auth · rate limits · x402 opcional"]
ACCESS --> SETUP["Conversación y estado"]
SETUP --> PLAN["PLANNING"]
PLAN --> TOOLS["Herramientas clínicas y fuentes"]
TOOLS --> RET["DAG / RAG / grafo"]
RET --> REPLY["REPLY o HYPOTHESIS"]
REPLY --> ORVS["ORVS + control PMID"]
ORVS --> ETH["Revisión ética"]
ETH --> UI
SR --> PLAN2["PLANNING con límite de herramientas"]
PLAN2 --> TOOLS2["Herramientas permitidas"]
TOOLS2 --> REPLY2["REPLY"]
REPLY2 --> STUDY
SETUP --> DB[("Supabase")]
TOOLS --> OBJ[("Storage S3 compatible")]
TOOLS --> LIT["PubMed · Semantic Scholar · OpenScholar · conocimiento local"]
```
## Componentes [Sección titulada «Componentes»](#componentes) ### Cliente web [Sección titulada «Cliente web»](#cliente-web) La interfaz Preact gestiona sesiones, autenticación, carga de archivos, envío de mensajes, citas y estados de pago. El backend sirve el bundle y usa una ruta fallback para la aplicación de una sola página. ### API Elysia [Sección titulada «API Elysia»](#api-elysia) `src/index.ts` monta las rutas de autenticación, configuración, chat, investigación profunda, comunidad, búsqueda, criptografía y estudio. También sirve la aplicación web y expone endpoints de salud. ### Estado y persistencia [Sección titulada «Estado y persistencia»](#estado-y-persistencia) El chat normal crea usuarios, conversaciones, mensajes y estado en Supabase. Los archivos pueden ir a un proveedor S3 compatible y su texto analizado se agrega al estado del turno. Esto significa que `/api/chat` no es stateless. La ruta de estudio usa UUID nulos y omite identificadores de mensaje y estado. Sus escrituras quedan desactivadas por diseño. Acepta texto desidentificado, puede fijar un proveedor/modelo y devuelve atribución, herramientas usadas, latencia y uso de tokens. ### Planificador y herramientas [Sección titulada «Planificador y herramientas»](#planificador-y-herramientas) El registro descubre carpetas bajo `src/tools/` y carga los `index.ts` habilitados. Variables `TOOL__ENABLED` pueden activar o desactivar una herramienta. El planificador decide qué fuentes consultar y cuál acción principal ejecutar. Familias relevantes: * Evidencia: PubMed, Semantic Scholar, OpenScholar, conocimiento local y grafo. * Clínica: razonamiento clínico, interpretación de laboratorio, resumen para aseguradora y protocolos. * Síntesis: `REPLY`, `HYPOTHESIS`, reflexión y revisión ética. * Infraestructura: archivos, análisis de datos, almacenamiento y servicios x402. Que una carpeta exista no garantiza que cargue. La carga es dinámica, registra errores y continúa con las herramientas disponibles. ### Recuperación y ranking [Sección titulada «Recuperación y ranking»](#recuperación-y-ranking) El flujo puede combinar recuperación por embeddings, grafo local y búsqueda semántica. Después, `DAG-RETRIEVAL` reordena documentos para acercar la evidencia a la consulta. Si no hay contexto útil, algunos modos regresan a una respuesta directa del modelo. ### Modelos [Sección titulada «Modelos»](#modelos) La biblioteca de LLM expone una interfaz común para varios proveedores. `resilientChatCompletion` implementa fallbacks. En el estudio, un contexto fijado puede obligar a que todos los saltos observables usen un mismo proveedor y modelo, con fallback deshabilitado. ## Dos flujos que no deben confundirse [Sección titulada «Dos flujos que no deben confundirse»](#dos-flujos-que-no-deben-confundirse) | Aspecto | Chat normal | Ruta de estudio | | ----------------------------- | --------------------------------------------------- | ---------------------------------------------------- | | Endpoint | `/api/chat` | `/v1/study/rheumaai-reply` | | Persistencia | Sí | No, por diseño | | Archivos | Sí | Solo texto de nota | | Sesión | Conversación persistente | Una solicitud aislada | | Auth | Sesión, cliente o x402 según configuración | Bearer dedicado; cerrado si falta la clave | | Herramientas | Las elegidas por el planificador | Allowlist y techo de costo | | ORVS posterior a la respuesta | Sí en el flujo normal; modos explícitos disponibles | No hay una llamada ORVS final explícita en esta ruta | | Resultado | Respuesta conversacional | Respuesta, atribución y telemetría del turno | ## Dependencias externas [Sección titulada «Dependencias externas»](#dependencias-externas) * Supabase para conversaciones, mensajes, estados y búsquedas. * Storage S3 compatible para archivos cuando está configurado. * Proveedores LLM configurables. * PubMed, Semantic Scholar, OpenScholar y corpus local para evidencia. * Servicios médicos x402 opcionales. * Railway y el dominio `rheumai.xyz` según la configuración del repositorio. ## Evidencia en código [Sección titulada «Evidencia en código»](#evidencia-en-código) * [Servidor y montaje de rutas](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/index.ts) * [Ruta de chat](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/routes/chat.ts) * [Orquestación de herramientas](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/services/chat/tools.ts) * [Pipeline comparativo](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/services/chat/pipeline.ts) * [Registro de herramientas](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/tools/index.ts) * [Abstracción de LLM](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/llm/provider.ts) * [Ruta de estudio](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/study/rheumaai-reply-route.ts)
# Evaluación de notas y completitud
> Qué puede hacer RheumAI hoy y cómo debe conectarse con el flujo controlado de BiobadamexAI.
> Esta página define el límite de RheumAI. La implementación actual de ingesta, completitud, revisión y envío vive en el [workflow de BiobadamexAI](/biobadamexai/). ## El problema [Sección titulada «El problema»](#el-problema) Una nota puede estar bien redactada y aun así omitir un dato necesario. También puede contener un dato correcto que el sistema no logra estructurar. Por eso hay que separar tres preguntas: 1. ¿Qué dice la nota? 2. ¿Qué dato exige el flujo clínico o el registro? 3. ¿Qué debe preguntar o corregir el médico antes de aprobar? ## Capacidad actual de RheumAI [Sección titulada «Capacidad actual de RheumAI»](#capacidad-actual-de-rheumai) RheumAI puede: * recibir texto clínico y archivos en el chat; * extraer texto de PDF, hojas de cálculo, CSV, JSON, Markdown y texto; * usar visión para ciertos PDF e imágenes cuando está configurada; * clasificar el tipo de consulta y detectar algunos términos de alerta; * recuperar evidencia y producir una interpretación clínica; * pedir datos faltantes mediante instrucciones de la persona y del prompt; * revisar la calidad de su propia respuesta con ORVS; * procesar una nota desidentificada por una ruta stateless para el estudio. Estas capacidades ayudan a descubrir vacíos, pero hoy producen principalmente texto narrativo. ## Lo que no existe como contrato de RheumAI [Sección titulada «Lo que no existe como contrato de RheumAI»](#lo-que-no-existe-como-contrato-de-rheumai) El repositorio no expone todavía una respuesta estructurada con: * cada campo requerido por BIOBADAMEX; * valor extraído; * fragmento de procedencia; * estado `presente`, `ausente`, `desconocido` o `no_aplicable`; * regla clínica que explica por qué el campo se exige; * pregunta sugerida al médico; * aprobación o rechazo del médico. La ruta de estudio devuelve `content`, proveedor, modelo, herramientas, tokens, latencia y fallback. No devuelve una matriz de completitud de la nota. Tampoco aplica una comprobación determinista de desidentificación, límite específico para la longitud de la nota, ORVS final, revisión ética ni aprobación clínica. El llamador debe resolver esas obligaciones antes y después de la ruta. ## Diseño de integración recomendado [Sección titulada «Diseño de integración recomendado»](#diseño-de-integración-recomendado) El flujo institucional debe mantener responsabilidades separadas: | Etapa | Responsable | Salida | | --------------------- | ---------------------------------- | -------------------------------------------------------- | | Ingesta | BiobadamexAI | Nota controlada y su identificador interno | | Extracción | Servicio estructurado del registro | Campos candidatos con procedencia | | Completitud | Reglas BIOBADAMEX por diagnóstico | Campos presentes, ausentes, desconocidos o no aplicables | | Explicación clínica | RheumAI | Resumen de vacíos y preguntas útiles para el médico | | Revisión de respuesta | ORVS y controles deterministas | Flags de exactitud, seguridad, citas y cobertura | | Revisión humana | Médico autorizado | Corrección y aprobación explícita | | Envío | Servicio controlado del registro | Solo datos aprobados, con lectura posterior | ## Regla central [Sección titulada «Regla central»](#regla-central) RheumAI debe **detectar y preguntar**, no inventar. Si la nota no contiene un dato, la integración debe conservar el estado ausente o desconocido según la regla clínica. Una inferencia del modelo nunca se convierte automáticamente en un valor del registro. ## Flujo para “completitud” [Sección titulada «Flujo para “completitud”»](#flujo-para-completitud) 1. El extractor identifica valores candidatos y guarda el fragmento que los respalda. 2. El motor de reglas selecciona los campos aplicables al diagnóstico. 3. El sistema calcula completitud como presencia de campos aplicables. Exactitud y completitud se reportan por separado. 4. RheumAI transforma los vacíos en preguntas claras y priorizadas. 5. ORVS revisa la **respuesta de RheumAI**, no el expediente. 6. El médico confirma, corrige o marca que el dato no está disponible. 7. Solo la versión aprobada puede pasar al servicio de registro. ## Salvaguardas [Sección titulada «Salvaguardas»](#salvaguardas) * Usar notas sintéticas o desidentificadas fuera del entorno clínico autorizado. * Recordar que el chat normal puede persistir mensajes y archivos. Los PDF largos que parecen artículos también pueden entrar al directorio local de conocimiento según las reglas actuales. * No guardar notas reales en repositorios, documentación, logs ni fixtures. * Mantener procedencia campo por campo. * No convertir `0`, `false` o una fecha válida en “faltante” por una prueba booleana genérica. * Mantener `ausente`, `desconocido` y `no aplicable` como estados distintos donde la regla clínica lo requiera. * No permitir que ORVS o una respuesta narrativa salten la revisión del médico. * Registrar qué modelo y versión de reglas produjo cada borrador. ## Criterios antes de conectar el flujo [Sección titulada «Criterios antes de conectar el flujo»](#criterios-antes-de-conectar-el-flujo) La integración no debe considerarse lista hasta que existan: * contrato de entrada y salida versionado; * reglas de completitud revisadas por diagnóstico; * pruebas con notas sintéticas para campos presentes y faltantes; * revisión clínica de las preguntas sugeridas; * auditoría de que la ruta no escribe ni registra PHI de forma inesperada; * control de autenticación servicio a servicio; * fallback que preserve el borrador sin enviarlo si falla RheumAI. ## Evidencia en código [Sección titulada «Evidencia en código»](#evidencia-en-código) * [Ruta stateless de estudio](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/study/rheumaai-reply-route.ts) * [Pruebas de la ruta de estudio](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/study/rheumaai-reply-route.test.ts) * [Carga y análisis de archivos](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/tools/file-upload/index.ts) * [Interpretación de laboratorio](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/tools/lab-interpretation/index.ts) * [Razonamiento clínico](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/tools/clinical-reasoning/index.ts) * [ORVS](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/tools/verification/index.ts)
# Integraciones y cambios seguros
> Contratos externos, configuración y pruebas mínimas antes de modificar RheumAI.
## Integraciones principales [Sección titulada «Integraciones principales»](#integraciones-principales) | Integración | Uso | Fallo esperado | | ------------------------------ | ------------------------------------------------------- | ---------------------------------------------------------------------- | | Supabase | Usuarios, conversaciones, mensajes, estados y búsquedas | El chat persistente puede fallar o degradarse | | Storage S3 compatible | Archivos de conversación | El texto puede analizarse, pero la carga persistente puede omitirse | | Proveedores LLM | Planificación, herramientas, respuesta y evaluación | `resilientChatCompletion` puede usar fallback fuera del estudio fijado | | PubMed | Evidencia biomédica y PMID verificados | La respuesta debe declarar que no hubo PMID verificado | | Semantic Scholar / OpenScholar | Literatura adicional | La herramienta registra el fallo y el pipeline puede continuar | | Conocimiento local / grafo | Recuperación especializada | El pipeline puede usar otro método o una respuesta directa | | BiobadamexAI study harness | Llama la ruta stateless con nota desidentificada | 401 sin bearer válido; 503 si la clave no está configurada | | x402 | Pago opcional para API externa | 402 o 429 según el estado del pago y límites | ## Variables que definen comportamiento [Sección titulada «Variables que definen comportamiento»](#variables-que-definen-comportamiento) Esta lista nombra configuración, nunca valores: * Servidor: `PORT`, `HOST`, `CORS_ORIGINS`. * Estudio: `RHEUMAI_STUDY_API_KEY`, `CORS_STUDY_ORIGINS`, `RHEUMAI_STUDY_TOOL_ALLOWLIST`, `RHEUMAI_STUDY_MAX_PAID_CALLS`. * Modelos: `REPLY_LLM_PROVIDER`, `REPLY_LLM_MODEL`, `HYP_LLM_PROVIDER`, `HYP_LLM_MODEL`, `STRUCTURED_LLM_MODEL`. * ORVS: `ORVS_ENABLED`, `ORVS_VERSION`. * Storage y base de datos: variables documentadas por los proveedores configurados; no copiarlas a la documentación. * x402: `X402_ENABLED`, entorno, dirección de pago y credenciales del facilitador. ## Invariantes que no deben romperse [Sección titulada «Invariantes que no deben romperse»](#invariantes-que-no-deben-romperse) 1. La ruta de estudio permanece stateless y cerrada si falta la clave. 2. El cuerpo de una nota no aparece en logs ni errores. 3. Un pin de estudio obliga a usar el proveedor/modelo fijado y desactiva fallback entre modelos. 4. Herramientas con costo quedan fuera del allowlist por defecto y el techo pagado predeterminado es cero. 5. Los PMID publicados deben provenir del conjunto recuperado en ese turno. 6. El chat clínico normal termina en `REPLY`, salvo flujos explícitos de protocolo o investigación. 7. Un fallo de RheumAI nunca autoriza el envío de datos al registro. 8. Solo un médico autoriza datos clínicos finales. ## Mapa de cambios y pruebas [Sección titulada «Mapa de cambios y pruebas»](#mapa-de-cambios-y-pruebas) | Si cambias | Revisa | Pruebas mínimas | | -------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------ | | Montaje de rutas o CORS | `src/index.ts` y plugins Elysia | `bun run build`, smoke API sin mutaciones | | Chat, orden de pasos o fallbacks | `src/routes/chat.ts`, `src/services/chat/*` | tests de herramientas y smoke con datos sintéticos | | Ruta de estudio | `src/study/*` | `bun test src/study/rheumaai-reply-route.test.ts src/study/tool-cap.test.ts` | | Proveedores/modelos | `src/llm/*` | `bun test src/llm/resilient.test.ts` y pruebas de pin | | ORVS o citas | `src/tools/verification/*` y control PMID | tests unitarios, caso de PMID permitido y fabricado | | Archivos o laboratorio | `src/tools/file-upload/*`, `src/tools/lab-interpretation/*` | `bun test src/tools/lab-interpretation/lab-interpretation.test.ts` con fixtures sintéticos | | Herramientas dinámicas | `src/tools/index.ts` | arrancar el registro y revisar herramientas cargadas/fallidas | | Interfaz | `client/src/*` | `bun run check`, `bun run build` | ## Verificación local recomendada [Sección titulada «Verificación local recomendada»](#verificación-local-recomendada)
```bash
bun install --frozen-lockfile
bun run check
bun run build
bun test src/study/rheumaai-reply-route.test.ts \
src/study/tool-cap.test.ts \
src/tools/lab-interpretation/lab-interpretation.test.ts \
src/tools/knowledgeGraph/knowledgeGraph.test.ts
```
En el commit documentado, el conjunto enfocado anterior pasó **26 pruebas, 0 fallas** después de una instalación congelada. El arranque del registro también mostró warnings por herramientas sin credenciales o sin `index.ts`; las pruebas enfocadas no sustituyen una verificación completa del servidor. Los smoke tests que llaman servicios externos requieren configuración válida. No deben ejecutarse contra producción ni con notas reales como parte de una modificación rutinaria. ## Estados del código [Sección titulada «Estados del código»](#estados-del-código) * **Activo:** importado por `src/index.ts`, la ruta de chat o una herramienta cargada y cubierto por pruebas observables. * **Configurable:** activo solo cuando una variable o proveedor está presente. * **Experimental:** modos comparativos, benchmarks y scripts no usados por defecto. * **Placeholder o roto:** carpeta registrada sin `index.ts`, import que falla o integración que exige credenciales ausentes. * **Legado:** conservado para compatibilidad, pero no es el camino preferido. No documentes una carpeta como funcional solo porque existe. Confirma su importación, configuración y prueba. La cobertura automática no incluye las rutas principales de chat, investigación profunda, autenticación, comunidad, Telegram o x402, ni tiene pruebas de cliente. Una modificación en esas áreas necesita smoke checks sintéticos además de los tests existentes. ## Evidencia en código [Sección titulada «Evidencia en código»](#evidencia-en-código) * [Rutas y CORS](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/index.ts) * [Chat y controles PMID](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/routes/chat.ts) * [Registro de herramientas](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/tools/index.ts) * [Fallbacks de modelos](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/llm/resilient.ts) * [Ruta de estudio](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/study/rheumaai-reply-route.ts) * [Límite de herramientas del estudio](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/study/tool-cap.ts)
# ORVS en RheumAI
> Qué verifica ORVS, cómo se usa en el código actual y cuáles son sus límites.
ORVS significa **Optimistic Response Verification System**. El documento de referencia fue publicado como preprint por Erick Zamora el 28 de marzo de 2026 y tiene el DOI `10.55277/researchhub.qq2fdvw9`.\[1] En RheumAI, ORVS es una revisión **posterior a la generación**. Evalúa la respuesta producida por el modelo. No valida directamente que la nota clínica de entrada tenga todos los campos que exige BIOBADAMEX. ## Preprint y código actual [Sección titulada «Preprint y código actual»](#preprint-y-código-actual) No son el mismo contrato. El preprint describe una rúbrica de cuatro dimensiones, hasta tres ciclos y escalamiento humano.\[1] El chat normal usa la versión heredada de seis dimensiones, intenta una regeneración y no implementa una cola de escalamiento humano ORVS. Los modos v2 son extensiones experimentales: Quick usa cuatro dimensiones y Deep usa ocho. Los pesos de Quick se parecen al preprint, pero el significado actual de `TMP` y `RSC` cambió a razonamiento temporal y completitud terapéutica. ## Flujo conceptual [Sección titulada «Flujo conceptual»](#flujo-conceptual) 1. RheumAI genera una respuesta usando la consulta y la evidencia recuperada. 2. Un modelo evaluador puntúa varias dimensiones. 3. Si el resultado queda bajo el umbral, RheumAI agrega indicaciones de corrección y vuelve a generar o ampliar la respuesta. 4. Un control separado compara PMID citados contra los PMID recuperados en ese turno. 5. El sistema entrega la respuesta final o aplica un fallback si se agota el tiempo. ## Implementaciones presentes [Sección titulada «Implementaciones presentes»](#implementaciones-presentes) ### Flujo normal de chat [Sección titulada «Flujo normal de chat»](#flujo-normal-de-chat) El chat normal llama a la verificación v1 para las consultas clínicas y para preguntas que no son saludos básicos. Evalúa seis dimensiones en escala 0–100: * exactitud de citas; * exactitud clínica; * especificidad; * alineación con la evidencia; * completitud de la respuesta; * ausencia de contradicciones. El umbral actual es 70. Si no pasa, el chat intenta regenerar la respuesta usando los flags del evaluador. La respuesta regenerada no vuelve a pasar por ORVS en ese camino normal. ### Modos comparativos explícitos [Sección titulada «Modos comparativos explícitos»](#modos-comparativos-explícitos) Cuando el cliente envía `pipelineMode`, puede escoger `quick-orvs` o `full-orvs`: | Modo | Dimensiones | Uso | | ------------- | ---------------------------------------------------------------------------- | ------------------- | | Quick ORVS v2 | exactitud clínica, seguridad, razonamiento temporal, completitud terapéutica | Revisión rápida | | Deep ORVS v2 | las cuatro anteriores más completitud, citas, claridad y sesgo | Revisión más amplia | La versión v2 calcula un compuesto 0–100 y puede regenerar o añadir una ampliación hasta dos veces. ## Controles de citas [Sección titulada «Controles de citas»](#controles-de-citas) Además del puntaje ORVS, el chat aplica una regla concreta para PMID: * Si recuperó evidencia PubMed pero la respuesta no cita PMID, puede regenerar. * Solo permite identificadores presentes en la evidencia recuperada en ese turno. * Si persiste un PMID no permitido, lo reemplaza por `[PMID no verificado]`. Este control es más determinista que pedir a un modelo que decida si una cita parece correcta. ## Límites importantes [Sección titulada «Límites importantes»](#límites-importantes) * ORVS evalúa una salida generada. No demuestra que una recomendación sea clínicamente correcta. * El evaluador también es un modelo y puede equivocarse. * El preprint describe una propuesta y experimentos retrospectivos con escenarios construidos.\[1] No demuestra resultados clínicos, aprobación regulatoria ni seguridad para uso autónomo. * En el flujo actual, errores de parseo o API pueden producir un resultado marcado como `passed` con flags de error. Es un comportamiento fail-open, no una validación exitosa. * `ORVS_ENABLED=false` desactiva la revisión. * La ruta stateless del estudio no ejecuta una verificación ORVS final de forma explícita después de `REPLY`. * La dimensión “completeness” de ORVS pregunta si la **respuesta cubre la consulta**. No es la completitud del expediente ni del registro. ## Relación con completitud de notas [Sección titulada «Relación con completitud de notas»](#relación-con-completitud-de-notas) Para BiobadamexAI se necesitan dos evaluaciones separadas: 1. **Completitud de la nota:** campos requeridos presentes, ausentes, desconocidos o no aplicables, según la enfermedad y el contexto del registro. 2. **Calidad de la respuesta:** exactitud, seguridad, evidencia, claridad y utilidad de lo que RheumAI comunica al médico. ORVS puede cubrir la segunda. La primera necesita un esquema clínico explícito y procedencia campo por campo. Consulta [Evaluación de notas y completitud](/rheumai/clinical-notes/). ## Evidencia en código [Sección titulada «Evidencia en código»](#evidencia-en-código) * [Implementación ORVS](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/tools/verification/index.ts) * [Prompts de evaluación](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/tools/verification/prompts.ts) * [Uso en chat y control PMID](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/routes/chat.ts) * [Modos comparativos](https://github.com/poktalabs/rheuma-ai-bioagent/blob/149508b3ab79da0b88c56deb63f6fb66e35d0e68/src/services/chat/pipeline.ts) ## Sources [Sección titulada «Sources»](#sources) \[1] — Optimistic Response Verification System > “Optimistic Response Verification System” > “March 28, 2026”