# Configurar un flujo de trabajo de sincronización de especificaciones OpenAPI en este proyecto
Quiero el mismo flujo de trabajo de especificación de backend que uso en otro repositorio: un script que compara la especificación OpenAPI en vivo contra una instantánea local y escribe un informe de cambios **orientado al frontend**, más un comando de barra `/api-sync` que convierte ese informe en un plan por fases.
Completa estos datos desde el repositorio antes de comenzar (pregúntame solo si no puedes resolverlo):
- **Fuente de la especificación**: encuéntrala tú mismo - ver Parte 0. No me preguntes por la URL hasta que hayas buscado. Lo que encuentres se convierte en el valor predeterminado del script, anulable mediante una variable de entorno `OPENAPI_URL`.
- **Ruta de la instantánea**: `api-spec/openapi.yaml` · **Ruta del informe**: `api-spec/CHANGES.md`
- **Raíz de origen para referencias cruzadas**: `src/` (ajusta al diseño de este repositorio)
- **Biblioteca de validación de respuestas**: zod (ajusta si este repositorio usa otra cosa)
- **¿Instantánea en git?** Mantén el yaml **ignorado por git** (demasiado grande/ruidoso para el historial) pero **confirma `CHANGES.md`** - el informe generado es el registro duradero de qué cambió y cuándo. También ignora `api-spec/openapi-*.yaml` y `api-spec/CHANGES-*.md` (archivos manuales con fecha).
Lee este repositorio primero (gestor de paquetes, convenciones de scripts, cómo se escriben las llamadas API y los esquemas de respuesta) y adapta su estilo. No inventes rutas - busca las reales con grep.
---
## Parte 0 - encuentra la especificación antes de escribir nada
Haz esto primero y dime lo que encontraste. No adivines una URL, y no me preguntes hasta que esto salga vacío.
**1. Los documentos - el lugar más barato, y normalmente correcto.** `README.md`, `CLAUDE.md`, `AGENTS.md`, `CONTRIBUTING.md`, cualquier cosa en `docs/`, `.github/`, `.cursor/rules/`, un archivo de prueba `*.http`/`*.rest`, o un checkout del wiki. La URL suele estar en prosa ("API docs: …/swagger"), en un paso de configuración, o junto al enlace del repositorio backend:
```bash
grep -rniE 'swagger|openapi|api-?docs|redoc|\/v3\/api-docs' --include='*.md' --include='*.mdx' --include='*.txt' --include='*.http' --include='*.rest' . | grep -v node_modules
```
Dos trampas: un **enlace de Swagger UI** (`…/swagger-ui/index.html`, `…/docs`, `…/redoc`) es una página HTML, no la especificación - deriva la URL de máquina a partir de ella (`/swagger-ui/index.html` → `/v3/api-docs`, `/docs` → `/openapi.json`, `/redoc` → el `spec-url` en su HTML) y verifica con curl. Y una URL de documentos puede estar **desactualizada** - confirma que responde antes de adoptarla, y dime si el README apunta a algo muerto.
**2. Un archivo de especificación ya en o cerca del repositorio** - alguien suele dejar uno:
```bash
find . -path ./node_modules -prune -o -iregex '.*\(swagger\|openapi\|api-docs\).*\.\(ya?ml\|json\)' -print
git ls-files | grep -iE 'swagger|openapi|api-docs'
```
También revisa `node_modules/.cache/`, `.next/cache/`, `dist/`, `build/`, `coverage/` y cualquier carpeta ignorada por git `api/`, `api-spec/`, `docs/`, `schemas/` - una ejecución anterior de generación de código suele dejar una copia allí. Una copia en caché desactualizada sigue siendo útil: es una **línea base para sembrar la instantánea**, para que la primera diferencia real sea significativa en lugar de "todo es nuevo". Si encuentras una, di qué tan antigua es (`git log -1` / mtime del archivo) antes de decidir confiar en ella.
**3. Una configuración de generador que ya nombre la fuente** - este es el hallazgo de mayor señal, porque apunta a la URL o ruta que el equipo realmente usa:
```bash
grep -rniE 'openapi|swagger|api-docs' --include='*.json' --include='*.ts' --include='*.js' --include='*.mjs' --include='*.yaml' --include='*.yml' --include='.env*' --include='Makefile' --include='*.sh' -l . | grep -v node_modules
```
Busca específicamente: configuración de `openapi-typescript` / `orval.config.*` / `kubb.config.*` / `swagger-typescript-api` / `@hey-api/openapi-ts`, un script npm tipo `openapi` en package.json, una URL base de API en `.env*`, URLs de servicio en `docker-compose.yml`, pasos de flujo de trabajo de CI, o un cliente generado confirmado cuyo comentario de cabecera cite su especificación fuente.
**4. Derívala de la URL base de la API.** Si solo encuentras una URL base, prueba las rutas convencionales para el framework de ese backend antes de preguntarme - FastAPI `/openapi.json`, Spring/springdoc `/v3/api-docs` (+ `.yaml`), ASP.NET `/swagger/v1/swagger.json`, NestJS `/api-json`, Rails/rswag `/api-docs/v1/swagger.yaml`, más `/openapi.yaml` y `/swagger.json` simples:
```bash
curl -sS -o /dev/null -w '%{http_code} %{content_type} %{url_effective}\n' <BASE>/openapi.json
```
Informa cuáles respondieron. Si todas requieren autenticación, dilo - no incorpores un token en el script.
**5. ¿Nada funciona?** Entonces pregúntame, y dime qué descartaste.
### Si la especificación no es accesible por HTTP
No fuerces el diseño de descarga. Haz que la fuente sea un único `SPEC_SOURCE` que puede ser **una URL, una ruta local, o un comando de shell** (por ejemplo, el propio `make openapi` del repositorio backend, o un archivo generado de un checkout hermano), resuelto en ese orden: flag `--to <archivo>` → variable de entorno `OPENAPI_URL` → el valor predeterminado que descubriste. Todo lo posterior - diferencia, informe, instantánea - no cambia. Di en `CLAUDE.md` cuál usa este repositorio y cómo actualizarlo.
## Parte 1 - `scripts/sync-api.mjs`
Un script Node ESM único con pocas dependencias (`js-yaml` es la única dependencia nueva; usa el gestor de paquetes del repositorio). Flags:
```
node scripts/sync-api.mjs descargar remoto → comparar vs instantánea → escribir informe + sobrescribir instantánea
node scripts/sync-api.mjs --check solo comparar, instantánea intacta, salir con 1 si hay desviación (compatible con CI)
node scripts/sync-api.mjs --from <archivo> comparar contra <archivo> en lugar de la instantánea
node scripts/sync-api.mjs --to <archivo> tratar <archivo> como "remoto" en lugar de descargar (sin conexión)
node scripts/sync-api.mjs --json también imprimir la diferencia cruda como JSON en stdout
```
Añade `"sync:api": "node scripts/sync-api.mjs"` a package.json.
**Si aún no existe una instantánea**: escribe la especificación descargada como instantánea, imprime "sembrada - vuelve a ejecutar después de que el backend publique para ver una diferencia", sal con 0. Nunca informes toda la API como "nueva". Excepción: si la Parte 0 encontró una especificación en caché/vendida más antigua, siembra la instantánea desde **esa** en su lugar y ejecuta una diferencia real contra la especificación en vivo en la primera ejecución - dime la fecha de la copia en caché para que sepa qué significa la línea base.
### Qué debe comparar
Aplana `paths` en un mapa de operaciones `"GET /a/b"` → operación y compara operaciones *y* `components.schemas` por separado:
**Operaciones**
- añadidas / eliminadas / cambiadas
- parámetros: nuevos (marca `required`), cambios requerido↔opcional, valores de enum añadidos/eliminados - identifica un parámetro por `in:name` (o `ref:Name` para parámetros `$ref`), no por índice de array
- esquema del cuerpo de solicitud y de respuesta exitosa: si el nombre del `$ref` cambió, informa el renombrado; si la forma es **en línea** (sin `$ref`), compara sus propiedades aquí - un esquema sin nombre se compara aquí o en ningún lugar. Resuelve envoltorios `allOf: [$ref]` al nombre subyacente, y renderiza uniones `oneOf`/`anyOf` como `A | B` (ganar/perder un miembro de unión es un cambio de comportamiento real).
- códigos de estado no-2xx nuevos/eliminados
- cambios en requisitos de seguridad, recién `deprecated`
**Esquemas**
- propiedades añadidas (marca requeridas) / eliminadas / con tipo cambiado
- valores de enum añadidos o eliminados (en el esquema y en cada propiedad, incluyendo `items.enum`)
- cambios requerido↔opcional
### Las dos cosas que hacen que este informe valga la pena
1. **Extracción de `error_code` de la prosa de respuesta.** Los códigos de error de máquina suelen estar documentados en ningún lugar excepto en el texto `description` de cada respuesta no-2xx ("… ya es un miembro activo (already_member)"), así que una nueva rama que necesitamos manejar parece *nada cambió* para una diferencia a nivel de esquema. Parsee por **contexto, no vocabulario**: tokens dentro de `(...)`, tokens después de prosa tipo `error_code`, y cualquier token en snake_case que aparezca en el enum literal `error_code` de algún esquema. **No** filtres tokens que colisionen con nombres de campos o valores de enum - esas colisiones son exactamente los códigos que más importan. Descarta tokens introducidos por la frase "campo X" (esos son nombres de campos, no códigos). Informa códigos añadidos, y para un código que dejó de documentarse, busca en el código fuente `"ese_codigo"` y di **qué archivo ramifica sobre él** - esa es una rama muerta.
2. **Referencia cruzada de cada cambio contra el código real.** Carga cada archivo fuente una vez mediante `git ls-files --cached --others --exclude-standard <raíz src>` (incluye no rastreados para que un sitio de llamada añadido en esta sesión cuente; omite archivos listados pero eliminados del árbol de trabajo), luego:
- **Sitios de llamada** para una ruta: convierte parámetros de ruta en comodines de un solo segmento y exige que la coincidencia termine en una comilla/backtick/`?` para que `/orgs/{id}` no coincida con `/orgs/${id}/archive`. Cada operación eliminada/cambiada lista sus sitios de llamada, o "⛔ sin sitio de llamada".
- **Espejos de esquema**: encuentra el archivo que contiene nuestro espejo de validación de respuesta de un esquema de especificación - coincide con `fooBarSchema` en cualquier lugar, más el nombre PascalCase simple **solo dentro de un `schemas.ts`** (en otros lugares colisiona con identificadores TS no relacionados). Adapta la convención de nombres a lo que este repositorio realmente usa - busca con grep primero.
- Una nueva operación cuya ruta ya está referenciada en `src/` recibe una nota "⚠️ ruta ya referenciada - verifica el método".
### Formato del informe (`api-spec/CHANGES.md`)
Encabezado con fecha de generación, etiqueta de línea base, `info.version` de la especificación, y conteos de operaciones y esquemas antes→después; luego una pequeña tabla de añadidos/eliminados/cambiados; luego secciones, en este orden:
- `## 🔴 Operaciones eliminadas - rompe si las llamamos` (con sitios de llamada)
- `## 🟡 Operaciones cambiadas` (viñetas anidadas por cambio + sitios de llamada)
- `## 🟢 Operaciones nuevas`, agrupadas por área de ruta (primer segmento, con casos especiales sensatos para los prefijos de esta API) - resumen, esquema de respuesta, esquema del cuerpo de solicitud
- `## 🔴 Esquemas eliminados` (con archivos espejo)
- `## 🟡 Esquemas cambiados` - **los espejados primero ordenados y en negrita con el archivo espejo**, ya que esos son los que pueden romper el parseo hoy; el resto son informativos
- `## 🟢 Esquemas nuevos` (una línea separada por comas)
Si nada cambió, el cuerpo es exactamente "Sin cambios desde la última instantánea." Pon al inicio del archivo "no editar a mano".
Mantén el script comentado donde una decisión no sea obvia (la heurística de error_code, el ancla final del comparador de rutas, por qué se incluyen archivos no rastreados) - el yo futuro lee esos comentarios.
## Parte 2 - `.claude/commands/api-sync.md`
Un comando de barra (`/api-sync [alcance]`, alcance opcional, también acepta `implement`) que ejecuta el flujo de trabajo. Frontmatter: `description` + `argument-hint`. Pasos:
1. **Comparar** - ejecuta `npm run sync:api`, lee `api-spec/CHANGES.md`. Señala las dos decisiones de juicio que el informe puede marcar pero no decidir: un **cuerpo de solicitud** cambiado en un sitio de llamada en vivo es accionable incluso sin espejo de esquema (construimos cuerpos a mano), y un **`error_code`** nuevo es una rama que aún no tenemos - si es un error de campo debe renderizarse en línea en el campo, no solo como un toast. Si el informe dice que no hay cambios, dilo y detente - no inventes trabajo.
2. **Clasifica cada elemento** en: Rompe (P0) · Silenciosamente incorrecto - un esquema espejado ganó un campo requerido o un enum creció con valores que nuestro validador rechaza (P0) · Ahora-incompleto - un cuerpo de solicitud construido a mano ganó un campo, o un nuevo `error_code` en el que no ramificamos (P1) · Des-mockea una pantalla (P1) · Extiende una pantalla (P2) · Característica completamente nueva (P3) · Solo-backend (descartar). Nunca omitas un elemento; si no encaja en ningún lugar, lístalo como una pregunta abierta. Verifica cada clasificación contra el código en lugar de asumir - abrie el archivo espejo nombrado, busca con grep el fixture mock, confirma que la pantalla existe.
3. **Escribe el plan** - una sección con fecha en `ROADMAP.md` (o el equivalente de este repositorio; crea uno si no hay), ordenada por esas prioridades y por fases para que cada fase se publique de forma independiente. Por elemento: los endpoints y archivos que cambian, qué puede hacer el usuario después que no puede hoy (el punto del trabajo - no "conectar endpoint X"), y si está bloqueado y por quién. Una línea por elemento. Luego actualiza cualquier documento de cobertura/seguimiento que este repositorio mantenga.
4. **Informa de vuelta en el chat** - qué publicó el backend en un párrafo en lenguaje simple, cualquier cosa rota ahora mismo con el archivo a corregir, las fases una línea cada una, y preguntas genuinas para el dev del backend solamente. Detente ahí; solo si el argumento contiene `implement`, construye **solo la Fase 1**, luego ejecuta el typecheck + lint de este repositorio e informa antes de continuar.
## Parte 3 - conéctalo
- Añade las entradas de gitignore.
- Añade una sección corta **Flujo de trabajo de cambios de backend** a `CLAUDE.md` (críala si falta): la instantánea es local y está ignorada por git, `CHANGES.md` es el registro confirmado, `CHANGES.md` se genera así que nunca lo edites a mano, `/api-sync` cuando el dev del backend diga que algo se publicó, `npm run sync:api -- --check` para detectar desviación, y el hábito de archivar un `api-spec/openapi-YYYY-MM-DD.yaml` con fecha antes de un gran cambio de backend como punto de referencia confirmado.
- Siembra la instantánea ejecutando el script una vez, y muéstrame el primer informe - más una nota de una línea sobre de dónde vino la especificación y, si sembraste desde una copia en caché, qué tan desactualizada estaba.
Discusión
0 comentarios