Quality Gates Reference (Español)
Inventario de gates (~90 scripts)
Sección titulada «Inventario de gates (~90 scripts)»Los scripts se encuentran en scripts/check/ (gates de políticas) y scripts/quality/ (motor de ratchet).
La fuente de referencia de CI es .github/workflows/ci.yml.
Vía rápida para PRs de release (quality.yml)
Sección titulada «Vía rápida para PRs de release (quality.yml)».github/workflows/quality.yml se ejecuta en los PRs dirigidos a release/**. Permite que las ramas de los colaboradores
sigan avanzando mediante gates rápidos filtrados por rutas, además de una señal informativa de compilación de producción para los cambios de
código:
| Job | Alcance | Bloqueante |
|---|---|---|
Build (advisory) |
PRs de código que no sean borradores y ramas de cola de Mergify; Node 24, npm-ci-retry, check:node-runtime, npm run build con OMNIROUTE_USE_TURBOPACK=1; no se cargan artefactos porque ningún job de calidad posterior los consume |
Informativo (continue-on-error: true; eliminar después de una semana de ejecuciones estables en PRs de release) |
Docs Gates (fast-path) |
PRs de documentación/código; referencias de documentación de la API y docs-all | Sí |
Fast Quality Gates |
PRs de código; comprobaciones estáticas, comprobación de tipos, comprobación de tipos del dashboard y pruebas unitarias afectadas | Sí |
Forgotten sibling tests |
PRs de código; módulos modificados rastreados hasta consumidores estáticos y posibles pruebas hermanas; las rutas de barrel e importación dinámica se notifican como diagnósticos informativos, con referencia a las excepciones de la lista de permitidos | Informativo |
Vitest (fast-path) |
PRs de código; suite rápida de Vitest | Sí |
Unit Tests fast-path |
PRs de código; suite de pruebas unitarias en 4 shards | Sí |
No new ESLint warnings |
PRs de código; protección de lint que tiene en cuenta las supresiones | Sí para el mismo origen, informativo para forks |
Merge integrity (changelog + generated skills) |
PRs que no sean borradores; sincronización del changelog y de las skills generadas | Sí para el mismo origen, informativo para forks |
Informe de pruebas hermanas olvidadas
Sección titulada «Informe de pruebas hermanas olvidadas»npm run check:forgotten-sibling-tests reutiliza el solucionador de importaciones que sustenta el mapa de impacto de las pruebas.
Para cada módulo de producción modificado, informa de cadenas deterministas
módulo/símbolo modificado -> consumidor estático -> posible prueba hermana cuando la prueba candidata
no está presente en el diff del pull request. El resumen en Markdown y el resultado JSON se conservan como
artefacto forgotten-sibling-tests del workflow para su calibración antes de cualquier implementación bloqueante.
Las reexportaciones mediante barrels y las importaciones dinámicas son únicamente diagnósticos de resolución; nunca generan un
hallazgo bloqueante. Las excepciones revisadas se encuentran en
config/quality/forgotten-sibling-allowlist.json. Cada entrada debe indicar el consumidor y la prueba
candidata, proporcionar una justificación específica y enlazar a un issue o pull request de GitHub. Las entradas con formato incorrecto
provocan un fallo por defecto. Las excepciones no pueden suprimir una prueba candidata eliminada ni un diff que añada .skip/.todo;
el debilitamiento de aserciones y otros tipos de enmascaramiento siguen siendo responsabilidad del gate bloqueante e independiente
check:test-masking.
Job: lint
Sección titulada «Job: lint»Se ejecuta en cada PR dirigido a main. Bloquea la fusión si falla.
Script (npm run ...) |
Valida | Bloqueante |
|---|---|---|
check:node-runtime |
La versión de Node.js se encuentra dentro del rango compatible | Sí |
check:cycles |
Importaciones circulares — todos los módulos de src/ + open-sse/ |
Sí |
check:route-validation:t06 |
Los esquemas de Zod están presentes en todas las rutas (política de nivel 6) | Sí |
check:any-budget:t11 |
El número de @ts-expect-error // any no supera el límite (catraca de nivel 11) |
Sí |
check:provider-consistency |
Cada proveedor en providers.ts tiene una entrada correspondiente en providerRegistry.ts (y viceversa, dentro de la lista de permitidos) |
Sí |
check:model-lifecycle |
Las tres tablas de enrutamiento mantenidas manualmente permanecen coherentes con la instantánea del ciclo de vida incluida en el repositorio (#11503): FITNESS_TABLE (taskFitness.ts) no puntúa ningún id retirado que REGISTRY pueda enrutar; cada destino de BUILT_IN_ALIASES está presente en REGISTRY y ausente de la instantánea de ids retirados; cada id retirado que aún esté en REGISTRY se redirige o figura en allowedRetiredInCatalog; y ninguna fuente ni destino de DEFAULT_DEGRADATION_MAP aparece como retirado en esa instantánea. Esto no demuestra que un modelo sea servido actualmente por un upstream activo. Sin conexión: compara con config/quality/model-lifecycle.json, que se actualiza manualmente con npm run quality:refresh-model-lifecycle (requiere red; no está integrado en CI). allowedRetiredInCatalog es un mecanismo de reducción progresiva: añade una entrada únicamente con una incidencia de seguimiento. |
Sí |
check:fetch-targets |
Cada fetch("/api/...") en el código del cliente dentro de src/ se resuelve a un route.ts real |
Sí |
check:deps |
Todas las dependencias instalables mediante npm install de cada package.json del repositorio están en dependency-allowlist.json; se marcan los paquetes nuevos sin versión fijada o que podrían ser objeto de slopsquatting |
Sí |
audit:deps |
npm audit (raíz + electron): sin avisos de gravedad alta o crítica (se solapa con check:vuln-ratchet de osv; consulta el Backlog de racionalización) |
Sí |
check:lockfile |
Integridad de package-lock.json: registro https, hashes de integridad y ninguna sobrescritura de host |
Sí |
check:licenses |
Lista de licencias SPDX permitidas para las dependencias de producción | Sí |
check:tracked-artifacts |
Sin artefactos de compilación ni enlaces simbólicos de node_modules confirmados (también se ejecuta en el pre-commit de husky; el pre-push es intencionadamente ligero — #6716) |
Sí |
check:ai-attribution |
Sin pie Co-Authored-By de IA/bot ni pie de generación por IA en los commits, el título o el cuerpo del PR — Regla estricta n.º 16 (en el bucle de comprobaciones rápidas de quality.yml para PR→release/** — lee la carga útil del evento y no realiza ninguna acción fuera de los PR — y en un paso exclusivo para PR en el lint de ci.yml para PR→main; también en el hook commit-msg de husky; se permiten coautores humanos; #14436) |
|
check:vitest-exclusions |
Cada exclusión de Vitest menciona una incidencia de seguimiento y aparece en config/quality/vitest-exclusions.json (#13204) |
Sí |
check:file-size |
Ningún archivo fuente supera el límite por extensión (ajuste progresivo: archivos grandes inmovilizados en la lista frozen) |
Sí |
check:error-helper |
Las respuestas de error en ejecutores/controladores usan buildErrorBody() / sanitizeErrorMessage() (Regla estricta n.º 12) |
Sí |
check:migration-numbering |
Los archivos SQL de migración están numerados secuencialmente, sin saltos ni duplicados | Sí |
check:public-creds |
No hay valores literales de OAuth client_id/client_secret ni claves web de Firebase fuera de publicCreds.ts (regla estricta n.º 11) |
Sí |
check:db-rules |
No hay SQL sin procesar fuera de los módulos de src/lib/db/; no hay importaciones de barril desde localDb.ts (reglas estrictas n.º 2 y n.º 5) |
Sí |
check:known-symbols |
Los ejecutores de proveedores, las estrategias de enrutamiento y los traductores registrados en sus tablas de despacho coinciden con los archivos del disco; no hay símbolos huérfanos ni sin declarar | Sí |
check:route-guard-membership |
Todas las rutas que generan un proceso secundario están clasificadas mediante isLocalOnlyPath() (reglas estrictas n.º 15 y n.º 17) |
Sí |
check:test-discovery |
Todos los archivos *.test.ts / *.spec.ts del repositorio son recopilados por al menos un ejecutor de pruebas (mecanismo de trinquete: la lista de archivos huérfanos de test-discovery-baseline.json solo puede reducirse) |
Sí |
check:agent-skills-sync |
Los artefactos generados de agent-skills coinciden con su catálogo de origen (sin desviaciones) | |
check:provider-asset-provenance |
Los logotipos/recursos de proveedores cuentan con una entrada de procedencia registrada | |
lint:json |
Los archivos de configuración JSON se analizan correctamente y cumplen las reglas de lint del repositorio | |
typecheck:core |
Compilación de TypeScript sin errores (solo advertencias informativas) | Sí |
typecheck:noimplicit:core |
noImplicitAny estricto: orientado al futuro; muchos puntos de llamada preexistentes aún necesitan anotaciones |
Informativo (continue-on-error: true) |
check:dashboard-typecheck |
tsc limitado a src/app/(dashboard)/** (#7033): la lista de permitidos seleccionada de 27 archivos de typecheck:core no incluye ningún TSX del panel, y next build tampoco comprueba sus tipos (next.config.mjs establece ignoreBuildErrors: true), por lo que las regresiones de identificadores huérfanos allí (#6625/#6909) eran invisibles para CI. Compara con una línea base fija del recuento por archivo y por código TS (config/quality/dashboard-typecheck-baseline.json, el mismo patrón de aplicación de obsolescencia que check:known-symbols): solo los errores NUEVOS que superen el recuento de la línea base hacen que falle la comprobación; redúzcala con --update cuando se corrija un error preexistente. |
Sí |
Trabajo: quality-gate
Sección titulada «Trabajo: quality-gate»Se ejecuta después de test-coverage. Bloquea la fusión si falla.
| Script | Valida | Bloqueante |
|---|---|---|
quality:collect |
Genera quality-metrics.json (número de advertencias de ESLint, cobertura del informe combinado de fragmentos) |
Sí (previo al ratchet) |
quality:ratchet |
Ninguna métrica de quality-baseline.json ha empeorado (advertencias de ESLint ≤ línea base; cobertura ≥ línea base) |
Sí |
check:duplication |
La duplicación de código (jscpd@4) no supera la línea base de quality-baseline.json |
Sí |
check:complexity |
La complejidad ciclomática por archivo no supera el límite (reglas complexity + max-lines-per-function del núcleo de ESLint) |
Sí |
check:cognitive-complexity |
Ratchet de complejidad cognitiva (eslint-plugin-sonarjs): pasada de ESLint independiente; CI ejecuta ambas combinadas en el único paso check:complexity-ratchets |
Sí |
check:dead-code |
El ratchet de exportaciones/archivos sin usar (knip) no empeora con respecto a la línea base | Sí |
check:compression-budget |
Presupuesto del benchmark de compresión: los valores mínimos de ahorro de tokens por motor no deben empeorar | Sí |
check:type-coverage |
El ratchet del porcentaje tipado (type-coverage) no empeora; sustituye en gran medida a typecheck:noimplicit:core |
Sí |
check:codeql-ratchet |
El número de alertas abiertas de CodeQL no empeora (se consulta mediante gh api; omisión controlada sin token); para la frecuencia de actualización y la activación manual, consulte «Ratchet de CodeQL» más abajo |
Sí |
Trabajo: quality-extended
Sección titulada «Trabajo: quality-extended»El trabajo completo es informativo (continue-on-error: true). Los ratchets basados en npm se ejecutan
realmente; los escáneres externos se instalan mediante gh release download y se omiten automáticamente (código de salida 0)
si un binario sigue sin estar presente.
| Script | Valida | Bloqueante |
|---|---|---|
check:circular-deps |
No hay dependencias circulares (dpdm) | Informativo |
check:bundle-size |
El tamaño del bundle no supera el límite | Informativo |
check:secrets |
Análisis de secretos (gitleaks): se omite si el binario no está presente | Informativo |
check:vuln-ratchet |
Las vulnerabilidades de las dependencias (osv-scanner) no empeoran; se omite si el binario no está presente | Informativo |
check:workflows |
Lint de workflows (actionlint + zizmor): se omite si los binarios no están presentes | Informativo |
check:openapi-breaking |
Cambios incompatibles en el contrato de la API pública (openapi.yaml) con respecto a la rama base (oasdiff): genera openapiBreaking=N; se omite si oasdiff no está presente o no se puede resolver la especificación base |
Informativo |
Trabajo: docs-sync-strict
Sección titulada «Trabajo: docs-sync-strict»Se ejecuta en cada PR dirigida a main. Bloquea la fusión si falla.
| Script | Valida | Bloqueante |
|---|---|---|
check:docs-all |
Metapuerta que ejecuta secuencialmente las 6 puertas secundarias indicadas a continuación | Sí |
↳ check:docs-sync |
Coherencia de versiones entre CHANGELOG / OpenAPI / llm.txt | Sí |
↳ check:docs-counts |
Los recuentos en el texto (cantidad de proveedores, cantidad de migraciones, etc.) están dentro de la ventana de ajuste progresivo de los recuentos reales | Sí |
↳ check:env-doc-sync |
Cada variable de entorno de .env.example está documentada en una tabla de la documentación, y viceversa |
Sí |
↳ check:deprecated-versions |
No hay cadenas de versiones obsoletas en la documentación | Sí |
↳ check:doc-links |
Los enlaces markdown internos de la documentación apuntan a archivos reales (formato [texto]/(ruta)) |
Sí |
↳ check:fabricated-docs |
Las rutas, variables de entorno, comandos de CLI, nombres de hooks y rutas de archivo citados en la documentación existen en el código fuente. Puerta estricta mediante --strict; fallo no bloqueante sin la opción. |
Sí (mediante --strict en CI) |
check:cli-i18n |
Las cadenas de comandos de la CLI están presentes en todos los archivos de configuración regional de i18n | Sí |
check:openapi-coverage |
La especificación OpenAPI cubre al menos un mínimo ajustado progresivamente de las rutas reales | Sí |
check:openapi-security-tiers |
Las anotaciones de niveles de seguridad de openapi.yaml son coherentes con las clasificaciones de routeGuard.ts |
Informativo |
check:openapi-routes |
Cada ruta de openapi.yaml apunta a un archivo route.ts real (contra alucinaciones) |
Sí |
check:docs-symbols |
Cada referencia /api/... de docs/**/*.md apunta a un archivo route.ts real (contra alucinaciones) |
Sí |
i18n translation drift |
Claves sin traducir en los archivos de configuración regional de i18n; solo se emite una advertencia | Informativo |
Trabajo: i18n-ui-coverage
Sección titulada «Trabajo: i18n-ui-coverage»| Script | Valida | Bloqueante |
|---|---|---|
check-ui-keys-coverage (en línea) |
La cobertura de claves de i18n de la interfaz de usuario es ≥ 65 % | Sí |
check-ui-value-drift (en línea) |
Un valor en inglés reescrito no deja traducciones obsoletas | Sí |
check-new-key-coverage (en línea) |
Una clave en inglés nueva está traducida en cada configuración regional; se rechaza un marcador __MISSING__: |
Sí |
check-translation-ratio |
La proporción de traducciones reales por configuración regional (valores idénticos al inglés, marcadores de posición o elementos faltantes fuera de la lista de permitidos) no debe superar config/quality/i18n-translation-baseline.json + margen |
Informativo |
Requiere fetch-depth: 0: la puerta de divergencia de valores compara en.json con la base de fusión.
check-ui-value-drift — puerta de traducciones obsoletas
Sección titulada «check-ui-value-drift — puerta de traducciones obsoletas»Detecta la única regresión de i18n que las demás puertas no pueden detectar estructuralmente: se reescribe un valor en inglés y las traducciones derivadas del inglés anterior permanecen, por lo que los usuarios que no usan inglés siguen leyendo un texto formulado con seguridad, pero ahora incorrecto.
Esto llegó realmente a producción. oauthModal.googleOAuthWarning se reescribió cuando se incorporó el asistente
de inicio de sesión de Antigravity (#5203); 39 de 43 configuraciones regionales conservaron un texto que indicaba a los operadores que debían «copiar la
URL completa y pegarla a continuación», un flujo que no puede completarse con ese proveedor. Pasó
desapercibido hasta #8463 porque:
sync-ui-keyssolo rellena las claves que están ausentes, nunca las que están obsoletas;check-ui-keys-coveragecuenta la presencia de claves, por lo que una traducción obsoleta se considera cubierta;check-translation-driftrealiza el seguimiento de los reflejos de documentación dedocs/i18n/<locale>/**.md; nunca leesrc/i18n/messages/*.json. Es bloqueante en el trabajodocs-sync-strictdesde la resincronización de 2026-09: edite un documento principal →npm run i18n:run -- --files=<doc>(por sección, económico).
Consciente del diff, no respaldado por una línea base. Compara en.json en la base de fusión con el
árbol de trabajo; para cada clave cuyo valor en inglés haya cambiado, cualquier configuración regional que aún conserve una
traducción intacta está obsoleta. Esto congela deliberadamente la deuda preexistente: un diff
no puede revelar de qué versión antigua del texto en inglés procede una traducción existente desde hace tiempo, por lo que la comprobación evalúa
únicamente lo que afecta el cambio actual. La alternativa (una línea base de hashes por clave) costaría
un archivo generado de ~600 KB, 3 veces más grande que la mayor línea base existente, y cambiaría en cada PR de i18n.
Hay dos formas de satisfacerla:
- actualizar las traducciones afectadas, o
- establecerlas en
__MISSING__:<nuevo texto en inglés>; en ese caso, el entorno de ejecución sirve el texto en inglés corregido (src/i18n/request.ts::deepMergeFallback, #7258) y la clave queda en cola para su traducción.
Si cambió el significado de la cadena, es preferible renombrar la clave: una clave nueva no puede heredar una traducción obsoleta. Ese es el patrón que utilizó #8463.
npm run i18n:check-value-drift # estricto (lo que ejecuta CI)npm run i18n:check-value-drift:warn # solo informaBASE_REF=origin/release/vX.Y.Z npm run i18n:check-value-driftFinaliza con código 0 y SKIP reason=base-unresolved cuando no se puede leer el catálogo base (un clon
superficial sin la referencia base), al igual que check-openapi-breaking.
Trabajo: i18n
Sección titulada «Trabajo: i18n»Matriz completa de validación de i18n (un trabajo por configuración regional). El trabajo completo es informativo.
| Script | Valida | Bloqueo |
|---|---|---|
validate_translation.py quick |
Integridad de la traducción por configuración regional | Informativo (continue-on-error: true en todo el trabajo) |
Trabajo: pr-test-policy
Sección titulada «Trabajo: pr-test-policy»Se ejecuta únicamente en solicitudes de incorporación de cambios.
| Script | Valida | Bloqueo |
|---|---|---|
check:pr-test-policy |
Las PR que cambian código de producción en src/, open-sse/, electron/ o bin/ deben incluir o actualizar pruebas (Regla estricta n.º 8) |
Sí |
check:test-masking |
Los archivos de prueba modificados no reducen el número neto de aserciones ni añaden tautologías assert.ok(true) |
Sí |
check:pr-evidence |
El cuerpo de la PR cita pruebas o evidencias de VPS para el cambio (automatiza la Regla estricta n.º 18 buscando en el texto de la PR; es frágil, véase la lista de pendientes) | Sí |
Trabajo: test-vitest
Sección titulada «Trabajo: test-vitest»Se ejecuta después de build. Bloquea la fusión si falla.
| Suite | Valida | Bloqueo |
|---|---|---|
test:vitest |
Servidor MCP (110 herramientas), autoCombo, caché — ejecutor vitest | Sí |
test:vitest:ui |
Pruebas de componentes de IU — ejecutor vitest | Bloqueante: los fallos preexistentes se excluyen explícitamente en vitest.config.ts; los fallos nuevos hacen fallar el trabajo |
Flujos de trabajo nocturnos (programados, informativos)
Sección titulada «Flujos de trabajo nocturnos (programados, informativos)»Se ejecutan según una programación cron (y mediante workflow_dispatch), nunca en PR. Todos son informativos.
| Flujo de trabajo | Valida | Bloqueo |
|---|---|---|
nightly-property |
Pruebas de propiedades de fast-check con una semilla aleatoria y un número elevado de ejecuciones | Informativo |
nightly-resilience |
Comprobación del crecimiento del heap, inyección caótica de fallos y pruebas de carga/resistencia con k6 | Informativo |
nightly-llm-security |
Protección contra inyección de promptfoo (modo de bloqueo) y sondas de garak (se omiten sin un secreto del proveedor) | Informativo |
nightly-schemathesis |
Fuzzing de contratos OpenAPI (schemathesis) contra una instancia activa de OmniRoute mediante docs/openapi.yaml; detecta infracciones de la especificación y errores 500 no controlados (Fase 8 B.4) |
Informativo |
nightly-mutation |
Puntuación de pruebas de mutación de Stryker sobre la vía rápida de pruebas unitarias; los mutantes supervivientes revelan aserciones débiles | Informativo |
nightly-compat |
Matriz de compatibilidad del motor de Node para los rangos compatibles de engines.node |
Informativo |
Fase de velocidad (2026-08-30 → v4.0 LTS): todos los valores de referencia se flexibilizaron un 20%
Sección titulada «Fase de velocidad (2026-08-30 → v4.0 LTS): todos los valores de referencia se flexibilizaron un 20%»Decisión del responsable (2026-08-30): hasta la modularización de v4.0, la velocidad de entrega importa más
que contener la deuda. Todos los valores de referencia numéricos del mecanismo de ajuste progresivo se flexibilizaron un 20% en una
única operación auditable, y la fase se declara en config/quality/quality-baseline.json:
"_policy": { "phase": "velocity", "since": "2026-08-30", "until": "4.0.0", "relaxPct": 20, "requireTighten": false }| Qué cambió | Dónde |
|---|---|
metrics.*.value — recuentos donde un valor menor es mejor ×1.2, porcentajes donde un valor mayor es mejor ÷1.2 (se mantiene el mínimo de cobertura de 60, eslintErrors permanece en 0, eslintWarnings 0 → 20% del recuento congelado de supresiones) |
quality-baseline.json (la nota _relax_velocity_2026_08_30 enumera cada valor anterior → posterior) |
count ×1.2 / percentage ×1.2 |
complexity-baseline.json, duplication-baseline.json |
cap, testCap, cada límite de líneas de frozen[*] / testFrozen[*] ×1.2 |
file-size-baseline.json |
| recuentos por archivo / por código TS ×1.2 | api-typecheck-baseline.json, dashboard-typecheck-baseline.json, open-sse-typecheck-baseline.json |
THRESHOLD 36 → 30 |
scripts/check/check-openapi-coverage.mjs |
--require-tighten pasa a ser informativo mientras _policy.requireTighten === false |
scripts/quality/check-quality-ratchet.mjs |
el proceso nocturno bank-ratchet-shrinks se pausa (registraría la reducción medida y eliminaría el margen disponible) |
.github/workflows/nightly-release-green.yml |
Las listas de elementos permitidos (eslint-suppressions.json, test-masking-allowlist.json, test-discovery-baseline.json,
…) no son presupuestos y no se modificaron. Las puertas de política de aprobación/rechazo (secretos, reglas SQL,
contrato de documentación/entorno, paridad de i18n, pruebas unitarias) no cambian: una prueba en rojo sigue siendo una prueba en rojo.
Herramientas
npm run quality:relax-baselines -- --pct 20 --note velocity_YYYY_MM_DD [--dry-run]— la flexibilización de una sola ejecución (scripts/quality/relax-baselines.mjs); se niega a ejecutarse dos veces con la misma nota.npm run quality:headroom [-- --only deadExports,fileSize] [--json out.json --md out.md]— mide cada puerta numérica del mismo modo que CI y muestra el margen restante por puerta (scripts/quality/baseline-headroom.mjs). El trabajo nocturnobaseline-headroompublica la tabla en la incidencia activa 📈 Margen de los valores de referencia (fase de velocidad) y añade la etiquetaheadroom-alertcuando alguna puerta está a menos del 10% de su límite o ya lo ha superado. Esa incidencia sirve como alerta temprana: un presupuesto que se agota en cuestión de días significa que la flexibilización está siendo consumida por unos pocos PR, no por todo el equipo; consulte las notas_rebaseline_*de la puerta infractora.
Modo de código nuevo (Clean-as-You-Code): desde 2026-08-30, solo para la ruta rápida de PR
En eventos pull_request, quality.yml pasa --base-ref <PR base SHA> a check:file-size,
check:complexity-ratchets y check:dead-code. En ese modo, la puerta compara HEAD con la
base de fusión restringida a los archivos modificados por el PR (scripts/check/newCodeMode.mjs: la
base de fusión se materializa en un git worktree desechable; ESLint/knip se ejecutan allí y en HEAD, y se
calculan las diferencias de los recuentos por archivo):
- bloqueante — el PR añadió infracciones de complejidad ciclomática/cognitiva o exportaciones sin usar en los archivos que modificó
(
complexityNewCode=,cognitiveComplexityNewCode=,deadExportsNewCode=en el registro); - informativo — el total global frente al valor de referencia congelado. La desviación heredada nunca pone en rojo un PR que no la causó; la desviación se vuelve a congelar durante la conciliación de la versión y queda supervisada por el trabajo de margen.
Las ejecuciones de workflow_dispatch, la comprobación integral de release-green y el trabajo nocturno de margen no tienen una base de PR
y mantienen la comparación absoluta (global). La cobertura, la duplicación y la cobertura de tipos siguen siendo globales
por ahora (sus herramientas no generan de forma económica una diferencia por archivo); son candidatas para recibir el mismo tratamiento.
Cierre de la fase en v4.0 (LTS = más estricto que antes, no «volver a la normalidad»)
- En la punta limpia de
release/v4.0.0: ejecutarnpm run quality:headroom --jsonpara dejar constancia y, a continuación,npm run quality:ratchet -- --update,check:file-size --update,check:complexity-ratchets --update,check:dead-code --updatey el--updatede cada puerta de verificación de tipos; cada línea base se reduce al valor medido. - Eliminar
_policydequality-baseline.json(reactiva--require-tighteny la acumulación nocturna) y restaurarTHRESHOLD = 36(o un valor superior) encheck-openapi-coverage.mjs. - Endurecer los límites más allá de lo medido donde la modularización haya dado resultado: restablecer el
capde tamaño de archivo a 1000 (u 800), aumentar en 5 los mínimos de cobertura y fijar en 0 las exportaciones sin usar para los paquetes modularizados.
Línea base del ratchet (quality-baseline.json)
Sección titulada «Línea base del ratchet (quality-baseline.json)»El motor del ratchet (scripts/quality/check-quality-ratchet.mjs) lee quality-baseline.json
y lo compara con el archivo quality-metrics.json recién generado. Cualquier métrica que empeore
más allá de su épsilon hace que la compilación falle.
Métricas supervisadas actualmente:
| Métrica | Dirección | Significado |
|---|---|---|
eslintWarnings |
down |
El número de advertencias de ESLint no debe aumentar |
coverage.statements |
up |
La cobertura de sentencias no debe disminuir |
coverage.lines |
up |
La cobertura de líneas no debe disminuir |
coverage.functions |
up |
La cobertura de funciones no debe disminuir |
coverage.branches |
up |
La cobertura de ramas no debe disminuir |
Para actualizar la línea base después de una mejora real:
npm run quality:ratchet -- --updategit add quality-baseline.jsonLa opción --update escribe los valores medidos actuales en quality-baseline.json.
Confirma este archivo junto con el cambio que mejoró la métrica. Un PR que mejore una
métrica sin actualizar la línea base será detectado por --require-tighten (Fase 6A.5,
implementación pendiente).
Ratchet de CodeQL: frecuencia de actualización y activación manual
Sección titulada «Ratchet de CodeQL: frecuencia de actualización y activación manual»check:codeql-ratchet lee el estado del repositorio, actualizado según una programación, no en cada PR.
gh api repos/diegosouzapw/OmniRoute/code-scanning/default-setup informa
state: configured, schedule: weekly: es el análisis de configuración predeterminada de GitHub, no un
análisis por cada push. Consecuencia: después de que se fusiona un PR que CORRIGE alertas, el ratchet sigue leyendo
el recuento anterior, más alto, hasta que se ejecuta el siguiente análisis programado, por lo que informa de una regresión
en cada PR abierto, incluidos los PR de seguimiento del propio PR correctivo, hasta que el análisis se actualiza.
Actualización manual: gh workflow run codeql.yml --ref release/vX.Y.Z vuelve a ejecutar el
análisis y republica las alertas en cuestión de minutos. Lee .github/workflows/codeql.yml
primero: su encabezado explica que solo usa workflow_dispatch porque entra en conflicto con
la “configuración predeterminada” de GitHub (CodeQL analyses from advanced configurations cannot be processed when the default setup is enabled). Restaurar los activadores push/pull_request/
schedule requiere primero una acción del propietario: Settings → Code security →
CodeQL: Default → Advanced. No añadas un activador schedule: sin realizar ese cambio, ya que
solo producirá ejecuciones fallidas.
Ajusta la línea base después de que disminuya el recuento: node scripts/check/check-codeql-ratchet.mjs --update escribe el nuevo recuento medido en quality-baseline.json →
metrics.codeqlAlerts.value, para que el ratchet no permita silenciosamente una regresión hasta
el límite anterior. Ejemplo práctico (2026-09-02/03): el PR #12502 corrigió 7 alertas reales
(13 → 6 abiertas según la medición); el PR #12530 ajustó la línea base congelada de 11 → 6 para que coincidiera; las
6 restantes se descartaron después con una justificación individual para cada alerta, hasta llegar a 0 abiertas.
Los descartes son decisión del operador (Regla estricta #14): nunca descartes una alerta de CodeQL
sin registrar la justificación técnica en el comentario de descarte: won't fix para
un requisito de un protocolo externo, used in tests para un fixture de prueba, false positive
para un sanitizador que CodeQL no puede detectar (precedente: docs/security/ERROR_SANITIZATION.md).
Política de reintentos de pruebas (WS5.4, v3.8.49)
Sección titulada «Política de reintentos de pruebas (WS5.4, v3.8.49)»Los reintentos se configuran por runner, nunca como una política global indiscriminada, ya que un reintento indiscriminado convierte regresiones reales en fallos intermitentes invisibles:
| Runner | Política | Motivo |
|---|---|---|
| Playwright (e2e) | retries: 1 solo en CI, con trace: on-first-retry |
Los tiempos del navegador y de la red son realmente no deterministas; un reintento con una traza convierte un fallo intermitente en un artefacto diagnosticable |
| Vitest | SIN reintentos globales. Una prueba con intermitencia demostrada recibe un reintento explícito por prueba (visible en el diff y revisado en el PR) | Mantiene la lista de cuarentena en el repositorio, nunca opaca |
| node:test (unit) | SIN reintentos, nunca | Una prueba unitaria intermitente es un error en la prueba: hay que corregirla, no volver a ejecutarla al azar |
SLO objetivo una vez que esté disponible la telemetría de intermitencias (WS5.2/5.3): tasa de intermitencia <1% por prueba (umbral de «corregir ahora»), tasa de éxito ≥95% por pipeline. Valores de referencia del sector: deben recalibrarse con nuestras propias mediciones.
Deriva del ratchet en el nivel de release (WS5.5, v3.8.49)
Sección titulada «Deriva del ratchet en el nivel de release (WS5.5, v3.8.49)»Cuando un ratchet (tamaño de archivo, complejidad, advertencias de eslint) sufre una regresión en la punta PURA de la release, es decir, la COMBINACIÓN de merges provoca la regresión y ningún PR individual la reproduce en su propia rama, la corrección corresponde al capitán de la release, una sola vez, en la rama de release: debe preferirse la extracción o refactorización; solo se debe reajustar la línea base con la entrada de justificación documentada. Nunca se debe trasladar la deriva combinada al PR de un colaborador ni reajustar la línea base por PR (eso oculta regresiones reales). Primero hay que distinguir el origen: se debe reproducir el estado rojo contra la punta pura en un worktree de prueba antes de asumir que el PR lo causó.
Consolidación de reducciones del ratchet: la dirección descendente (#8584)
Sección titulada «Consolidación de reducciones del ratchet: la dirección descendente (#8584)»El ratchet solo está automatizado a medias, y precisamente en la mitad equivocada. Elevar un límite es una
edición manual de JSON que tarda diez segundos y constituye la forma más rápida de desbloquear un PR en rojo.
Reducirlo exige que alguien ejecute --update y confirme el resultado; y hasta que
se incorporó el job bank-ratchet-shrinks, ningún workflow lo ejecutaba. La consecuencia medida
(2026-07-25): 18 archivos congelados ya estaban en el límite de 800 líneas para archivos nuevos o por debajo de él; el peor
estaba en 132× (src/shared/validation/schemas.ts, 19 líneas con un límite de 2,523); el
techo de complejidad pasó de 1794 → 2169 a lo largo de ~37 notas de reajuste de línea base, con exactamente una
reducción (−1); y «ajustar mediante --update en el próximo ciclo» se escribió 31 veces y se cumplió
una sola vez. Un límite que sobrevive al código que lo justificó convierte silenciosamente cada
descomposición completada en un margen de crecimiento para quien edite el archivo a continuación.
nightly-release-green.yml → el job bank-ratchet-shrinks cierra ese ciclo:
| Se ejecuta en | schedule (3×/día) + workflow_dispatch; deliberadamente no en push |
| Mide | la release/vX.Y.Z más alta, con la misma resolución y protección contra inyección que release-green |
| Escribe | check:file-size --update y check:complexity-ratchets --update (ambos solo reducen por diseño) |
| Verifica | npm run check:ratchet-bank (scripts/quality/verify-ratchet-bank.mjs) |
| Entrega | un único PR siempre actualizado contra la rama de release: se actualiza de forma forzada, sin generar nunca spam |
La consolidación se realiza por lotes en vez de por cada push porque no tiene requisitos de latencia (una reducción
consolidada en un plazo de 8 h es suficiente), mientras que una ejecución por merge reconstruiría repetidamente la rama del PR
durante las campañas de merge y pagaría el coste de un recorrido completo de ESLint cada vez. La detección permanece en
push (release-green); solo la consolidación se procesa por lotes.
El verificador de seguridad
Sección titulada «El verificador de seguridad»El job escribe en las líneas base sin supervisión, por lo que verify-ratchet-bank.mjs es lo que hace
que esto sea aceptable. Compara mediante diff el árbol posterior a --update con HEAD y cancela el job
antes de que exista ningún commit, sin abrir ningún PR, salvo que cada cambio sea uno de los siguientes:
- una entrada numérica
frozen/testFrozenreducida o eliminada complexity-baseline.json→countreducidoquality-baseline.json→metrics.cognitiveComplexity.valuereducido
Cualquier otra cosa falla: elevar un número, añadir una entrada, cambiar cap/testCap o
eliminar/reescribir una nota _rebaseline_* (esas notas constituyen el registro de auditoría que explica por qué existe cada
techo y se almacenan dentro del mismo objeto frozen que las entradas de archivo).
Un bot capaz de elevar un límite sería estrictamente peor que la situación actual. Protección contra regresiones:
tests/unit/verify-ratchet-bank.test.ts.
El job nunca hace push a release/*: un humano fusiona el PR, por lo que una medición errónea
no puede incorporarse sin revisión.
Política de lista de permitidos
Sección titulada «Política de lista de permitidos»Cada control que no puede fallar por infracciones preexistentes utiliza una lista de permitidos congelada
(p. ej., KNOWN_STALE_DOC_REFS, KNOWN_MISSING, KNOWN_RAW_SQL). La política es:
Corrija la causa raíz; use la lista de permitidos solo cuando la infracción sea preexistente y no pueda corregirse en el mismo PR.
Al añadir una entrada a una lista de permitidos:
- Incluya un comentario con la justificación.
- Haga referencia a la incidencia de seguimiento (p. ej.,
// #3498 — Funcionalidad de la fase 2, aún no implementada). - Elimine la entrada en el mismo PR que corrige la infracción; una entrada obsoleta que ya no suprima una infracción activa constituye en sí misma un defecto (la comprobación de entradas obsoletas de 6A.3 hará que el control falle por una entrada huérfana en la lista de permitidos una vez implementada).
No añada entradas a la lista de permitidos para hacer que las pruebas pasen más rápido. Un control en verde con una lista de permitidos creciente genera una falsa sensación de calidad.
Cuando un control falla en su PR
Sección titulada «Cuando un control falla en su PR»- Lea detenidamente la salida del control: le indica exactamente qué archivo o símbolo infringió la regla.
- Corrija la infracción: la mayoría de los controles son comprobaciones deterministas del sistema de archivos que pasan en cuanto el código es correcto.
- Si la infracción es preexistente (es decir, usted no la introdujo, pero el control ahora la abarca): añada una entrada a la lista de permitidos con un comentario justificativo y una incidencia de seguimiento.
- Si el control es incremental (cobertura, advertencias de ESLint, duplicación, complejidad):
su cambio empeoró la métrica. Corrija el problema subyacente o, en casos excepcionales, ejecute
npm run quality:ratchet -- --updatesi el cambio es intencionado y la degradación de la métrica es aceptable, pero documente el motivo en la descripción del PR. - Los controles informativos (
continue-on-error: true) son meramente informativos: no bloquean la fusión, pero aparecen en el resumen de CI. Corríjalos de todos modos.
Añadir un nuevo control
Sección titulada «Añadir un nuevo control»- Cree
scripts/check/check-<name>.mjs(o.ts). Los controles de políticas terminan con el código 0/1. Los controles de tipo incremental emiten una métrica aquality-metrics.jsonmediantecollect-metrics.mjs. - Añada
"check:<name>": "node scripts/check/check-<name>.mjs"apackage.json. - Intégrelo en
.github/workflows/ci.ymldentro del trabajo correspondiente (política →lintodocs-sync-strict; incremental →quality-gate). - Si tiene una lista de permitidos, aplique
reportStaleEntries()desdescripts/check/lib/allowlist.mjspara que las entradas obsoletas se detecten automáticamente. - Escriba una prueba en
tests/unit/build/que cubra la lógica de detección del control. - Actualice este documento (añada una fila a la tabla del trabajo correspondiente).
Herramientas para agentes: LSP integrado en el proceso (opcional)
Sección titulada «Herramientas para agentes: LSP integrado en el proceso (opcional)»Además de los controles de CI, OmniRoute incluye una estructura base opcional de agent-lsp
(un archivo .mcp.json a nivel de proyecto, Fase 7 Tarea 15). Cree .mcp.json
para exponer un servidor de lenguaje TypeScript a los agentes de programación, de modo que resuelvan símbolos /
diagnósticos antes de escribir código: un complemento de compilación antes de afirmar que funciona para
typecheck:core, que reduce de raíz los errores de «símbolos inventados». Intencionadamente,
no se carga de forma automática (usted elige y verifica el puente MCP↔LSP); una entrada defectuosa solo registra un
error de conexión y nunca interrumpe las sesiones.
Backlog de racionalización (revisión de ROI — Fase 9 Onda 3)
Sección titulada «Backlog de racionalización (revisión de ROI — Fase 9 Onda 3)»Este inventario se concilió con ci.yml el 2026-06-17 (la versión anterior omitía
audit:deps, check:tracked-artifacts, check:lockfile, check:licenses,
check:dead-code, check:cognitive-complexity, check:type-coverage,
check:codeql-ratchet, check:pr-evidence). Una revisión de ROI del conjunto conciliado
identificó los siguientes candidatos para racionalización. Las fusiones son cambios
mecánicos de CI; las activaciones/eliminaciones son decisiones de política reservadas
para el operador. Nada de lo indicado a continuación se ha aplicado todavía.
También sin documentar anteriormente (consultivo, señal débil): el trabajo docs-lint
(markdownlint + Vale, todo el trabajo con continue-on-error) y los flujos de trabajo
independientes de análisis semgrep.yml / codeql.yml / scorecard.yml.
semgrepFindings: 0 está en quality-baseline.json, pero no está conectado a un ratchet
bloqueante en ci.yml; actualmente la métrica está huérfana.
Fusión / deduplicación (mecánico, menor riesgo)
Sección titulada «Fusión / deduplicación (mecánico, menor riesgo)»Cada candidato se validó respecto al estado real de los controles el 2026-06-17 (confiar, pero verificar); varias fusiones «obvias» resultaron ocultar deuda y no son sustituciones directas sin complicaciones.
check:docs-syncse ejecuta dos veces — de forma independiente en el trabajolinty de nuevo dentro decheck:docs-all(docs-sync-strict) y del hook de pre-commit de husky. ✅ HECHO — se eliminó la invocación independiente delint.- Análisis de CVE — ❌ NO es una fusión limpia.
audit:depsfalla de forma estricta ante cualquier CVE alta/crítica;check:vuln-ratchet(osv) solo falla ante una regresión respecto a la línea base (actualmente 1 MODERATE). Semánticas diferentes: eliminaraudit:depssupondría perder el control absoluto de vulnerabilidades altas/críticas. Mantener ambos. - Detección de ciclos — ❌ NO es una fusión limpia.
check:circular-deps(dpdm) informa de 91 ciclos (por eso es consultivo); no puede convertirse en bloqueante sin resolverlos primero y tiene un alcance más amplio que el control verde y seleccionadocheck:cycles. Mantenercheck:cyclescomo bloqueante; resolver los 91 ciclos de dpdm constituye su propio backlog. - Complejidad — ✅ HECHO (
check:complexity-ratchets/eslint.complexity-ratchets.config.mjs): un recorrido de ESLint, con recuentos por ruleId para que las líneas base de complejidad ciclomática+máximo de líneas y complejidad cognitiva sigan siendo independientes; los controles individualescheck:complexity/check:cognitive-complexityse mantienen para ejecutar--updatelocalmente. - Antialucinación de
/api— ✅ HECHO (check:api-docs-refs+scripts/check/lib/apiRoutes.mjs): un inventario del sistema de archivos desrc/app/api; openapi-routes + docs-symbols siguen informando de forma independiente; los controles individuales se mantienen para las ejecuciones locales. check:node-runtimese ejecuta en 11 trabajos — ⚠️ ROI bajo. Cada uno utiliza un ejecutor independiente y la comprobación tarda <1 s; el ahorro total sería de ~10 s, a cambio de perder una protección económica por trabajo. No merece la agitación.typecheck:noimplicit:coreen el lint de CI — ✅ eliminado del trabajo lint (era consultivo concontinue-on-error); la superficie de tipos bloqueante estypecheck:core+check:type-coverage. Se conserva el script local.
Activar / decidir (política del operador)
Sección titulada «Activar / decidir (política del operador)»check:openapi-security-tiers(consultivo) — ❌ NO se puede activar limpiamente. Termina con 0, pero advierte que varias rutas detraffic-inspectorbajoLOCAL_ONLY_API_PREFIXEScarecen de la anotaciónx-loopback-only: true. Aplicarlo de forma obligatoria requiere añadir primero esas anotaciones aopenapi.yaml.typecheck:noimplicit:core(consultivo) — sustituido en gran medida por el ratchet bloqueantecheck:type-coverage. Convertirlo en ratchet o eliminar la segunda pasada redundante detsc.test:vitest:ui(ahora bloqueante) — los fallos preexistentes están excluidos explícitamente envitest.config.tsmediante comentarios de seguimiento// #8618; los fallos nuevos hacen que el trabajo falle.check:secrets(gitleaks, ratchet bloqueante fijado en 3 falsos positivos documentados) — añadir los 3 a la lista de permitidos para llegar a 0, o degradarlo a consultivo. Se solapa con el análisis de secretos nativo de GitHub +check:public-creds.check:pr-evidence(bloqueante, busca patrones en el texto del cuerpo del PR) — alto riesgo de falsos positivos; eliminarlo debilita la aplicación de la Regla estricta n.º 18, por lo que se trata de una auténtica decisión de política.semgrep(independiente y consultivo) — se solapa con CodeQL en las familias de OWASP; conectar su línea base a un ratchet o eliminarlo.
Documentación relacionada
Sección titulada «Documentación relacionada»- Cadena de suministro (procedencia, SBOM, Trivy, Scorecard):
docs/security/SUPPLY_CHAIN.md
check-key-completeness — control de paridad del conjunto de claves
Sección titulada «check-key-completeness — control de paridad del conjunto de claves»scripts/i18n/check-key-completeness.mjs (npm run i18n:check-keys, trabajo i18n-ui-coverage).
Compara el conjunto de claves hoja de cada src/i18n/messages/<locale>.json con en.json y falla
ante cualquier clave hoja ausente o adicional, independientemente de cuándo se añadió. Los marcadores
__MISSING__: cuentan como presentes (su contenido es responsabilidad del control de proporción). Es el
complemento absoluto de los dos controles basados en diferencias/porcentajes: check-ui-keys-coverage
impone un mínimo del 80 % por configuración regional (43 claves ausentes de unas ~13,000 siguen dando
un 99.7 %) y check-new-key-coverage evalúa únicamente las claves que un PR añade a en.json. Un lote
de configuraciones regionales se genera a partir del en.json del día en que se crea su rama y se
traduce durante días mientras la base sigue añadiendo claves; el PR del lote no añade ninguna clave por
sí mismo, por lo que ambos controles relacionados permanecieron en silencio cuando el lote 1 (#13044)
se incorporó con 43 claves menos en nueve configuraciones regionales y el lote 2 (#13660), con 10 claves
menos en ocho (2026-09-15). Corrija un fallo con
node scripts/i18n/sync-ui-keys.mjs --locale=<codes> --translate-markers; una clave hoja extra
significa que el origen la eliminó: elimínela de la configuración regional. --warn informa sin
provocar un fallo. --catalog=cli ejecuta la misma comparación sobre bin/cli/locales
(npm run i18n:check-keys:cli); ambos pasos se encuentran en el trabajo i18n-ui-coverage.
check-new-key-coverage — control de i18n para claves nuevas
Sección titulada «check-new-key-coverage — control de i18n para claves nuevas»Control relacionado con check-ui-value-drift. Este último detecta un valor en inglés que fue
reescrito mientras sus traducciones quedaron desactualizadas; este detecta una clave en inglés que
fue añadida sin que algunas configuraciones regionales llegaran a recibirla.
check-ui-keys-coverage no puede detectar esta clase de problema: impone un porcentaje mínimo por
configuración regional, y once claves ausentes de unas ~13,000 mantienen la cobertura en el 99.9 %. Un
porcentaje por idioma no puede expresar «esta funcionalidad se publicó sin traducir»: una funcionalidad
completa puede incorporarse a una nueva configuración regional sin texto alguno y no alterar nunca la
cifra.
El incidente que codifica: la fase 3 del Lienzo de orquestación tradujo sus once claves en las 42
configuraciones regionales que existían en ese momento. Horas después, el lote de idiomas de la UE
(#13044) elevó el repositorio a 51 configuraciones regionales, y las nueve nuevas (el, et, ga,
hr, lt, lv, mt, sl, sr) nunca las recibieron. deepMergeFallback sustituye una clave
ausente por el inglés, por lo que el modo de fallo fue una interfaz de usuario sin traducir en lugar de
una interfaz en blanco: real y silencioso por diseño.
Al igual que su control relacionado, tiene en cuenta las diferencias y compara el inglés en la base de fusión con el árbol de trabajo, por lo que las carencias preexistentes permanecen congeladas y el control no necesitó ninguna migración para activarse.
Un marcador __MISSING__:<english> no lo satisface (desde 2026-09-17). Antes era el aplazamiento
documentado —el entorno de ejecución recurre al inglés correcto—, hasta que ocho PR de funcionalidades
del 2026-09-16 añadieron 61 claves e insertaron el marcador en las 65 configuraciones regionales en vez
de traducirlas: este control aceptó todos los casos, nada bloqueó los PR y el control bloqueante de
proporción de traducciones reales falló después en la punta de la versión para todo el mundo (pt-BR
3.2 % > 2.5 % + 0.5). Ahora un marcador se considera una traducción ausente. Corrija un fallo con
node scripts/i18n/sync-ui-keys.mjs --locale=<codes> --translate-markers --batch-size=40, o procese
todas las configuraciones regionales en paralelo con npm run i18n:translate-new-keys
(scripts/i18n/translate-new-keys.sh, seguro en modo desacoplado, se niega a iniciarse sin las
variables de entorno OMNIROUTE_TRANSLATION_*). Una clave que deba permanecer en inglés (el nombre
fijado de un producto, motor o indicador) debe incluirse en scripts/i18n/untranslatable-keys.json,
nunca ocultarse tras un marcador. vi prohíbe por completo los marcadores
(tests/unit/i18n-vi-completeness.test.ts).
check-vitest-exclusions — control de pruebas aparcadas
Sección titulada «check-vitest-exclusions — control de pruebas aparcadas»Un archivo incluido en la lista exclude de vitest.config.ts es una prueba que no se ejecuta, y para
quien examina el árbol parece cobertura. Sesenta y dos archivos se acumularon tras el comentario
// #8618 — fallo preexistente; elimine esta exclusión cuando se corrija. La incidencia #8618 se cerró
el 2026-08-11 mientras la lista que supervisaba aumentó de 45 entradas a 62, y cada nueva entrada
heredaba un comentario que apuntaba a una incidencia cerrada. Cuando finalmente se midió la lista
archivo por archivo (#13204), 51 de los 62 pasaron con el árbol actual sin ningún cambio en el código
fuente.
El control exige que toda exclusión que se resuelva en un archivo real (a) indique una incidencia de
seguimiento y (b) aparezca en config/quality/vitest-exclusions.json con su estado medido, de modo que
añadir una sea una diferencia revisable en un archivo específico, en vez de otra línea más en un array
de 60 entradas. Deliberadamente, no vuelve a ejecutar las pruebas excluidas: eso cuesta unos 10 minutos
y corresponde a un trabajo periódico; el inventario registra cuándo se midió cada una por última vez.
HagiCode
HagiCode es un espacio de trabajo de programación con agentes, flujos estructurados, ejecución multiagente y vistas de Hero Dungeon.
Convierte ideas en software útil con un flujo de trabajo con agentes más inteligente, rápido y ameno.

- SmartLos flujos estructurados convierten la intención en un itinerario ejecutable desde la idea hasta la entrega.
- EfficientLos flujos multiagente permiten avanzar en paralelo con la investigación, implementación y revisión.
- FunHero Dungeon hace que las largas sesiones de programación sean visuales y colaborativas.