{
  "date": "2026-09-23",
  "method": "Definiciones de test extraídas por AST sin ejecutar código. No son casos parametrizados expandidos ni resultados; las ejecuciones siguen en la evidencia de cada función.",
  "summary": {
    "source_files_scanned": 616,
    "files_with_test_definitions": 438,
    "files_without_test_definitions": 178,
    "test_definitions": 2662,
    "by_type": {
      "unit": {
        "files": 358,
        "test_definitions": 2132
      },
      "integration": {
        "files": 34,
        "test_definitions": 406
      },
      "e2e": {
        "files": 44,
        "test_definitions": 122
      },
      "setup": {
        "files": 1,
        "test_definitions": 1
      },
      "demo-harness": {
        "files": 1,
        "test_definitions": 1
      },
      "script-spec": {
        "files": 0,
        "test_definitions": 0
      }
    },
    "by_mode": {
      "run": 2661,
      "skip": 1
    },
    "parameterized_definitions": 128,
    "dynamic_definitions": 135,
    "harness_definitions_not_expanded": 1
  },
  "contracts": [
    {
      "path": "docs/qa/golden-journeys.json",
      "kind": "golden-journey-contract-not-a-playwright-spec",
      "entries": [
        {
          "id": "GJ-01",
          "title": "Alta y acceso del paciente",
          "status": "active"
        },
        {
          "id": "GJ-02",
          "title": "Plan nutricional completo",
          "status": "active"
        },
        {
          "id": "GJ-03",
          "title": "Rutina completa",
          "status": "active"
        },
        {
          "id": "GJ-04",
          "title": "Día real del paciente",
          "status": "active"
        },
        {
          "id": "GJ-05",
          "title": "Cambio sin pérdida de historial",
          "status": "contract"
        },
        {
          "id": "GJ-06",
          "title": "Seguimiento y ajuste rápido de dieta",
          "status": "active"
        },
        {
          "id": "GJ-07",
          "title": "Seguimiento y ajuste rápido de rutina",
          "status": "contract"
        }
      ]
    },
    {
      "path": "demos/tours/tour-definitions.ts",
      "kind": "tour-source-catalog-not-a-playwright-spec",
      "entries": [
        {
          "id": "VT-01",
          "title": "Dashboard: la operación del día"
        },
        {
          "id": "VT-02",
          "title": "Pacientes: lista, búsqueda y expediente"
        },
        {
          "id": "VT-03",
          "title": "Plan nutricional: del cálculo a las recetas"
        },
        {
          "id": "VT-04",
          "title": "Chat: la conversación con contexto clínico"
        },
        {
          "id": "VT-05",
          "title": "Agenda: citas y seguimiento en calendario"
        },
        {
          "id": "VT-06",
          "title": "Modo AAR: el cockpit del atleta competitivo"
        },
        {
          "id": "VT-07",
          "title": "Portal del paciente: la experiencia móvil"
        },
        {
          "id": "VT-08",
          "title": "Métricas del negocio en el celular"
        }
      ]
    }
  ],
  "limitations": [
    "Counts AST declarations, not runner-expanded parameter cases, runtime factory calls, or executable tests.",
    "Recognizes direct test/it/describe declarations and aliases imported from vitest, @playwright/test, or node:test. Indirect factories and globals without an import are intentionally not inferred.",
    "Dynamic titles, loop-generated declarations, and demo harness definitions are marked dynamic and are not expanded.",
    "Types follow directory/config conventions: tests/integration is integration, tests is unit, e2e is e2e, auth/setup files are setup, demos/e2e/specs is a demo harness, and detected definitions elsewhere are script-spec.",
    "Golden journeys and tours are catalog contracts reported separately and are not counted as Playwright tests. Files without detected declarations are excluded from files and counted as helpers/setup candidates."
  ],
  "files": [
    {
      "path": "demos/e2e/specs/golden-journeys.spec.ts",
      "type": "demo-harness",
      "sha256": "3d6c5a312d688b6ed40b5a13c1f53ad889610c32157cedd791b65092f33f7456",
      "tests": [
        {
          "id": "9c9af71855ea307a0415",
          "title": "<dynamic template>",
          "full_title": "<dynamic template>",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": true,
          "definition": "harness-definition-not-expanded"
        }
      ]
    },
    {
      "path": "e2e/auth.roles.setup.ts",
      "type": "setup",
      "sha256": "ef4c4b6bd01c8e90cfd2fecac7d7b9798d9b7b3aa31299f95d0a410a3c32319f",
      "tests": [
        {
          "id": "2883592b0e0335e9c6e2",
          "title": "authenticate as <dynamic>",
          "full_title": "authenticate as <dynamic>",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": true,
          "definition": "dynamic-definition-not-expanded"
        }
      ]
    },
    {
      "path": "e2e/staging/aar-cockpit-demo-kpis.spec.ts",
      "type": "e2e",
      "sha256": "da460d401a80b101c884a9a28e5a27cdb3040fa2f1f0e1bd47f420029733c206",
      "tests": [
        {
          "id": "5ead8e7da8c3781e0da4",
          "title": "objetivo y sets se leen igual en escritorio y 390 px",
          "full_title": "Cockpit AAR para demo de objetivo y KPI > objetivo y sets se leen igual en escritorio y 390 px",
          "line": 75,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/aar-cockpit-evidencia.spec.ts",
      "type": "e2e",
      "sha256": "2767a5d9d2689c6bc8fc86edf43a39f9dce4b836b9cdbeb6b9615a3cdd197a59",
      "tests": [
        {
          "id": "860a852c8a9cfc501067",
          "title": "Semana dieta y Semana rutina con el atleta dummy",
          "full_title": "Semanas AAR y preview clásica — evidencia con datos reales > Semana dieta y Semana rutina con el atleta dummy",
          "line": 152,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "63007c061c947a7d6447",
          "title": "viewport grande + panel AAR estrecho conserva cuatro columnas",
          "full_title": "Semanas AAR y preview clásica — evidencia con datos reales > viewport grande + panel AAR estrecho conserva cuatro columnas",
          "line": 186,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c3fbeaf7d1bd1bc3a771",
          "title": "viewport grande + Preview semanal estrecha conserva cuatro columnas",
          "full_title": "Semanas AAR y preview clásica — evidencia con datos reales > viewport grande + Preview semanal estrecha conserva cuatro columnas",
          "line": 210,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e85be77025d272bd32e1",
          "title": "el detector reconoce texto largo sin recorte",
          "full_title": "Semanas AAR y preview clásica — evidencia con datos reales > el detector reconoce texto largo sin recorte",
          "line": 230,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/aar-competencias-aislado.spec.ts",
      "type": "e2e",
      "sha256": "b53c8aa4e2f4054db59dc5d78faf542d0b91a31109e5f855791ac6e1dc30c295",
      "tests": [
        {
          "id": "3ba874c87ba1450b3708",
          "title": "perfil, preparación, fases manuales y competencias persisten con atleta sintético",
          "full_title": "AAR competencias aislado > perfil, preparación, fases manuales y competencias persisten con atleta sintético",
          "line": 123,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/aar-competencias-dominio.spec.ts",
      "type": "e2e",
      "sha256": "fe2c39ab8bdca9797ebff2d43d814596a7c6e71bd39bf5197c36fd45e51faa52",
      "tests": [
        {
          "id": "848bfe8032761f85c6c4",
          "title": "alta en desorden, borrado real y estado vacío persistente",
          "full_title": "dominio de competencias AAR > alta en desorden, borrado real y estado vacío persistente",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/agenda-extra-recipe-regression.spec.ts",
      "type": "e2e",
      "sha256": "471c620e932af50fd1cdd157749e518e5c59c7e8428d660318a1884862cad0ba",
      "tests": [
        {
          "id": "6ebebf4a180d7e3d8bcc",
          "title": "un extra registrado permanece junto a la receta prescrita",
          "full_title": "Agenda · receta prescrita y extra > un extra registrado permanece junto a la receta prescrita",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/ajuste-fecha-vigencia.spec.ts",
      "type": "e2e",
      "sha256": "afa5fded3fa7097d61c865b4324f0877b375e00e3d6b3fa9cd487446f8e0e471",
      "tests": [
        {
          "id": "7620446d92178db32202",
          "title": "el selector aparece con la fecha del automatismo y el diálogo sigue cabiendo",
          "full_title": "fecha de vigencia del ajuste rápido > el selector aparece con la fecha del automatismo y el diálogo sigue cabiendo",
          "line": 101,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c9ceb9f0346f88cfceec",
          "title": "una fecha futura se anuncia como programada y no reescribe el plan de hoy",
          "full_title": "fecha de vigencia del ajuste rápido > una fecha futura se anuncia como programada y no reescribe el plan de hoy",
          "line": 121,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/auth-permissions.spec.ts",
      "type": "e2e",
      "sha256": "675e4963ffc220c9c6bd80eba97768e02933820a7b55fb358b7d2a5711267853",
      "tests": [
        {
          "id": "8fe36c9c931383d8a6f3",
          "title": "<dynamic template>",
          "full_title": "staging auth-permissions by exact role > <dynamic template>",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": true,
          "definition": "dynamic-definition-not-expanded"
        }
      ]
    },
    {
      "path": "e2e/staging/comercial-seguimiento-qa.spec.ts",
      "type": "e2e",
      "sha256": "7af2053c7f75325fa37c58b54566a2ad33e5503f065b9261c9a96c11183d64c2",
      "tests": [
        {
          "id": "ef2671988a0eb2e620cd",
          "title": "QA comercial: alta, ficha, edición, etapa y archivar/restaurar con paciente propio",
          "full_title": "QA comercial: alta, ficha, edición, etapa y archivar/restaurar con paciente propio",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/configurar-plan-ingestas.spec.ts",
      "type": "e2e",
      "sha256": "a8447ce0202f1fd0464a832a23ce900cb3f119d40331784e8b7bdb2807e7cee2",
      "tests": [
        {
          "id": "c0b513cbe096377415c2",
          "title": "el botón global abre solo Ingestas y Agregar menú ofrece origen",
          "full_title": "Paso 3 · Configurar plan > el botón global abre solo Ingestas y Agregar menú ofrece origen",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1277ce4c45bd63ecabf3",
          "title": "en Calendario, Agregar template ofrece origen",
          "full_title": "Paso 3 · Configurar plan > en Calendario, Agregar template ofrece origen",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/crear-dieta-wizard.spec.ts",
      "type": "e2e",
      "sha256": "2ba3eca0d043eb3388a483082f8b518efcbc6e43f82808fc210f9aa1e16da665",
      "tests": [
        {
          "id": "9826f540fd3cd6b17209",
          "title": "BK-14 — cache local llena no bloquea crear dieta",
          "full_title": "Crear dieta — wizard funcional > BK-14 — cache local llena no bloquea crear dieta",
          "line": 286,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3e97397b13970e360b86",
          "title": "01 — Calculo mantiene resumen y editores clinicos compactos",
          "full_title": "Crear dieta — wizard funcional > 01 — Calculo mantiene resumen y editores clinicos compactos",
          "line": 321,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e35f583e91edc88e384d",
          "title": "02 — Forma de calculo modifica el resultado numerico documentado",
          "full_title": "Crear dieta — wizard funcional > 02 — Forma de calculo modifica el resultado numerico documentado",
          "line": 338,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "16b76b839ce52b8c0bc2",
          "title": "BK-01 — formulas de masa libre de grasa usan la medicion mas reciente",
          "full_title": "Crear dieta — wizard funcional > BK-01 — formulas de masa libre de grasa usan la medicion mas reciente",
          "line": 359,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "00d1cc23ae204bb17bae",
          "title": "03 — Distribucion abre Configurar plan sin atorarse y deja evidencia visual",
          "full_title": "Crear dieta — wizard funcional > 03 — Distribucion abre Configurar plan sin atorarse y deja evidencia visual",
          "line": 381,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1ec3e04791372dff9b4c",
          "title": "04 — Ajustes caloricos de menu no ocultan estados incompletos",
          "full_title": "Crear dieta — wizard funcional > 04 — Ajustes caloricos de menu no ocultan estados incompletos",
          "line": 400,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eb431f0e0cfabc259b3c",
          "title": "05 — Selector de recetas conecta opciones del slot con la distribucion",
          "full_title": "Crear dieta — wizard funcional > 05 — Selector de recetas conecta opciones del slot con la distribucion",
          "line": 426,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d9fbda8583eb0d118f11",
          "title": "06 — Vista final bloquea activacion cuando faltan datos o recetas",
          "full_title": "Crear dieta — wizard funcional > 06 — Vista final bloquea activacion cuando faltan datos o recetas",
          "line": 508,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "17b8fd3c4f3068d701ea",
          "title": "07 — Happy path: autollenado de porciones queda documentado y guardado",
          "full_title": "Crear dieta — wizard funcional > 07 — Happy path: autollenado de porciones queda documentado y guardado",
          "line": 525,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4d08acc5641c55ebe3b0",
          "title": "08 — Happy path: distribucion y menus +10/-10 respetan porciones guardadas",
          "full_title": "Crear dieta — wizard funcional > 08 — Happy path: distribucion y menus +10/-10 respetan porciones guardadas",
          "line": 550,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5234a483cfac6ed1f1de",
          "title": "09 — Sugerir distribucion respeta pisos de snack y envia kcal razonables al selector",
          "full_title": "Crear dieta — wizard funcional > 09 — Sugerir distribucion respeta pisos de snack y envia kcal razonables al selector",
          "line": 620,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "953eede440db1f154a01",
          "title": "10 — Personalizar tiempos por menu no crea columnas duplicadas",
          "full_title": "Crear dieta — wizard funcional > 10 — Personalizar tiempos por menu no crea columnas duplicadas",
          "line": 678,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1f2c3c97a36f7ece90dc",
          "title": "11 — Distribucion permite edicion manual controlada",
          "full_title": "Crear dieta — wizard funcional > 11 — Distribucion permite edicion manual controlada",
          "line": 736,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/fitness-session-version-pin.spec.ts",
      "type": "e2e",
      "sha256": "34dc5442df58d69db22868d91266395aa818848ddcd1b1c644ceeeebe67b5622",
      "tests": [
        {
          "id": "28bb39941fcfb612ad6f",
          "title": "actualiza una rutina no iniciada y fija la prescripción desde el primer set",
          "full_title": "actualiza una rutina no iniciada y fija la prescripción desde el primer set",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/fr03-fr09-bk47-safe.spec.ts",
      "type": "e2e",
      "sha256": "781dccdc97b95ac322a7903579ab0d02e19f6e0806ef8e495d93fe05dfc3939c",
      "tests": [
        {
          "id": "44280c2ed8c3650eef2d",
          "title": "FR-03 — limita porciones y cancelar no altera el override",
          "full_title": "FR-03 — limita porciones y cancelar no altera el override",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0f5a54bd1cbd4aa098a0",
          "title": "FR-09 y BK-47 — selección estable y porciones canónicas de solo lectura",
          "full_title": "FR-09 y BK-47 — selección estable y porciones canónicas de solo lectura",
          "line": 93,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/fr15-bk54-safe.spec.ts",
      "type": "e2e",
      "sha256": "e1ed2c3c91d790ca8b6d8e8dbc38e64bff46981965ae137ba0f787027354378a",
      "tests": [
        {
          "id": "6a742283c0fb03b2e0f4",
          "title": "resumen automatizado FR-15 y BK-54",
          "full_title": "resumen automatizado FR-15 y BK-54",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/fr16-bk63-safe.spec.ts",
      "type": "e2e",
      "sha256": "cda743c802edcdbdd8b6d8a1f0f2c4b115871691aa7673d10022e72bfdaa3a76",
      "tests": [
        {
          "id": "34dbc410e7ea2a9a4927",
          "title": "resumen automatizado FR-16 y BK-63",
          "full_title": "resumen automatizado FR-16 y BK-63",
          "line": 7,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/fr17-bk70-bk72-safe.spec.ts",
      "type": "e2e",
      "sha256": "da1420a83fac0c4066ef9b6e7893c23bcc722391c7a122d8f8e1c1cc4649f18a",
      "tests": [
        {
          "id": "3398700927208e8e3a99",
          "title": "Resumen automatizado del batch",
          "full_title": "Resumen automatizado del batch",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aa081ac110e245af3dde",
          "title": "BK-72 — admin ve la matriz configurable de permisos",
          "full_title": "BK-72 — admin ve la matriz configurable de permisos",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "86378dbac795a4e4e4d3",
          "title": "FR-17/BK-70 — paciente no puede abrir una ruta staff",
          "full_title": "FR-17/BK-70 — paciente no puede abrir una ruta staff",
          "line": 94,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/golden-b-cross-role.spec.ts",
      "type": "e2e",
      "sha256": "c5b25ec6ee1dc21d057a64a513203138375e3a005b676b423b58debc77d17979",
      "tests": [
        {
          "id": "ca4348fd6248bfe7a8f3",
          "title": "BK-09 — entrada libre persiste en portal y aparece en staff",
          "full_title": "Golden B — validaciones cross-role > BK-09 — entrada libre persiste en portal y aparece en staff",
          "line": 38,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "49705bb536039d0d2207",
          "title": "staff no renderiza etiquetas de tiempos consecutivas repetidas",
          "full_title": "Golden B — validaciones cross-role > staff no renderiza etiquetas de tiempos consecutivas repetidas",
          "line": 147,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2b013e641cd7b0cab64d",
          "title": "FR-01 — paciente y especialista comparten el mismo hilo canónico",
          "full_title": "Golden B — validaciones cross-role > FR-01 — paciente y especialista comparten el mismo hilo canónico",
          "line": 165,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/integrations-longitudinal.spec.ts",
      "type": "e2e",
      "sha256": "97c25569461e6a45bfa5d0f54a7d4857b23a5fe3e82d2149b7e9c1b65eba2570",
      "tests": [
        {
          "id": "d0da4b2e5e43f9cf6245",
          "title": "admin validates Google Calendar multi-user surface and appointment overlap entrypoints",
          "full_title": "staging integrations and 7-day longitudinal continuity > admin validates Google Calendar multi-user surface and appointment overlap entrypoints",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fc561585435164889588",
          "title": "admin validates Stripe test-mode checkout surfaces without using live cards",
          "full_title": "staging integrations and 7-day longitudinal continuity > admin validates Stripe test-mode checkout surfaces without using live cards",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "52dd81aab6e23d65fe7f",
          "title": "especialista validates seven-day nutrition, fitness, AAR and history continuity entrypoints",
          "full_title": "staging integrations and 7-day longitudinal continuity > especialista validates seven-day nutrition, fitness, AAR and history continuity entrypoints",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/light-mode-catalogos.spec.ts",
      "type": "e2e",
      "sha256": "22843241a8bf20c1c5d0073029b0e255cc1d086f08d4ba544d51c3a9451f29e0",
      "tests": [
        {
          "id": "1ddfa9d41cd7d7dde058",
          "title": "<dynamic template>",
          "full_title": "catálogos en modo claro > <dynamic template>",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": true,
          "definition": "dynamic-definition-not-expanded"
        },
        {
          "id": "53ba6aacc88a96d9c6a4",
          "title": "<dynamic template>",
          "full_title": "catálogos en modo claro > <dynamic template>",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": true,
          "definition": "dynamic-definition-not-expanded"
        }
      ]
    },
    {
      "path": "e2e/staging/nutrition-clinical-eligibility-fixture.spec.ts",
      "type": "e2e",
      "sha256": "f29d7616febca706c4d97cc416d8cb8eaa33b91530826e5f9e3bbe0afeb645c8",
      "tests": [
        {
          "id": "2859f9a48de0a4d64594",
          "title": "paciente cambia un platillo compatible, excluye el incompatible y conserva el cambio al recargar",
          "full_title": "paciente cambia un platillo compatible, excluye el incompatible y conserva el cambio al recargar",
          "line": 38,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/nutrition-clinical-eligibility.spec.ts",
      "type": "e2e",
      "sha256": "4d128145da8a7bd7b4630fd59d959ce1918211f5b7b389d9d345931200990208",
      "tests": [
        {
          "id": "2e193409335f34fb9238",
          "title": "admin abre el selector staff y no ofrece recetas incompatibles",
          "full_title": "admin abre el selector staff y no ofrece recetas incompatibles",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1adeaf0fa0431685711a",
          "title": "paciente abre el plan sin recetas incompatibles y sin overlay",
          "full_title": "paciente abre el plan sin recetas incompatibles y sin overlay",
          "line": 147,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/patient-portal.spec.ts",
      "type": "e2e",
      "sha256": "3b7f88dc89afd048b9d93f7cf42545ff78abed4e467283bbea14e758c04b5a92",
      "tests": [
        {
          "id": "d7d429bb3e208b324800",
          "title": "paciente validates portal navigation, plan, recipes, routine, progress and profile",
          "full_title": "staging patient portal mobile and PWA continuity > paciente validates portal navigation, plan, recipes, routine, progress and profile",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "05fc4a39ee9f427ad480",
          "title": "paciente validates progress drawers and seven-day longitudinal surfaces",
          "full_title": "staging patient portal mobile and PWA continuity > paciente validates progress drawers and seven-day longitudinal surfaces",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e5e982599f8f48b0dbb2",
          "title": "paciente validates training session entrypoints and freestyle fallback",
          "full_title": "staging patient portal mobile and PWA continuity > paciente validates training session entrypoints and freestyle fallback",
          "line": 129,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "501c39d44f7fa756ebae",
          "title": "FR-03 — cambio de ingrediente no puede exceder porciones prescritas",
          "full_title": "staging patient portal mobile and PWA continuity > FR-03 — cambio de ingrediente no puede exceder porciones prescritas",
          "line": 147,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2cf0d3a0c7524318a1a2",
          "title": "PWA public assets and offline screen remain available",
          "full_title": "staging patient portal mobile and PWA continuity > PWA public assets and offline screen remain available",
          "line": 200,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/photo-capture-qr.spec.ts",
      "type": "e2e",
      "sha256": "dbe13485f2b4b466298878d73fc81f4b99951dd0c2591f2c45b0dc76be6a3478",
      "tests": [
        {
          "id": "79ab1e4726ab2c2f7bdc",
          "title": "captura QR envia fotos con filename seguro",
          "full_title": "Captura de fotos por QR > captura QR envia fotos con filename seguro",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "11c684997256180658c7",
          "title": "captura QR muestra error general cuando la API no responde JSON",
          "full_title": "Captura de fotos por QR > captura QR muestra error general cuando la API no responde JSON",
          "line": 96,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1860a9d62f7cc5cc8d8a",
          "title": "foto grande de cámara se optimiza antes de aplicar el límite final",
          "full_title": "Captura de fotos por QR > foto grande de cámara se optimiza antes de aplicar el límite final",
          "line": 121,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "51886e854f2ce66d8964",
          "title": "HEIC no convertible explica formato y dispositivo en vez de culpar la conexión",
          "full_title": "Captura de fotos por QR > HEIC no convertible explica formato y dispositivo en vez de culpar la conexión",
          "line": 166,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/plan-reparar-stripe.spec.ts",
      "type": "e2e",
      "sha256": "b96375a61f5c2e7b50af4b21e15ea946fc0a7fca8fd18a6125e3746c70c782b8",
      "tests": [
        {
          "id": "93a399eeb038350cf8af",
          "title": "reparar recrea el precio archivado y la tarjeta vuelve a verde",
          "full_title": "reparar recrea el precio archivado y la tarjeta vuelve a verde",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/planes-servicio-manager.spec.ts",
      "type": "e2e",
      "sha256": "1caa9a4053868a2553a40d378c08d03f840c29440c0e08e40856c6e340119010",
      "tests": [
        {
          "id": "29dd712442b43ddfbcdd",
          "title": "<dynamic template>",
          "full_title": "gestor de planes comerciales > <dynamic template>",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": true,
          "definition": "dynamic-definition-not-expanded"
        },
        {
          "id": "b0e784bdd372c261476a",
          "title": "guardar sigue deshabilitado hasta que se edita algo",
          "full_title": "gestor de planes comerciales > guardar sigue deshabilitado hasta que se edita algo",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "92465fd2fccdcb342927",
          "title": "la tarjeta declara si el plan está conectado a Stripe",
          "full_title": "gestor de planes comerciales > la tarjeta declara si el plan está conectado a Stripe",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dddce9fce5393bc54373",
          "title": "el diálogo de edición cabe en pantalla y su footer es alcanzable",
          "full_title": "gestor de planes comerciales > el diálogo de edición cabe en pantalla y su footer es alcanzable",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "10913a39338329faf87f",
          "title": "alto rendimiento no ofrece modalidad presencial",
          "full_title": "gestor de planes comerciales > alto rendimiento no ofrece modalidad presencial",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "be76258006b33b948599",
          "title": "el interruptor publica el paquete entero, no una sola variante",
          "full_title": "gestor de planes comerciales > el interruptor publica el paquete entero, no una sola variante",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4b7bc5f7740deb86dc4f",
          "title": "el diálogo de nuevo paquete cabe en móvil y su footer es alcanzable",
          "full_title": "gestor de planes comerciales > el diálogo de nuevo paquete cabe en móvil y su footer es alcanzable",
          "line": 115,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "792d880fa8450cb60bd3",
          "title": "CRUD: archivar una variante y volver a crearla deja el catálogo como estaba",
          "full_title": "gestor de planes comerciales > CRUD: archivar una variante y volver a crearla deja el catálogo como estaba",
          "line": 138,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9a9307af6113a8ac6354",
          "title": "modo claro y oscuro conservan el contraste de la tarjeta destacada",
          "full_title": "gestor de planes comerciales > modo claro y oscuro conservan el contraste de la tarjeta destacada",
          "line": 184,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/portal-loading.spec.ts",
      "type": "e2e",
      "sha256": "b0745cbd930e861685717d7af358e3a6f6eac441d35ff8c04151b5284255cfea",
      "tests": [
        {
          "id": "da88d8f35d147908c43e",
          "title": "portal: retorno por el dock sin recargar y con datos confirmados",
          "full_title": "portal: retorno por el dock sin recargar y con datos confirmados",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "be9068961487266b6b9c",
          "title": "portal: caché visible, espera prolongada, fallo parcial y recuperación",
          "full_title": "portal: caché visible, espera prolongada, fallo parcial y recuperación",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d15499693c3b0a645e0b",
          "title": "portal: entrenamiento muestra la consulta pendiente sin registrar una sesión",
          "full_title": "portal: entrenamiento muestra la consulta pendiente sin registrar una sesión",
          "line": 118,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/portal-plan-online-version-propagation.spec.ts",
      "type": "e2e",
      "sha256": "ecf7ab181d16ac237719d138e46e044f246d0b81de13e0fabccc14bca6b39ab4",
      "tests": [
        {
          "id": "53940b11709b3f90ccd4",
          "title": "portal muestra plan y receta vigentes, después recibe versión publicada",
          "full_title": "Portal · versión nutricional online > portal muestra plan y receta vigentes, después recibe versión publicada",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fb88be6764d683f5bb91",
          "title": "portal anuncia versión futura sin adelantar plan vigente",
          "full_title": "Portal · versión nutricional online > portal anuncia versión futura sin adelantar plan vigente",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/portal-plan-version-propagation.spec.ts",
      "type": "e2e",
      "sha256": "9e4bf5cb30288445ab11b1ac6ad177766a3e0fc43cf72bad7dd0eb8107eaf5db",
      "tests": [
        {
          "id": "8fc1db7c33e411a6042f",
          "title": "una versión publicada tras poblar el caché se ve al recargar, y el caché se actualiza",
          "full_title": "Portal · el ajuste de dieta llega al paciente > una versión publicada tras poblar el caché se ve al recargar, y el caché se actualiza",
          "line": 110,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8f2524a86b59e128792d",
          "title": "una versión para mañana se anuncia sin reemplazar el plan vigente",
          "full_title": "Portal · el ajuste de dieta llega al paciente > una versión para mañana se anuncia sin reemplazar el plan vigente",
          "line": 139,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/portion-editor.spec.ts",
      "type": "e2e",
      "sha256": "877ae53d46ff8f03d3b385ac678cc302f10105d19339808b32e1a9a10ef0c92e",
      "tests": [
        {
          "id": "00763152a0d8d993862a",
          "title": "familia única, targets alcanzables y sheet contenido",
          "full_title": "Editor de porciones — validación visual > familia única, targets alcanzables y sheet contenido",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3f5b03219e99c5e5cfa5",
          "title": "el ajuste sólo persiste al confirmarlo, y sobrevive a la recarga",
          "full_title": "Editor de porciones — validación visual > el ajuste sólo persiste al confirmarlo, y sobrevive a la recarga",
          "line": 229,
          "mode": "skip",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/push-debug.spec.ts",
      "type": "e2e",
      "sha256": "ce10ad1a18b107bb9f3d6258a4c7cb91c7d748a9c8d326b9e751c43fdff008e0",
      "tests": [
        {
          "id": "d3fde39ca378f0ff2e50",
          "title": "admin monta /debug/push con todos los estados y consola limpia",
          "full_title": "Push diagnostics > admin monta /debug/push con todos los estados y consola limpia",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b1cad08da5ac5fdecd6b",
          "title": "paciente no puede usar /debug/push",
          "full_title": "Push diagnostics > paciente no puede usar /debug/push",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/pwa.spec.ts",
      "type": "e2e",
      "sha256": "1e83b8c876e9b78271117038378992e006788ff3a8672205ea7de644268f1220",
      "tests": [
        {
          "id": "e7c0b7d2e804d1377982",
          "title": "manifest.webmanifest responde 200 con content-type correcto",
          "full_title": "PWA — manifest, service worker y offline > manifest.webmanifest responde 200 con content-type correcto",
          "line": 7,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e1479a3720687a754303",
          "title": "sw.js responde 200",
          "full_title": "PWA — manifest, service worker y offline > sw.js responde 200",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6f79d697f5664b049d77",
          "title": "firebase-messaging-sw.js responde 200 y controla el render en background",
          "full_title": "PWA — manifest, service worker y offline > firebase-messaging-sw.js responde 200 y controla el render en background",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e5f3a5e7d1f49e7865f2",
          "title": "release-notes.json responde 200 con estructura estable",
          "full_title": "PWA — manifest, service worker y offline > release-notes.json responde 200 con estructura estable",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "30f5c049c2379e09a94e",
          "title": "página /offline carga sin autenticación",
          "full_title": "PWA — manifest, service worker y offline > página /offline carga sin autenticación",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "db6059a2893dd119234c",
          "title": "el <head> incluye link rel=manifest apuntando al manifest",
          "full_title": "PWA — manifest, service worker y offline > el <head> incluye link rel=manifest apuntando al manifest",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cc9930359939b623e46d",
          "title": "/login carga correctamente (regresión)",
          "full_title": "PWA — manifest, service worker y offline > /login carga correctamente (regresión)",
          "line": 77,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "67fe5914c723fd12d108",
          "title": "/debug/push requiere sesión",
          "full_title": "PWA — manifest, service worker y offline > /debug/push requiere sesión",
          "line": 84,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b0e3e7478b8c8e51e3a0",
          "title": "iconos PWA responden 200",
          "full_title": "PWA — manifest, service worker y offline > iconos PWA responden 200",
          "line": 90,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/quick-group-adjust-real-save.spec.ts",
      "type": "e2e",
      "sha256": "adbf37e5181db28110583cb741f22924e1f3e6bad4909986ad8a26904cc1158b",
      "tests": [
        {
          "id": "7dfeca1c5cd819fcb229",
          "title": "guardar ajuste de Cena crea versión, historial y proyección sin tocar comida registrada",
          "full_title": "Ajuste rápido por grupo · guardado real QA > guardar ajuste de Cena crea versión, historial y proyección sin tocar comida registrada",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/quick-group-adjust-regressions.spec.ts",
      "type": "e2e",
      "sha256": "c6db27bb9f9dbc78c0876d3be486578d6507fb92622e183dabede3667465a07e",
      "tests": [
        {
          "id": "9fe77a08a4a76c886102",
          "title": "un check-in en Desayuno no abre el gate al ajustar sólo Cena",
          "full_title": "ST-299134 · ajuste por grupo > un check-in en Desayuno no abre el gate al ajustar sólo Cena",
          "line": 125,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "294998bd5643358c0bf1",
          "title": "el ajuste por grupo pasa por el gate de check-ins y conserva la propuesta al cancelar",
          "full_title": "ST-299134 · ajuste por grupo > el ajuste por grupo pasa por el gate de check-ins y conserva la propuesta al cancelar",
          "line": 149,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ec905f7a121d37711a7a",
          "title": "un remanente menor a 0.5 explica la granularidad y no deja continuar",
          "full_title": "ST-299134 · ajuste por grupo > un remanente menor a 0.5 explica la granularidad y no deja continuar",
          "line": 199,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "95d8badf465d9b770d45",
          "title": "una receta compatible se adapta sin mostrar la advertencia de porciones libres",
          "full_title": "ST-299134 · ajuste por grupo > una receta compatible se adapta sin mostrar la advertencia de porciones libres",
          "line": 213,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/recetas-tiempo-popover.spec.ts",
      "type": "e2e",
      "sha256": "d68cb3587f64733d39ce302b4ebf0710f3464ce47b8712710a7809bd338b9a8a",
      "tests": [
        {
          "id": "5cf7b6b0eb571f19d1ee",
          "title": "sigue abierto al mover el cursor del disparador a la lista",
          "full_title": "Popover de recetas por tiempo > sigue abierto al mover el cursor del disparador a la lista",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dd5b7894801590b17a9f",
          "title": "se cierra al alejar el cursor de verdad",
          "full_title": "Popover de recetas por tiempo > se cierra al alejar el cursor de verdad",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/roles-comercial-configurable.spec.ts",
      "type": "e2e",
      "sha256": "8bba60c7d51fb0a04fd638e6261c244c968a2fade70fcf18f181a566b0e35f98",
      "tests": [
        {
          "id": "36d7146e98449f8a05d4",
          "title": "darle ver_pacientes cambia lo que ve, y quitárselo lo revierte",
          "full_title": "comercial se configura desde la matriz como cualquier rol > darle ver_pacientes cambia lo que ve, y quitárselo lo revierte",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/roles-custom-lifecycle.spec.ts",
      "type": "e2e",
      "sha256": "5812457b738f427a51f6bb5584db83eeb71921d42f782cc9df7b84caf51b6a3f",
      "tests": [
        {
          "id": "40c198ff9881a9d9e67a",
          "title": "crear, persistir y eliminar un rol propio",
          "full_title": "un workspace crea y retira sus propios roles > crear, persistir y eliminar un rol propio",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "15db69bb184871bf38b2",
          "title": "rechaza un identificador reservado",
          "full_title": "un workspace crea y retira sus propios roles > rechaza un identificador reservado",
          "line": 156,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/roles-matrix-pipeline-projection.spec.ts",
      "type": "e2e",
      "sha256": "95d212fcf01cb64c7c8cae7714c3ae34e50da7e800ae78e1300acb8916bfbb27",
      "tests": [
        {
          "id": "19d56514582f7cf2a8f0",
          "title": "un rol no comercial recibe el pipeline sanitizado si la matriz lo configura asi",
          "full_title": "la matriz decide quien ve la proyeccion sanitizada > un rol no comercial recibe el pipeline sanitizado si la matriz lo configura asi",
          "line": 115,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/roles-matrix-toggle.spec.ts",
      "type": "e2e",
      "sha256": "97d678a18e09f7fad98b6cc890953008005258d6d184a167fd659b764491a0f3",
      "tests": [
        {
          "id": "2720a2d69baf4ca766d8",
          "title": "apagar y volver a encender un permiso abre y cierra su ruta",
          "full_title": "la matriz de permisos guardada gobierna el acceso real > apagar y volver a encender un permiso abre y cierra su ruta",
          "line": 84,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/roles-matrix-visual.spec.ts",
      "type": "e2e",
      "sha256": "fd92432c1350061ebbc99a7a5a0d42a083f9e4c8cf5b667082adbd017f1f07da",
      "tests": [
        {
          "id": "0638492311b9c539681c",
          "title": "la etiqueta del permiso sigue visible en <dynamic>",
          "full_title": "matriz de permisos: la columna de etiquetas resiste el scroll > la etiqueta del permiso sigue visible en <dynamic>",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": true,
          "definition": "dynamic-definition-not-expanded"
        }
      ]
    },
    {
      "path": "e2e/staging/roles-propagacion-en-vivo.spec.ts",
      "type": "e2e",
      "sha256": "105acc336272ebcdd69b890bc2f3845dec74825cb8b58661ad7ee7c281b2f1c1",
      "tests": [
        {
          "id": "608d134551133799208b",
          "title": "la sesión abierta pierde y recupera la sección sin tocar nada",
          "full_title": "un cambio de permisos llega sin recargar > la sesión abierta pierde y recupera la sección sin tocar nada",
          "line": 69,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/routine-occurrence-flow.spec.ts",
      "type": "e2e",
      "sha256": "a962ebc4eec9fde1611f2697550417659d40da1bc4b50e5c9dccefebcb7689f4",
      "tests": [
        {
          "id": "675d813b7246027e6abf",
          "title": "QA-PORTAL-ROUTINE-SUMMARY muestra distribución y avance real en móvil",
          "full_title": "QA-PORTAL-ROUTINE-SUMMARY muestra distribución y avance real en móvil",
          "line": 9,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f4c88ec1eec6ca7f04c7",
          "title": "QA-PORTAL-COMPLETED-DAY confirma una declaración y abre progreso @regression",
          "full_title": "QA-PORTAL-COMPLETED-DAY confirma una declaración y abre progreso @regression",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ec171bd355cfa6a0cd10",
          "title": "QA-PORTAL-COMPLETED-DAY muestra PR sólo si hubo una serie registrada con marca @regression",
          "full_title": "QA-PORTAL-COMPLETED-DAY muestra PR sólo si hubo una serie registrada con marca @regression",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fd3d0c2b95f2d48f2590",
          "title": "QA-PORTAL-PAST-VACATION declara ayer y descansa rutinas en vacaciones @regression",
          "full_title": "QA-PORTAL-PAST-VACATION declara ayer y descansa rutinas en vacaciones @regression",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1e124790632e9cb6d81a",
          "title": "QA-PORTAL-ROUTINE-DAY-CHOICE selecciona rutina y descanso en móvil @regression",
          "full_title": "QA-PORTAL-ROUTINE-DAY-CHOICE selecciona rutina y descanso en móvil @regression",
          "line": 165,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1e8e852a3e71bf6dd8ed",
          "title": "QA-PORTAL-ROUTINE-DAY-CHOICE permite elegir mientras se actualizan opciones y confirma descanso futuro",
          "full_title": "QA-PORTAL-ROUTINE-DAY-CHOICE permite elegir mientras se actualizan opciones y confirma descanso futuro",
          "line": 221,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "495fa6565bf0a61a6639",
          "title": "QA-PORTAL-ROUTINE-DAY-CHOICE no informa fallo si el descanso futuro se guardó y se perdió la respuesta",
          "full_title": "QA-PORTAL-ROUTINE-DAY-CHOICE no informa fallo si el descanso futuro se guardó y se perdió la respuesta",
          "line": 251,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a22e29ef7f792a232f54",
          "title": "ST-333691 calendario, preparación persistente y completado de bloque @regression",
          "full_title": "ST-333691 calendario, preparación persistente y completado de bloque @regression",
          "line": 284,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1ddbe9d319b6c25ebc55",
          "title": "Checklist: historial, borrador, check fuera de orden y descanso compacto @regression",
          "full_title": "Checklist: historial, borrador, check fuera de orden y descanso compacto @regression",
          "line": 510,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fce6bde058294dd0e208",
          "title": "Bloques: añadir, confirmar posición, reordenar y elegir destino @regression",
          "full_title": "Bloques: añadir, confirmar posición, reordenar y elegir destino @regression",
          "line": 753,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/rutina-day-copy.spec.ts",
      "type": "e2e",
      "sha256": "5d8a632d006fbcefe562ca5111e3ab7e8c1a41ed371275274fee6eadf7cb77a7",
      "tests": [
        {
          "id": "58ffc6c551207657ce39",
          "title": "FIT-SEP-01: copia día 1 a 3 con confirmación visible, cancelación y reemplazo",
          "full_title": "FIT-SEP-01: copia día 1 a 3 con confirmación visible, cancelación y reemplazo",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/rutinas-asignacion.spec.ts",
      "type": "e2e",
      "sha256": "cf3cc682cc40033325ce3f4c56ed2ca6cf2c335012ef7eeb0f7166e1febc3b10",
      "tests": [
        {
          "id": "ac9951dc859e05bc7e14",
          "title": "Plantillas es el flujo default y crear desde cero queda disponible",
          "full_title": "Rutinas — asignacion rapida > Plantillas es el flujo default y crear desde cero queda disponible",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "79adf1dfb760c18e8962",
          "title": "Asignacion desde plantilla permite editar nombre y dias antes de asignar",
          "full_title": "Rutinas — asignacion rapida > Asignacion desde plantilla permite editar nombre y dias antes de asignar",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "908da8fe2e9c853c7a08",
          "title": "El constructor avanzado sigue abriendo el wizard completo",
          "full_title": "Rutinas — asignacion rapida > El constructor avanzado sigue abriendo el wizard completo",
          "line": 115,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f1bdcfc6d194bc678a7a",
          "title": "La biblioteca abre el configurador con plantilla preseleccionada y selector de paciente",
          "full_title": "Rutinas — asignacion rapida > La biblioteca abre el configurador con plantilla preseleccionada y selector de paciente",
          "line": 123,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/staff-core-flows.spec.ts",
      "type": "e2e",
      "sha256": "b4bdfbf29a9b9b9b6af9cc40c70162c9a8463ee81e3c1c50f56a479d9e6e4aca",
      "tests": [
        {
          "id": "14f74c6962de8001c781",
          "title": "admin creates a QA CRM patient and validates patient operations entrypoints",
          "full_title": "staging staff CRUD and clinical continuity > admin creates a QA CRM patient and validates patient operations entrypoints",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c0d6c7d621b7113fbeaf",
          "title": "especialista validates patient detail, AAR cockpit, nutrition and fitness continuity entrypoints",
          "full_title": "staging staff CRUD and clinical continuity > especialista validates patient detail, AAR cockpit, nutrition and fitness continuity entrypoints",
          "line": 110,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "beb6383c350042e98609",
          "title": "nutriologo validates nutrition catalogs, wizard entry and portal-facing plan view",
          "full_title": "staging staff CRUD and clinical continuity > nutriologo validates nutrition catalogs, wizard entry and portal-facing plan view",
          "line": 141,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3eb56db60a89bcb3d6ab",
          "title": "coach validates routine assignment, exercise catalog and blocked nutrition permissions",
          "full_title": "staging staff CRUD and clinical continuity > coach validates routine assignment, exercise catalog and blocked nutrition permissions",
          "line": 162,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a89e148df62bd2efdbf1",
          "title": "asistente and recepcionista validate operational routes and role exceptions",
          "full_title": "staging staff CRUD and clinical continuity > asistente and recepcionista validate operational routes and role exceptions",
          "line": 191,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "e2e/staging/training-options-pr.spec.ts",
      "type": "e2e",
      "sha256": "870b80bda0fd718ed9c90311f49965bac9e39f9d57f1c5658187db8bc925c5e5",
      "tests": [
        {
          "id": "45fb6e6c1da1b1ac3db7",
          "title": "el drawer de ejercicio convierte todas las series y conserva la eliminación al recargar",
          "full_title": "el drawer de ejercicio convierte todas las series y conserva la eliminación al recargar",
          "line": 7,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "78e27a382e0eb7b28377",
          "title": "reordena las series en el drawer y conserva su orden al recargar",
          "full_title": "reordena las series en el drawer y conserva su orden al recargar",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "37c695bd3dd269c78ca1",
          "title": "define descanso entre series y muestra el reloj flotante sólo cuando inicia",
          "full_title": "define descanso entre series y muestra el reloj flotante sólo cuando inicia",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/aar-perfil-write.test.ts",
      "type": "integration",
      "sha256": "27484f97413e1f5debf63ac150b6ead1b529957fb4b1e95e04a18d1a67098519",
      "tests": [
        {
          "id": "f62880107bbf0981913d",
          "title": "CONTROL NEGATIVO: set(merge:true) conserva las competencias borradas",
          "full_title": "escritura del perfil AAR: merge profundo vs reemplazo > CONTROL NEGATIVO: set(merge:true) conserva las competencias borradas",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "505ff76ebf0f71832926",
          "title": "CONTROL POSITIVO: update() reemplaza el mapa completo y sí borra",
          "full_title": "escritura del perfil AAR: merge profundo vs reemplazo > CONTROL POSITIVO: update() reemplaza el mapa completo y sí borra",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "08ab63f8201de7a80aeb",
          "title": "update() falla si el expediente no existe: por eso el servicio necesita el fallback",
          "full_title": "escritura del perfil AAR: merge profundo vs reemplazo > update() falla si el expediente no existe: por eso el servicio necesita el fallback",
          "line": 82,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/appointment-installments.test.ts",
      "type": "integration",
      "sha256": "fdd615732f58af150f13ebb5cffa87d38dd102a0777bb82a7de532f46404efd4",
      "tests": [
        {
          "id": "9cfc284eb59f8d2a4c0c",
          "title": "reconstruye el orden completo, limpia enlaces previos y tolera reintentos concurrentes",
          "full_title": "asociacion transaccional cita-parcialidad > reconstruye el orden completo, limpia enlaces previos y tolera reintentos concurrentes",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/assignment-versions.test.ts",
      "type": "integration",
      "sha256": "dc54bbd4858031eb407054deb6ffa213a284a81c7063b058a3bb070d83cece9b",
      "tests": [
        {
          "id": "9d70411fc86af98b8ee4",
          "title": "publica reemplazo de una ocurrencia y conserva snapshot histórico",
          "full_title": "POST /api/pacientes/[pacienteId]/assignment-versions replaceRoutine > publica reemplazo de una ocurrencia y conserva snapshot histórico",
          "line": 111,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e7e3c45d77d8247ff3b1",
          "title": "programa una versión futura sin adelantar el documento vivo",
          "full_title": "POST /api/pacientes/[pacienteId]/assignment-versions replaceRoutine > programa una versión futura sin adelantar el documento vivo",
          "line": 133,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d1b6b37813eddfec8405",
          "title": "proyecta una versión inmediata dentro de la misma transacción",
          "full_title": "POST /api/pacientes/[pacienteId]/assignment-versions replaceRoutine > proyecta una versión inmediata dentro de la misma transacción",
          "line": 145,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "49a410684a42dd686485",
          "title": "rechaza una edición sobre una versión que dejó de ser la vigente",
          "full_title": "POST /api/pacientes/[pacienteId]/assignment-versions replaceRoutine > rechaza una edición sobre una versión que dejó de ser la vigente",
          "line": 153,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "126799b2b5180adf27dc",
          "title": "guarda programación y series con historial atómico y reintento idempotente",
          "full_title": "POST /api/pacientes/[pacienteId]/assignment-versions replaceRoutine > guarda programación y series con historial atómico y reintento idempotente",
          "line": 169,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "904192c241fb3baab2b2",
          "title": "rechaza sustituir una programación modificada después de abrir el borrador",
          "full_title": "POST /api/pacientes/[pacienteId]/assignment-versions replaceRoutine > rechaza sustituir una programación modificada después de abrir el borrador",
          "line": 187,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e51528be0324da39e304",
          "title": "devuelve la versión vigente del hilo de suplementación del plan",
          "full_title": "GET /api/pacientes/[pacienteId]/assignment-versions type=supplementation > devuelve la versión vigente del hilo de suplementación del plan",
          "line": 239,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/auth-profile.test.ts",
      "type": "integration",
      "sha256": "9d4c2dd10dc1d89e2e76bd4c80f0448117e1c1cf40042dd5d6d81e9bf230c58a",
      "tests": [
        {
          "id": "8aa9f25b9af094acc983",
          "title": "acepta un paciente cuyo perfil y expediente comparten tenant",
          "full_title": "GET /api/auth/profile > acepta un paciente cuyo perfil y expediente comparten tenant",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e02f4e2543052c4eba0e",
          "title": "distingue perfil inexistente y cuenta inactiva",
          "full_title": "GET /api/auth/profile > distingue perfil inexistente y cuenta inactiva",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d2830533966ff69fe2d9",
          "title": "rechaza pacienteId ausente o expediente inexistente",
          "full_title": "GET /api/auth/profile > rechaza pacienteId ausente o expediente inexistente",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a9503adabac00d19537c",
          "title": "rechaza un expediente vinculado a otro tenant",
          "full_title": "GET /api/auth/profile > rechaza un expediente vinculado a otro tenant",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/checkin-summary-materialization.test.ts",
      "type": "integration",
      "sha256": "f717377a627742c85c61873c68aea2aca8a6463f155c4daa74cf79e27622bb25",
      "tests": [
        {
          "id": "c4ddaa8dda89cabc1115",
          "title": "materializa siete registros y el replay no reescribe la proyeccion",
          "full_title": "patient check-in summary materialization > materializa siete registros y el replay no reescribe la proyeccion",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "32fa86e1d4a46525a3af",
          "title": "actualiza al cambiar la fuente y elimina al desaparecer paciente",
          "full_title": "patient check-in summary materialization > actualiza al cambiar la fuente y elimina al desaparecer paciente",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/citas.test.ts",
      "type": "integration",
      "sha256": "bbb3ebbd8780df7a5e807241fb7162e93f463e143adf0696516921103bbf7ec5",
      "tests": [
        {
          "id": "7ff6d8cfded28bbfaac2",
          "title": "rechaza con 401 una request sin usuario autenticado",
          "full_title": "agenda autoritativa /api/citas > rechaza con 401 una request sin usuario autenticado",
          "line": 161,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d9f17bbabd59fa8d0966",
          "title": "rechaza con 403 a un rol no permitido (paciente)",
          "full_title": "agenda autoritativa /api/citas > rechaza con 403 a un rol no permitido (paciente)",
          "line": 177,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9abb298a922aa07072b4",
          "title": "rechaza con 400 a un usuario staff sin tenantId configurado",
          "full_title": "agenda autoritativa /api/citas > rechaza con 400 a un usuario staff sin tenantId configurado",
          "line": 196,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "62130262073c50a9549d",
          "title": "crea la cita en Firestore con el tenantId del actor y su consulta asociada",
          "full_title": "agenda autoritativa /api/citas > crea la cita en Firestore con el tenantId del actor y su consulta asociada",
          "line": 209,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a165b5bde450d08c50d0",
          "title": "crea una cita operativa sin consulta cuando el switch está apagado",
          "full_title": "agenda autoritativa /api/citas > crea una cita operativa sin consulta cuando el switch está apagado",
          "line": 259,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4ab481d83b2ddf012a23",
          "title": "programa, reagenda y cancela recordatorios desde el comando autoritativo",
          "full_title": "agenda autoritativa /api/citas > programa, reagenda y cancela recordatorios desde el comando autoritativo",
          "line": 275,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d4237e4665e6a2ad27cc",
          "title": "omite el aviso semanal cuando su instante ya llegó y conserva los posteriores",
          "full_title": "agenda autoritativa /api/citas > omite el aviso semanal cuando su instante ya llegó y conserva los posteriores",
          "line": 355,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a78c23181e96117006e4",
          "title": "deriva el recordatorio manual de cita y evita duplicarlo con la misma llave",
          "full_title": "agenda autoritativa /api/citas > deriva el recordatorio manual de cita y evita duplicarlo con la misma llave",
          "line": 373,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "082978413d70f68afd10",
          "title": "no permite enviar recordatorios de una cita de otro tenant",
          "full_title": "agenda autoritativa /api/citas > no permite enviar recordatorios de una cita de otro tenant",
          "line": 410,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "937ca28416cc55231236",
          "title": "persiste la ventana de acceso configurada por el tenant",
          "full_title": "agenda autoritativa /api/citas > persiste la ventana de acceso configurada por el tenant",
          "line": 427,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e64efd47e359047ceb45",
          "title": "rechaza con 400 un paciente que pertenece a otro tenant",
          "full_title": "agenda autoritativa /api/citas > rechaza con 400 un paciente que pertenece a otro tenant",
          "line": 453,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3678191401040737b4a6",
          "title": "rechaza con 409 un horario ya ocupado (SLOT_CONFLICT)",
          "full_title": "agenda autoritativa /api/citas > rechaza con 409 un horario ya ocupado (SLOT_CONFLICT)",
          "line": 481,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f06a3338c79c93b8bd60",
          "title": "rechaza con 400 una fecha fuera de intervalos de 15 minutos",
          "full_title": "agenda autoritativa /api/citas > rechaza con 400 una fecha fuera de intervalos de 15 minutos",
          "line": 505,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6628253065aeb6f69af7",
          "title": "resuelve dos reservas concurrentes con una sola cita y un 409",
          "full_title": "agenda autoritativa /api/citas > resuelve dos reservas concurrentes con una sola cita y un 409",
          "line": 524,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7ac4445b9000a7fa2ff8",
          "title": "trata intervalos como [inicio, fin) y permite una cita en el límite exacto",
          "full_title": "agenda autoritativa /api/citas > trata intervalos como [inicio, fin) y permite una cita en el límite exacto",
          "line": 538,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0221ced45c3c73b9740d",
          "title": "respeta buffers configurados del especialista y libera el límite al terminar el buffer",
          "full_title": "agenda autoritativa /api/citas > respeta buffers configurados del especialista y libera el límite al terminar el buffer",
          "line": 552,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "99118aed9ed0bb35f863",
          "title": "bloquea disponibilidad explícita y ocupación externa de Google",
          "full_title": "agenda autoritativa /api/citas > bloquea disponibilidad explícita y ocupación externa de Google",
          "line": 569,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eb1b9bc73188bb3304b9",
          "title": "vincula el evento espejo de Google por el comando autoritativo sin duplicarlo",
          "full_title": "agenda autoritativa /api/citas > vincula el evento espejo de Google por el comando autoritativo sin duplicarlo",
          "line": 592,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b2bec3ebe1a296b67e5b",
          "title": "rechaza un supuesto espejo que ya no existe sin crear cita ni consulta",
          "full_title": "agenda autoritativa /api/citas > rechaza un supuesto espejo que ya no existe sin crear cita ni consulta",
          "line": 625,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9ced77c0a7e77e81dc69",
          "title": "reagenda conservando el ID, auditoría y consulta; libera el horario anterior",
          "full_title": "agenda autoritativa /api/citas > reagenda conservando el ID, auditoría y consulta; libera el horario anterior",
          "line": 646,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b88a6909057ff17f9ddc",
          "title": "rechaza cambiar de paciente al reagendar aunque la cita no tenga consulta",
          "full_title": "agenda autoritativa /api/citas > rechaza cambiar de paciente al reagendar aunque la cita no tenga consulta",
          "line": 672,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "990f1f7a9a0f52081c6b",
          "title": "serializa dos reagendados concurrentes de la misma cita sin dejar locks huérfanos",
          "full_title": "agenda autoritativa /api/citas > serializa dos reagendados concurrentes de la misma cita sin dejar locks huérfanos",
          "line": 703,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d3658d9cae48c2acc5be",
          "title": "preserva una transición concurrente y rechaza el reagendado sin reabrir la cita",
          "full_title": "agenda autoritativa /api/citas > preserva una transición concurrente y rechaza el reagendado sin reabrir la cita",
          "line": 751,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "323e16395ba6a67bc962",
          "title": "serializa cancelación contra reagenda y usa el vínculo fresco de consulta y Google",
          "full_title": "agenda autoritativa /api/citas > serializa cancelación contra reagenda y usa el vínculo fresco de consulta y Google",
          "line": 792,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ef139f8668dff7995a5b",
          "title": "crea una consulta faltante de forma transaccional e idempotente",
          "full_title": "agenda autoritativa /api/citas > crea una consulta faltante de forma transaccional e idempotente",
          "line": 857,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5b45e3aa3def7749ac4d",
          "title": "rechaza reagendar una cita en progreso o terminal sin cambiar fecha ni consulta",
          "full_title": "agenda autoritativa /api/citas > rechaza reagendar una cita en progreso o terminal sin cambiar fecha ni consulta",
          "line": 899,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "351a7fdbe624f710edbf",
          "title": "cancelar libera locks y permite reservar nuevamente el mismo horario",
          "full_title": "agenda autoritativa /api/citas > cancelar libera locks y permite reservar nuevamente el mismo horario",
          "line": 924,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "03edecc3746b310db3ee",
          "title": "aplica la máquina de estados y consume una sesión exactamente una vez",
          "full_title": "agenda autoritativa /api/citas > aplica la máquina de estados y consume una sesión exactamente una vez",
          "line": 942,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "70f43aad443d3e626ecc",
          "title": "rechaza saltar de programada a completada",
          "full_title": "agenda autoritativa /api/citas > rechaza saltar de programada a completada",
          "line": 981,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d3bd82a43e8586dc6403",
          "title": "crea la cita aunque el especialista no tenga Google Calendar conectado",
          "full_title": "agenda autoritativa /api/citas > crea la cita aunque el especialista no tenga Google Calendar conectado",
          "line": 1001,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c177b1479d55c3d5ba1c",
          "title": "no toca Google cuando la reserva pide crearEnGoogle:false",
          "full_title": "agenda autoritativa /api/citas > no toca Google cuando la reserva pide crearEnGoogle:false",
          "line": 1023,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "43075afd39a18dac623c",
          "title": "marca esBaseline solo en la primera consulta no cancelada del paciente",
          "full_title": "agenda autoritativa /api/citas > marca esBaseline solo en la primera consulta no cancelada del paciente",
          "line": 1044,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c4d6ac11f94635912b7f",
          "title": "asigna los cuestionarios seleccionados en el segundo paso",
          "full_title": "agenda autoritativa /api/citas > asigna los cuestionarios seleccionados en el segundo paso",
          "line": 1062,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "40420a86be52c2bb3844",
          "title": "rechaza con 422 una cita fuera del horario de atención",
          "full_title": "agenda autoritativa /api/citas > rechaza con 422 una cita fuera del horario de atención",
          "line": 1111,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "14aee1a2fece470166b5",
          "title": "acepta fuera de horario cuando la política del workspace lo permite",
          "full_title": "agenda autoritativa /api/citas > acepta fuera de horario cuando la política del workspace lo permite",
          "line": 1125,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "802f0212c43266e3ded1",
          "title": "respeta el horario configurado por el workspace, no el de fábrica",
          "full_title": "agenda autoritativa /api/citas > respeta el horario configurado por el workspace, no el de fábrica",
          "line": 1140,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1243f24a34aefa873812",
          "title": "guarda el contratacionId recibido y lo prefiere al completar la cita",
          "full_title": "agenda autoritativa /api/citas > guarda el contratacionId recibido y lo prefiere al completar la cita",
          "line": 1160,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e7c56bf4df01c31b1b11",
          "title": "rechaza un contratacionId de otro paciente",
          "full_title": "agenda autoritativa /api/citas > rechaza un contratacionId de otro paciente",
          "line": 1200,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5919dd8694b3f4b10a77",
          "title": "resuelve la ubicación desde la sede configurada, no desde el cuerpo",
          "full_title": "agenda autoritativa /api/citas > resuelve la ubicación desde la sede configurada, no desde el cuerpo",
          "line": 1218,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c51e1d9710a639a5ae9f",
          "title": "rechaza una sede inactiva",
          "full_title": "agenda autoritativa /api/citas > rechaza una sede inactiva",
          "line": 1246,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3f46bf6d56039b06896b",
          "title": "no guarda ubicación en una cita en línea aunque se mande sede",
          "full_title": "agenda autoritativa /api/citas > no guarda ubicación en una cita en línea aunque se mande sede",
          "line": 1264,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f356270252363e0c2b01",
          "title": "guarda instrucciones y solicitudes, y descarta tipos desconocidos",
          "full_title": "agenda autoritativa /api/citas > guarda instrucciones y solicitudes, y descarta tipos desconocidos",
          "line": 1293,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c47688977ce17f822398",
          "title": "conserva lo que el paciente ya entregó al reeditar la preparación",
          "full_title": "agenda autoritativa /api/citas > conserva lo que el paciente ya entregó al reeditar la preparación",
          "line": 1318,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "db9d64f0bf576b1b7e0c",
          "title": "no permite preparar una cita de otro workspace",
          "full_title": "agenda autoritativa /api/citas > no permite preparar una cita de otro workspace",
          "line": 1352,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d9163bfbc07deed6fdce",
          "title": "rechaza a un paciente que intente escribir su propia preparación",
          "full_title": "agenda autoritativa /api/citas > rechaza a un paciente que intente escribir su propia preparación",
          "line": 1367,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4818c597f7a59b75ac0c",
          "title": "rechaza documentos con protocolos inseguros",
          "full_title": "agenda autoritativa /api/citas > rechaza documentos con protocolos inseguros",
          "line": 1381,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0833047d928ae3df6866",
          "title": "reserva una sola sesión ante dos agendamientos concurrentes",
          "full_title": "agenda autoritativa /api/citas > reserva una sola sesión ante dos agendamientos concurrentes",
          "line": 1396,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "074dde30160f88719a4d",
          "title": "reagenda la misma reserva y al completar la convierte exactamente una vez",
          "full_title": "agenda autoritativa /api/citas > reagenda la misma reserva y al completar la convierte exactamente una vez",
          "line": 1416,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9f3054ee2385e7367879",
          "title": "mantiene no_show pendiente hasta que el equipo decide liberar la sesión",
          "full_title": "agenda autoritativa /api/citas > mantiene no_show pendiente hasta que el equipo decide liberar la sesión",
          "line": 1448,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ba923c280867eb932ea9",
          "title": "bloquea la atención, reactiva antes del límite y autocancela después liberando la sesión",
          "full_title": "agenda autoritativa /api/citas > bloquea la atención, reactiva antes del límite y autocancela después liberando la sesión",
          "line": 1484,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ad99a7766d420597d463",
          "title": "no bloquea una cita si la parcialidad se confirmó antes del commit",
          "full_title": "agenda autoritativa /api/citas > no bloquea una cita si la parcialidad se confirmó antes del commit",
          "line": 1545,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "acbb5df179bea69406c9",
          "title": "impide que la ruta de status reactive o cancele manualmente una cita bloqueada por pago",
          "full_title": "agenda autoritativa /api/citas > impide que la ruta de status reactive o cancele manualmente una cita bloqueada por pago",
          "line": 1576,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8b6fbeb73021c1bea3f8",
          "title": "mantiene cerrada la operación clínica mientras la parcialidad espera reintento, pero libera terminales",
          "full_title": "agenda autoritativa /api/citas > mantiene cerrada la operación clínica mientras la parcialidad espera reintento, pero libera terminales",
          "line": 1608,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "93a1146c02cfba162b66",
          "title": "confirma la cita y deja un efecto reintentable si falla el vínculo post-commit de parcialidad",
          "full_title": "agenda autoritativa /api/citas > confirma la cita y deja un efecto reintentable si falla el vínculo post-commit de parcialidad",
          "line": 1690,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f2590db865ec4a885b93",
          "title": "bloquea de inmediato la cita ligada a una parcialidad pendiente y la cancela a los 15 minutos",
          "full_title": "agenda autoritativa /api/citas > bloquea de inmediato la cita ligada a una parcialidad pendiente y la cancela a los 15 minutos",
          "line": 1741,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2b4be08bbc9dec7be147",
          "title": "mantiene activa la cita ligada cuando su parcialidad ya está pagada",
          "full_title": "agenda autoritativa /api/citas > mantiene activa la cita ligada cuando su parcialidad ya está pagada",
          "line": 1840,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "46be5fdab65dd7453c7c",
          "title": "sólo permite una excepción sin contrato con motivo y permiso de pagos",
          "full_title": "agenda autoritativa /api/citas > sólo permite una excepción sin contrato con motivo y permiso de pagos",
          "line": 1879,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/enviar-enlace-pago.test.ts",
      "type": "integration",
      "sha256": "33c0748e54d4aab8faa7348ef54ecab20f0e00d432a6f1080161727728d5b577",
      "tests": [
        {
          "id": "907f3b0dfc8dc020f340",
          "title": "rechaza con 401 una request sin usuario autenticado",
          "full_title": "POST /api/enviar-enlace-pago > rechaza con 401 una request sin usuario autenticado",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6308615a6b373a0c3944",
          "title": "rechaza con 403 a un rol no permitido (coach)",
          "full_title": "POST /api/enviar-enlace-pago > rechaza con 403 a un rol no permitido (coach)",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "09396504964ff19ee2a0",
          "title": "rechaza con 400 cuando faltan campos requeridos",
          "full_title": "POST /api/enviar-enlace-pago > rechaza con 400 cuando faltan campos requeridos",
          "line": 92,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ecc2b3d494e88f2b3c61",
          "title": "rechaza con 400 un correo inválido",
          "full_title": "POST /api/enviar-enlace-pago > rechaza con 400 un correo inválido",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "54bad0a2acd8a81ff6b1",
          "title": "rechaza con 400 una URL de pago que no es http(s)",
          "full_title": "POST /api/enviar-enlace-pago > rechaza con 400 una URL de pago que no es http(s)",
          "line": 114,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "860bc3316c4638a29560",
          "title": "simula el envío cuando el email no está configurado",
          "full_title": "POST /api/enviar-enlace-pago > simula el envío cuando el email no está configurado",
          "line": 128,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fcbed4f774fd7f8e64c7",
          "title": "envía el enlace con los datos normalizados y responde success",
          "full_title": "POST /api/enviar-enlace-pago > envía el enlace con los datos normalizados y responde success",
          "line": 139,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "255c1e40f93c5037056e",
          "title": "responde 500 cuando el proveedor de email falla",
          "full_title": "POST /api/enviar-enlace-pago > responde 500 cuando el proveedor de email falla",
          "line": 155,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/fitness-session.test.ts",
      "type": "integration",
      "sha256": "62f56764b890737a8b7a952faa87691e9955f63f004b2b7cd11899b1faf63deb",
      "tests": [
        {
          "id": "2c15f78fe5cb0d9bacbb",
          "title": "conserva bloques libres en la versión fijada sin inventar series",
          "full_title": "POST /api/portal/fitness/session > conserva bloques libres en la versión fijada sin inventar series",
          "line": 184,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b833db9848a33f14c6e7",
          "title": "inicia una sola sesión contra la versión vigente e ignora assignmentVersionId del cliente",
          "full_title": "POST /api/portal/fitness/session > inicia una sola sesión contra la versión vigente e ignora assignmentVersionId del cliente",
          "line": 200,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4161c366eec7aad41ee0",
          "title": "consulta sólo el avance real de una sesión propia sin cargar toda la semana",
          "full_title": "POST /api/portal/fitness/session > consulta sólo el avance real de una sesión propia sin cargar toda la semana",
          "line": 217,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3ff216c3c2f61d3b363d",
          "title": "conserva sesión, sets y snapshot cuando el staff publica una versión durante el entrenamiento",
          "full_title": "POST /api/portal/fitness/session > conserva sesión, sets y snapshot cuando el staff publica una versión durante el entrenamiento",
          "line": 237,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0fbf96155d9055952fdb",
          "title": "usa hoy la versión nueva cuando la sesión todavía no ha empezado",
          "full_title": "POST /api/portal/fitness/session > usa hoy la versión nueva cuando la sesión todavía no ha empezado",
          "line": 256,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b057a0bf44c89094382a",
          "title": "inicia la primera sesión y guarda una serie publicada después de las 12 UTC, reloj %s",
          "full_title": "POST /api/portal/fitness/session > inicia la primera sesión y guarda una serie publicada después de las 12 UTC, reloj %s",
          "line": 264,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "16afd1accc34206e883c",
          "title": "fija el reemplazo publicado después de las 12 UTC al registrar por primera vez",
          "full_title": "POST /api/portal/fitness/session > fija el reemplazo publicado después de las 12 UTC al registrar por primera vez",
          "line": 284,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "567586ff84ff742f5282",
          "title": "no fija una publicación de las 11 UTC cuando todavía son las 10 UTC",
          "full_title": "POST /api/portal/fitness/session > no fija una publicación de las 11 UTC cuando todavía son las 10 UTC",
          "line": 294,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cd4529a45191a61a1d9d",
          "title": "conserva una sesión iniciada y su serie cuando publican un reemplazo después de las 12 UTC",
          "full_title": "POST /api/portal/fitness/session > conserva una sesión iniciada y su serie cuando publican un reemplazo después de las 12 UTC",
          "line": 304,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1491e1402a569e8918aa",
          "title": "conserva la resolución de una fecha histórica sin usar la versión vigente hoy",
          "full_title": "POST /api/portal/fitness/session > conserva la resolución de una fecha histórica sin usar la versión vigente hoy",
          "line": 321,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a3bfc4e5fd838cd981f4",
          "title": "recupera la sesión original más antigua si el defecto ya dejó dos sesiones abiertas",
          "full_title": "POST /api/portal/fitness/session > recupera la sesión original más antigua si el defecto ya dejó dos sesiones abiertas",
          "line": 334,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6df4c299a0ab3e6c64c6",
          "title": "falla de forma explícita si la versión fijada por la sesión desapareció",
          "full_title": "POST /api/portal/fitness/session > falla de forma explícita si la versión fijada por la sesión desapareció",
          "line": 363,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4fb80356834d9a97bc53",
          "title": "falla si la versión fijada ya no pertenece al %s esperado",
          "full_title": "POST /api/portal/fitness/session > falla si la versión fijada ya no pertenece al %s esperado",
          "line": 373,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "549e920e08e6c7b5da29",
          "title": "usa la versión nueva en la siguiente sesión después de terminar la fijada",
          "full_title": "POST /api/portal/fitness/session > usa la versión nueva en la siguiente sesión después de terminar la fijada",
          "line": 387,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "40b0ecfc85bd66399f93",
          "title": "niega rutina no asignada, día inexistente e identidad ajena",
          "full_title": "POST /api/portal/fitness/session > niega rutina no asignada, día inexistente e identidad ajena",
          "line": 408,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "54f76e0f8f576f9c5374",
          "title": "bloquea una doble versión vigente en vez de elegir una arbitrariamente",
          "full_title": "POST /api/portal/fitness/session > bloquea una doble versión vigente en vez de elegir una arbitrariamente",
          "line": 427,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "50d10534a87befe8c6ed",
          "title": "requiere una sesión autenticada de paciente",
          "full_title": "POST /api/portal/fitness/session > requiere una sesión autenticada de paciente",
          "line": 439,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a91b81be46e18fdf89d4",
          "title": "deriva objetivos del snapshot, genera PR una vez y hace replay idempotente",
          "full_title": "POST /api/portal/fitness/session > deriva objetivos del snapshot, genera PR una vez y hace replay idempotente",
          "line": 447,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "46fc0bc71a2073eed9af",
          "title": "niega ejercicio/serie fuera de prescripción y métricas manipuladas",
          "full_title": "POST /api/portal/fitness/session > niega ejercicio/serie fuera de prescripción y métricas manipuladas",
          "line": 473,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7bbb5e4e8081e5ed57cb",
          "title": "cero sets no completa; una parte queda parcial y terminal",
          "full_title": "POST /api/portal/fitness/session > cero sets no completa; una parte queda parcial y terminal",
          "line": 489,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b952a836e26348b80eb9",
          "title": "sólo marca completada al registrar todas las series prescritas",
          "full_title": "POST /api/portal/fitness/session > sólo marca completada al registrar todas las series prescritas",
          "line": 507,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5db4bec3fb94934cf381",
          "title": "audita si el resultado fue manual o una copia explícita de la prescripción",
          "full_title": "POST /api/portal/fitness/session > audita si el resultado fue manual o una copia explícita de la prescripción",
          "line": 517,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "968380e253bdfe8ce6d8",
          "title": "guarda notas como dato del ejercicio y rechaza identidades fuera de la sesión",
          "full_title": "POST /api/portal/fitness/session > guarda notas como dato del ejercicio y rechaza identidades fuera de la sesión",
          "line": 540,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d884397ecd2a346be288",
          "title": "conserva series extra y sus PRs sin contarlas como cumplimiento prescrito",
          "full_title": "POST /api/portal/fitness/session > conserva series extra y sus PRs sin contarlas como cumplimiento prescrito",
          "line": 573,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bb2bfe9778749dde3319",
          "title": "valida ejercicios libres contra catálogo y completa con resultados reales",
          "full_title": "POST /api/portal/fitness/session > valida ejercicios libres contra catálogo y completa con resultados reales",
          "line": 595,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "652e8dc4d42108144bf8",
          "title": "permite omitir explícitamente sin resultados y no lo confunde con completada",
          "full_title": "POST /api/portal/fitness/session > permite omitir explícitamente sin resultados y no lo confunde con completada",
          "line": 621,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f83c47320b0857537964",
          "title": "versiona guardar_futuro desde el día siguiente sin mutar la rutina visible hoy",
          "full_title": "POST /api/portal/fitness/session > versiona guardar_futuro desde el día siguiente sin mutar la rutina visible hoy",
          "line": 633,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "93fc8b6d0051489632de",
          "title": "requiere revisión staff para convertir un ejercicio extra en prescripción futura",
          "full_title": "POST /api/portal/fitness/session > requiere revisión staff para convertir un ejercicio extra en prescripción futura",
          "line": 682,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/meal-checkins-offline.test.ts",
      "type": "integration",
      "sha256": "30490b2e61e8b1d771b7fa1871c830caa04f8a685b260b6612bbd15c52cc0568",
      "tests": [
        {
          "id": "6419f8ac178e6545fd7a",
          "title": "aplica last-write-wins al reproducir dos valores del mismo alimento",
          "full_title": "POST /api/pacientes/[pacienteId]/meal-checkins > aplica last-write-wins al reproducir dos valores del mismo alimento",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "67ddf34aa80241b419e0",
          "title": "conserva otras comidas al reproducir el último valor y acepta replays",
          "full_title": "POST /api/pacientes/[pacienteId]/meal-checkins > conserva otras comidas al reproducir el último valor y acepta replays",
          "line": 76,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5ecc65da0d448e1c69f3",
          "title": "rechaza escritura de otro tenant sin modificar el documento",
          "full_title": "POST /api/pacientes/[pacienteId]/meal-checkins > rechaza escritura de otro tenant sin modificar el documento",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/nutrition-revisions.test.ts",
      "type": "integration",
      "sha256": "137f46f561d9418aa40e34b9b4aa4ae1d82580ec8369d65868ad118e9e9d3fe4",
      "tests": [
        {
          "id": "c597b46706fd7ed3c804",
          "title": "conserva un ajuste programado cuando el editor no conocía su versión",
          "full_title": "POST /api/pacientes/[pacienteId]/nutrition-revisions > conserva un ajuste programado cuando el editor no conocía su versión",
          "line": 136,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "19aa7f32eee89a85f558",
          "title": "actualiza el plan, cierra la versión, crea revisión e historial exactamente una vez",
          "full_title": "POST /api/pacientes/[pacienteId]/nutrition-revisions > actualiza el plan, cierra la versión, crea revisión e historial exactamente una vez",
          "line": 144,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c6c14a20e7dff57b55d7",
          "title": "rechaza conflicto de edición, rol nutricional incorrecto y otro tenant",
          "full_title": "POST /api/pacientes/[pacienteId]/nutrition-revisions > rechaza conflicto de edición, rol nutricional incorrecto y otro tenant",
          "line": 174,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0ebd2cc5c8cd341bc29d",
          "title": "rechaza planes que exceden el límite del contrato",
          "full_title": "POST /api/pacientes/[pacienteId]/nutrition-revisions > rechaza planes que exceden el límite del contrato",
          "line": 185,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "79fb889cefa3634e682f",
          "title": "publica una versión completa y conserva hoy el snapshot del tiempo registrado",
          "full_title": "POST /api/pacientes/[pacienteId]/nutrition-revisions > publica una versión completa y conserva hoy el snapshot del tiempo registrado",
          "line": 193,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "914add8accdfefc68541",
          "title": "si todos los tiempos afectados están registrados guarda una sola versión sin reescribir el check-in",
          "full_title": "POST /api/pacientes/[pacienteId]/nutrition-revisions > si todos los tiempos afectados están registrados guarda una sola versión sin reescribir el check-in",
          "line": 221,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7ce1d02f5795ec73eb24",
          "title": "Desayuno registrado y ajuste exclusivo de Cena no activa preservación global",
          "full_title": "POST /api/pacientes/[pacienteId]/nutrition-revisions > Desayuno registrado y ajuste exclusivo de Cena no activa preservación global",
          "line": 238,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b901d4fd0e24e06dd458",
          "title": "reconoce el check-in booleano legacy al aplicar el gate del servidor",
          "full_title": "POST /api/pacientes/[pacienteId]/nutrition-revisions > reconoce el check-in booleano legacy al aplicar el gate del servidor",
          "line": 252,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c728a2199ad62de141cd",
          "title": "un plan futuro conserva exactamente el arranque del día de negocio",
          "full_title": "POST /api/pacientes/[pacienteId]/nutrition-revisions > un plan futuro conserva exactamente el arranque del día de negocio",
          "line": 270,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/onboarding-complete.test.ts",
      "type": "integration",
      "sha256": "a3d8285f53ec1d7e27c84e2ec3e02bbfc427ff7632091ce32fd9c63b8fa2917d",
      "tests": [
        {
          "id": "793daec4602ab21a7d07",
          "title": "preserva historia clínica, fotos, planes y mediciones al completar intake",
          "full_title": "POST /api/onboarding/complete > preserva historia clínica, fotos, planes y mediciones al completar intake",
          "line": 100,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5101934c8094852c8480",
          "title": "HIXION activa el acceso canónico y crea su evento en la misma transacción",
          "full_title": "POST /api/onboarding/complete > HIXION activa el acceso canónico y crea su evento en la misma transacción",
          "line": 124,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b7fee75d71bde199fcb0",
          "title": "HIXION no consume el token si pago o contrato no permiten activar acceso",
          "full_title": "POST /api/onboarding/complete > HIXION no consume el token si pago o contrato no permiten activar acceso",
          "line": 182,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b3307ab6945c1fe9eeab",
          "title": "clasifica al atleta como alto rendimiento cuando el expediente trae perfil AAR activo",
          "full_title": "POST /api/onboarding/complete > clasifica al atleta como alto rendimiento cuando el expediente trae perfil AAR activo",
          "line": 212,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "40a85f158453707d7a02",
          "title": "sin perfil AAR activo no escribe tipoPaciente y conserva la clasificación existente",
          "full_title": "POST /api/onboarding/complete > sin perfil AAR activo no escribe tipoPaciente y conserva la clasificación existente",
          "line": 231,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "10460e1b562d7af5fc1a",
          "title": "una segunda captura válida actualiza la medición inicial sin duplicarla",
          "full_title": "POST /api/onboarding/complete > una segunda captura válida actualiza la medición inicial sin duplicarla",
          "line": 248,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2545f7f3e6e2d7c07a4f",
          "title": "dos envíos concurrentes del mismo token sólo escriben una vez",
          "full_title": "POST /api/onboarding/complete > dos envíos concurrentes del mismo token sólo escriben una vez",
          "line": 277,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fc52b874d30c8b45c444",
          "title": "rechaza reutilizar una cuenta staff como paciente sin modificar el expediente",
          "full_title": "POST /api/onboarding/complete > rechaza reutilizar una cuenta staff como paciente sin modificar el expediente",
          "line": 296,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/patient-access-control.test.ts",
      "type": "integration",
      "sha256": "82cb8fa764473424a599815fb8d451b446660f0dfe882b8ae9d0d8b023599716",
      "tests": [
        {
          "id": "f32cbefdd459cda1c060",
          "title": "restablece acceso sólo después de revalidar pago y contrato, con idempotencia",
          "full_title": "POST /api/pacientes/[pacienteId]/access > restablece acceso sólo después de revalidar pago y contrato, con idempotencia",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c09d15299fb299efb9b0",
          "title": "no restaura cuando la contratación ya no es elegible",
          "full_title": "POST /api/pacientes/[pacienteId]/access > no restaura cuando la contratación ya no es elegible",
          "line": 97,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5ee9d2311235eee00973",
          "title": "exige gestionar_acceso y un motivo explícito",
          "full_title": "POST /api/pacientes/[pacienteId]/access > exige gestionar_acceso y un motivo explícito",
          "line": 106,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8d5a7209071f2058f768",
          "title": "suspende manualmente y no depende de un cambio de pago",
          "full_title": "POST /api/pacientes/[pacienteId]/access > suspende manualmente y no depende de un cambio de pago",
          "line": 122,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/patient-identity-link.test.ts",
      "type": "integration",
      "sha256": "624e9318fd1434a5bf9a31e3493f30cff6dd7b06714f6b977b33ab755baad68a",
      "tests": [
        {
          "id": "103f0d7cb47d136bae09",
          "title": "reasocia dentro del tenant y audita usuario, paciente anterior y nuevo en una transacción",
          "full_title": "POST /api/admin/users/[uid]/patient-link > reasocia dentro del tenant y audita usuario, paciente anterior y nuevo en una transacción",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f952da9669325e9a0124",
          "title": "reproduce la misma operación sin duplicar auditoría ni cambiar el resultado",
          "full_title": "POST /api/admin/users/[uid]/patient-link > reproduce la misma operación sin duplicar auditoría ni cambiar el resultado",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "25a549e6d253f6c790cd",
          "title": "rechaza una expectativa obsoleta para no sobrescribir otro cambio administrativo",
          "full_title": "POST /api/admin/users/[uid]/patient-link > rechaza una expectativa obsoleta para no sobrescribir otro cambio administrativo",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a96cb694d8e27a2e43f9",
          "title": "impide vincular un paciente ya asociado a otra cuenta",
          "full_title": "POST /api/admin/users/[uid]/patient-link > impide vincular un paciente ya asociado a otra cuenta",
          "line": 115,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b9182feb25e4f1fbe5f1",
          "title": "admin no puede reasociar contra un paciente de otro tenant",
          "full_title": "POST /api/admin/users/[uid]/patient-link > admin no puede reasociar contra un paciente de otro tenant",
          "line": 124,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f691c333d3f31a4246ed",
          "title": "paciente no puede ejecutar el comando administrativo",
          "full_title": "POST /api/admin/users/[uid]/patient-link > paciente no puede ejecutar el comando administrativo",
          "line": 134,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/photo-capture-upload.test.ts",
      "type": "integration",
      "sha256": "d7232f2dbd70ecc57fc0552f487565199c32e8ab3999852dfd79e831c220ecf5",
      "tests": [
        {
          "id": "e616fb87656e9dd42495",
          "title": "guarda una foto real en Storage y la enlaza al expediente",
          "full_title": "POST /api/photo-capture/[token]/upload > guarda una foto real en Storage y la enlaza al expediente",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8001962684fc298df1e4",
          "title": "guarda una foto sin consulta asociada sin enviar undefined a Firestore",
          "full_title": "POST /api/photo-capture/[token]/upload > guarda una foto sin consulta asociada sin enviar undefined a Firestore",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "10d3166655d94ab7e89c",
          "title": "confirma una carga temporal directa sin enviar el archivo por la funcion",
          "full_title": "POST /api/photo-capture/[token]/upload > confirma una carga temporal directa sin enviar el archivo por la funcion",
          "line": 121,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/photo-progress-albums.test.ts",
      "type": "integration",
      "sha256": "17317cb053edb31174348b69011bde0ee3980208833669e3948d951cea5f7f5a",
      "tests": [
        {
          "id": "e6f497076c93450ed086",
          "title": "permite al paciente propietario crear una pose personalizada",
          "full_title": "POST /api/pacientes/[pacienteId]/photo-progress > permite al paciente propietario crear una pose personalizada",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ef53ffadf13025f8bcf5",
          "title": "permite a staff editor del mismo tenant",
          "full_title": "POST /api/pacientes/[pacienteId]/photo-progress > permite a staff editor del mismo tenant",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "355371cbeef1ade624b5",
          "title": "mueve una foto entre álbumes y conserva la fecha del registro",
          "full_title": "POST /api/pacientes/[pacienteId]/photo-progress > mueve una foto entre álbumes y conserva la fecha del registro",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d574dc5aaa96c42c5062",
          "title": "rechaza paciente ajeno, tenant cruzado y staff sin permiso",
          "full_title": "POST /api/pacientes/[pacienteId]/photo-progress > rechaza paciente ajeno, tenant cruzado y staff sin permiso",
          "line": 62,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/role-change.test.ts",
      "type": "integration",
      "sha256": "a15799ceae398170e9d2d18b179df069bfd8a6126944bf3a935787f0408ca782",
      "tests": [
        {
          "id": "10d55c14e85f87cd051e",
          "title": "cambia el rol y deja auditoría en una transacción",
          "full_title": "POST /api/admin/users/[uid]/role > cambia el rol y deja auditoría en una transacción",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e620bf4618d938749b9a",
          "title": "asigna un rol propio del workspace",
          "full_title": "POST /api/admin/users/[uid]/role > asigna un rol propio del workspace",
          "line": 109,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "72e420fccaf2bf230842",
          "title": "rechaza un rol que no existe en el workspace",
          "full_title": "POST /api/admin/users/[uid]/role > rechaza un rol que no existe en el workspace",
          "line": 118,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2c8e6bbc181b5cf7712d",
          "title": "reproduce la misma operación sin duplicar auditoría",
          "full_title": "POST /api/admin/users/[uid]/role > reproduce la misma operación sin duplicar auditoría",
          "line": 126,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4fd33db731a2a6592f4a",
          "title": "rechaza una expectativa obsoleta en vez de pisar otro cambio",
          "full_title": "POST /api/admin/users/[uid]/role > rechaza una expectativa obsoleta en vez de pisar otro cambio",
          "line": 136,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a4c6027ae695f1f5d194",
          "title": "no permite cambiarse el rol a uno mismo",
          "full_title": "POST /api/admin/users/[uid]/role > no permite cambiarse el rol a uno mismo",
          "line": 144,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dd4d24803ca858d0ef13",
          "title": "no toca el rol superadministrador en ninguna dirección",
          "full_title": "POST /api/admin/users/[uid]/role > no toca el rol superadministrador en ninguna dirección",
          "line": 154,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1682541eae593fde878e",
          "title": "no convierte entre paciente y equipo",
          "full_title": "POST /api/admin/users/[uid]/role > no convierte entre paciente y equipo",
          "line": 161,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e2fe4acd8ce664f14e5b",
          "title": "bloquea la degradación del último administrador activo",
          "full_title": "POST /api/admin/users/[uid]/role > bloquea la degradación del último administrador activo",
          "line": 173,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "70e229d4e4f963f80a38",
          "title": "permite degradar a un admin cuando queda otro activo",
          "full_title": "POST /api/admin/users/[uid]/role > permite degradar a un admin cuando queda otro activo",
          "line": 190,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "508ce85585c0c69c3fc4",
          "title": "no cruza tenants",
          "full_title": "POST /api/admin/users/[uid]/role > no cruza tenants",
          "line": 205,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6fb88ffc0368a1e63123",
          "title": "exige justificación de 10 a 500 caracteres",
          "full_title": "POST /api/admin/users/[uid]/role > exige justificación de 10 a 500 caracteres",
          "line": 212,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "951be1e2bb29edb21844",
          "title": "exige expectedCurrentRol explícito",
          "full_title": "POST /api/admin/users/[uid]/role > exige expectedCurrentRol explícito",
          "line": 222,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0dd55f306a9fe5e15b94",
          "title": "rechaza a quien no es administrador",
          "full_title": "POST /api/admin/users/[uid]/role > rechaza a quien no es administrador",
          "line": 230,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/routine-occurrences.test.ts",
      "type": "integration",
      "sha256": "565189508015cf053b09b5f48b22a428396a23fcaaaf50b7d1efc47e50e15221",
      "tests": [
        {
          "id": "d7f2caba8fcf72fa56d5",
          "title": "completa el entrenamiento libre en lote, conserva registros y permite deshacer sólo el lote",
          "full_title": "routine occurrence calendar and moves > completa el entrenamiento libre en lote, conserva registros y permite deshacer sólo el lote",
          "line": 107,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2ecef0eb33eb9362f33b",
          "title": "reads without creating sessions or preparation documents",
          "full_title": "routine occurrence calendar and moves > reads without creating sessions or preparation documents",
          "line": 189,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "63e8b67373bc36cc0440",
          "title": "records a past scheduled day as rested or completed without fabricating sets",
          "full_title": "routine occurrence calendar and moves > records a past scheduled day as rested or completed without fabricating sets",
          "line": 197,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3fe1146533c8928b019d",
          "title": "declara una rutina grande sin preparación guardada ni exceder el límite de Firestore",
          "full_title": "routine occurrence calendar and moves > declara una rutina grande sin preparación guardada ni exceder el límite de Firestore",
          "line": 214,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "97b0b38f93dcd8ef4d45",
          "title": "puts assigned dates in vacation rest and restores future dates when vacation ends",
          "full_title": "routine occurrence calendar and moves > puts assigned dates in vacation rest and restores future dates when vacation ends",
          "line": 231,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "610905b06eca0ca438f1",
          "title": "replaces only today's program day, can rest and restore it, and does not duplicate the scheduled occurrence",
          "full_title": "routine occurrence calendar and moves > replaces only today's program day, can rest and restore it, and does not duplicate the scheduled occurrence",
          "line": 247,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0a3ab1cf5e39d2c8931d",
          "title": "adds a program session to a scheduled rest date without changing other dates",
          "full_title": "routine occurrence calendar and moves > adds a program session to a scheduled rest date without changing other dates",
          "line": 274,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a8ca2b914b46ba2069ed",
          "title": "rejects stale day changes and changes to a started or past session",
          "full_title": "routine occurrence calendar and moves > rejects stale day changes and changes to a started or past session",
          "line": 295,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9439c5de09041da73d26",
          "title": "rejects the former date exchange command",
          "full_title": "routine occurrence calendar and moves > rejects the former date exchange command",
          "line": 305,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c8d3b4a7943384739038",
          "title": "does not allow another patient or tenant to choose this occurrence",
          "full_title": "routine occurrence calendar and moves > does not allow another patient or tenant to choose this occurrence",
          "line": 311,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "df5f9141a0dc191ffca2",
          "title": "persists preparation without starting, and uses it when starting from the existing Home endpoint",
          "full_title": "routine occurrence calendar and moves > persists preparation without starting, and uses it when starting from the existing Home endpoint",
          "line": 319,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d5e3f5ebec25cbb484c4",
          "title": "keeps operational audit bounded while preserving all edits and legacy history in details",
          "full_title": "routine occurrence calendar and moves > keeps operational audit bounded while preserving all edits and legacy history in details",
          "line": 375,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a1d0149f5ca1d0659723",
          "title": "rejects a substituted catalog exercise conflicting with patient clinical history on the server",
          "full_title": "routine occurrence calendar and moves > rejects a substituted catalog exercise conflicting with patient clinical history on the server",
          "line": 399,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "589b1b5d11364cbfa600",
          "title": "blocks starting after the coach changes a prepared prescription",
          "full_title": "routine occurrence calendar and moves > blocks starting after the coach changes a prepared prescription",
          "line": 408,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "999461a97235798b9276",
          "title": "conserva la preparación de una sesión anterior al rechazar aceptar una prescripción nueva",
          "full_title": "routine occurrence calendar and moves > conserva la preparación de una sesión anterior al rechazar aceptar una prescripción nueva",
          "line": 492,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3f3531ad838fa7db6bdf",
          "title": "añade y reordena bloques sólo en la sesión, con auditoría e idempotencia",
          "full_title": "routine occurrence calendar and moves > añade y reordena bloques sólo en la sesión, con auditoría e idempotencia",
          "line": 514,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "34d3b47342534930ae9c",
          "title": "completes a block atomically, preserves manual results and undoes only the batch",
          "full_title": "routine occurrence calendar and moves > completes a block atomically, preserves manual results and undoes only the batch",
          "line": 542,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "72705f4bb02c93014542",
          "title": "bulk uses the visible draft, including cleared weight and advanced fields",
          "full_title": "routine occurrence calendar and moves > bulk uses the visible draft, including cleared weight and advanced fields",
          "line": 603,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "47cb3f814bb4988060fa",
          "title": "counts no-data completion separately from measurable results",
          "full_title": "routine occurrence calendar and moves > counts no-data completion separately from measurable results",
          "line": 622,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e349648df7ea7ff806cb",
          "title": "persists one deadline, pauses explicitly and invalidates it when the session ends",
          "full_title": "routine occurrence calendar and moves > persists one deadline, pauses explicitly and invalidates it when the session ends",
          "line": 684,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "771e61dbc46ff60488ac",
          "title": "keeps repeated exercise appearances and added sets separate during an active session",
          "full_title": "routine occurrence calendar and moves > keeps repeated exercise appearances and added sets separate during an active session",
          "line": 744,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2c17311db195593fc9e1",
          "title": "requires details for an empty block, supports undo, and completes without invented sets",
          "full_title": "routine occurrence calendar and moves > requires details for an empty block, supports undo, and completes without invented sets",
          "line": 842,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ac38f839a11e3d28693a",
          "title": "sends a due rest notice once and ignores stale or cancelled tasks",
          "full_title": "routine occurrence calendar and moves > sends a due rest notice once and ignores stale or cancelled tasks",
          "line": 915,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aea2080bb3b78d3d77d6",
          "title": "reads an old session from its pinned version after the plan dates change",
          "full_title": "routine occurrence calendar and moves > reads an old session from its pinned version after the plan dates change",
          "line": 958,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/rutina-day-templates.test.ts",
      "type": "integration",
      "sha256": "66c2350d64d0e5b700a6ec0e31e7deed9098e78ebcdf4eff4b25477960c90aa1",
      "tests": [
        {
          "id": "4229eaa38126667f4637",
          "title": "guardar y volver a consultar conserva un bloque vacío con duración e instrucciones",
          "full_title": "guardar y volver a consultar conserva un bloque vacío con duración e instrucciones",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "42b827cff6cfffc9292a",
          "title": "conserva un ejercicio de otra categoría confirmado en el constructor",
          "full_title": "conserva un ejercicio de otra categoría confirmado en el constructor",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/staff-assignments.test.ts",
      "type": "integration",
      "sha256": "87c93bfdaa5fabb5d6226ce2ac6d18eb7197af85e9f610eb5103226f2e432b81",
      "tests": [
        {
          "id": "342e90c8c3820f83fcdc",
          "title": "crea dieta, versión, operación y proyección del paciente en una sola transacción",
          "full_title": "POST /api/staff/assignments > crea dieta, versión, operación y proyección del paciente en una sola transacción",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e786e183a299265181fd",
          "title": "aplica permisos por especialidad a nutrición y rutina",
          "full_title": "POST /api/staff/assignments > aplica permisos por especialidad a nutrición y rutina",
          "line": 136,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1c670453bd6aab969825",
          "title": "activa un borrador existente conservando su ID y fecha de creación",
          "full_title": "POST /api/staff/assignments > activa un borrador existente conservando su ID y fecha de creación",
          "line": 155,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "444e0d6ecdff533c7f4f",
          "title": "al reemplazar cierra todas las asignaciones y versiones activas anteriores",
          "full_title": "POST /api/staff/assignments > al reemplazar cierra todas las asignaciones y versiones activas anteriores",
          "line": 196,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1266acb32a66dc6b2ea7",
          "title": "repetir el mismo operationId devuelve los mismos IDs sin duplicar documentos",
          "full_title": "POST /api/staff/assignments > repetir el mismo operationId devuelve los mismos IDs sin duplicar documentos",
          "line": 237,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f72d07e0a3b8a04a34f5",
          "title": "rechaza un paciente o asignación perteneciente a otro tenant",
          "full_title": "POST /api/staff/assignments > rechaza un paciente o asignación perteneciente a otro tenant",
          "line": 260,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a2107ebc4fa7a3f9eddb",
          "title": "desasigna de forma idempotente y repara múltiples activas sin borrar historial",
          "full_title": "POST /api/staff/assignments > desasigna de forma idempotente y repara múltiples activas sin borrar historial",
          "line": 287,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e5b15bf31113a16015ee",
          "title": "archiva el plan efectivo y cierra su versiÃ³n y proyecciÃ³n en la misma transacciÃ³n",
          "full_title": "POST /api/staff/assignments > archiva el plan efectivo y cierra su versiÃ³n y proyecciÃ³n en la misma transacciÃ³n",
          "line": 351,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c136d05fef73c13f1098",
          "title": "rechaza una desasignación obsoleta sin cerrar la asignación vigente",
          "full_title": "POST /api/staff/assignments > rechaza una desasignación obsoleta sin cerrar la asignación vigente",
          "line": 411,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b841f462d2059c359131",
          "title": "BK-63 warns but allows a routine when declared equipment does not cover the exercise",
          "full_title": "POST /api/staff/assignments > BK-63 warns but allows a routine when declared equipment does not cover the exercise",
          "line": 462,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f8361a04e3aa20d858c9",
          "title": "BK-63 omits equipment warnings when the patient trains without equipment",
          "full_title": "POST /api/staff/assignments > BK-63 omits equipment warnings when the patient trains without equipment",
          "line": 502,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "25d44401f48f6d037c29",
          "title": "BK-63 omits equipment warnings while the equipment profile is unconfigured",
          "full_title": "POST /api/staff/assignments > BK-63 omits equipment warnings while the equipment profile is unconfigured",
          "line": 539,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e1d8e02c07bae28d30a6",
          "title": "BK-63 records clinical findings as warnings without blocking assignment",
          "full_title": "POST /api/staff/assignments > BK-63 records clinical findings as warnings without blocking assignment",
          "line": 570,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d43bae9ba4b0fa40312e",
          "title": "BK-63 does not require or audit an override to assign a clinical warning",
          "full_title": "POST /api/staff/assignments > BK-63 does not require or audit an override to assign a clinical warning",
          "line": 619,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1a1b91b15768d7b6d80d",
          "title": "BK-63 ignores legacy override data when replaying the same operation",
          "full_title": "POST /api/staff/assignments > BK-63 ignores legacy override data when replaying the same operation",
          "line": 665,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/supplementation-revisions.test.ts",
      "type": "integration",
      "sha256": "781de34f8b7622d5e8ed39413f3c27041b6db097bd7185fd3ea2b169bf68e89c",
      "tests": [
        {
          "id": "5149ee1510f6d9e768a3",
          "title": "escribe el stack, abre un hilo supplementation y no toca nutrición",
          "full_title": "POST /api/pacientes/[pacienteId]/supplementation-revisions > escribe el stack, abre un hilo supplementation y no toca nutrición",
          "line": 116,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "98301a2442b4e34edc96",
          "title": "rechaza un plan que ya no está activo",
          "full_title": "POST /api/pacientes/[pacienteId]/supplementation-revisions > rechaza un plan que ya no está activo",
          "line": 139,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0a43b03e17e027afa791",
          "title": "devuelve 409 si otra sesión ya actualizó el plan",
          "full_title": "POST /api/pacientes/[pacienteId]/supplementation-revisions > devuelve 409 si otra sesión ya actualizó el plan",
          "line": 146,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/training-history.test.ts",
      "type": "integration",
      "sha256": "ef230ed8cb453642381e88546cdb3fb646995af9985feaa324f3469456053c72",
      "tests": [
        {
          "id": "60b62849844197889335",
          "title": "isolates tenant and patient and returns old comparable history without a 90-day cutoff",
          "full_title": "isolates tenant and patient and returns old comparable history without a 90-day cutoff",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "badc195f31bb43badc6a",
          "title": "rejects foreign sessions and staff roles",
          "full_title": "rejects foreign sessions and staff roles",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/api/webhooks-stripe.test.ts",
      "type": "integration",
      "sha256": "1620185e0b176bf43a71c77eb31a88d9390d9b0179046fed5fc562de07171df6",
      "tests": [
        {
          "id": "6948b21c9a9f7fbf2871",
          "title": "rechaza con 400 una firma inválida sin registrar el evento",
          "full_title": "POST /api/webhooks/stripe > rechaza con 400 una firma inválida sin registrar el evento",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "597c351439c3e503b94f",
          "title": "acepta con 200 un evento no manejado y lo marca como procesado",
          "full_title": "POST /api/webhooks/stripe > acepta con 200 un evento no manejado y lo marca como procesado",
          "line": 102,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "389cd0c741d11ff492d5",
          "title": "exige el secreto dedicado para cualquier evento etiquetado HIXION",
          "full_title": "POST /api/webhooks/stripe > exige el secreto dedicado para cualquier evento etiquetado HIXION",
          "line": 120,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9d082649ea4f1232c854",
          "title": "acepta HIXION sólo con llave, cuenta y secreto dedicados",
          "full_title": "POST /api/webhooks/stripe > acepta HIXION sólo con llave, cuenta y secreto dedicados",
          "line": 132,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3a26f1423b59b1206d3e",
          "title": "responde duplicate=true sin reprocesar un evento ya procesado (idempotencia)",
          "full_title": "POST /api/webhooks/stripe > responde duplicate=true sin reprocesar un evento ya procesado (idempotencia)",
          "line": 153,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aca35bf92bafe26a01e8",
          "title": "procesa con advertencias un checkout.session.completed sin pacienteId en metadata",
          "full_title": "POST /api/webhooks/stripe > procesa con advertencias un checkout.session.completed sin pacienteId en metadata",
          "line": 175,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e0f6f33d1bb841732003",
          "title": "aísla una suscripción cuyo tenant metadata no coincide con el paciente",
          "full_title": "POST /api/webhooks/stripe > aísla una suscripción cuyo tenant metadata no coincide con el paciente",
          "line": 204,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8ada7618f30dd95c01d6",
          "title": "responde 202 processing=true cuando el evento sigue bloqueado en procesamiento",
          "full_title": "POST /api/webhooks/stripe > responde 202 processing=true cuando el evento sigue bloqueado en procesamiento",
          "line": 253,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3bcb26ad0a8471c996ba",
          "title": "resuelve el mismo customer de forma aislada entre las cuentas Gera y HIXION",
          "full_title": "POST /api/webhooks/stripe > resuelve el mismo customer de forma aislada entre las cuentas Gera y HIXION",
          "line": 271,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8759ccd5d99b11a86f28",
          "title": "rechaza fallo y disputa si planPagoId pertenece a otro paciente sin bloquear ni atribuir incidentes",
          "full_title": "POST /api/webhooks/stripe > rechaza fallo y disputa si planPagoId pertenece a otro paciente sin bloquear ni atribuir incidentes",
          "line": 345,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "68fb649d68d4be91cca7",
          "title": "confirma la parcialidad y mueve un interesado a pagado sólo desde el webhook firmado",
          "full_title": "POST /api/webhooks/stripe > confirma la parcialidad y mueve un interesado a pagado sólo desde el webhook firmado",
          "line": 421,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dff8024fa967e2e7de3b",
          "title": "clasifica el checkout $label sin inventar una contratación",
          "full_title": "POST /api/webhooks/stripe > clasifica el checkout $label sin inventar una contratación",
          "line": 481,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "f1103c82494c6317d88f",
          "title": "mantiene el rechazo de checkout HIXION sin intento aunque falte billingMode",
          "full_title": "POST /api/webhooks/stripe > mantiene el rechazo de checkout HIXION sin intento aunque falte billingMode",
          "line": 512,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "741c1ad5e71859749879",
          "title": "conserva todas las deudas de una cita y sólo la reactiva al liquidar la última",
          "full_title": "POST /api/webhooks/stripe > conserva todas las deudas de una cita y sólo la reactiva al liquidar la última",
          "line": 536,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "83b8c98f42e9616a95f1",
          "title": "reintenta una conciliación transitoria sin duplicar pago ni contratación",
          "full_title": "POST /api/webhooks/stripe > reintenta una conciliación transitoria sin duplicar pago ni contratación",
          "line": 640,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "121f120d7a5b4ca44883",
          "title": "persiste la contratación manual con modalidad $label sin duplicar al reintentar",
          "full_title": "POST /api/webhooks/stripe > persiste la contratación manual con modalidad $label sin duplicar al reintentar",
          "line": 792,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "7858a40cbbe8cbf10862",
          "title": "mantiene la cita bloqueada si el adeudo se paga pero el contrato sigue pausado",
          "full_title": "POST /api/webhooks/stripe > mantiene la cita bloqueada si el adeudo se paga pero el contrato sigue pausado",
          "line": 819,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3310bfd03d59e1c4f955",
          "title": "usa la hora efectiva de Stripe y acepta un pago a +14 procesado después de +15",
          "full_title": "POST /api/webhooks/stripe > usa la hora efectiva de Stripe y acepta un pago a +14 procesado después de +15",
          "line": 894,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7eb721f08ed4ae3e9cc3",
          "title": "rechaza un intento adulterado antes de escribir pago o contrato",
          "full_title": "POST /api/webhooks/stripe > rechaza un intento adulterado antes de escribir pago o contrato",
          "line": 967,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e9cd5660f83f494f75a3",
          "title": "aplica cada reembolso como delta una sola vez aunque Stripe lo actualice en otro evento",
          "full_title": "POST /api/webhooks/stripe > aplica cada reembolso como delta una sola vez aunque Stripe lo actualice en otro evento",
          "line": 1044,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6b8f83451796241ba8e3",
          "title": "pausa una disputa y no restaura contrato ni acceso al cerrarse como ganada",
          "full_title": "POST /api/webhooks/stripe > pausa una disputa y no restaura contrato ni acceso al cerrarse como ganada",
          "line": 1134,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "124babad4c9fff66d876",
          "title": "marca intento, plan e incidente cuando expira un checkout sin pago",
          "full_title": "POST /api/webhooks/stripe > marca intento, plan e incidente cuando expira un checkout sin pago",
          "line": 1223,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/rules/catalogos.rules.test.ts",
      "type": "integration",
      "sha256": "05f7093d25424eaad64385e359f9aa77d5c734caf6bd697ef393c799a0a7708a",
      "tests": [
        {
          "id": "40a484a1ed6f1b39634a",
          "title": "$col: docs globales son legibles por staff de cualquier tenant",
          "full_title": "Catalogos duales (visibleTo global | tenant) > lectura > $col: docs globales son legibles por staff de cualquier tenant",
          "line": 39,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "57fca5777e0d965a9588",
          "title": "$col: docs globales son legibles por pacientes autenticados",
          "full_title": "Catalogos duales (visibleTo global | tenant) > lectura > $col: docs globales son legibles por pacientes autenticados",
          "line": 44,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "d5e31dd5c7c66d6046f0",
          "title": "$col: docs del tenant A SI son legibles por su tenant",
          "full_title": "Catalogos duales (visibleTo global | tenant) > lectura > $col: docs del tenant A SI son legibles por su tenant",
          "line": 48,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "f39fb9f5def15ab781f2",
          "title": "$col: docs del tenant A NO son legibles por el tenant B",
          "full_title": "Catalogos duales (visibleTo global | tenant) > lectura > $col: docs del tenant A NO son legibles por el tenant B",
          "line": 52,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "396de2d30bb109882599",
          "title": "$col: staff A SI crea docs con visibleTo [su tenant]",
          "full_title": "Catalogos duales (visibleTo global | tenant) > escritura (copy-on-write hacia el tenant propio) > $col: staff A SI crea docs con visibleTo [su tenant]",
          "line": 58,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "6250347fd857a7dc667f",
          "title": "$col: staff A NO crea docs con visibleTo [global]",
          "full_title": "Catalogos duales (visibleTo global | tenant) > escritura (copy-on-write hacia el tenant propio) > $col: staff A NO crea docs con visibleTo [global]",
          "line": 65,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "9fa43b50157943293271",
          "title": "$col: staff A NO crea docs con visibleTo de otro tenant",
          "full_title": "Catalogos duales (visibleTo global | tenant) > escritura (copy-on-write hacia el tenant propio) > $col: staff A NO crea docs con visibleTo de otro tenant",
          "line": 72,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "e819b2d3120dbf2422ac",
          "title": "$col: staff A NO crea docs con tenantId de otro tenant",
          "full_title": "Catalogos duales (visibleTo global | tenant) > escritura (copy-on-write hacia el tenant propio) > $col: staff A NO crea docs con tenantId de otro tenant",
          "line": 79,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "1f78c598e69a85ab895b",
          "title": "$col: staff A NO modifica un doc global (con tenantId hixion)",
          "full_title": "Catalogos duales (visibleTo global | tenant) > escritura (copy-on-write hacia el tenant propio) > $col: staff A NO modifica un doc global (con tenantId hixion)",
          "line": 86,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "09bd65e8a426db36be1f",
          "title": "$col: superadmin SI escribe docs globales",
          "full_title": "Catalogos duales (visibleTo global | tenant) > escritura (copy-on-write hacia el tenant propio) > $col: superadmin SI escribe docs globales",
          "line": 95,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "75d5bfa109070a24a715",
          "title": "recetas: staff A NO secuestra un doc global sin campo tenantId",
          "full_title": "Catalogos duales (visibleTo global | tenant) > escritura (copy-on-write hacia el tenant propio) > recetas: staff A NO secuestra un doc global sin campo tenantId",
          "line": 102,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2420020c26c153a234aa",
          "title": "recetasCatalogShards: nadie escribe desde el cliente, ni superadmin",
          "full_title": "Catalogos duales (visibleTo global | tenant) > shards de catalogo (solo lectura) > recetasCatalogShards: nadie escribe desde el cliente, ni superadmin",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/rules/chat.rules.test.ts",
      "type": "integration",
      "sha256": "7b836022cce35faf2412a6f48a3bc6af8c6561fbeee4c9a49d4bf05c53cc3467",
      "tests": [
        {
          "id": "e130af1ee49935469ef6",
          "title": "staff crea la conversación determinista del especialista asignado",
          "full_title": "Chat canónico y autoritativo > staff crea la conversación determinista del especialista asignado",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "295721c6fbe89c49418d",
          "title": "staff no crea una conversación paralela con su propio uid",
          "full_title": "Chat canónico y autoritativo > staff no crea una conversación paralela con su propio uid",
          "line": 83,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9a39a0d18c7ee4d5fc3b",
          "title": "staff no usa un id aleatorio aunque los metadatos sean correctos",
          "full_title": "Chat canónico y autoritativo > staff no usa un id aleatorio aunque los metadatos sean correctos",
          "line": 93,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4c2444aa58f324fb6809",
          "title": "paciente crea únicamente su conversación canónica",
          "full_title": "Chat canónico y autoritativo > paciente crea únicamente su conversación canónica",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0e0c35092a23a0bd632a",
          "title": "nadie cambia paciente, especialista ni participantes después de crear",
          "full_title": "Chat canónico y autoritativo > nadie cambia paciente, especialista ni participantes después de crear",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cb54adff38e4e47bad6f",
          "title": "permite actualizar sólo estado operativo de la conversación canónica",
          "full_title": "Chat canónico y autoritativo > permite actualizar sólo estado operativo de la conversación canónica",
          "line": 134,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "01b8745f59f571cee910",
          "title": "paciente crea mensajes sólo con su identidad y rol reales",
          "full_title": "Chat canónico y autoritativo > paciente crea mensajes sólo con su identidad y rol reales",
          "line": 139,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e8b9cfd757a1dfc0bbb2",
          "title": "staff crea mensajes sólo con su propio uid",
          "full_title": "Chat canónico y autoritativo > staff crea mensajes sólo con su propio uid",
          "line": 147,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "654ef2f444f25d6ebe0d",
          "title": "niega mensajes cross-tenant",
          "full_title": "Chat canónico y autoritativo > niega mensajes cross-tenant",
          "line": 167,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/rules/clinical-notes.rules.test.ts",
      "type": "integration",
      "sha256": "3f92cb966a56f7b517029b6198186c3300cf04a950fae2c79be8d9f6c50b7cf5",
      "tests": [
        {
          "id": "e66ca30e034f4124c54b",
          "title": "niega al paciente leer un expediente mientras contenga notas legacy",
          "full_title": "notas clínicas internas e instrucciones públicas > niega al paciente leer un expediente mientras contenga notas legacy",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bca2cb559a12b243998a",
          "title": "permite el expediente al paciente después de vaciar historialClinico",
          "full_title": "notas clínicas internas e instrucciones públicas > permite el expediente al paciente después de vaciar historialClinico",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b389eb999dfc0a4bebd7",
          "title": "niega lectura y escritura de notas internas al paciente",
          "full_title": "notas clínicas internas e instrucciones públicas > niega lectura y escritura de notas internas al paciente",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d59f724b47bd417f39d6",
          "title": "permite al paciente leer instrucciones y le niega publicarlas",
          "full_title": "notas clínicas internas e instrucciones públicas > permite al paciente leer instrucciones y le niega publicarlas",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "62af5db25135bac04845",
          "title": "permite al especialista publicar en su tenant y niega cross-tenant",
          "full_title": "notas clínicas internas e instrucciones públicas > permite al especialista publicar en su tenant y niega cross-tenant",
          "line": 82,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c394926041c1c4346fec",
          "title": "mantiene la auditoría clínica privada, atribuida e inmutable",
          "full_title": "notas clínicas internas e instrucciones públicas > mantiene la auditoría clínica privada, atribuida e inmutable",
          "line": 102,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/rules/configuracion.rules.test.ts",
      "type": "integration",
      "sha256": "b9bb00d941c8a5e05bd53b9292fd1f20f514abd945d7556652c2d743b7ec86f4",
      "tests": [
        {
          "id": "789d9cf432ccfafca0af",
          "title": "%s: un nutriólogo puede escribirlo",
          "full_title": "configuracion: permiso por tipo de documento > catálogos de workspace → gestionar_catalogos > %s: un nutriólogo puede escribirlo",
          "line": 49,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "e12c5f6c67ad39a7cf0a",
          "title": "%s: un paciente no puede escribirlo",
          "full_title": "configuracion: permiso por tipo de documento > catálogos de workspace → gestionar_catalogos > %s: un paciente no puede escribirlo",
          "line": 55,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "ee334287b00818c27028",
          "title": "no cruza tenants: el nutriólogo de A no escribe el catálogo de B",
          "full_title": "configuracion: permiso por tipo de documento > catálogos de workspace → gestionar_catalogos > no cruza tenants: el nutriólogo de A no escribe el catálogo de B",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bf6330bf281712d4fdaf",
          "title": "la recepcionista puede escribirla: gestiona la agenda",
          "full_title": "configuracion: permiso por tipo de documento > política de agenda → gestionar_agenda > la recepcionista puede escribirla: gestiona la agenda",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7d8d035c5ffff9944f12",
          "title": "el paciente no puede escribirla",
          "full_title": "configuracion: permiso por tipo de documento > política de agenda → gestionar_agenda > el paciente no puede escribirla",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bb00804a29e359c17768",
          "title": "un nutriólogo NO puede escribir la información general de la clínica",
          "full_title": "configuracion: permiso por tipo de documento > el resto sigue exigiendo configuracion > un nutriólogo NO puede escribir la información general de la clínica",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "24cffe82cabd30cd0cb9",
          "title": "un admin sí puede",
          "full_title": "configuracion: permiso por tipo de documento > el resto sigue exigiendo configuracion > un admin sí puede",
          "line": 94,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8394010eba61afeda9ba",
          "title": "la matriz de roles sigue reservada a admin, no la abre gestionar_catalogos",
          "full_title": "configuracion: permiso por tipo de documento > el resto sigue exigiendo configuracion > la matriz de roles sigue reservada a admin, no la abre gestionar_catalogos",
          "line": 100,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "87723cbbf7ceec68628e",
          "title": "un documento nuevo desconocido exige configuracion",
          "full_title": "configuracion: permiso por tipo de documento > documentos no enumerados heredan el permiso más restrictivo > un documento nuevo desconocido exige configuracion",
          "line": 108,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7ac896356fe1c5177fa0",
          "title": "un id que sólo contiene el nombre de un catálogo no cuela por prefijo",
          "full_title": "configuracion: permiso por tipo de documento > documentos no enumerados heredan el permiso más restrictivo > un id que sólo contiene el nombre de un catálogo no cuela por prefijo",
          "line": 117,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/rules/contenido-sitio.rules.test.ts",
      "type": "integration",
      "sha256": "2d97cc44d6c41fca5db49bd4f44b996f0a8f673fb844a0b25b56c4739bc1a232",
      "tests": [
        {
          "id": "797d2def10fa23b3942f",
          "title": "el sitio público lee las imágenes sin estar autenticado",
          "full_title": "Storage — contenido-sitio/ se lee sin sesión y solo lo escribe el gestor > el sitio público lee las imágenes sin estar autenticado",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c483bfbef85d7b9fd6b3",
          "title": "la lectura pública no arrastra permiso de escritura ni de borrado",
          "full_title": "Storage — contenido-sitio/ se lee sin sesión y solo lo escribe el gestor > la lectura pública no arrastra permiso de escritura ni de borrado",
          "line": 108,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0c98669cb89202081e25",
          "title": "staff @hixion.mx sube y borra contenido del sitio",
          "full_title": "Storage — contenido-sitio/ se lee sin sesión y solo lo escribe el gestor > staff @hixion.mx sube y borra contenido del sitio",
          "line": 116,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "589b3625be5e5a603205",
          "title": "Gera sube solo mientras conserve rol admin",
          "full_title": "Storage — contenido-sitio/ se lee sin sesión y solo lo escribe el gestor > Gera sube solo mientras conserve rol admin",
          "line": 124,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b1d61d86a2941ad1d794",
          "title": "un admin de otro correo no toca el contenido del sitio",
          "full_title": "Storage — contenido-sitio/ se lee sin sesión y solo lo escribe el gestor > un admin de otro correo no toca el contenido del sitio",
          "line": 133,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0095b2d39341656e3ad0",
          "title": "solo acepta imágenes: rechaza PDF y contenido ejecutable",
          "full_title": "Storage — contenido-sitio/ se lee sin sesión y solo lo escribe el gestor > solo acepta imágenes: rechaza PDF y contenido ejecutable",
          "line": 138,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5b836070d3dd10b3dbda",
          "title": "el PDF de planes se lee sin estar autenticado",
          "full_title": "Storage — comercial/ entrega el PDF de planes sin sesión > el PDF de planes se lee sin estar autenticado",
          "line": 146,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "931769d15f5b57c7f1ca",
          "title": "solo escribe quien administra pagos o configuración",
          "full_title": "Storage — comercial/ entrega el PDF de planes sin sesión > solo escribe quien administra pagos o configuración",
          "line": 153,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "246f5182590e1b6b70a3",
          "title": "aísla escritura y borrado por tenant",
          "full_title": "Storage — comercial/ entrega el PDF de planes sin sesión > aísla escritura y borrado por tenant",
          "line": 165,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "696354242379288e93fb",
          "title": "rechaza archivos que no declaran MIME PDF",
          "full_title": "Storage — comercial/ entrega el PDF de planes sin sesión > rechaza archivos que no declaran MIME PDF",
          "line": 175,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9b4383b40eda8d9e8546",
          "title": "un anónimo sigue sin leer expedientes, fotos de progreso ni chat",
          "full_title": "Storage — abrir la lectura pública no expuso datos clínicos > un anónimo sigue sin leer expedientes, fotos de progreso ni chat",
          "line": 183,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "22e38f1ebd00d8bb7f04",
          "title": "gestionar el sitio no da acceso al expediente de un paciente",
          "full_title": "Storage — abrir la lectura pública no expuso datos clínicos > gestionar el sitio no da acceso al expediente de un paciente",
          "line": 194,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/rules/estudios-corporales.rules.test.ts",
      "type": "integration",
      "sha256": "224b7aefdac9f9224237fe8745407eea390f7ea15a21722692ec6e647575419c",
      "tests": [
        {
          "id": "1f12182d4086ec1be660",
          "title": "staff clinico del tenant lee el estudio",
          "full_title": "Estudios corporales — Firestore > staff clinico del tenant lee el estudio",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cda0a0bd03d40a875ef3",
          "title": "no se filtra a otro tenant, al paciente ni a anonimos",
          "full_title": "Estudios corporales — Firestore > no se filtra a otro tenant, al paciente ni a anonimos",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "11f8e852893fd190f7d8",
          "title": "ningun cliente escribe el estudio: solo el Admin SDK del endpoint",
          "full_title": "Estudios corporales — Firestore > ningun cliente escribe el estudio: solo el Admin SDK del endpoint",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5096b554b6dab7595db5",
          "title": "el PDF hereda el ACL de pacientes/{id}: staff del tenant escribe y lee",
          "full_title": "Estudios corporales — Storage > el PDF hereda el ACL de pacientes/{id}: staff del tenant escribe y lee",
          "line": 97,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8f2277908e843fffa915",
          "title": "bloquea cross-tenant, recepcionista y anonimos",
          "full_title": "Estudios corporales — Storage > bloquea cross-tenant, recepcionista y anonimos",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aa3cfa8ed3423d64a1fe",
          "title": "el paciente lee su propio estudio y no el de otro paciente",
          "full_title": "Estudios corporales — Storage > el paciente lee su propio estudio y no el de otro paciente",
          "line": 112,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f3c9e48e85c081d6d62e",
          "title": "rechaza contenido ejecutable en la ruta de estudios",
          "full_title": "Estudios corporales — Storage > rechaza contenido ejecutable en la ruta de estudios",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/rules/roles.rules.test.ts",
      "type": "integration",
      "sha256": "b91c394f699effe7440f500c21108898653404031906dd2de98846fdd15909c7",
      "tests": [
        {
          "id": "7a633681f086af28e9fe",
          "title": "HIXION: una suspensión canónica corta los datos del portal aunque la sesión siga autenticada",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > HIXION: una suspensión canónica corta los datos del portal aunque la sesión siga autenticada",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dee9b9e1a78b41223470",
          "title": "otros tenants conservan su acceso hasta una migración explícita",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > otros tenants conservan su acceso hasta una migración explícita",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dd0e5d24b1d7356a2837",
          "title": "SI lee su propio documento de pacientes",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > SI lee su propio documento de pacientes",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4135b8d0c12712099a54",
          "title": "NO lee el documento de otro paciente del MISMO tenant",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > NO lee el documento de otro paciente del MISMO tenant",
          "line": 84,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8abaf9b9ab32418eed79",
          "title": "NO lee pacientes de otro tenant",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > NO lee pacientes de otro tenant",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "89f1ed32adbeeeabaac2",
          "title": "SI lee su propio plan nutricional",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > SI lee su propio plan nutricional",
          "line": 94,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e768d82e1b94711c5010",
          "title": "NO lee el plan de otro paciente del mismo tenant",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > NO lee el plan de otro paciente del mismo tenant",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1b411e9aa85687589c88",
          "title": "NO crea ni edita planes nutricionales",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > NO crea ni edita planes nutricionales",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6c8686d2d9212d8bae49",
          "title": "BK-49: lee su override pero NO lo crea, edita ni elimina desde el cliente",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > BK-49: lee su override pero NO lo crea, edita ni elimina desde el cliente",
          "line": 112,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7ccc6646414ee843384e",
          "title": "SI lee su propio plan de pago y NO el de otro paciente",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > SI lee su propio plan de pago y NO el de otro paciente",
          "line": 139,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "282b599372f0a830e0d3",
          "title": "NO escribe en colecciones de pagos",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > NO escribe en colecciones de pagos",
          "line": 145,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ca4af07b6535117f94cb",
          "title": "SI lee y escribe su propio expediente, NO el de otro paciente",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > SI lee y escribe su propio expediente, NO el de otro paciente",
          "line": 151,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "65685afd451b21b52b47",
          "title": "SI lee su cita y NO la cita de otro tenant",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > SI lee su cita y NO la cita de otro tenant",
          "line": 159,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c9bb8bccfcd11a764fe5",
          "title": "NO lee perfiles users de otros usuarios",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > NO lee perfiles users de otros usuarios",
          "line": 165,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "96f7e8375a0224ac3f4c",
          "title": "NO puede cambiar rol/tenantId/pacienteId/status en su propio perfil users",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > NO puede cambiar rol/tenantId/pacienteId/status en su propio perfil users",
          "line": 171,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "32131eb77af6bca2222a",
          "title": "lee su historial fitness pero NO crea ni altera sesiones, sets o PRs directamente",
          "full_title": "Matriz de roles > paciente: solo accede a lo suyo > lee su historial fitness pero NO crea ni altera sesiones, sets o PRs directamente",
          "line": 179,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b01fe301a2b0837b7674",
          "title": "asistente SI lee pacientes de su tenant",
          "full_title": "Matriz de roles > asistente / recepcionista: limites de escritura > asistente SI lee pacientes de su tenant",
          "line": 228,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3467198718eeaaf36718",
          "title": "asistente NO crea planes nutricionales (solo admin/nutriologo)",
          "full_title": "Matriz de roles > asistente / recepcionista: limites de escritura > asistente NO crea planes nutricionales (solo admin/nutriologo)",
          "line": 233,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c9e90014d5b1a14a93b6",
          "title": "asistente NO escribe en planesPago/suscripciones/transacciones (solo admin)",
          "full_title": "Matriz de roles > asistente / recepcionista: limites de escritura > asistente NO escribe en planesPago/suscripciones/transacciones (solo admin)",
          "line": 240,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "170546002a134396b62f",
          "title": "asistente NO elimina pacientes (solo admin)",
          "full_title": "Matriz de roles > asistente / recepcionista: limites de escritura > asistente NO elimina pacientes (solo admin)",
          "line": 247,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "15a91251b4800877034a",
          "title": "recepcionista SI crea citas de su tenant",
          "full_title": "Matriz de roles > asistente / recepcionista: limites de escritura > recepcionista SI crea citas de su tenant",
          "line": 252,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "37acdccbdad1f0e0003a",
          "title": "recepcionista NO crea planes nutricionales",
          "full_title": "Matriz de roles > asistente / recepcionista: limites de escritura > recepcionista NO crea planes nutricionales",
          "line": 259,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5ee0fb02b95e614c01e8",
          "title": "nutriologo SI crea planes nutricionales de su tenant",
          "full_title": "Matriz de roles > asistente / recepcionista: limites de escritura > nutriologo SI crea planes nutricionales de su tenant",
          "line": 266,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0accad30a655831af067",
          "title": "un override del tenant niega pacientes y conserva agenda para asistente",
          "full_title": "Matriz de roles > BK-72: permisos configurables son autoritativos en datos > un override del tenant niega pacientes y conserva agenda para asistente",
          "line": 275,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1cdc305721470e95de4e",
          "title": "un override puede retirar crear planes a un nutriólogo",
          "full_title": "Matriz de roles > BK-72: permisos configurables son autoritativos en datos > un override puede retirar crear planes a un nutriólogo",
          "line": 302,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "123aee815fb93c3ca08b",
          "title": "el mismo actor lee o no lee el mismo documento segun el valor guardado",
          "full_title": "Matriz de roles > matriz configurable: control positivo > el mismo actor lee o no lee el mismo documento segun el valor guardado",
          "line": 346,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0e709a60f4d395dff5dc",
          "title": "la matriz OTORGA por encima del default, no solo recorta",
          "full_title": "Matriz de roles > matriz configurable: control positivo > la matriz OTORGA por encima del default, no solo recorta",
          "line": 365,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "63cb2eaf12e113464112",
          "title": "un rol sin entrada en la matriz guardada pierde el default",
          "full_title": "Matriz de roles > matriz configurable: control positivo > un rol sin entrada en la matriz guardada pierde el default",
          "line": 380,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "531dffa04df46c74513f",
          "title": "la matriz de un tenant no altera los permisos de otro",
          "full_title": "Matriz de roles > matriz configurable: control positivo > la matriz de un tenant no altera los permisos de otro",
          "line": 391,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "756cc2103f750761e5e7",
          "title": "ver_expediente gobierna la lectura del expediente y sus derivados",
          "full_title": "Matriz de roles > los dominios migrados responden a su permiso > expediente clinico > ver_expediente gobierna la lectura del expediente y sus derivados",
          "line": 429,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2b51dc830a6e346dd3a2",
          "title": "ver_pacientes ya no arrastra el expediente",
          "full_title": "Matriz de roles > los dominios migrados responden a su permiso > expediente clinico > ver_pacientes ya no arrastra el expediente",
          "line": 441,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ac999c445f56fd58b160",
          "title": "editar_expediente gobierna la escritura",
          "full_title": "Matriz de roles > los dominios migrados responden a su permiso > expediente clinico > editar_expediente gobierna la escritura",
          "line": 449,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "47adc3f8f5ba48faaf72",
          "title": "ver_fitness gobierna la lectura de rutinas",
          "full_title": "Matriz de roles > los dominios migrados responden a su permiso > fitness > ver_fitness gobierna la lectura de rutinas",
          "line": 459,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "80e38dc3398e3d5bf788",
          "title": "gestionar_fitness gobierna la escritura de rutinas",
          "full_title": "Matriz de roles > los dominios migrados responden a su permiso > fitness > gestionar_fitness gobierna la escritura de rutinas",
          "line": 467,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "06da299c192a8ebe4eb2",
          "title": "gestionar_catalogos gobierna la escritura de ejercicios del tenant",
          "full_title": "Matriz de roles > los dominios migrados responden a su permiso > catalogos > gestionar_catalogos gobierna la escritura de ejercicios del tenant",
          "line": 481,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1558ad6a41785f20949e",
          "title": "gestionar_catalogo_comercial gobierna la escritura de suplementos",
          "full_title": "Matriz de roles > los dominios migrados responden a su permiso > catalogo comercial > gestionar_catalogo_comercial gobierna la escritura de suplementos",
          "line": 498,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c8ebf73786444e5690f7",
          "title": "crear_planes gobierna los kits de suplementación",
          "full_title": "Matriz de roles > los dominios migrados responden a su permiso > catalogo comercial > crear_planes gobierna los kits de suplementación",
          "line": 506,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3acff0d37056f72d9ef3",
          "title": "un rol staff con el resto de catalogos abiertos sigue sin tocarlo",
          "full_title": "Matriz de roles > los dominios migrados responden a su permiso > catalogo comercial > un rol staff con el resto de catalogos abiertos sigue sin tocarlo",
          "line": 515,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9dd182957937b1654de6",
          "title": "gestionar_equipo gobierna los perfiles del equipo",
          "full_title": "Matriz de roles > los dominios migrados responden a su permiso > equipo > gestionar_equipo gobierna los perfiles del equipo",
          "line": 522,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ac209dd339e8b753e890",
          "title": "admin conserva todo aunque la matriz intente recortarle un dominio",
          "full_title": "Matriz de roles > los dominios migrados responden a su permiso > admin conserva todo aunque la matriz intente recortarle un dominio",
          "line": 531,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0cc7f150d9bb9b0c4b57",
          "title": "una matriz SIN rolesStaff no rompe a los roles del sistema",
          "full_title": "Matriz de roles > roles propios del tenant > una matriz SIN rolesStaff no rompe a los roles del sistema",
          "line": 571,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "301185cc33e074f3eb84",
          "title": "una matriz SIN rolesStaff deniega al rol propio, sin romper nada más",
          "full_title": "Matriz de roles > roles propios del tenant > una matriz SIN rolesStaff deniega al rol propio, sin romper nada más",
          "line": 578,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "44238a2ce37513261be3",
          "title": "declarado en rolesStaff, el rol propio opera con sus permisos",
          "full_title": "Matriz de roles > roles propios del tenant > declarado en rolesStaff, el rol propio opera con sus permisos",
          "line": 589,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "391c7bfd0b2d6831ea0d",
          "title": "quitarlo de rolesStaff lo saca del equipo aunque conserve permisos",
          "full_title": "Matriz de roles > roles propios del tenant > quitarlo de rolesStaff lo saca del equipo aunque conserve permisos",
          "line": 599,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8323170c53bf94cb1bbd",
          "title": "un rol propio de otro tenant no cruza",
          "full_title": "Matriz de roles > roles propios del tenant > un rol propio de otro tenant no cruza",
          "line": 616,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6aa5f7f966913ad1e2db",
          "title": "NO puede reescribir la matriz aunque tenga gestionar_roles",
          "full_title": "Matriz de roles > roles propios del tenant > NO puede reescribir la matriz aunque tenga gestionar_roles",
          "line": 621,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "27b4954834cbf55c56f8",
          "title": "un rol propio con `configuracion` sigue guardando el resto de ajustes",
          "full_title": "Matriz de roles > roles propios del tenant > un rol propio con `configuracion` sigue guardando el resto de ajustes",
          "line": 645,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5ded030ee0f7861d5c5c",
          "title": "sin ver_pacientes no lee la ficha del paciente",
          "full_title": "Matriz de roles > comercial se configura como cualquier otro rol > sin ver_pacientes no lee la ficha del paciente",
          "line": 678,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b6b314d1a02db797bb8a",
          "title": "con ver_pacientes SÍ la lee — el punto de toda la matriz",
          "full_title": "Matriz de roles > comercial se configura como cualquier otro rol > con ver_pacientes SÍ la lee — el punto de toda la matriz",
          "line": 683,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a5de95493f87f4363d44",
          "title": "los permisos se pueden dar por separado, sin arrastres",
          "full_title": "Matriz de roles > comercial se configura como cualquier otro rol > los permisos se pueden dar por separado, sin arrastres",
          "line": 690,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7337f8555cb5718d4bbf",
          "title": "ver_pacientes gobierna tambien patientRecipes, planOverrides y el buscador",
          "full_title": "Matriz de roles > comercial se configura como cualquier otro rol > ver_pacientes gobierna tambien patientRecipes, planOverrides y el buscador",
          "line": 698,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f8c06b1699e34c19321e",
          "title": "admin crea y actualiza overrides de su tenant, pero no cruza tenants",
          "full_title": "Matriz de roles > BK-49: planOverrides se escriben solo por staff autorizado > admin crea y actualiza overrides de su tenant, pero no cruza tenants",
          "line": 712,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8aec097744bd3080cb5c",
          "title": "SI lee documentos de cualquier tenant",
          "full_title": "Matriz de roles > superadmin (workspace hixion): bypass de tenant > SI lee documentos de cualquier tenant",
          "line": 730,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a90d1f86e6ab25e84432",
          "title": "SI crea documentos con tenantId de otro tenant (provisioning)",
          "full_title": "Matriz de roles > superadmin (workspace hixion): bypass de tenant > SI crea documentos con tenantId de otro tenant (provisioning)",
          "line": 737,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "397dfae339bfd5748284",
          "title": "SI actualiza y elimina documentos de otros tenants",
          "full_title": "Matriz de roles > superadmin (workspace hixion): bypass de tenant > SI actualiza y elimina documentos de otros tenants",
          "line": 747,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f81f3343177e950b49d8",
          "title": "el rollout HIXION exige comandos server para citas y dinero",
          "full_title": "Matriz de roles > superadmin (workspace hixion): bypass de tenant > el rollout HIXION exige comandos server para citas y dinero",
          "line": 753,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8e5a990f0bedaa8c8e60",
          "title": "ningún cliente puede fabricar pago, contrato o acceso en la ficha",
          "full_title": "Matriz de roles > superadmin (workspace hixion): bypass de tenant > ningún cliente puede fabricar pago, contrato o acceso en la ficha",
          "line": 777,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2be310faaf5db1b75aa8",
          "title": "NO puede fabricar onboardingTokens desde el cliente",
          "full_title": "Matriz de roles > superadmin (workspace hixion): bypass de tenant > NO puede fabricar onboardingTokens desde el cliente",
          "line": 816,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b8eae0c570c83e5c00c3",
          "title": "NO cambia identidad de users desde cliente; la reasociación exige comando auditado",
          "full_title": "Matriz de roles > superadmin (workspace hixion): bypass de tenant > NO cambia identidad de users desde cliente; la reasociación exige comando auditado",
          "line": 827,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "528514ed164de2fa89dc",
          "title": "NO lee documentos de otro tenant",
          "full_title": "Matriz de roles > superadmin fuera de hixion: sin bypass global > NO lee documentos de otro tenant",
          "line": 837,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "65b4e93bf039a82b4c19",
          "title": "SI conserva acceso administrativo dentro de su tenant",
          "full_title": "Matriz de roles > superadmin fuera de hixion: sin bypass global > SI conserva acceso administrativo dentro de su tenant",
          "line": 842,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a07ce152f5916055f9dc",
          "title": "NO lee pacientes, planes, citas, pagos ni users",
          "full_title": "Matriz de roles > usuario no autenticado: rechazado en todo > NO lee pacientes, planes, citas, pagos ni users",
          "line": 850,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1158532ee4dcd7444931",
          "title": "NO lee catalogos aunque sean globales",
          "full_title": "Matriz de roles > usuario no autenticado: rechazado en todo > NO lee catalogos aunque sean globales",
          "line": 859,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f48f3ccc9fb11dacc2ae",
          "title": "NO crea documentos en ninguna coleccion operativa",
          "full_title": "Matriz de roles > usuario no autenticado: rechazado en todo > NO crea documentos en ninguna coleccion operativa",
          "line": 865,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "082a26dbba0d0af8ae86",
          "title": "SI puede hacer get de un onboardingToken (por diseno: el token es el secreto)",
          "full_title": "Matriz de roles > usuario no autenticado: rechazado en todo > SI puede hacer get de un onboardingToken (por diseno: el token es el secreto)",
          "line": 872,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "83d0f6e05aa98b712d2f",
          "title": "NO puede listar onboardingTokens (previene escaneo masivo)",
          "full_title": "Matriz de roles > usuario no autenticado: rechazado en todo > NO puede listar onboardingTokens (previene escaneo masivo)",
          "line": 877,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/rules/routine-occurrences.rules.test.ts",
      "type": "integration",
      "sha256": "51bba0303592edc89e45bbc06bfaef61973abd269e2dee52850fcc3005375fe8",
      "tests": [
        {
          "id": "5090fc3db5593b859a9b",
          "title": "allows the owner and specialist to read, denying other patients and tenants",
          "full_title": "routine occurrences remain server-owned > allows the owner and specialist to read, denying other patients and tenants",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ec53930d0d513f6a2e1c",
          "title": "denies direct writes and access to operations or device tokens",
          "full_title": "routine occurrences remain server-owned > denies direct writes and access to operations or device tokens",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/rules/rutina-drafts.rules.test.ts",
      "type": "integration",
      "sha256": "7f57cd9d4034204b8a29ab8353dba0a78d0d8e3b1edb5b5ff1c6f87f34b3d075",
      "tests": [
        {
          "id": "945400a85fe0bcec94be",
          "title": "permite al coach crear, recuperar, editar y descartar su borrador de %s",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > permite al coach crear, recuperar, editar y descartar su borrador de %s",
          "line": 59,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "634eae4a0672eb5100d8",
          "title": "permite consultar su borrador ausente de %s sin permission-denied",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > permite consultar su borrador ausente de %s sin permission-denied",
          "line": 77,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "04458c30b163450877f7",
          "title": "permite recuperar borradores distintos del mismo autor sin mezclar pacientes",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > permite recuperar borradores distintos del mismo autor sin mezclar pacientes",
          "line": 83,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e78da5ff947f72b8b9ac",
          "title": "aplica gestionar_fitness a un rol staff configurado",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > aplica gestionar_fitness a un rol staff configurado",
          "line": 93,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ef6440d8d1632ea8cc24",
          "title": "deniega al autor cuando la matriz le retira gestionar_fitness",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > deniega al autor cuando la matriz le retira gestionar_fitness",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0ef565558e4356f3c2ea",
          "title": "deniega leer, sobrescribir y borrar el borrador ajeno a $uid",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > deniega leer, sobrescribir y borrar el borrador ajeno a $uid",
          "line": 118,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "3a719739acee1c38ec3c",
          "title": "deniega el borrador del mismo uid si su perfil cambió de tenant",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > deniega el borrador del mismo uid si su perfil cambió de tenant",
          "line": 127,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d6d2593e56fa1b8452d3",
          "title": "deniega crear con $label",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > deniega crear con $label",
          "line": 137,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "9dee6edc220e76f75e8b",
          "title": "conserva $label al actualizar",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > conserva $label al actualizar",
          "line": 148,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "4cb4f14ba4ff8963dcf0",
          "title": "deniega un borrador cuyo userId no corresponde a su id, incluso a quien ocupa el sufijo",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > deniega un borrador cuyo userId no corresponde a su id, incluso a quien ocupa el sufijo",
          "line": 159,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3d37b5df843392690349",
          "title": "deniega crear y leer al $label aunque use su propio uid",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > deniega crear y leer al $label aunque use su propio uid",
          "line": 167,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "7bc4ed049ca80a817ed1",
          "title": "deniega operaciones anónimas y lectura ausente de otro autor",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > deniega operaciones anónimas y lectura ausente de otro autor",
          "line": 179,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2c477239d95c1252060c",
          "title": "no permite enumerar los borradores privados",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > no permite enumerar los borradores privados",
          "line": 189,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a7b7b98fdd3ed43c4287",
          "title": "deniega consultar un borrador ausente para %s",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > deniega consultar un borrador ausente para %s",
          "line": 195,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "ef5d733a4a8bffd9455e",
          "title": "compara literalmente el uid al consultar documentos ausentes",
          "full_title": "rutinaDrafts: autor, permiso y aislamiento > compara literalmente el uid al consultar documentos ausentes",
          "line": 199,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/rules/soporte.rules.test.ts",
      "type": "integration",
      "sha256": "521802bac3f32b998613102a342b2b8dcebb9facfb76f2aa04ac95495f8fdd3f",
      "tests": [
        {
          "id": "bd0f48e4cc06475ab48e",
          "title": "un correo @hixion.mx puede leer el Kanban, pero las mutaciones pasan por la API",
          "full_title": "Firestore — autoridad de tickets de soporte > un correo @hixion.mx puede leer el Kanban, pero las mutaciones pasan por la API",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a17070a561b29a785b58",
          "title": "un correo @hixion.mx inactivo pierde acceso directo al Kanban",
          "full_title": "Firestore — autoridad de tickets de soporte > un correo @hixion.mx inactivo pierde acceso directo al Kanban",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0e2f2f61a3cefb3a76d3",
          "title": "un admin del cliente no puede leer ni modificar tickets directamente",
          "full_title": "Firestore — autoridad de tickets de soporte > un admin del cliente no puede leer ni modificar tickets directamente",
          "line": 75,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b87bfdf3edcb58b6c185",
          "title": "el solicitante conserva la creación, pero no obtiene controles operativos directos",
          "full_title": "Firestore — autoridad de tickets de soporte > el solicitante conserva la creación, pero no obtiene controles operativos directos",
          "line": 82,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/rules/storage.rules.test.ts",
      "type": "integration",
      "sha256": "5ce5df96fbe7c97772bc73b3c780d195ac1269c9adc66fd42c8eff4b7b6382d3",
      "tests": [
        {
          "id": "d6606a1ba15745eb1b18",
          "title": "HIXION: una suspensión corta archivos privados del paciente pero no el acceso de staff",
          "full_title": "Storage — aislamiento por tenant, paciente y conversación > HIXION: una suspensión corta archivos privados del paciente pero no el acceso de staff",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1a3def34da63d1b20fc0",
          "title": "Storage preserva otros tenants hasta su migración explícita",
          "full_title": "Storage — aislamiento por tenant, paciente y conversación > Storage preserva otros tenants hasta su migración explícita",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f648b7044ede6af7cfd1",
          "title": "paciente opera sólo bajo su propio documento patients",
          "full_title": "Storage — aislamiento por tenant, paciente y conversación > paciente opera sólo bajo su propio documento patients",
          "line": 91,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ebe42beb235f66d690a8",
          "title": "staff clínico respeta tenant y recepcionista no abre expediente",
          "full_title": "Storage — aislamiento por tenant, paciente y conversación > staff clínico respeta tenant y recepcionista no abre expediente",
          "line": 102,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "600a296dfdfa4c310fd1",
          "title": "paciente accede sólo a adjuntos de sus conversaciones",
          "full_title": "Storage — aislamiento por tenant, paciente y conversación > paciente accede sólo a adjuntos de sus conversaciones",
          "line": 112,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fa6e0ceea035b475fdef",
          "title": "staff autorizado conserva adjuntos legacy del tenant sin abrirlos cross-tenant",
          "full_title": "Storage — aislamiento por tenant, paciente y conversación > staff autorizado conserva adjuntos legacy del tenant sin abrirlos cross-tenant",
          "line": 125,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "12a60169bc617ea10d26",
          "title": "BK-72: Storage respeta el permiso de chat configurado para el tenant",
          "full_title": "Storage — aislamiento por tenant, paciente y conversación > BK-72: Storage respeta el permiso de chat configurado para el tenant",
          "line": 132,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e9a1692b2529f94a1dbe",
          "title": "rechaza usuarios anónimos en archivos clínicos y chat",
          "full_title": "Storage — aislamiento por tenant, paciente y conversación > rechaza usuarios anónimos en archivos clínicos y chat",
          "line": 151,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "67ce0ccb411b8309d715",
          "title": "acepta documentos de UI y rechaza contenido ejecutable",
          "full_title": "Storage — aislamiento por tenant, paciente y conversación > acepta documentos de UI y rechaza contenido ejecutable",
          "line": 157,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c442725f5d31f0b0b071",
          "title": "fotos de perfil sólo se leen dentro del mismo tenant",
          "full_title": "Storage — aislamiento por tenant, paciente y conversación > fotos de perfil sólo se leen dentro del mismo tenant",
          "line": 168,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/integration/rules/tenancy.rules.test.ts",
      "type": "integration",
      "sha256": "178fb22d5fb88d9ab52e91009ee9fb4df463059474727f5edf95889756e11ab3",
      "tests": [
        {
          "id": "7906e5d1b02ca6840bc2",
          "title": "un perfil inactivo pierde inmediatamente lectura operativa aunque conserve un token",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > un perfil inactivo pierde inmediatamente lectura operativa aunque conserve un token",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "48e308828dd35ec28ce1",
          "title": "$col: staff A SI lee documentos de su tenant",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > lectura > $col: staff A SI lee documentos de su tenant",
          "line": 83,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "bdca6aa973f422fa33ab",
          "title": "$col: staff A NO lee documentos del tenant B",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > lectura > $col: staff A NO lee documentos del tenant B",
          "line": 88,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "e94fbbc707455c83202f",
          "title": "$col: staff no-admin (asistente A) NO lee documentos del tenant B",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > lectura > $col: staff no-admin (asistente A) NO lee documentos del tenant B",
          "line": 93,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "d8b236fcbaac2c3f19ad",
          "title": "$col: admin A SI crea documentos con tenantId propio",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > creacion > $col: admin A SI crea documentos con tenantId propio",
          "line": 103,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "57bdd3b9d9d72afe4a38",
          "title": "$col: admin A NO crea documentos con tenantId del tenant B",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > creacion > $col: admin A NO crea documentos con tenantId del tenant B",
          "line": 108,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "2dc7c30edbab0c460817",
          "title": "$col: admin A NO crea documentos sin tenantId",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > creacion > $col: admin A NO crea documentos sin tenantId",
          "line": 116,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "11c8db7490344f6d47c6",
          "title": "$col: admin A SI actualiza un documento de su tenant sin tocar tenantId",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > actualizacion > $col: admin A SI actualiza un documento de su tenant sin tocar tenantId",
          "line": 123,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "f075f45df4d5e1a42c20",
          "title": "$col: admin A NO actualiza documentos del tenant B",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > actualizacion > $col: admin A NO actualiza documentos del tenant B",
          "line": 131,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "e090ca7d2609aae3a25f",
          "title": "$col: admin A NO puede cambiar el tenantId de un documento (A -> B)",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > actualizacion > $col: admin A NO puede cambiar el tenantId de un documento (A -> B)",
          "line": 140,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "58fae52f4bbbbba203e7",
          "title": "pacientes: admin A NO puede cambiar el tenantId de un paciente (A -> B)",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > actualizacion > pacientes: admin A NO puede cambiar el tenantId de un paciente (A -> B)",
          "line": 149,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "253d69aaea6d95b9122f",
          "title": "conversaciones: admin A NO puede cambiar el tenantId de una conversacion (A -> B)",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > actualizacion > conversaciones: admin A NO puede cambiar el tenantId de una conversacion (A -> B)",
          "line": 154,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d2db09847a272818cdcd",
          "title": "users: admin A NO puede cambiar el tenantId de un usuario de su tenant",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > actualizacion > users: admin A NO puede cambiar el tenantId de un usuario de su tenant",
          "line": 159,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4164cb1508a91af863c7",
          "title": "users: admin A NO puede auto-asignarse rol superadmin",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > actualizacion > users: admin A NO puede auto-asignarse rol superadmin",
          "line": 164,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0ebb6cbecc5011706562",
          "title": "users: admin A SI puede actualizar estado operativo sin tocar identidad",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > actualizacion > users: admin A SI puede actualizar estado operativo sin tocar identidad",
          "line": 169,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "87a4e96e90cad7d1c59c",
          "title": "$sub: staff A SI lee la subcoleccion de su paciente",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > subcolecciones de pacientes (tenant via doc padre) > $sub: staff A SI lee la subcoleccion de su paciente",
          "line": 183,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "2834ff27929da0fa991e",
          "title": "$sub: staff A NO lee la subcoleccion de un paciente del tenant B",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > subcolecciones de pacientes (tenant via doc padre) > $sub: staff A NO lee la subcoleccion de un paciente del tenant B",
          "line": 188,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "9ef7384af6ca3e87b47b",
          "title": "$sub: staff A NO escribe en la subcoleccion de un paciente del tenant B",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > subcolecciones de pacientes (tenant via doc padre) > $sub: staff A NO escribe en la subcoleccion de un paciente del tenant B",
          "line": 196,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "ff4ef15ec839be0aaf1c",
          "title": "consultas: el cliente NO crea consultas huérfanas",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > subcolecciones de pacientes (tenant via doc padre) > consultas: el cliente NO crea consultas huérfanas",
          "line": 204,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "babc24cfa6c75e51cca7",
          "title": "pacientes: staff A NO puede listar pacientes del tenant B",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > listados (queries) > pacientes: staff A NO puede listar pacientes del tenant B",
          "line": 213,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fa8fe398636556c2070e",
          "title": "pacientes: staff A SI puede listar pacientes de su tenant",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > listados (queries) > pacientes: staff A SI puede listar pacientes de su tenant",
          "line": 218,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6cd74f6ce9ab69ecff73",
          "title": "patientCheckinSummaries: staff sólo lista resúmenes de su tenant",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > listados (queries) > patientCheckinSummaries: staff sólo lista resúmenes de su tenant",
          "line": 223,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "edfa991223499450433e",
          "title": "patientCheckinSummaries: paciente no puede leer ni escribir proyecciones",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > listados (queries) > patientCheckinSummaries: paciente no puede leer ni escribir proyecciones",
          "line": 229,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fbc9a565ff2db7673871",
          "title": "patientCheckinSummaries: staff no puede falsificar proyecciones",
          "full_title": "Aislamiento multi-tenant (staff del tenant A) > listados (queries) > patientCheckinSummaries: staff no puede falsificar proyecciones",
          "line": 235,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/api/fitness-rest-task.test.ts",
      "type": "unit",
      "sha256": "0d58996330df32cf538736382b55035ba4783c00371ced55e48bb91720b0d1f7",
      "tests": [
        {
          "id": "347f09e7554a4178203f",
          "title": "remains disabled until infrastructure is configured",
          "full_title": "rest task authentication > remains disabled until infrastructure is configured",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1bd9b546a98aaa6bd54d",
          "title": "rejects missing and invalid OIDC tokens",
          "full_title": "rest task authentication > rejects missing and invalid OIDC tokens",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "811fc5f9f7df8a72e36d",
          "title": "rejects another verified service account",
          "full_title": "rest task authentication > rejects another verified service account",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6c182dfe09d4983dd94e",
          "title": "checks the exact audience and dispatches an authenticated request",
          "full_title": "rest task authentication > checks the exact audience and dispatches an authenticated request",
          "line": 62,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/api/portal-plan-mutations-offline.test.ts",
      "type": "unit",
      "sha256": "77ee53a47331bb5d954c409a7aa6c1c69e9293bd495072595e3a23a67c63c711",
      "tests": [
        {
          "id": "619133c88ba951f77626",
          "title": "rechaza un ajuste offline de una versión antigua antes de escribir",
          "full_title": "rechaza un ajuste offline de una versión antigua antes de escribir",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ccc2317dc56392e48383",
          "title": "acepta la versión vigente y reconoce un reintento ya aplicado",
          "full_title": "acepta la versión vigente y reconoce un reintento ya aplicado",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/agenda-calendar-dedupe.test.ts",
      "type": "unit",
      "sha256": "6bf90b2435b445c36f25ed96aeb3d3552bd86dd64b84f2bb870724d2364d02a5",
      "tests": [
        {
          "id": "c9fdb059e47105d19d91",
          "title": "oculta tanto el evento canónico como el anterior pendiente de limpiar",
          "full_title": "deduplicación de Agenda después de mover un evento > oculta tanto el evento canónico como el anterior pendiente de limpiar",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/auth-profile.test.ts",
      "type": "unit",
      "sha256": "8f8ea0448ef5dc914a7a421ae32852700125c693f455ac48bc1f524ce349029c",
      "tests": [
        {
          "id": "504fe8c1fa2d9bdcdafd",
          "title": "devuelve el perfil cuando el vínculo del paciente es canónico",
          "full_title": "GET /api/auth/profile > devuelve el perfil cuando el vínculo del paciente es canónico",
          "line": 68,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "429baea98e655d843bf1",
          "title": "distingue perfil inexistente y cuenta inactiva",
          "full_title": "GET /api/auth/profile > distingue perfil inexistente y cuenta inactiva",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f7f3170b46e2b5be400a",
          "title": "rechaza vínculos incompletos con código estable",
          "full_title": "GET /api/auth/profile > rechaza vínculos incompletos con código estable",
          "line": 86,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "f531450c2ee9fa76edb2",
          "title": "rechaza un expediente de otro tenant",
          "full_title": "GET /api/auth/profile > rechaza un expediente de otro tenant",
          "line": 97,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "664670117d5c70564e9a",
          "title": "carga un perfil staff con un rol propio configurado por el tenant",
          "full_title": "GET /api/auth/profile > carga un perfil staff con un rol propio configurado por el tenant",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/calendar-conciliacion.test.ts",
      "type": "unit",
      "sha256": "41833a21567e4a7f8a8f45f135e4419a78cea8fbde4c3661de6be7dca3b253d6",
      "tests": [
        {
          "id": "b6dab78b690b23ac8d47",
          "title": "guarda el vínculo y marca el evento en Google",
          "full_title": "POST /api/calendar/conciliacion > guarda el vínculo y marca el evento en Google",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "68b088c3731d57a217d1",
          "title": "conserva el vínculo aunque Google rechace la escritura",
          "full_title": "POST /api/calendar/conciliacion > conserva el vínculo aunque Google rechace la escritura",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "db588e07044ec4bad933",
          "title": "ignorar no toca el evento en Google",
          "full_title": "POST /api/calendar/conciliacion > ignorar no toca el evento en Google",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1575ef027282f279a8b7",
          "title": "rechaza vincular a un paciente de otro espacio de trabajo",
          "full_title": "POST /api/calendar/conciliacion > rechaza vincular a un paciente de otro espacio de trabajo",
          "line": 110,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6cf6f8d2a56cff68bf81",
          "title": "rechaza vincular a un paciente archivado",
          "full_title": "POST /api/calendar/conciliacion > rechaza vincular a un paciente archivado",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "616fed121cb924c1c930",
          "title": "rechaza vincular sin paciente",
          "full_title": "POST /api/calendar/conciliacion > rechaza vincular sin paciente",
          "line": 134,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f8e15b3637e496ccea21",
          "title": "rechaza una acción desconocida",
          "full_title": "POST /api/calendar/conciliacion > rechaza una acción desconocida",
          "line": 141,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7a5a6acd0d1944e7631f",
          "title": "exige permiso de gestionar agenda",
          "full_title": "POST /api/calendar/conciliacion > exige permiso de gestionar agenda",
          "line": 148,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d83bd3cdfaf340f31c0d",
          "title": "es estable para el mismo evento",
          "full_title": "conciliacionDocId > es estable para el mismo evento",
          "line": 161,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aee54f18aefc254ad3af",
          "title": "no colisiona entre tenants ni entre calendarios",
          "full_title": "conciliacionDocId > no colisiona entre tenants ni entre calendarios",
          "line": 165,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f5dee75848ebb3cf5343",
          "title": "escapa las barras, que Firestore no admite en un id de documento",
          "full_title": "conciliacionDocId > escapa las barras, que Firestore no admite en un id de documento",
          "line": 170,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/calendar-events-vinculo.test.ts",
      "type": "unit",
      "sha256": "e970cb4bfbbcbf2297b00cf82639415bd24c90d03b12c830437828e9709234f0",
      "tests": [
        {
          "id": "3ef109f5f7b80e42af65",
          "title": "prefiere el pacienteId declarado en extendedProperties",
          "full_title": "mapGoogleEventToCita — resolución de identidad > prefiere el pacienteId declarado en extendedProperties",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "52934b304f0c5379ec57",
          "title": "ignora un pacienteId que no existe o es de otro tenant y cae al correo",
          "full_title": "mapGoogleEventToCita — resolución de identidad > ignora un pacienteId que no existe o es de otro tenant y cae al correo",
          "line": 76,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "23cab56d79b0df8eb11a",
          "title": "resuelve por correo del invitado, normalizando mayúsculas y espacios",
          "full_title": "mapGoogleEventToCita — resolución de identidad > resuelve por correo del invitado, normalizando mayúsculas y espacios",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5af16efd82afbbc62749",
          "title": "no adivina un paciente cuando varios invitados coinciden con expedientes distintos",
          "full_title": "mapGoogleEventToCita — resolución de identidad > no adivina un paciente cuando varios invitados coinciden con expedientes distintos",
          "line": 100,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b3135a4d83ced53523a5",
          "title": "puede reconocer al único paciente aunque Google incluya otro invitado sin expediente",
          "full_title": "mapGoogleEventToCita — resolución de identidad > puede reconocer al único paciente aunque Google incluya otro invitado sin expediente",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9d91223d43f4aa8781c6",
          "title": "descarta al dueño del calendario y las salas como candidatos",
          "full_title": "mapGoogleEventToCita — resolución de identidad > descarta al dueño del calendario y las salas como candidatos",
          "line": 131,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1b9690ab346c25fe2c4e",
          "title": "marca origen 'titulo' cuando solo coincide el patrón que escribe la app",
          "full_title": "mapGoogleEventToCita — resolución de identidad > marca origen 'titulo' cuando solo coincide el patrón que escribe la app",
          "line": 147,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "841c0150ceb329439663",
          "title": "deja pacienteId vacío cuando no hay ninguna señal, sin inventar un id sintético",
          "full_title": "mapGoogleEventToCita — resolución de identidad > deja pacienteId vacío cuando no hay ninguna señal, sin inventar un id sintético",
          "line": 156,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3fe4f931aba22a9aad9b",
          "title": "descarta eventos sin hora de inicio o fin",
          "full_title": "mapGoogleEventToCita — resolución de identidad > descarta eventos sin hora de inicio o fin",
          "line": 165,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8194c724702045cb1485",
          "title": "conserva los datos del evento externo al vincular",
          "full_title": "mapGoogleEventToCita — resolución de identidad > conserva los datos del evento externo al vincular",
          "line": 170,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fa1c4b57c20079fd2a22",
          "title": "lee la marca sin perder la identificación del paciente por el título",
          "full_title": "mapGoogleEventToCita — marca de confirmación > lee la marca sin perder la identificación del paciente por el título",
          "line": 190,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0669d10172e8f02b735c",
          "title": "no arrastra el glifo al nombre de un evento sin vínculo",
          "full_title": "mapGoogleEventToCita — marca de confirmación > no arrastra el glifo al nombre de un evento sin vínculo",
          "line": 202,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c64d11f6fd9ee2323888",
          "title": "omite el campo cuando la clínica no marcó el evento",
          "full_title": "mapGoogleEventToCita — marca de confirmación > omite el campo cuando la clínica no marcó el evento",
          "line": 209,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/captacion-checkout-discount.test.ts",
      "type": "unit",
      "sha256": "35a645f25ac213fbc970d60df74908bed5ead586adc28bd8e18b9a4eb6fad75c",
      "tests": [
        {
          "id": "c817d99ba6984165fa0d",
          "title": "aplica 5% en servidor y conserva el monto original para auditoría",
          "full_title": "checkout público con pago completo > aplica 5% en servidor y conserva el monto original para auditoría",
          "line": 148,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c02bd4427fe5d6d1fe16",
          "title": "valida cuenta y metadata canónicas antes de cobrar en HIXION",
          "full_title": "checkout público con pago completo > valida cuenta y metadata canónicas antes de cobrar en HIXION",
          "line": 193,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0befa5f214105c8ba0c9",
          "title": "impide cobrar VIP incluso si el cliente fuerza el checkout",
          "full_title": "checkout público con pago completo > impide cobrar VIP incluso si el cliente fuerza el checkout",
          "line": 239,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/captacion-documentos.test.ts",
      "type": "unit",
      "sha256": "602a6c51e25623c7bfcba1c16579109d0a38c883e03efc9f20226464db2a49c1",
      "tests": [
        {
          "id": "0c829644d7f46e83ed1a",
          "title": "rechaza MIME y firma inválidos sin alterar la versión vigente",
          "full_title": "/api/configuracion/captacion/documentos > rechaza MIME y firma inválidos sin alterar la versión vigente",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "567b28c79650277f52cd",
          "title": "sube bajo el tenant de la sesión y activa metadatos antes de borrar la versión previa",
          "full_title": "/api/configuracion/captacion/documentos > sube bajo el tenant de la sesión y activa metadatos antes de borrar la versión previa",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dc5505038b430c59c307",
          "title": "desactiva la liga antes de limpiar el archivo retirado",
          "full_title": "/api/configuracion/captacion/documentos > desactiva la liga antes de limpiar el archivo retirado",
          "line": 101,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/captacion-pdf-link.test.ts",
      "type": "unit",
      "sha256": "3e67d7ed27d7bc0ac044d42ab2503ff8500064af60c06070938a3008fb5500a5",
      "tests": [
        {
          "id": "8843b24f10a52797982a",
          "title": "redirige la liga estable al archivo vigente",
          "full_title": "GET /d/[tenantSlug]/[documentKey] > redirige la liga estable al archivo vigente",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "03bcb3562460c89e9253",
          "title": "queda no disponible al retirar el archivo",
          "full_title": "GET /d/[tenantSlug]/[documentKey] > queda no disponible al retirar el archivo",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "83fac83bf87916e1f5b1",
          "title": "rechaza tenants y claves fuera del contrato",
          "full_title": "GET /d/[tenantSlug]/[documentKey] > rechaza tenants y claves fuera del contrato",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/captacion-route.test.ts",
      "type": "unit",
      "sha256": "ea62e90c6d43e03addd495857ff037b2d13bd39314f97eace8bfd525acc5f088",
      "tests": [
        {
          "id": "df310ea6122f5c2b92dd",
          "title": "publica la página elegida del PDF de planes sin exponer la guía privada",
          "full_title": "GET /api/captacion/[tenantSlug] > publica la página elegida del PDF de planes sin exponer la guía privada",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "55fc0bcac5398f2949e2",
          "title": "envía notificación interna y correo cuando el lead es nuevo",
          "full_title": "POST /api/captacion/[tenantSlug] > envía notificación interna y correo cuando el lead es nuevo",
          "line": 139,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d8305bc25eaf4e766bce",
          "title": "reintenta el correo pendiente de un lead existente sin duplicar la notificación interna",
          "full_title": "POST /api/captacion/[tenantSlug] > reintenta el correo pendiente de un lead existente sin duplicar la notificación interna",
          "line": 170,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8ae9abfe516852bf048f",
          "title": "no duplica ninguna alerta cuando ambas ya fueron procesadas",
          "full_title": "POST /api/captacion/[tenantSlug] > no duplica ninguna alerta cuando ambas ya fueron procesadas",
          "line": 187,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "02137c30666814880392",
          "title": "conserva el lead y deja el correo pendiente si Resend falla",
          "full_title": "POST /api/captacion/[tenantSlug] > conserva el lead y deja el correo pendiente si Resend falla",
          "line": 204,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6255f8ef5b5b6521fc76",
          "title": "Quiero más información registra el lead, avisa por correo y abre WhatsApp con los datos",
          "full_title": "POST /api/captacion/[tenantSlug] > Quiero más información registra el lead, avisa por correo y abre WhatsApp con los datos",
          "line": 221,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/chat-notify.test.ts",
      "type": "unit",
      "sha256": "0beb97825d1411de982c9cbcfbf1a100f72733e91e793db7a1173b109102e24e",
      "tests": [
        {
          "id": "595d8ad3059c3d301ca4",
          "title": "rechaza una sesión ausente",
          "full_title": "POST /api/chat/notify > rechaza una sesión ausente",
          "line": 122,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8e9deb341e401634051c",
          "title": "rechaza una conversación de otro tenant",
          "full_title": "POST /api/chat/notify > rechaza una conversación de otro tenant",
          "line": 133,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "60280a74cb2cf65776cc",
          "title": "deriva contenido y destinatario del mensaje persistido, no del body",
          "full_title": "POST /api/chat/notify > deriva contenido y destinatario del mensaje persistido, no del body",
          "line": 143,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "056cb08093f09a36a14e",
          "title": "rechaza notificar un hilo legacy o paralelo antes de reconciliarlo",
          "full_title": "POST /api/chat/notify > rechaza notificar un hilo legacy o paralelo antes de reconciliarlo",
          "line": 161,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4f5b02f25097127e3091",
          "title": "rechaza que un paciente notifique un mensaje atribuido a staff",
          "full_title": "POST /api/chat/notify > rechaza que un paciente notifique un mensaje atribuido a staff",
          "line": 169,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ef727caa92bbc40f7fa9",
          "title": "rechaza que una sesión notifique el mensaje de otro usuario",
          "full_title": "POST /api/chat/notify > rechaza que una sesión notifique el mensaje de otro usuario",
          "line": 178,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1222341d70f9d45dcbba",
          "title": "staff autorizado notifica sólo a usuarios portal vinculados al paciente",
          "full_title": "POST /api/chat/notify > staff autorizado notifica sólo a usuarios portal vinculados al paciente",
          "line": 187,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "500b5b84648ef9701579",
          "title": "asistente conserva el envío autorizado sin asumir la identidad del asignado",
          "full_title": "POST /api/chat/notify > asistente conserva el envío autorizado sin asumir la identidad del asignado",
          "line": 206,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "991ea130e285ef406cba",
          "title": "rechaza roles staff sin acceso al chat",
          "full_title": "POST /api/chat/notify > rechaza roles staff sin acceso al chat",
          "line": 220,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/commercial-transition-route.test.ts",
      "type": "unit",
      "sha256": "56343b26ee9320c593ca07684c5edd26dd1250899633354f8a5f4abfe0f4411b",
      "tests": [
        {
          "id": "577128abd9bbbd1cfc6b",
          "title": "falla cerrado fuera del rollout",
          "full_title": "POST transición comercial > falla cerrado fuera del rollout",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8ff17852da4f983578e8",
          "title": "ver el pipeline no autoriza moverlo",
          "full_title": "POST transición comercial > ver el pipeline no autoriza moverlo",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "44ababcfe4e187241864",
          "title": "gestionar_pipeline autoriza sólo orígenes interactivos",
          "full_title": "POST transición comercial > gestionar_pipeline autoriza sólo orígenes interactivos",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7418dae0504b15d4a772",
          "title": "separa acceso de permisos financieros",
          "full_title": "POST transición comercial > separa acceso de permisos financieros",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/cron-billing-enforcement.test.ts",
      "type": "unit",
      "sha256": "6bdbfb821e034781b73c4787ab5dbba84a6c07d5be5d4c81c14d4ee816c472ef",
      "tests": [
        {
          "id": "a827efde0e47c6d00565",
          "title": "mantiene Gera sin cambios de acceso, contrato o parcialidad aunque llegue a +5",
          "full_title": "enforcement del cron de cobranza > mantiene Gera sin cambios de acceso, contrato o parcialidad aunque llegue a +5",
          "line": 91,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "25bba341a0a68a502f56",
          "title": "al vencer en HIXION marca deuda y contrato en revision sin suspender acceso",
          "full_title": "enforcement del cron de cobranza > al vencer en HIXION marca deuda y contrato en revision sin suspender acceso",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c13ff4552f70041c2083",
          "title": "a +5 en HIXION agrega suspension de acceso revalidada",
          "full_title": "enforcement del cron de cobranza > a +5 en HIXION agrega suspension de acceso revalidada",
          "line": 116,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fcdd784684f47765e39d",
          "title": "aplica el bloqueo financiero aunque falle la notificación y deja el aviso reintentable",
          "full_title": "enforcement del cron de cobranza > aplica el bloqueo financiero aunque falle la notificación y deja el aviso reintentable",
          "line": 126,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f605a28f2a5679963cbc",
          "title": "marca applied únicamente cuando todas las citas terminan sin fallos",
          "full_title": "reintento del bloqueo financiero de citas > marca applied únicamente cuando todas las citas terminan sin fallos",
          "line": 153,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a0713ed7bffbb7946f46",
          "title": "no marca applied si el helper reporta un resultado parcial y deja el claim reintentable",
          "full_title": "reintento del bloqueo financiero de citas > no marca applied si el helper reporta un resultado parcial y deja el claim reintentable",
          "line": 177,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5d3b14a7d9eda31bca46",
          "title": "conserva pending cuando el helper rechaza el lote",
          "full_title": "reintento del bloqueo financiero de citas > conserva pending cuando el helper rechaza el lote",
          "line": 203,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/cron-billing-reminders.test.ts",
      "type": "unit",
      "sha256": "bc2f2e24c6ec504a800858267ac20559918c3dbf017fc5269ef4d88b7492f1c7",
      "tests": [
        {
          "id": "ff55af10fd594813ec69",
          "title": "mapea %s al hito %s/%s por dia calendario UTC",
          "full_title": "cron de cobranza por parcialidades > mapea %s al hito %s/%s por dia calendario UTC",
          "line": 34,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "6ec6f829831f530fda20",
          "title": "no dispara alertas fuera de los hitos ni con fechas invalidas",
          "full_title": "cron de cobranza por parcialidades > no dispara alertas fuera de los hitos ni con fechas invalidas",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f2f3d7813543452049df",
          "title": "flaggea citas HIXION desde el vencimiento y mantiene Gera sin el rollout",
          "full_title": "cron de cobranza por parcialidades > flaggea citas HIXION desde el vencimiento y mantiene Gera sin el rollout",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/enviar-acceso-guide.test.ts",
      "type": "unit",
      "sha256": "8f0af1d7ea8b8835a27942b331c74d3bd5ffe41e6bd69a3d0b237baf5fe889ce",
      "tests": [
        {
          "id": "a8051d692f7cfa69b990",
          "title": "muestra el nombre y la liga estable en la previsualización",
          "full_title": "/api/enviar-acceso — guía por tenant > muestra el nombre y la liga estable en la previsualización",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "292a1335d27d05ec9229",
          "title": "conserva el envío legacy de Gera sin ejecutar pago, contrato o transición v2",
          "full_title": "/api/enviar-acceso — guía por tenant > conserva el envío legacy de Gera sin ejecutar pago, contrato o transición v2",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bfada1164d797ad05a67",
          "title": "mantiene gestionar_acceso como permiso obligatorio también fuera de HIXION",
          "full_title": "/api/enviar-acceso — guía por tenant > mantiene gestionar_acceso como permiso obligatorio también fuera de HIXION",
          "line": 101,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ff828836fde8e6f20095",
          "title": "proyecta el acceso en otro workspace sin aplicarle la puerta de elegibilidad",
          "full_title": "/api/enviar-acceso — guía por tenant > proyecta el acceso en otro workspace sin aplicarle la puerta de elegibilidad",
          "line": 113,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c47346e8cbb02865231f",
          "title": "no proyecta nada si el workspace no lleva estado comercial",
          "full_title": "/api/enviar-acceso — guía por tenant > no proyecta nada si el workspace no lleva estado comercial",
          "line": 128,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7f309847cb57cee7c0ec",
          "title": "responde éxito aunque la proyección del estado comercial falle",
          "full_title": "/api/enviar-acceso — guía por tenant > responde éxito aunque la proyección del estado comercial falle",
          "line": 138,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/estado-stripe.test.ts",
      "type": "unit",
      "sha256": "c6958334364edb43143af0140c92415812d48c66f47a387476cabbb30fd84b72",
      "tests": [
        {
          "id": "2ea7a5bd0917cca29182",
          "title": "marca alineado cuando el precio está activo y coincide",
          "full_title": "GET /api/planes-servicio/estado-stripe > marca alineado cuando el precio está activo y coincide",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "216b9c607ee9b3fe2383",
          "title": "detecta un precio archivado desde el dashboard",
          "full_title": "GET /api/planes-servicio/estado-stripe > detecta un precio archivado desde el dashboard",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "963444bcdf2938a7273a",
          "title": "detecta que Stripe cobraría otro importe",
          "full_title": "GET /api/planes-servicio/estado-stripe > detecta que Stripe cobraría otro importe",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "22d04e0f08f098a1f097",
          "title": "trata un precio inexistente como desalineado, no como error",
          "full_title": "GET /api/planes-servicio/estado-stripe > trata un precio inexistente como desalineado, no como error",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b4f89a56927f53f6f09e",
          "title": "marca sin_precio y no llama a Stripe cuando el plan no tiene precio",
          "full_title": "GET /api/planes-servicio/estado-stripe > marca sin_precio y no llama a Stripe cuando el plan no tiene precio",
          "line": 101,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "56899cdb82d1ca0b03e0",
          "title": "ignora los planes archivados",
          "full_title": "GET /api/planes-servicio/estado-stripe > ignora los planes archivados",
          "line": 110,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9adf634163e4a55fc49e",
          "title": "no confunde céntimos por redondeo de coma flotante",
          "full_title": "GET /api/planes-servicio/estado-stripe > no confunde céntimos por redondeo de coma flotante",
          "line": 125,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c31eb46fc121bfa7842a",
          "title": "exige permiso para ver pagos",
          "full_title": "GET /api/planes-servicio/estado-stripe > exige permiso para ver pagos",
          "line": 136,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/pagos-manual.test.ts",
      "type": "unit",
      "sha256": "d586a2bc828022bb7013ede5ba40190e93d72451c2c3373f036e4641ea6739bb",
      "tests": [
        {
          "id": "6291ddd80fa90068abcd",
          "title": "registra efectivo con recibo, nota y actor del servidor antes de activar",
          "full_title": "POST /api/pagos/manual > registra efectivo con recibo, nota y actor del servidor antes de activar",
          "line": 142,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9ce267c7e68d1534fcf0",
          "title": "deriva el monto exclusivamente del catalogo aunque el cliente mande otro",
          "full_title": "POST /api/pagos/manual > deriva el monto exclusivamente del catalogo aunque el cliente mande otro",
          "line": 161,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1fcb305a36c14bb29688",
          "title": "registra una parcialidad existente con su importe canonico sin crear otro plan",
          "full_title": "POST /api/pagos/manual > registra una parcialidad existente con su importe canonico sin crear otro plan",
          "line": 174,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a2e628b73d0e0531674b",
          "title": "exige clave de idempotencia y no crea obligaciones sin ella",
          "full_title": "POST /api/pagos/manual > exige clave de idempotencia y no crea obligaciones sin ella",
          "line": 204,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a7a1add289e870ae4d63",
          "title": "rechaza la misma clave con un payload distinto antes de registrar dinero",
          "full_title": "POST /api/pagos/manual > rechaza la misma clave con un payload distinto antes de registrar dinero",
          "line": 211,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d724cdef27bba8edbe9b",
          "title": "deja pending y responde retryable cuando alguna cita no puede reactivarse",
          "full_title": "POST /api/pagos/manual > deja pending y responde retryable cuando alguna cita no puede reactivarse",
          "line": 220,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8c2370eb0cc2630f4b85",
          "title": "reanuda con la misma key una parcialidad ya pagada sin duplicar pago ni contrato",
          "full_title": "POST /api/pagos/manual > reanuda con la misma key una parcialidad ya pagada sin duplicar pago ni contrato",
          "line": 258,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1c8f0f87a88c4e617c2c",
          "title": "mantiene 409 si otra key intenta cobrar una parcialidad ya pagada",
          "full_title": "POST /api/pagos/manual > mantiene 409 si otra key intenta cobrar una parcialidad ya pagada",
          "line": 338,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "24d2ddbbfacce9c63dec",
          "title": "ya no permite activar el contrato sin registrar dinero",
          "full_title": "POST /api/pagos/manual > ya no permite activar el contrato sin registrar dinero",
          "line": 366,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8744005e9bfd1ebb22d5",
          "title": "exige comprobante para %s",
          "full_title": "POST /api/pagos/manual > exige comprobante para %s",
          "line": 374,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "7da9c9aedeb2bd66f577",
          "title": "vincula el comprobante a la transaccion y lo marca consumido",
          "full_title": "POST /api/pagos/manual > vincula el comprobante a la transaccion y lo marca consumido",
          "line": 381,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4090d7212d0c1e6009ef",
          "title": "rechaza comprobante de otro actor, paciente o tenant",
          "full_title": "POST /api/pagos/manual > rechaza comprobante de otro actor, paciente o tenant",
          "line": 398,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f44e01c90a1911ed7f86",
          "title": "rechaza efectivo sin recibo o sin nota",
          "full_title": "POST /api/pagos/manual > rechaza efectivo sin recibo o sin nota",
          "line": 406,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ad5f3c32269561182891",
          "title": "respeta las parcialidades permitidas por el plan",
          "full_title": "POST /api/pagos/manual > respeta las parcialidades permitidas por el plan",
          "line": 415,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f836c07571e4b05e9bf8",
          "title": "rechaza paciente ajeno y plan inactivo",
          "full_title": "POST /api/pagos/manual > rechaza paciente ajeno y plan inactivo",
          "line": 427,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "83bbd796240f73d7cb81",
          "title": "no crea nada sin permiso de gestionar pagos",
          "full_title": "POST /api/pagos/manual > no crea nada sin permiso de gestionar pagos",
          "line": 438,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/planes-servicio-crud.test.ts",
      "type": "unit",
      "sha256": "a3a20c5c40688157e62e488086189799541197b4e81de95a3588e8d86c8ebf46",
      "tests": [
        {
          "id": "448ce7ddf90b9b7f8dd1",
          "title": "crea el plan y su producto en Stripe, enlazados",
          "full_title": "POST /api/planes-servicio > crea el plan y su producto en Stripe, enlazados",
          "line": 94,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "230ea039d780ef945f49",
          "title": "no escribe en Firestore si Stripe falla",
          "full_title": "POST /api/planes-servicio > no escribe en Firestore si Stripe falla",
          "line": 117,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9a29213534ae61fafb36",
          "title": "rechaza duplicar una variante que ya existe",
          "full_title": "POST /api/planes-servicio > rechaza duplicar una variante que ya existe",
          "line": 127,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "51ce0f6f200c38a11bb0",
          "title": "permite recrear una variante cuyo plan está archivado",
          "full_title": "POST /api/planes-servicio > permite recrear una variante cuyo plan está archivado",
          "line": 138,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a0465ec809112d0290a2",
          "title": "valida paquete, modalidad y plazo",
          "full_title": "POST /api/planes-servicio > valida paquete, modalidad y plazo",
          "line": 144,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6145e197a3ca5d654dd1",
          "title": "exige nombre, precio y citas utilizables",
          "full_title": "POST /api/planes-servicio > exige nombre, precio y citas utilizables",
          "line": 151,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d46c9ced0e9b6ef6910a",
          "title": "acepta un paquete nuevo que no es de 2026",
          "full_title": "POST /api/planes-servicio > acepta un paquete nuevo que no es de 2026",
          "line": 157,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "00f2e383bdc1ff9a933e",
          "title": "respeta limitedCapacity del cuerpo en un paquete que no es VIP",
          "full_title": "POST /api/planes-servicio > respeta limitedCapacity del cuerpo en un paquete que no es VIP",
          "line": 163,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "82d09499b7930fb71cc9",
          "title": "marca cupos limitados por defecto solo en VIP",
          "full_title": "POST /api/planes-servicio > marca cupos limitados por defecto solo en VIP",
          "line": 168,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ef409310dc5d2af52cc7",
          "title": "archiva sin borrar y lo retira del sitio",
          "full_title": "DELETE /api/planes-servicio > archiva sin borrar y lo retira del sitio",
          "line": 181,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "37e8be3bb1e72aa61934",
          "title": "informa de las contrataciones vivas pero no bloquea",
          "full_title": "DELETE /api/planes-servicio > informa de las contrataciones vivas pero no bloquea",
          "line": 193,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "72185b216e01ca97e27b",
          "title": "no archiva un plan de otro tenant",
          "full_title": "DELETE /api/planes-servicio > no archiva un plan de otro tenant",
          "line": 206,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "509f2c349e5fa83ce7ac",
          "title": "devuelve 404 si el plan no existe",
          "full_title": "DELETE /api/planes-servicio > devuelve 404 si el plan no existe",
          "line": 213,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6f9caf8796ec26b30dd5",
          "title": "exige el id del plan",
          "full_title": "DELETE /api/planes-servicio > exige el id del plan",
          "line": 219,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "be9edb559b6a6204a974",
          "title": "escribe la visibilidad en todas las variantes del paquete",
          "full_title": "PATCH /api/planes-servicio > escribe la visibilidad en todas las variantes del paquete",
          "line": 236,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f77fddc090a40217e60d",
          "title": "no toca variantes archivadas",
          "full_title": "PATCH /api/planes-servicio > no toca variantes archivadas",
          "line": 259,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "991dff1d9f4597de5c80",
          "title": "devuelve 404 si el paquete no tiene variantes vigentes",
          "full_title": "PATCH /api/planes-servicio > devuelve 404 si el paquete no tiene variantes vigentes",
          "line": 276,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/site-content.test.ts",
      "type": "unit",
      "sha256": "3e9f6f938d47873b0ceb6f60628f7af3e79ae9cf7dbefe83da4af8f842f9344a",
      "tests": [
        {
          "id": "297931aa4ef3ffc604f2",
          "title": "publica un testimonio antiguo sin campos multimedia",
          "full_title": "GET /api/site-content · testimonios > publica un testimonio antiguo sin campos multimedia",
          "line": 63,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4465e00c2beaeb981898",
          "title": "emite galeria, comparaciones, metricas y serie de video",
          "full_title": "GET /api/site-content · testimonios > emite galeria, comparaciones, metricas y serie de video",
          "line": 71,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b9fd99dfd5560b92d806",
          "title": "conserva la metrica sin valor: el hueco es el dato",
          "full_title": "GET /api/site-content · testimonios > conserva la metrica sin valor: el hueco es el dato",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c72afafd65d713d2a1d9",
          "title": "no filtra campos operativos aunque esten en el documento",
          "full_title": "GET /api/site-content · testimonios > no filtra campos operativos aunque esten en el documento",
          "line": 121,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eaa43cd79c0b9685675c",
          "title": "descarta lo no aprobado y lo no visible",
          "full_title": "GET /api/site-content · testimonios > descarta lo no aprobado y lo no visible",
          "line": 138,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/soporte-create.test.ts",
      "type": "unit",
      "sha256": "84aa445adce80784318471028d2636fff35eaf73ffe2b8fcc6c9561658538026",
      "tests": [
        {
          "id": "3557ff9c7564c68592c4",
          "title": "responde error cuando la escritura en Firestore falla en la superficie web",
          "full_title": "API /api/soporte > responde error cuando la escritura en Firestore falla en la superficie web",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "abb723b270cf54006a89",
          "title": "no entrega folio cuando la escritura falla",
          "full_title": "API /api/soporte > no entrega folio cuando la escritura falla",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f388e795cb5212594b9c",
          "title": "confirma el envío cuando el ticket sí se guardó",
          "full_title": "API /api/soporte > confirma el envío cuando el ticket sí se guardó",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1430d90d6d64e896fc24",
          "title": "impide que un correo cliente archive tickets",
          "full_title": "API /api/soporte > impide que un correo cliente archive tickets",
          "line": 109,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "57cadd731eb6300b3305",
          "title": "permite que una sesión @hixion.mx solicite archivar",
          "full_title": "API /api/soporte > permite que una sesión @hixion.mx solicite archivar",
          "line": 125,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6f5e9989c7c70f411566",
          "title": "rechaza un valor de archivado que no sea booleano",
          "full_title": "API /api/soporte > rechaza un valor de archivado que no sea booleano",
          "line": 150,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5ef57646ae6a74ac6c19",
          "title": "rechaza una petición sin cambios operativos",
          "full_title": "API /api/soporte > rechaza una petición sin cambios operativos",
          "line": 166,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "68f568d22ed710394d5b",
          "title": "propaga el código de conflicto del workflow",
          "full_title": "API /api/soporte > propaga el código de conflicto del workflow",
          "line": 182,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "41c2caf796c39abd81df",
          "title": "rechaza combinar archivado con un cambio de estado",
          "full_title": "API /api/soporte > rechaza combinar archivado con un cambio de estado",
          "line": 205,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "146848495d1f1954456f",
          "title": "impide que un correo cliente elimine tickets",
          "full_title": "API /api/soporte > impide que un correo cliente elimine tickets",
          "line": 221,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4ee7a87dcf986a08899f",
          "title": "elimina el ticket archivado y limpia sus adjuntos para HIXION",
          "full_title": "API /api/soporte > elimina el ticket archivado y limpia sus adjuntos para HIXION",
          "line": 237,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2a3c0fd666750b15aa34",
          "title": "avisa cuando algún adjunto queda pendiente de limpieza",
          "full_title": "API /api/soporte > avisa cuando algún adjunto queda pendiente de limpieza",
          "line": 262,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/stripe-catalog-patch.test.ts",
      "type": "unit",
      "sha256": "844fb9ffa101b44d5d7b761baaec4437236b32831ecb03fd75b6577bca06f6f4",
      "tests": [
        {
          "id": "90eced1e1782f7ff7dd4",
          "title": "crea un precio nuevo en Stripe cuando cambia el importe",
          "full_title": "PATCH /api/stripe/catalog > crea un precio nuevo en Stripe cuando cambia el importe",
          "line": 90,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a3b54132f54a701a597f",
          "title": "recrea el precio con forzarPrecio aunque el importe no cambie",
          "full_title": "PATCH /api/stripe/catalog > recrea el precio con forzarPrecio aunque el importe no cambie",
          "line": 112,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "17a1f0ac669d76a9a576",
          "title": "no toca Stripe cuando el importe no cambia",
          "full_title": "PATCH /api/stripe/catalog > no toca Stripe cuando el importe no cambia",
          "line": 129,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5fdceabc5648454be368",
          "title": "no escribe en Firestore si Stripe falla al cambiar el precio",
          "full_title": "PATCH /api/stripe/catalog > no escribe en Firestore si Stripe falla al cambiar el precio",
          "line": 141,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5764190ace000ed0072c",
          "title": "rechaza cambiar el precio de un plan sin producto de Stripe",
          "full_title": "PATCH /api/stripe/catalog > rechaza cambiar el precio de un plan sin producto de Stripe",
          "line": 153,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "50279826eaba74a1f678",
          "title": "rechaza un plan de otro tenant",
          "full_title": "PATCH /api/stripe/catalog > rechaza un plan de otro tenant",
          "line": 162,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "783d2e11f487e13e306b",
          "title": "rechaza dejar el plan sin ninguna forma de pago",
          "full_title": "PATCH /api/stripe/catalog > rechaza dejar el plan sin ninguna forma de pago",
          "line": 171,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cc0f5befb33a3eed1e98",
          "title": "descarta opciones de pago que Stripe no sabe repartir",
          "full_title": "PATCH /api/stripe/catalog > descarta opciones de pago que Stripe no sabe repartir",
          "line": 180,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f7392b9b37d44c86e11f",
          "title": "limpia beneficios vacíos en vez de guardarlos",
          "full_title": "PATCH /api/stripe/catalog > limpia beneficios vacíos en vez de guardarlos",
          "line": 191,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c48e61a181d984069630",
          "title": "rechaza citas incluidas no enteras o menores que uno",
          "full_title": "PATCH /api/stripe/catalog > rechaza citas incluidas no enteras o menores que uno",
          "line": 204,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "571d6abb825a7ad6e3bd",
          "title": "devuelve 404 si el plan no existe",
          "full_title": "PATCH /api/stripe/catalog > devuelve 404 si el plan no existe",
          "line": 212,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "03515af963672ec93e29",
          "title": "rellena termsVersion si el plan de cobro no lo tenía",
          "full_title": "PATCH /api/stripe/catalog > rellena termsVersion si el plan de cobro no lo tenía",
          "line": 218,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6f17e11a366476bfce12",
          "title": "no pisa un termsVersion ya guardado",
          "full_title": "PATCH /api/stripe/catalog > no pisa un termsVersion ya guardado",
          "line": 229,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/stripe-checkout-authority.test.ts",
      "type": "unit",
      "sha256": "21b2facff21c2ea36a58273670faef19f7eabdcac101d7c736891c13ef867741",
      "tests": [
        {
          "id": "b5f469e98e0237bff1e5",
          "title": "rechaza monto y concepto libres sin obligacion ni precio de catalogo",
          "full_title": "POST /api/stripe/checkout authority > rechaza monto y concepto libres sin obligacion ni precio de catalogo",
          "line": 120,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2b9baafa31702cf910bb",
          "title": "usa identidad, precio y concepto del servidor para un plan de catalogo",
          "full_title": "POST /api/stripe/checkout authority > usa identidad, precio y concepto del servidor para un plan de catalogo",
          "line": 132,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1f848b0e87449227b15f",
          "title": "deriva exactamente el monto de la parcialidad pendiente",
          "full_title": "POST /api/stripe/checkout authority > deriva exactamente el monto de la parcialidad pendiente",
          "line": 166,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "88fb2114e00d11587fa8",
          "title": "rechaza una parcialidad pagada o de otro workspace",
          "full_title": "POST /api/stripe/checkout authority > rechaza una parcialidad pagada o de otro workspace",
          "line": 187,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "de2c390360e40647b04f",
          "title": "usa Checkout setup cuando el saldo cubre el cargo de hoy",
          "full_title": "POST /api/stripe/checkout authority > usa Checkout setup cuando el saldo cubre el cargo de hoy",
          "line": 208,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/api/stripe-subscriptions-setup.test.ts",
      "type": "unit",
      "sha256": "b52bcae0157195239373ce35491cb11ff4c87fa1b81ec68032951aecd71ca47c",
      "tests": [
        {
          "id": "41fd79ec249363261ef6",
          "title": "deja Checkout subscription cuando el saldo no cubre el mensual",
          "full_title": "POST /api/stripe/subscriptions > deja Checkout subscription cuando el saldo no cubre el mensual",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f944a96c9c5d2f7bfb35",
          "title": "usa Checkout setup cuando el saldo cubre el primer mes y no se salta Stripe",
          "full_title": "POST /api/stripe/subscriptions > usa Checkout setup cuando el saldo cubre el primer mes y no se salta Stripe",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/patient-agenda-return-navigation.test.ts",
      "type": "unit",
      "sha256": "d52a22960cdda5dc1b71e1355671f1f828c76468710bde90ec3de6dfddc49067",
      "tests": [
        {
          "id": "177aee82990c3cad108a",
          "title": "delega el regreso a Agenda al único cierre del modal",
          "full_title": "alta de paciente iniciada desde Agenda > delega el regreso a Agenda al único cierre del modal",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dbe315e4413bb2623f74",
          "title": "limpia el borrador únicamente después de completar el alta",
          "full_title": "alta de paciente iniciada desde Agenda > limpia el borrador únicamente después de completar el alta",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/portal-layout-loading.test.tsx",
      "type": "unit",
      "sha256": "46ed00840364ada989089dc733e395817122eed84b3f137556957854221ec5da",
      "tests": [
        {
          "id": "72edc6fac2f034832536",
          "title": "muestra la carga de marca y aplaza las lecturas anticipadas mientras el perfil de paciente está pendiente",
          "full_title": "muestra la carga de marca y aplaza las lecturas anticipadas mientras el perfil de paciente está pendiente",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a3c2c7c78a4846983c2b",
          "title": "activa la precarga sólo cuando el paciente ya tiene acceso confirmado",
          "full_title": "activa la precarga sólo cuando el paciente ya tiene acceso confirmado",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/portal-recipe-detail-page.test.ts",
      "type": "unit",
      "sha256": "f6744511f19a9f855a96953921c93492beda7c3e070f7a2b6a6e7a923a147423",
      "tests": [
        {
          "id": "7ab49e4c5777734c3de0",
          "title": "resuelve el snapshot de la fecha del query y lo declara de solo lectura",
          "full_title": "cableado de /portal/plan/receta/[tiempo] > resuelve el snapshot de la fecha del query y lo declara de solo lectura",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "952b75c216e624370721",
          "title": "la página invoca el resolvedor de detalle en vez de reimplementar el slot",
          "full_title": "cableado de /portal/plan/receta/[tiempo] > la página invoca el resolvedor de detalle en vez de reimplementar el slot",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/portal-training-loading.test.tsx",
      "type": "unit",
      "sha256": "c3f0d57f23494880f6c0fd2680fead9529bed23a31065631785abbd3ba5ca915",
      "tests": [
        {
          "id": "9a5dd267afec2c3eef31",
          "title": "mantiene la carga de una sesión concreta mientras llega su ocurrencia aunque la lista general esté vacía",
          "full_title": "mantiene la carga de una sesión concreta mientras llega su ocurrencia aunque la lista general esté vacía",
          "line": 33,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/public-capture-cta-styles.test.ts",
      "type": "unit",
      "sha256": "a7087d38eec6e8ff00dae42b83f118f38feef2e637e9f033f1b1970643c00f81",
      "tests": [
        {
          "id": "38f65dccb0b6dbd2cb02",
          "title": "mantiene fondo amarillo y texto negro también en modo claro",
          "full_title": "CTA de captación pública > mantiene fondo amarillo y texto negro también en modo claro",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/root-loading.test.ts",
      "type": "unit",
      "sha256": "701a4cc5e8df45631784a3d6b02219609b49168689ea73348809c19aa6b479b6",
      "tests": [
        {
          "id": "e894822f46fc22c503d7",
          "title": "no depende de AppShell ni del SidebarProvider de rutas privadas",
          "full_title": "root loading > no depende de AppShell ni del SidebarProvider de rutas privadas",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/routine-quick-adjust-distribution.test.ts",
      "type": "unit",
      "sha256": "535a879ebf47aded5bbc09c2e8d7304c2dbb8684c149cb7b056ed754e74c90d4",
      "tests": [
        {
          "id": "a73e600c33b09f503922",
          "title": "reutiliza el gráfico compartido con el borrador y el catálogo actuales",
          "full_title": "distribución en el ajuste rápido de rutina > reutiliza el gráfico compartido con el borrador y el catálogo actuales",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/app/sentry-trace-propagation.test.ts",
      "type": "unit",
      "sha256": "4a2ad32e901f69e40132249dc6a54ff32d9ebf62e30834e254487188723a751c",
      "tests": [
        {
          "id": "6a9c5d5428eefc57c2a7",
          "title": "se limita a las APIs propias y no alcanza la descarga pública del PDF",
          "full_title": "propagación de trazas de Sentry > se limita a las APIs propias y no alcanza la descarga pública del PDF",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/aar-kpi-consumers.test.ts",
      "type": "unit",
      "sha256": "18aedf5a885d68e82fdd9a25a02d240fba762a29cf94441e768998da5cbbf32b",
      "tests": [
        {
          "id": "0f9fdc7c2f6ce467b1f6",
          "title": "el calendario consume la presentación del expediente y el builder de KPIs",
          "full_title": "cableado del objetivo y KPI de sets > el calendario consume la presentación del expediente y el builder de KPIs",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b70c6cbf3515a044886e",
          "title": "el cockpit renderiza el KPI principal de sets y el objetivo resuelto",
          "full_title": "cableado del objetivo y KPI de sets > el cockpit renderiza el KPI principal de sets y el objetivo resuelto",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/completed-routine-day-pr.test.tsx",
      "type": "unit",
      "sha256": "6dc1717561cb4dbb2a279e94576e77bd0a6bf57349e1372a1038e9892d195039",
      "tests": [
        {
          "id": "7ea0f4f9ace4e2d50607",
          "title": "muestra series sin inventar PRs y muestra PRs sólo con una marca registrada",
          "full_title": "muestra series sin inventar PRs y muestra PRs sólo con una marca registrada",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/contextual-navigation.test.tsx",
      "type": "unit",
      "sha256": "7bbba4e8cc8ba57c3859eefaaade5bb346bac23993d5064d6d3ef1eb10f39bce",
      "tests": [
        {
          "id": "ccfd1c21a065799af1a8",
          "title": "regresa desde dos secciones del sidebar al borrador y paso de rutina",
          "full_title": "regresa desde dos secciones del sidebar al borrador y paso de rutina",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dc305d210dc4c8fc5de4",
          "title": "recupera dieta desde plantilla aunque el acceso normal ignore borradores",
          "full_title": "recupera dieta desde plantilla aunque el acceso normal ignore borradores",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "300545909d31fcc393aa",
          "title": "conserva sección del perfil y no ofrece retorno al cambiar de identidad",
          "full_title": "conserva sección del perfil y no ofrece retorno al cambiar de identidad",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8845cf2338d93cffdbbb",
          "title": "navegar entre secciones principales no crea un retorno",
          "full_title": "navegar entre secciones principales no crea un retorno",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "efe99f0e2847ed89e58a",
          "title": "no permite salir si no puede persistir el trabajo",
          "full_title": "no permite salir si no puede persistir el trabajo",
          "line": 75,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "97c73b3e02ce1a50a62c",
          "title": "la edición de dieta sin autosave remoto se recupera con su prescripción",
          "full_title": "la edición de dieta sin autosave remoto se recupera con su prescripción",
          "line": 84,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8e0bda31d39f71815972",
          "title": "consumir el regreso no crea una flecha inversa y descartar invalida el contexto",
          "full_title": "consumir el regreso no crea una flecha inversa y descartar invalida el contexto",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8354316becf4dec535a2",
          "title": "volver a otro paciente no aplica el estado del perfil anterior",
          "full_title": "volver a otro paciente no aplica el estado del perfil anterior",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "07bd8742415265917385",
          "title": "impide abandonar un formulario abierto pero permite el menú móvil",
          "full_title": "impide abandonar un formulario abierto pero permite el menú móvil",
          "line": 110,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "59e98fa8c12d878edbce",
          "title": "conserva el regreso del perfil al editor después de una visita al sidebar",
          "full_title": "conserva el regreso del perfil al editor después de una visita al sidebar",
          "line": 117,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f9ed0f3aa3c63fd76c24",
          "title": "captura el enlace del shell y la flecha accesible navega al editor conservado",
          "full_title": "captura el enlace del shell y la flecha accesible navega al editor conservado",
          "line": 134,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a3f048b64d594276f471",
          "title": "ignora punteros incompletos o corruptos: %s",
          "full_title": "ignora punteros incompletos o corruptos: %s",
          "line": 144,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        }
      ]
    },
    {
      "path": "tests/unit/components/diet-assignment-adjuster.test.tsx",
      "type": "unit",
      "sha256": "37efd463343a294ab50b2ee1462f917acdc66833cefb1c68c0fb044680d24a73",
      "tests": [
        {
          "id": "a19f553e74a6ebf34ae9",
          "title": "acumula cambios en dos menús, publica una revisión y conserva la versión base al fallar",
          "full_title": "acumula cambios en dos menús, publica una revisión y conserva la versión base al fallar",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/ejercicio-planeacion-drawer.test.tsx",
      "type": "unit",
      "sha256": "e0ded6ffcef0cdaf289e1fd320c427813e6a466a943bbfefcde360ff131e973d",
      "tests": [
        {
          "id": "e55978213e400a491cb1",
          "title": "permite leer datos relacionados sin reemplazar hasta confirmar",
          "full_title": "permite leer datos relacionados sin reemplazar hasta confirmar",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c43a7749210e8bd53176",
          "title": "desde biblioteca sólo consulta y no ofrece reemplazar una ocurrencia inexistente",
          "full_title": "desde biblioteca sólo consulta y no ofrece reemplazar una ocurrencia inexistente",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c744e03a3cbed662f0f9",
          "title": "consulta sin media, cierra con Escape y devuelve el foco sin mutar la prescripción",
          "full_title": "consulta sin media, cierra con Escape y devuelve el foco sin mutar la prescripción",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/ejercicios-selector-filters.test.tsx",
      "type": "unit",
      "sha256": "19febcd82431915df0fe5e04f90f9ad457732990ae1be56ae7580dfaeba09e17",
      "tests": [
        {
          "id": "febe8437ebce3aa8ada3",
          "title": "expande categoría antes de los otros filtros, permite varias chips con iconos y muestra sus contadores",
          "full_title": "expande categoría antes de los otros filtros, permite varias chips con iconos y muestra sus contadores",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/ejercicios-selector-modal.test.tsx",
      "type": "unit",
      "sha256": "2f1f3a34ab8db0d1ef6af42d4c95f6a8117053ed1f0f9a9d9b17ef3ffc198a25",
      "tests": [
        {
          "id": "6a68384be13396d45f42",
          "title": "añade repetidos, quita sólo la ocurrencia elegida y conserva el borrador al reabrir",
          "full_title": "añade repetidos, quita sólo la ocurrencia elegida y conserva el borrador al reabrir",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dbdf6f95de48579fd4a8",
          "title": "conserva búsqueda y scroll al consultar el detalle y regresar",
          "full_title": "conserva búsqueda y scroll al consultar el detalle y regresar",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "18c9275f633ab551886f",
          "title": "cambiar el día conserva la ficha y exige confirmar antes de añadir otra categoría",
          "full_title": "cambiar el día conserva la ficha y exige confirmar antes de añadir otra categoría",
          "line": 84,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b2ac0869bc0069ea0184",
          "title": "Global y Mis ejercicios usan el origen del catálogo y se combinan con destacados y búsqueda",
          "full_title": "Global y Mis ejercicios usan el origen del catálogo y se combinan con destacados y búsqueda",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "735622f455ec74e9c816",
          "title": "un día vacío requiere crear bloque y selecciona el nuevo destino",
          "full_title": "un día vacío requiere crear bloque y selecciona el nuevo destino",
          "line": 111,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "afbed6a87ba8b16b2d7e",
          "title": "abrir una rutina antigua no la migra; añadir conserva la ocurrencia original en Libre",
          "full_title": "abrir una rutina antigua no la migra; añadir conserva la ocurrencia original en Libre",
          "line": 121,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3814d26d8d19d922bc7d",
          "title": "muestra errores del catálogo y permite reintentar",
          "full_title": "muestra errores del catálogo y permite reintentar",
          "line": 131,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4d73f22c221e614da528",
          "title": "bloquea el alta por elegibilidad clínica tanto en catálogo como en detalle",
          "full_title": "bloquea el alta por elegibilidad clínica tanto en catálogo como en detalle",
          "line": 139,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9979da7794e798a13231",
          "title": "mantiene filtros manuales al cambiar de día y limita En este bloque al destino actual",
          "full_title": "mantiene filtros manuales al cambiar de día y limita En este bloque al destino actual",
          "line": 150,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "27a89d029dd5ea76e3f0",
          "title": "Escape regresa del detalle antes de cerrar el selector",
          "full_title": "Escape regresa del detalle antes de cerrar el selector",
          "line": 162,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eeeeb481aacf0567c69b",
          "title": "lleva el foco a Regresar al abrir la ficha y lo devuelve a la tarjeta",
          "full_title": "lleva el foco a Regresar al abrir la ficha y lo devuelve a la tarjeta",
          "line": 172,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4a28e2938f58906349c4",
          "title": "muestra el contexto del paciente y conserva los bloqueos de su expediente",
          "full_title": "muestra el contexto del paciente y conserva los bloqueos de su expediente",
          "line": 181,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "53b2a0b79dba46227f0d",
          "title": "mueve por menú a un bloque vacío, permite cancelar y preserva las series al confirmar",
          "full_title": "mueve por menú a un bloque vacío, permite cancelar y preserva las series al confirmar",
          "line": 191,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "742f76122363bce699e1",
          "title": "limpiar categorías permite añadir otro tipo desde la biblioteca lateral con confirmación",
          "full_title": "limpiar categorías permite añadir otro tipo desde la biblioteca lateral con confirmación",
          "line": 210,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1107cc03aac39eb31d00",
          "title": "muestra condiciones registradas sin convertir notas en etiquetas y conserva su evaluación clínica",
          "full_title": "muestra condiciones registradas sin convertir notas en etiquetas y conserva su evaluación clínica",
          "line": 223,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/exercise-swap-dialog.test.tsx",
      "type": "unit",
      "sha256": "21a75cadfb258efbbcb99fb94fbd673ed3656b8f8c162bf7a742803d2ea3d468",
      "tests": [
        {
          "id": "084257768eaadc8518c0",
          "title": "abre el único grupo guiado y oculta el acceso al catálogo aunque la búsqueda no coincida",
          "full_title": "ExerciseSwapDialog ST-944380 > abre el único grupo guiado y oculta el acceso al catálogo aunque la búsqueda no coincida",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f241aab372d8b863fef5",
          "title": "abre una regresión que llega después de abrir el panel y retira el acceso al catálogo",
          "full_title": "ExerciseSwapDialog ST-944380 > abre una regresión que llega después de abrir el panel y retira el acceso al catálogo",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "074157d46ce8e23db387",
          "title": "muestra el fallo del catálogo y permite reintentarlo en vez de afirmar que no hay resultados",
          "full_title": "ExerciseSwapDialog ST-944380 > muestra el fallo del catálogo y permite reintentarlo en vez de afirmar que no hay resultados",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b5f3df6906250f04269e",
          "title": "distingue un catálogo vacío confirmado de un filtro sin coincidencias",
          "full_title": "ExerciseSwapDialog ST-944380 > distingue un catálogo vacío confirmado de un filtro sin coincidencias",
          "line": 108,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "89d4e2cfc13f467e0fa3",
          "title": "muestra secciones colapsables con conteo y match en porcentaje",
          "full_title": "ExerciseSwapDialog ST-944380 > muestra secciones colapsables con conteo y match en porcentaje",
          "line": 132,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9de81fe3b9cf5ec613e3",
          "title": "mantiene scroll interno con muchas alternativas guiadas",
          "full_title": "ExerciseSwapDialog ST-944380 > mantiene scroll interno con muchas alternativas guiadas",
          "line": 201,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/exercise-visual-sheet.test.tsx",
      "type": "unit",
      "sha256": "b1f69ea337e8e6d47f2b8a5e0f91678b8c91641b21cceb247e78415d50f6dc63",
      "tests": [
        {
          "id": "a18626e052a8debd5264",
          "title": "abre la ficha fija sobre la pantalla, no al final del documento",
          "full_title": "abre la ficha fija sobre la pantalla, no al final del documento",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/google-calendar-integrations.test.ts",
      "type": "unit",
      "sha256": "231ac4a846ec42821c8dc177e2bc04f455f4ce13013a3a3fc35b7fcd1a19c2a2",
      "tests": [
        {
          "id": "3a8c312ccda50757bf3b",
          "title": "valida con Google el estado del equipo y nombra calendario y persona",
          "full_title": "GoogleCalendarIntegrations team diagnosis > valida con Google el estado del equipo y nombra calendario y persona",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/google-sync-indicator.test.ts",
      "type": "unit",
      "sha256": "002d68907b1d045bf529320187d09bb73456a44033041de730413c50a1f4197d",
      "tests": [
        {
          "id": "5d0edba73870cbcb1a3f",
          "title": "lleva el error de sync a Configuración > Integraciones",
          "full_title": "GoogleSyncIndicator settings access > lleva el error de sync a Configuración > Integraciones",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/home-contextual-routine-card.test.tsx",
      "type": "unit",
      "sha256": "41baea852b33c90913980b581c4335d506d8a99c2cc014297e4c1fab35defa4e",
      "tests": [
        {
          "id": "4e9feed9757ca6fd32f6",
          "title": "no repite la tarjeta de rutina si la acción principal ya es %s",
          "full_title": "no repite la tarjeta de rutina si la acción principal ya es %s",
          "line": 22,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "18dd3b1653f138e903bc",
          "title": "conserva el contexto de rutina cuando la acción principal es una comida",
          "full_title": "conserva el contexto de rutina cuando la acción principal es una comida",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/ingredient-equivalent-field.test.ts",
      "type": "unit",
      "sha256": "ae435e84c734d95edd5434c6c22215fdec6f7d325f1952bbd54a5c7209a48776",
      "tests": [
        {
          "id": "895d7f48384483218c29",
          "title": "busca por nombre con el mismo contrato de scroll que el combobox de catálogo",
          "full_title": "IngredientEquivalentField > busca por nombre con el mismo contrato de scroll que el combobox de catálogo",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/invite-team-dialog-overflow.test.ts",
      "type": "unit",
      "sha256": "1d59f7eb5d50edab372b58e128cca12c2ca60b989144ad82a00eb36037345828",
      "tests": [
        {
          "id": "b7110e727eb0d95c7fc3",
          "title": "acota el popup al viewport y da scroll propio a la lista",
          "full_title": "InviteTeamDialog overflow contract > acota el popup al viewport y da scroll propio a la lista",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/kits-suplementacion-dialog.test.ts",
      "type": "unit",
      "sha256": "14e02c210ae90174ab1ae93d3c79c00b67476a024599db8de2fdfa755eee12f6",
      "tests": [
        {
          "id": "296494b0e92d82b72672",
          "title": "el alta vive en un diálogo y la pestaña solo renderiza tarjetas",
          "full_title": "pestaña Kits en Dietas > el alta vive en un diálogo y la pestaña solo renderiza tarjetas",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "54cd4c7a21c88f2245f0",
          "title": "el listado del catálogo deja marcar varios sin cerrarse",
          "full_title": "pestaña Kits en Dietas > el listado del catálogo deja marcar varios sin cerrarse",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4985e72824ec88f77a0a",
          "title": "aplicar un kit apunta a Dietas, no a Productos",
          "full_title": "pestaña Kits en Dietas > aplicar un kit apunta a Dietas, no a Productos",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/mobile-bottom-nav.test.ts",
      "type": "unit",
      "sha256": "67254e7fd41f18efb05662d09f87a6dcb2deba848092cf6b2bbe38c6c590130b",
      "tests": [
        {
          "id": "9cde8f255ca88b35a35f",
          "title": "integra Spotlight y soporte en el menú y elimina la bolita flotante",
          "full_title": "MobileBottomNav staff dock contract > integra Spotlight y soporte en el menú y elimina la bolita flotante",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e0b8acc6e449c526ea10",
          "title": "muestra Copilot solo como fila del menú cuando está activo",
          "full_title": "MobileBottomNav staff dock contract > muestra Copilot solo como fila del menú cuando está activo",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/paciente-crm-drawer-archive.test.ts",
      "type": "unit",
      "sha256": "037e6e3f77a9683fc7998e5bfa2fbd846cc4a5931fdae3f3d9fb074acd516c2b",
      "tests": [
        {
          "id": "eaee055ef8aad5c5c9e5",
          "title": "coloca Archivar a la extrema derecha de Guardar cambios",
          "full_title": "PacienteCrmDrawer archive action > coloca Archivar a la extrema derecha de Guardar cambios",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ba517a7ada81cb79d60b",
          "title": "reutiliza el mismo toggle de archivo que el Kanban",
          "full_title": "PacienteCrmDrawer archive action > reutiliza el mismo toggle de archivo que el Kanban",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "536ca32a20fd077526ae",
          "title": "captura contacto con TelefonoInput y edad desde fecha de nacimiento",
          "full_title": "PacienteCrmDrawer field contract restored from flujo-comercial-integral > captura contacto con TelefonoInput y edad desde fecha de nacimiento",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b4a4a4fd37a97c6caec4",
          "title": "usa campos CRM de catálogo y no mueve etapa al guardar el resto",
          "full_title": "PacienteCrmDrawer field contract restored from flujo-comercial-integral > usa campos CRM de catálogo y no mueve etapa al guardar el resto",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2cda4cb55d87946c9142",
          "title": "deriva citas y pago del sistema en vez de checkboxes manuales",
          "full_title": "PacienteCrmDrawer field contract restored from flujo-comercial-integral > deriva citas y pago del sistema en vez de checkboxes manuales",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/patient-exercise-picker.test.tsx",
      "type": "unit",
      "sha256": "05bb225cc5d633a6abde34823504f8d37ac2c07b08bfac35b929a061b35805b0",
      "tests": [
        {
          "id": "e92e744684d7cf2d1736",
          "title": "filtra el catálogo y consultar Info no añade ni inicia una sesión",
          "full_title": "filtra el catálogo y consultar Info no añade ni inicia una sesión",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ce208965f6867493f43f",
          "title": "conserva los bloqueos clínicos y permite consultar la información",
          "full_title": "conserva los bloqueos clínicos y permite consultar la información",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/patient-search-combobox-scroll.test.ts",
      "type": "unit",
      "sha256": "40a7f2ead00f555aaeab4cd1245b39cd0e73cbbda085f245d778e82856c19455",
      "tests": [
        {
          "id": "2b48562edf4e15ecabcc",
          "title": "toma el control modal y conserva scroll propio dentro de diálogos",
          "full_title": "PatientSearchCombobox agenda contract > toma el control modal y conserva scroll propio dentro de diálogos",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a067c5088ba114516643",
          "title": "entrega la búsqueda al alta en vez de navegar perdiendo el texto",
          "full_title": "PatientSearchCombobox agenda contract > entrega la búsqueda al alta en vez de navegar perdiendo el texto",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6c8ef4c54f17a2704d22",
          "title": "vincula identidad por conciliación sin intentar crear una cita",
          "full_title": "PatientSearchCombobox agenda contract > vincula identidad por conciliación sin intentar crear una cita",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/patient-support-navigation.test.tsx",
      "type": "unit",
      "sha256": "56174f53de56c8867192eea47a006073abb86ce2be5ccea7baca346046db0b99",
      "tests": [
        {
          "id": "71d70ab64a9ac6fad4d0",
          "title": "abre el registro y Cerrar regresa a la sección y fecha de origen",
          "full_title": "abre el registro y Cerrar regresa a la sección y fecha de origen",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "751fc05f070c70e7d8f3",
          "title": "permite ver tickets desde el chat y crear otro desde el botón fijo",
          "full_title": "permite ver tickets desde el chat y crear otro desde el botón fijo",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/plan-final-save-owner.test.ts",
      "type": "unit",
      "sha256": "8f8a765d37d439a5f693556f06e5813021e06b1ed0c19a1360efd0562dc07b4f",
      "tests": [
        {
          "id": "cb8e2b425bba618f94ed",
          "title": "deja la escritura y el baseline en el wizard y retira la barra independiente de Vista Final",
          "full_title": "propietario del guardado al editar una dieta > deja la escritura y el baseline en el wizard y retira la barra independiente de Vista Final",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/plan-servicio-draft.test.ts",
      "type": "unit",
      "sha256": "63a458cd3cf9fcaa81bff86408bb69c492ab0ed12f5022bb002c063b4e43a14f",
      "tests": [
        {
          "id": "caa24bb04a60c4a6016f",
          "title": "no marca cambios al hidratar un plan sin tocarlo",
          "full_title": "planToDraft > no marca cambios al hidratar un plan sin tocarlo",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "946f50e2f19a8cbc47ed",
          "title": "cae a consultationsIncluded cuando falta citasIncluidas",
          "full_title": "planToDraft > cae a consultationsIncluded cuando falta citasIncluidas",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4d36ea9303ed990335a7",
          "title": "ofrece todas las opciones de pago cuando el plan no restringe ninguna",
          "full_title": "planToDraft > ofrece todas las opciones de pago cuando el plan no restringe ninguna",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "74c00e97126185a83ce3",
          "title": "respeta la restricción de pagos guardada en el plan",
          "full_title": "planToDraft > respeta la restricción de pagos guardada en el plan",
          "line": 33,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "38356ec6278e04aa5a4d",
          "title": "detecta el cambio de un beneficio, no solo de los campos escalares",
          "full_title": "planToDraft > detecta el cambio de un beneficio, no solo de los campos escalares",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4fff7c53bc09d72652ce",
          "title": "detecta el reordenamiento de beneficios",
          "full_title": "planToDraft > detecta el reordenamiento de beneficios",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "15593c384578387f91a0",
          "title": "usa planCode cuando existe",
          "full_title": "resolución de variante > usa planCode cuando existe",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d59414069f0a670a9ef5",
          "title": "deduce el paquete del variantCode, también en un paquete que no es de 2026",
          "full_title": "resolución de variante > deduce el paquete del variantCode, también en un paquete que no es de 2026",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2a1b61c2649626bcc229",
          "title": "no inventa un paquete si el variantCode no trae modalidad y plazo",
          "full_title": "resolución de variante > no inventa un paquete si el variantCode no trae modalidad y plazo",
          "line": 60,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "48f1e437a0ea854f378a",
          "title": "solo acepta modalidades que la rejilla sabe mostrar",
          "full_title": "resolución de variante > solo acepta modalidades que la rejilla sabe mostrar",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3222edebf6a50bf6070f",
          "title": "deriva el plazo de duracion cuando falta termMonths",
          "full_title": "resolución de variante > deriva el plazo de duracion cuando falta termMonths",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1ffcb81cc523b7ed7006",
          "title": "abre una columna por planCode y omite planes de prueba sin código",
          "full_title": "paquetesDesdePlanes > abre una columna por planCode y omite planes de prueba sin código",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "869704faad229affaeef",
          "title": "incluye un paquete que no está en el catálogo 2026",
          "full_title": "paquetesDesdePlanes > incluye un paquete que no está en el catálogo 2026",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/portal-dock-prefetch.test.tsx",
      "type": "unit",
      "sha256": "e1f49258458475ab2a0a28eaab1769683d7f0c4d8aa3ae088ef93db05e1e508d",
      "tests": [
        {
          "id": "9b8c1b692bdac3906137",
          "title": "calienta sólo la ruta visitada al salir, sin lecturas de datos",
          "full_title": "calienta sólo la ruta visitada al salir, sin lecturas de datos",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d429c487334811e96e54",
          "title": "no conserva rutas de otro paciente",
          "full_title": "no conserva rutas de otro paciente",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/portal-loading-indicator.test.tsx",
      "type": "unit",
      "sha256": "d9a1e2a039598870993a15ac24920888217f8097f6295c9aec8bcbf7eafdcd13",
      "tests": [
        {
          "id": "ee0937dd68b95b4b0205",
          "title": "delays the spinner, keeps cached data on return, and does not overwrite user input",
          "full_title": "portal loading experience > delays the spinner, keeps cached data on return, and does not overwrite user input",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4579a96896646565c5ca",
          "title": "offers retry after 15 seconds and retains content on failure",
          "full_title": "portal loading experience > offers retry after 15 seconds and retains content on failure",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5a274b6d961cdfd50959",
          "title": "does not announce a failed sibling while another reader already has data",
          "full_title": "portal loading experience > does not announce a failed sibling while another reader already has data",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9440634ac5df6d34ae9c",
          "title": "still offers retry when nothing on the route has data",
          "full_title": "portal loading experience > still offers retry when nothing on the route has data",
          "line": 83,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0cfa74f860025f1576b3",
          "title": "clears data across identities and ignores the previous response",
          "full_title": "portal loading experience > clears data across identities and ignores the previous response",
          "line": 97,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b8c5b1120b10d53a88dd",
          "title": "finishes independent derived consumers of the same resource",
          "full_title": "portal loading experience > finishes independent derived consumers of the same resource",
          "line": 108,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0018ca0c6f94e29f5691",
          "title": "does not leave the dock pill stuck when each reader settles on its own turn",
          "full_title": "portal loading experience > does not leave the dock pill stuck when each reader settles on its own turn",
          "line": 124,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bbd688eebeaf96a94fed",
          "title": "clears 'No se pudo actualizar' when the reader that failed later settles",
          "full_title": "portal loading experience > clears 'No se pudo actualizar' when the reader that failed later settles",
          "line": 141,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "047b7e29aea9772aff23",
          "title": "clears a staggered dock failure before the next view, and that view stays clear",
          "full_title": "portal loading experience > clears a staggered dock failure before the next view, and that view stays clear",
          "line": 157,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3780deb7f693b818546f",
          "title": "reports offline and suppresses the automatic indicator during a manual pull",
          "full_title": "portal loading experience > reports offline and suppresses the automatic indicator during a manual pull",
          "line": 177,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bfb8b526550f39d52ecf",
          "title": "no confunde una respuesta fallida del servicio con falta de conexión",
          "full_title": "portal loading experience > no confunde una respuesta fallida del servicio con falta de conexión",
          "line": 186,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/portal/agenda/agenda-data-assignment.test.ts",
      "type": "unit",
      "sha256": "f86a081c67665312d70693fbe86d19fa3cb6e7dab2ab11f0479c9bcb4f4f534b",
      "tests": [
        {
          "id": "6e13d170320520755f7a",
          "title": "muestra ambas asignaciones y la ejecución real de peso/reps en el mismo día",
          "full_title": "agenda operativa con dieta y rutina > muestra ambas asignaciones y la ejecución real de peso/reps en el mismo día",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/portal/agenda/agenda-data-registered-meal.test.ts",
      "type": "unit",
      "sha256": "5071faf839c865260ffa5c4309d03434c9c43b20c1968cd043a344b987d8c3eb",
      "tests": [
        {
          "id": "43e38202fe3e57b26720",
          "title": "conserva el snapshot legacy aunque el ajuste haya vaciado receta y porciones",
          "full_title": "getAgendaMealRecipes con una comida ya registrada > conserva el snapshot legacy aunque el ajuste haya vaciado receta y porciones",
          "line": 92,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7f8dbc07c7088f05f763",
          "title": "muestra un tiempo libre en agenda aunque no tenga receta base",
          "full_title": "getAgendaMealRecipes con una comida ya registrada > muestra un tiempo libre en agenda aunque no tenga receta base",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e8eead1519939358fa42",
          "title": "muestra lo que el paciente comió, no la receta que el especialista puso después",
          "full_title": "getAgendaMealRecipes con una comida ya registrada > muestra lo que el paciente comió, no la receta que el especialista puso después",
          "line": 116,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e6ac0e594d87cc0b98b9",
          "title": "la deja marcada: el paciente ya la registró, no está pendiente",
          "full_title": "getAgendaMealRecipes con una comida ya registrada > la deja marcada: el paciente ya la registró, no está pendiente",
          "line": 124,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "120228489b24c872f3a5",
          "title": "conserva la palomita de un check-in legacy sin snapshots",
          "full_title": "getAgendaMealRecipes con una comida ya registrada > conserva la palomita de un check-in legacy sin snapshots",
          "line": 130,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1bb9dbb2b90a8500723c",
          "title": "expone la clave del registro, para que desmarcar deshaga el que existe",
          "full_title": "getAgendaMealRecipes con una comida ya registrada > expone la clave del registro, para que desmarcar deshaga el que existe",
          "line": 145,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6811c43195e0b483799f",
          "title": "sin ajuste de por medio sigue resolviendo contra la receta vigente",
          "full_title": "getAgendaMealRecipes con una comida ya registrada > sin ajuste de por medio sigue resolviendo contra la receta vigente",
          "line": 153,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7e7bc22a78780bab4789",
          "title": "un platillo adicional patient-* no sustituye ni duplica la receta prescrita",
          "full_title": "getAgendaMealRecipes con una comida ya registrada > un platillo adicional patient-* no sustituye ni duplica la receta prescrita",
          "line": 161,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/public-plan-catalog.test.ts",
      "type": "unit",
      "sha256": "45e90c507149d187ce86aa33dbe230c1515f0c92328748ae90cd35ecd7692b25",
      "tests": [
        {
          "id": "7ba453cca2e60bc2454d",
          "title": "Quiero pagar conserva las respuestas y cambia a la vista de planes",
          "full_title": "catálogo integrado después de la captación > Quiero pagar conserva las respuestas y cambia a la vista de planes",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cb027c0b1ae04e66982b",
          "title": "las tarjetas integran plazo, modalidad, pago completo y parcialidades",
          "full_title": "catálogo integrado después de la captación > las tarjetas integran plazo, modalidad, pago completo y parcialidades",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "edf07d73e831587d423a",
          "title": "checkout reutiliza respuestas y VIP genera una consulta contextual",
          "full_title": "catálogo integrado después de la captación > checkout reutiliza respuestas y VIP genera una consulta contextual",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/recipe-options-switch-modal.test.tsx",
      "type": "unit",
      "sha256": "cc13624cb8ae41fe362b3b7f0800b3ce947d2b632ce9d6051b0eba04db2df600",
      "tests": [
        {
          "id": "92dc6615bf995a891678",
          "title": "muestra las kcal efectivas del menú asignado al día",
          "full_title": "RecipeOptionsSwitchModal > muestra las kcal efectivas del menú asignado al día",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/routine-assignment-adjuster.test.tsx",
      "type": "unit",
      "sha256": "3c0500b43355dac1ba279de9a171eea6f061a2e7dc4d59fa5cf8d4130d01111d",
      "tests": [
        {
          "id": "cdeb8a6b5cc9af2fb229",
          "title": "conserva dos ejercicios y reintenta con la misma operación tras un fallo de red",
          "full_title": "ajuste completo de rutina > conserva dos ejercicios y reintenta con la misma operación tras un fallo de red",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1c588d9c1b9cc443583b",
          "title": "explica antes de guardar que difiere a mañana cuando hoy tiene actividad",
          "full_title": "ajuste completo de rutina > explica antes de guardar que difiere a mañana cuando hoy tiene actividad",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/routine-calendar-loading.test.tsx",
      "type": "unit",
      "sha256": "2285d56c3cd55992934818b5fb1a7ec803e194c32a0099ad8cf3262be9231812",
      "tests": [
        {
          "id": "4cd2e4b4f880b282963a",
          "title": "no anuncia falta de rutina antes de recibir la primera semana",
          "full_title": "no anuncia falta de rutina antes de recibir la primera semana",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2c36bc7f1e26ae4d36ba",
          "title": "conserva una sesión reciente visible mientras se actualiza la semana",
          "full_title": "conserva una sesión reciente visible mientras se actualiza la semana",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ad9e8059d077ed018df3",
          "title": "conserva la semana visible si falla su actualización en segundo plano",
          "full_title": "conserva la semana visible si falla su actualización en segundo plano",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e426689986d5f1090c89",
          "title": "mantiene el calendario al pasar a una semana todavía pendiente sin anunciar que no hay rutina",
          "full_title": "mantiene el calendario al pasar a una semana todavía pendiente sin anunciar que no hay rutina",
          "line": 107,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "15494fe7f0c89edf99fc",
          "title": "ofrece cuatro acciones en una sesión pendiente y marca descanso sólo para esa ocurrencia",
          "full_title": "ofrece cuatro acciones en una sesión pendiente y marca descanso sólo para esa ocurrencia",
          "line": 115,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ae942493c8206e728172",
          "title": "elige otro día del programa sin pedir una fecha de destino",
          "full_title": "elige otro día del programa sin pedir una fecha de destino",
          "line": 137,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "950f7077a44a0a3a169e",
          "title": "permite elegir una rutina desde una fecha de descanso del programa",
          "full_title": "permite elegir una rutina desde una fecha de descanso del programa",
          "line": 159,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3da8705c2579ce4a40c9",
          "title": "muestra la rutina completada y el guardado al confirmar, y revierte si falla",
          "full_title": "muestra la rutina completada y el guardado al confirmar, y revierte si falla",
          "line": 175,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "91324fe8da4970da878f",
          "title": "conserva el cierre confirmado mientras la semana devuelve todavía datos anteriores",
          "full_title": "conserva el cierre confirmado mientras la semana devuelve todavía datos anteriores",
          "line": 195,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/routine-muscle-summary.test.tsx",
      "type": "unit",
      "sha256": "65a9d982d0fbf3903252ee3ababeef37c6556411ebf10910890bb6cd6c7fa6be",
      "tests": [
        {
          "id": "d2572c4ad8fe1dc289ca",
          "title": "resume todos los grupos en una barra apilada y conserva los bloques debajo",
          "full_title": "resume todos los grupos en una barra apilada y conserva los bloques debajo",
          "line": 7,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/routine-preparation-progress.test.tsx",
      "type": "unit",
      "sha256": "15bc1346ee0a58ca7eb49283cf239066cc99212102794af9f340bd29b5461705",
      "tests": [
        {
          "id": "1bd359071213ec301080",
          "title": "integra el progreso en las tarjetas y no muestra el dump Lo registrado",
          "full_title": "integra el progreso en las tarjetas y no muestra el dump Lo registrado",
          "line": 62,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3ea036979f898ee54617",
          "title": "mantiene visible la revisión mientras se confirma la prescripción y muestra el error si falla",
          "full_title": "mantiene visible la revisión mientras se confirma la prescripción y muestra el error si falla",
          "line": 134,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fe7acd2520b77edf3268",
          "title": "muestra una sesión anterior como consulta sin ofrecer reemplazar su preparación",
          "full_title": "muestra una sesión anterior como consulta sin ofrecer reemplazar su preparación",
          "line": 175,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/routine-preparation-reorder.test.tsx",
      "type": "unit",
      "sha256": "f9bd53326c904d7e16bc21c64531cfdf36c63089a2087b3a2cf1d265d4968a5c",
      "tests": [
        {
          "id": "d35f1fcb138a5ccb2f4f",
          "title": "reordena ejercicios en preparación y persiste con prepare",
          "full_title": "reordena ejercicios en preparación y persiste con prepare",
          "line": 71,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "670f009a651bb089c9a8",
          "title": "muestra la preparación confirmada al volver mientras se actualiza",
          "full_title": "muestra la preparación confirmada al volver mientras se actualiza",
          "line": 148,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/routine-preparation-swap.test.tsx",
      "type": "unit",
      "sha256": "a00d286269a76258eceff39ef949a53b03cc4153f27deb54938667164f5b2cde",
      "tests": [
        {
          "id": "c55fb27d658bdcd4b890",
          "title": "muestra intercambio en una sesión en curso y guarda en la misma sesión activa",
          "full_title": "muestra intercambio en una sesión en curso y guarda en la misma sesión activa",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/routine-session-progress.test.tsx",
      "type": "unit",
      "sha256": "3f80801fff29c874b24515299c185c00c32aa75e278fcd29dec4953e0c5afd83",
      "tests": [
        {
          "id": "cf25be71e65350e1a8b0",
          "title": "muestra el avance confirmado de la sesión seleccionada y conserva el dato durante la actualización",
          "full_title": "muestra el avance confirmado de la sesión seleccionada y conserva el dato durante la actualización",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1afa600cd0ff39b88b89",
          "title": "ofrece reintento cuando todavía no hay un avance confirmado",
          "full_title": "ofrece reintento cuando todavía no hay un avance confirmado",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/rutina-autosave-status.test.tsx",
      "type": "unit",
      "sha256": "3e3a3d24cf4c61cec22aabb13ebd12e15b9e93d44c31ad2cd1eb3769bf58d24d",
      "tests": [
        {
          "id": "808ac8bf517d66fe8021",
          "title": "informa sólo escrituras confirmadas y conserva el borrador local tras fallar, recuperarse y volver a fallar",
          "full_title": "informa sólo escrituras confirmadas y conserva el borrador local tras fallar, recuperarse y volver a fallar",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7316ed7201ae66cdd5e2",
          "title": "reintenta al volver al contenido anterior después de un fallo y espera confirmación para anunciar éxito",
          "full_title": "reintenta al volver al contenido anterior después de un fallo y espera confirmación para anunciar éxito",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5f38a75b6abcd14cfc84",
          "title": "no anuncia guardado de una edición fallida al completarse una escritura anterior",
          "full_title": "no anuncia guardado de una edición fallida al completarse una escritura anterior",
          "line": 128,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f5462f3fa87adb05bb02",
          "title": "ignora un rechazo anterior cuando la edición actual ya confirmó su guardado",
          "full_title": "ignora un rechazo anterior cuando la edición actual ya confirmó su guardado",
          "line": 149,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1457fb764d3afbcace94",
          "title": "ignora confirmaciones pendientes después de %s",
          "full_title": "ignora confirmaciones pendientes después de %s",
          "line": 168,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "23c2ed184b7ef7b91bc9",
          "title": "guarda el mismo contenido para otra identidad de %s sin reutilizar confirmaciones",
          "full_title": "guarda el mismo contenido para otra identidad de %s sin reutilizar confirmaciones",
          "line": 182,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "cc2a01ee8e13f3524005",
          "title": "descarta la respuesta pendiente de otra identidad de %s",
          "full_title": "descarta la respuesta pendiente de otra identidad de %s",
          "line": 202,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "f9fd74f6d1de418c70ee",
          "title": "vuelve a escribir el contenido anterior si una edición distinta todavía está pendiente",
          "full_title": "vuelve a escribir el contenido anterior si una edición distinta todavía está pendiente",
          "line": 221,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/rutina-biblioteca-categorias.test.tsx",
      "type": "unit",
      "sha256": "6013e7be3884e9eda75749f36d2f0ca9a8edb336ebc898ec08166a145dc3f2a1",
      "tests": [
        {
          "id": "40e2c4bf93a1283d9817",
          "title": "seleccionar bloques sincroniza badge y categorías y deseleccionar restaura todas",
          "full_title": "seleccionar bloques sincroniza badge y categorías y deseleccionar restaura todas",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "96b9c73f8a18c050d42c",
          "title": "permite ajustar las categorías del bloque con click y recupera el automático al cambiarlo",
          "full_title": "permite ajustar las categorías del bloque con click y recupera el automático al cambiarlo",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "249dd7bad7dbd657006f",
          "title": "el cuerpo alterna configuración y el área de imagen/título abre detalle sin expandir",
          "full_title": "el cuerpo alterna configuración y el área de imagen/título abre detalle sin expandir",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/rutina-detail-dialog-versioning.test.ts",
      "type": "unit",
      "sha256": "f126549e0ba4658844b80671c5c4041e840e3af5d28961a0037c105ae85748b6",
      "tests": [
        {
          "id": "1ad3328fc0f74e0af68d",
          "title": "parte del snapshot vigente y reemplaza con control de concurrencia",
          "full_title": "edición versionada de días de rutina > parte del snapshot vigente y reemplaza con control de concurrencia",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "89c26d5ad5407b522d55",
          "title": "el diálogo usa el reemplazo versionado y no escribe el documento raíz",
          "full_title": "edición versionada de días de rutina > el diálogo usa el reemplazo versionado y no escribe el documento raíz",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/rutina-dias-calendar-panel.test.ts",
      "type": "unit",
      "sha256": "d481d22e7f0f9a0f7aad441482a5f3f84cb424e4e7c0e721ecc5777993e0e0fe",
      "tests": [
        {
          "id": "81eb7bbd5ea693d95b93",
          "title": "infers large muscle groups from common day names",
          "full_title": "rutina-dias-calendar-panel helpers > infers large muscle groups from common day names",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f90e739162ece23c1988",
          "title": "clones a day with new ids and remapped segmentos",
          "full_title": "rutina-dias-calendar-panel helpers > clones a day with new ids and remapped segmentos",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b1b85aa8dc9b57c654e8",
          "title": "lists weekdays assigned to a day id",
          "full_title": "rutina-dias-calendar-panel helpers > lists weekdays assigned to a day id",
          "line": 53,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/rutina-duration-surfaces.test.tsx",
      "type": "unit",
      "sha256": "99794ce7febd1d488eca9282c081e260b21a9fec4f7cc9b3a2c43231fc2cc21e",
      "tests": [
        {
          "id": "747b0a144c1533c43cf1",
          "title": "portal y contador del editor muestran el mismo cálculo sin usar el objetivo manual",
          "full_title": "portal y contador del editor muestran el mismo cálculo sin usar el objetivo manual",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/rutina-mover-bloques.test.tsx",
      "type": "unit",
      "sha256": "3296249dfeb403e4e743dcfec4cbabe68b88e1712b7a2ac80ec7327b90639e07",
      "tests": [
        {
          "id": "8a04377b60f46ea3260e",
          "title": "arrastrar desde catálogo a un bloque vacío confirma, cancela y agrega sólo al aceptar",
          "full_title": "arrastrar desde catálogo a un bloque vacío confirma, cancela y agrega sólo al aceptar",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4d655b66ebdbd10b518e",
          "title": "arrastrar una instancia a %s conserva su prescripción tras confirmar",
          "full_title": "arrastrar una instancia a %s conserva su prescripción tras confirmar",
          "line": 73,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "ae49fea38b9b1ddaa1be",
          "title": "mover desde Libre a Ejercicios compatible no pide confirmación",
          "full_title": "mover desde Libre a Ejercicios compatible no pide confirmación",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e7978dd651564d3f2f19",
          "title": "la biblioteca de series permite añadir otra categoría únicamente al confirmar",
          "full_title": "la biblioteca de series permite añadir otra categoría únicamente al confirmar",
          "line": 102,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/rutina-reload-draft.test.tsx",
      "type": "unit",
      "sha256": "0b59e5b5c2aaae42168979c4f92d2dac01e2bf469a072cc2e4c5e16dd1035d45",
      "tests": [
        {
          "id": "d41966c83f54b562fd3c",
          "title": "recarga un bloque vacío con duración %s sin exigir ejercicios",
          "full_title": "recarga un bloque vacío con duración %s sin exigir ejercicios",
          "line": 21,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "dd61b055cd2f954beb88",
          "title": "no restaura automáticamente otro usuario, otra entrada o una navegación nueva",
          "full_title": "no restaura automáticamente otro usuario, otra entrada o una navegación nueva",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ff8b601214b7c98652ab",
          "title": "crea un bloque, vuelve al paso %s y lo recupera al remontar el provider",
          "full_title": "crea un bloque, vuelve al paso %s y lo recupera al remontar el provider",
          "line": 47,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "30684e4700c2d20c5f3a",
          "title": "rechaza un borrador de otra URL aunque pertenezca al mismo usuario",
          "full_title": "rechaza un borrador de otra URL aunque pertenezca al mismo usuario",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "959196196fbf4a24f0e5",
          "title": "retorna desde catálogo en modo %s, conservando día y bloque antes del autosave remoto",
          "full_title": "retorna desde catálogo en modo %s, conservando día y bloque antes del autosave remoto",
          "line": 72,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        }
      ]
    },
    {
      "path": "tests/unit/components/rutina-reps-range-stepper-align.test.tsx",
      "type": "unit",
      "sha256": "27156819573395849d0c5d88cd466aa76d561f7ff9226f9eed99349a2c48b84f",
      "tests": [
        {
          "id": "fe41d318f9eb9f431752",
          "title": "en rango de reps el track deja sitio para los dos botones +",
          "full_title": "en rango de reps el track deja sitio para los dos botones +",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/rutina-series-isolated.test.tsx",
      "type": "unit",
      "sha256": "1677315cc3e24b455e8d2885d75239c7e97e21c4f5a5dc38f961d95f7dd93cf2",
      "tests": [
        {
          "id": "2a5d95d9f99db53285d5",
          "title": "editar descanso en modo individual preserva las otras series y sus técnicas",
          "full_title": "editar descanso en modo individual preserva las otras series y sus técnicas",
          "line": 7,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/rutina-wizard-floating-actions-ui.test.tsx",
      "type": "unit",
      "sha256": "55cef54973ef1f91649bb65a0cf6d8c140447cab4eb3df36acba9e52337599ac",
      "tests": [
        {
          "id": "ee0ed7badbd3d924aeb7",
          "title": "los controles compactos conservan nombre accesible y acciones de expediente y chat",
          "full_title": "los controles compactos conservan nombre accesible y acciones de expediente y chat",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1fc3fa47282b170959c7",
          "title": "conserva los botones e iconos al contraer y expandir entre pasos, incluso al cambiar la validación",
          "full_title": "conserva los botones e iconos al contraer y expandir entre pasos, incluso al cambiar la validación",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/rutina-wizard-floating-actions.test.ts",
      "type": "unit",
      "sha256": "47a9cac9299889ec88c862411370021b55b195ed1c70223abf6693adeb63b016",
      "tests": [
        {
          "id": "6b3baa0bacc989aaac33",
          "title": "usa Guardar cambios al editar y deja el expediente sólo en las acciones inferiores",
          "full_title": "acciones flotantes al asignar una rutina > usa Guardar cambios al editar y deja el expediente sólo en las acciones inferiores",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "34c4aed9ce2bd8cdfb80",
          "title": "$caseName refleja el estado real y sólo permite una escritura válida",
          "full_title": "acciones flotantes al asignar una rutina > $caseName refleja el estado real y sólo permite una escritura válida",
          "line": 68,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "fc3b3eaafad2f17379da",
          "title": "rechaza una rutina inválida antes de intentar publicar la versión",
          "full_title": "acciones flotantes al asignar una rutina > rechaza una rutina inválida antes de intentar publicar la versión",
          "line": 109,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b53570964ec8109a4b5a",
          "title": "rechaza la segunda pestaña con el ID abierto y conserva lo publicado primero",
          "full_title": "acciones flotantes al asignar una rutina > rechaza la segunda pestaña con el ID abierto y conserva lo publicado primero",
          "line": 121,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/rutina-wizard-navigation.test.tsx",
      "type": "unit",
      "sha256": "849a7950b8381b0aed4758bb07c882aa43e62f94d871e9872d4c7e664d88008c",
      "tests": [
        {
          "id": "9ca7f4fe47d4acabd037",
          "title": "permite saltar a Guardar y volver a Datos conservando cambios sin enviar el formulario",
          "full_title": "permite saltar a Guardar y volver a Datos conservando cambios sin enviar el formulario",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "67667a59a7327a0bd609",
          "title": "restaura el progreso verde al avanzar y lo revierte al volver sin bloquear saltos",
          "full_title": "restaura el progreso verde al avanzar y lo revierte al volver sin bloquear saltos",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/segmento-editor.test.tsx",
      "type": "unit",
      "sha256": "218a9fdcd1bd682b2afefb8e751c89a642b799a05a95293122254e28f6d35bed",
      "tests": [
        {
          "id": "32bf560520cebeb5c273",
          "title": "el coach cambia duración y mueve cardio completo al principio",
          "full_title": "el coach cambia duración y mueve cardio completo al principio",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "445a025d33f75e35a624",
          "title": "tocar la tarjeta selecciona el bloque y cambia la biblioteca compatible",
          "full_title": "tocar la tarjeta selecciona el bloque y cambia la biblioteca compatible",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d89dcccc7fab8edf15aa",
          "title": "volver a tocar el título deselecciona y devuelve todos los ejercicios",
          "full_title": "volver a tocar el título deselecciona y devuelve todos los ejercicios",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/sheet-swipe-translate.test.tsx",
      "type": "unit",
      "sha256": "96a3e1cf5b704a0c4c9088c9a112087b42797808f3d9158f57261e8d08f99c2d",
      "tests": [
        {
          "id": "522c0a0f49d9be5c27ed",
          "title": "does not keep a translate style while idle with swipe enabled",
          "full_title": "Sheet swipe translate residue > does not keep a translate style while idle with swipe enabled",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4f0e23b648a258a4a1b5",
          "title": "applies translate while dragging the handle and clears it after a short swipe",
          "full_title": "Sheet swipe translate residue > applies translate while dragging the handle and clears it after a short swipe",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "25c2b247d2b56cd7d450",
          "title": "clears stuck body pointer-events after swipe-dismiss closes the sheet",
          "full_title": "Sheet swipe translate residue > clears stuck body pointer-events after swipe-dismiss closes the sheet",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/suplementos-nested-dialog-z.test.ts",
      "type": "unit",
      "sha256": "3ba63f461b233807fb682778698e42aee4f516af51c15d7fb56d061b5fe917ae",
      "tests": [
        {
          "id": "aa0cd9ce03adb78c0f8c",
          "title": "el padre de suplementos del plan queda en z-70",
          "full_title": "diálogos anidados de suplementación > el padre de suplementos del plan queda en z-70",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c9a4e6e51e825c008eaa",
          "title": "agregar producto y aplicar kit se pintan por encima del padre",
          "full_title": "diálogos anidados de suplementación > agregar producto y aplicar kit se pintan por encima del padre",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/suplementos-section-scroll.test.ts",
      "type": "unit",
      "sha256": "d27c71b5939c2ae2eb55f87b78f3e2ce5c25f0c67ec20e99f85d864762d114d0",
      "tests": [
        {
          "id": "8a324e0c3805d9fff9b7",
          "title": "el split recorta y cada columna scrollea por dentro, sin pasar la rueda al wizard",
          "full_title": "popup de suplementos: contrato de scroll como recetas > el split recorta y cada columna scrollea por dentro, sin pasar la rueda al wizard",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/team-members-role-editor.test.ts",
      "type": "unit",
      "sha256": "b13cbeaca546885fc24c49aad3f9f3009dd94ca4bbb8818e203be0c3a159b51b",
      "tests": [
        {
          "id": "cf31baffb6a6f3562228",
          "title": "ofrece cambiar rol para otros miembros y conserva el usuario actual sin acciones",
          "full_title": "edición de rol desde Miembros > ofrece cambiar rol para otros miembros y conserva el usuario actual sin acciones",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eac75d91e12223a31feb",
          "title": "muestra el nombre del rol configurado en el workspace",
          "full_title": "edición de rol desde Miembros > muestra el nombre del rol configurado en el workspace",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/timer-descanso.test.tsx",
      "type": "unit",
      "sha256": "d3899a9c4633e076b3d24872eb28cb3a008ca2832f5f6a15b165855d72c6804e",
      "tests": [
        {
          "id": "d2a1427d3e98a3c42871",
          "title": "appears only after rest starts and expands into floating controls",
          "full_title": "appears only after rest starts and expands into floating controls",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/training-checklist.test.tsx",
      "type": "unit",
      "sha256": "dde274ef673b07002d1da32eeb7714b6e2c64aa7430e53cfa4c0a9f35a3c662c",
      "tests": [
        {
          "id": "b6d4c5368fa4a1ddc613",
          "title": "keeps coach preview visible and records an arbitrary series directly without fabricating effort",
          "full_title": "keeps coach preview visible and records an arbitrary series directly without fabricating effort",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "de0f97cb9f25b6945cfd",
          "title": "asks explicitly when metrics are missing and preserves the typed reps",
          "full_title": "asks explicitly when metrics are missing and preserves the typed reps",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c944a117924a2624eeeb",
          "title": "bulk uses edited drafts, and empty blocks ask for labeled activity and duration",
          "full_title": "bulk uses edited drafts, and empty blocks ask for labeled activity and duration",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5e0296c8c011e93aea2e",
          "title": "keeps exercise PR above series and shows reps reference only at the series weight",
          "full_title": "keeps exercise PR above series and shows reps reference only at the series weight",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d2ec3067648ea47d8ffb",
          "title": "shows the rest between sets and lets the patient define a missing duration",
          "full_title": "shows the rest between sets and lets the patient define a missing duration",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b32c29d8d43cc397b830",
          "title": "shows only weight conversion for loaded exercises and keeps removal unavailable after a completed set",
          "full_title": "shows only weight conversion for loaded exercises and keeps removal unavailable after a completed set",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/wizard-steps-clickable.test.ts",
      "type": "unit",
      "sha256": "55f0d215f6742d5a9c5c6d68b661bf3fa1de7d99eb0451b11ad1991bd70079c5",
      "tests": [
        {
          "id": "6136fc4ce3cb6ea85c58",
          "title": "permite ir a cualquier paso distinto del actual, incluso desde Cálculo o Grupos",
          "full_title": "saltos del wizard de dieta > permite ir a cualquier paso distinto del actual, incluso desde Cálculo o Grupos",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "09549eef6493573d103e",
          "title": "ya no exige haber alcanzado el paso anterior para saltar adelante",
          "full_title": "saltos del wizard de dieta > ya no exige haber alcanzado el paso anterior para saltar adelante",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "381f7462d99136cc1a51",
          "title": "no recorta el anillo hover de los dots con overflow-hidden",
          "full_title": "saltos del wizard de dieta > no recorta el anillo hover de los dots con overflow-hidden",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4937fee8862bae288151",
          "title": "en Calendario el trash del chip aparece en hover, igual que en Distribución",
          "full_title": "saltos del wizard de dieta > en Calendario el trash del chip aparece en hover, igual que en Distribución",
          "line": 33,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/components/workspace-catalog-combobox-scroll.test.ts",
      "type": "unit",
      "sha256": "3dfaa430964ea66c445507e6e26be3e6f40e9e75a99f0161f51ac22007a7090b",
      "tests": [
        {
          "id": "1e9f8209560ba60a8117",
          "title": "toma el control modal y conserva scroll propio dentro de diálogos",
          "full_title": "WorkspaceCatalogCombobox scroll contract > toma el control modal y conserva scroll propio dentro de diálogos",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/demos/golden-catalog.test.ts",
      "type": "unit",
      "sha256": "4bfa4637e80e3b883a23f09a80528b31bb7f5b22f78602cf12d1ffec172a30ff",
      "tests": [
        {
          "id": "e7501b4014932c30e140",
          "title": "mantiene IDs, QA links y pasos válidos",
          "full_title": "catálogo de Recorridos Dorados > mantiene IDs, QA links y pasos válidos",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f3f632b2e04e6cedbd44",
          "title": "detecta pasos documentados sin implementación y handlers sin documentación",
          "full_title": "catálogo de Recorridos Dorados > detecta pasos documentados sin implementación y handlers sin documentación",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "008076d2ebb5fbabbf08",
          "title": "selecciona recorridos por bloque, tier o ID sin ejecutar retirados",
          "full_title": "catálogo de Recorridos Dorados > selecciona recorridos por bloque, tier o ID sin ejecutar retirados",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/demos/record-parallel.test.ts",
      "type": "unit",
      "sha256": "b2b74e9aef5dd9da2a9ff4ea1b6ac498032c7511f3685f25d069c7d03e59fe0e",
      "tests": [
        {
          "id": "cc58fb528accdacc465c",
          "title": "mantiene un paciente sintético distinto por recorrido",
          "full_title": "grabación paralela de Recorridos Dorados > mantiene un paciente sintético distinto por recorrido",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5b19b0d4b217c54f984d",
          "title": "aísla credenciales y paciente en el entorno de cada proceso",
          "full_title": "grabación paralela de Recorridos Dorados > aísla credenciales y paciente en el entorno de cada proceso",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "54573c4a316b3398a5a7",
          "title": "publica estados y video terminado en la videoteca",
          "full_title": "grabación paralela de Recorridos Dorados > publica estados y video terminado en la videoteca",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8dea5018e078b893a7cc",
          "title": "genera IDs estables para el lote",
          "full_title": "grabación paralela de Recorridos Dorados > genera IDs estables para el lote",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/e2e/bounded-summary-reporter.test.ts",
      "type": "unit",
      "sha256": "664c1f5305ebefd4d0a17208c526d1d9efa5f110aad00a9717d499c88f116890",
      "tests": [
        {
          "id": "1a8e28a7ffe31a7f3a59",
          "title": "derives selected, observed and notRun from the Playwright suite",
          "full_title": "bounded summary reporter > derives selected, observed and notRun from the Playwright suite",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/e2e/production-readonly-selection.test.ts",
      "type": "unit",
      "sha256": "f0b0dcb2a9cb6b45757711721d9539aabf9c10fb709726f6391e6f49a48f9891",
      "tests": [
        {
          "id": "e92a7268d6b7bfb9811f",
          "title": "uses a positive tag and an explicit spec allowlist",
          "full_title": "post-deploy production-readonly selection > uses a positive tag and an explicit spec allowlist",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3f91cd4e4b1b7fd5a7a9",
          "title": "does not admit known fixture-writing specs",
          "full_title": "post-deploy production-readonly selection > does not admit known fixture-writing specs",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/patient-operational-routine-choice.test.ts",
      "type": "unit",
      "sha256": "e9e6c932c091ef59704d577584817fee6c1c29d2fba7d04c88cc36f0de7cf1af",
      "tests": [
        {
          "id": "d2c9dbe457f9747361f0",
          "title": "muestra al coach el descanso elegido por el paciente como ajuste de la fecha",
          "full_title": "muestra al coach el descanso elegido por el paciente como ajuste de la fecha",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c3d1377b240806f48f00",
          "title": "muestra el descanso por vacaciones y restaura fechas posteriores al periodo",
          "full_title": "muestra el descanso por vacaciones y restaura fechas posteriores al periodo",
          "line": 26,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/routine-calendar-weeks-refresh.test.tsx",
      "type": "unit",
      "sha256": "de39f1dea1a755f358098cca1b614adf3c88ec27dae8217b4745fb14a9f4b3f2",
      "tests": [
        {
          "id": "3a88365a918a25dd0909",
          "title": "vuelve a leer la semana si la mutación la invalida durante una lectura",
          "full_title": "vuelve a leer la semana si la mutación la invalida durante una lectura",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-fitness-paciente.test.tsx",
      "type": "unit",
      "sha256": "4f96f88791600b9d5cc1ff87a701ed7a97e732e66c7b87f81ac8a676fb55f402",
      "tests": [
        {
          "id": "b65ca0c5ab221f03e870",
          "title": "usa exactamente 30 días y prorratea cuatro semanas y dos días",
          "full_title": "useFitnessPaciente: sets de 30 días > usa exactamente 30 días y prorratea cuatro semanas y dos días",
          "line": 120,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "af461c00e7852c234270",
          "title": "carga las series una sola vez por la suscripción y espera su primera respuesta",
          "full_title": "useFitnessPaciente: sets de 30 días > carga las series una sola vez por la suscripción y espera su primera respuesta",
          "line": 132,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "88f116b8078c73923a5e",
          "title": "conserva 22/48 en el portal para tres sesiones de una rutina de 65 sets",
          "full_title": "useFitnessPaciente: sets de 30 días > conserva 22/48 en el portal para tres sesiones de una rutina de 65 sets",
          "line": 146,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-google-calendar-events.test.ts",
      "type": "unit",
      "sha256": "ca51d40ab2aadd2eb5593c6edcb3e4d43896702b9ec77e6179536b154c4f0317",
      "tests": [
        {
          "id": "812747211307284e6aad",
          "title": "no aborta el fetch: el overlay de Next trataba controller.abort como AbortError",
          "full_title": "useGoogleCalendarEvents abort contract > no aborta el fetch: el overlay de Next trataba controller.abort como AbortError",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-meal-checkins.test.tsx",
      "type": "unit",
      "sha256": "fe23a4c985dfe86bbbfda20abcc5833236f271abe8c5fca69f5277668363d887",
      "tests": [
        {
          "id": "03a8866aeafd3ac0a474",
          "title": "persiste la versión en un check-in anidado por receta",
          "full_title": "useMealCheckIns assignmentVersionId > persiste la versión en un check-in anidado por receta",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "60eb97e0f72ea82afda0",
          "title": "conserva la versión recibida por un check-in legacy de nivel superior",
          "full_title": "useMealCheckIns assignmentVersionId > conserva la versión recibida por un check-in legacy de nivel superior",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-mobile-work-center.test.ts",
      "type": "unit",
      "sha256": "238b89cea92cbec9b26864ccc2d208443d3aba8ed75018dbeeadea840e90e3f4",
      "tests": [
        {
          "id": "fe1e87376fff0b859394",
          "title": "usa textos cortos que caben en el menú móvil",
          "full_title": "useMobileWorkCenter quick actions copy > usa textos cortos que caben en el menú móvil",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-patient-active-plan.test.tsx",
      "type": "unit",
      "sha256": "d7d066bd276afc8983cde889eea908a9e22dcc9a09fa0ddedb5067f89b39a19f",
      "tests": [
        {
          "id": "34907cdc89973ace114f",
          "title": "resuelve el listener y una versión legacy invertida no tumba el plan válido",
          "full_title": "usePatientActivePlan > resuelve el listener y una versión legacy invertida no tumba el plan válido",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bc78088b23210d2f1b4d",
          "title": "una lectura offline lenta no repinta un snapshot de otra versión",
          "full_title": "usePatientActivePlan > una lectura offline lenta no repinta un snapshot de otra versión",
          "line": 120,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "53c4b864eb7ee443b75e",
          "title": "conserva el documento activo cuando el listener de versiones reporta error",
          "full_title": "usePatientActivePlan > conserva el documento activo cuando el listener de versiones reporta error",
          "line": 144,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "485e0250ea62dad09802",
          "title": "arranca en el día de negocio y alcanza los ajustes programados a futuro",
          "full_title": "usePatientActivePlan > arranca en el día de negocio y alcanza los ajustes programados a futuro",
          "line": 156,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f90d0ea946efb632542a",
          "title": "expone la fecha programada según el día de negocio",
          "full_title": "usePatientActivePlan > expone la fecha programada según el día de negocio",
          "line": 190,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4fe82b3d35c88576f103",
          "title": "renueva los listeners al cruzar la medianoche del negocio",
          "full_title": "usePatientActivePlan > renueva los listeners al cruzar la medianoche del negocio",
          "line": 208,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-patient-rutinas-programadas.test.tsx",
      "type": "unit",
      "sha256": "5a9a1bba8a2ee36dfdc044ecfc2c6836a7ac99e54a38af6268bf7938e451043e",
      "tests": [
        {
          "id": "0efd75cbd7bc1c765fb1",
          "title": "muestra una publicación posterior a las 12:00 UTC sin esperar al día siguiente",
          "full_title": "rutinas programadas vigentes en el instante de lectura > muestra una publicación posterior a las 12:00 UTC sin esperar al día siguiente",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c31d662950223abff544",
          "title": "no anticipa una versión futura del mismo día aunque comience antes de las 12:00 UTC",
          "full_title": "rutinas programadas vigentes en el instante de lectura > no anticipa una versión futura del mismo día aunque comience antes de las 12:00 UTC",
          "line": 116,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "73d0f3ce506a543ce090",
          "title": "actualiza la versión inmediata mediante %s",
          "full_title": "rutinas programadas vigentes en el instante de lectura > actualiza la versión inmediata mediante %s",
          "line": 129,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "e5906dcccdf8e22f17e4",
          "title": "$label en Ciudad de México",
          "full_title": "rutinas programadas vigentes en el instante de lectura > $label en Ciudad de México",
          "line": 150,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-permissions.test.tsx",
      "type": "unit",
      "sha256": "79fbe255070f263b2d98cd1c38b3d35922507f68bca10bfe1d19dd4e7d3f8f03",
      "tests": [
        {
          "id": "1f4ddbe270537541f1a6",
          "title": "refleja el permiso guardado en ambos sentidos",
          "full_title": "usePermissions > refleja el permiso guardado en ambos sentidos",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "663c73554786f2d81da9",
          "title": "un cambio en la matriz se refleja sin volver a montar el hook",
          "full_title": "usePermissions > un cambio en la matriz se refleja sin volver a montar el hook",
          "line": 60,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ce3bdc07853a19af7dfd",
          "title": "la matriz otorga por encima del rol predefinido",
          "full_title": "usePermissions > la matriz otorga por encima del rol predefinido",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8bdfc52a281b4fab5a51",
          "title": "sin matriz guardada cae a los permisos predefinidos del rol",
          "full_title": "usePermissions > sin matriz guardada cae a los permisos predefinidos del rol",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "24c11f55089254752d82",
          "title": "admin conserva todos los permisos aunque la matriz intente recortarlos",
          "full_title": "usePermissions > admin conserva todos los permisos aunque la matriz intente recortarlos",
          "line": 90,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2be0819e1e4e8e23e550",
          "title": "paciente no obtiene ningun permiso de staff",
          "full_title": "usePermissions > paciente no obtiene ningun permiso de staff",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ab7e6c6bfa925611d312",
          "title": "un rol propio del tenant entra al equipo y recibe sus permisos",
          "full_title": "usePermissions > un rol propio del tenant entra al equipo y recibe sus permisos",
          "line": 106,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a2cdc99a673c9dbde9d1",
          "title": "sin perfil no concede nada",
          "full_title": "usePermissions > sin perfil no concede nada",
          "line": 115,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-plan-overrides.test.ts",
      "type": "unit",
      "sha256": "30ed2084c13a9167a45cb0c8cf6e71fa5d715f09d707201017c35f1288595fde",
      "tests": [
        {
          "id": "e43976b64d960b5fd42a",
          "title": "devuelve todas las recetas asignadas al tiempo, no solo la principal",
          "full_title": "resolverOpcionesParaSlot > devuelve todas las recetas asignadas al tiempo, no solo la principal",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "deaf53c833425d84558d",
          "title": "usa el menú que el plan semanal asigna a esa fecha",
          "full_title": "resolverOpcionesParaSlot > usa el menú que el plan semanal asigna a esa fecha",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "61f299079cc8ab90d7dd",
          "title": "respeta el menú que el paciente eligió ese día",
          "full_title": "resolverOpcionesParaSlot > respeta el menú que el paciente eligió ese día",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "db824749fcf54714966a",
          "title": "ignora el menú elegido cuando corresponde a otra fecha",
          "full_title": "resolverOpcionesParaSlot > ignora el menú elegido cuando corresponde a otra fecha",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3c9e4dcf7bd156c2bc97",
          "title": "no ofrece opciones cuando el plan marca la comida como libre",
          "full_title": "resolverOpcionesParaSlot > no ofrece opciones cuando el plan marca la comida como libre",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0b8fbecb33d5814d780a",
          "title": "cae al plan legacy de receta única cuando no hay menús",
          "full_title": "resolverOpcionesParaSlot > cae al plan legacy de receta única cuando no hay menús",
          "line": 97,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1aede6bcbc070088cc91",
          "title": "devuelve array vacío cuando el tiempo no tiene recetas",
          "full_title": "resolverOpcionesParaSlot > devuelve array vacío cuando el tiempo no tiene recetas",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-portal-fitness-history.test.ts",
      "type": "unit",
      "sha256": "09561d133c1e7a8cab20bbab8b4abad1a4ab05af8d30df4bebbcc6b8796da4f8",
      "tests": [
        {
          "id": "30eb5a031a928a9d26a9",
          "title": "conserva resultados comparables por serie y notas propias del ejercicio",
          "full_title": "portal fitness history > conserva resultados comparables por serie y notas propias del ejercicio",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-portal-fitness-overview.test.tsx",
      "type": "unit",
      "sha256": "a0f15e3b4f1ef3059a54ba6d6abaf78b16cb50fe524bd2ae41951f4a36f3135f",
      "tests": [
        {
          "id": "16e0cf4ce70942b30960",
          "title": "puede pintar la tarjeta de Home mientras cargan las métricas complementarias",
          "full_title": "usePortalFitnessOverview > puede pintar la tarjeta de Home mientras cargan las métricas complementarias",
          "line": 75,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "48602aa3f81526c49a05",
          "title": "termina la espera con error si no hay sesiones confirmadas",
          "full_title": "usePortalFitnessOverview > termina la espera con error si no hay sesiones confirmadas",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "904a5b61e0ffacad43e7",
          "title": "no ofrece Continuar para una sesión terminal %s",
          "full_title": "usePortalFitnessOverview > no ofrece Continuar para una sesión terminal %s",
          "line": 107,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-portal-home-mounted.test.tsx",
      "type": "unit",
      "sha256": "b25b559f98317bae8c34bf6b67873b900578c34fbf34d20c02d3144bb908e0d1",
      "tests": [
        {
          "id": "1355e1e9bb409f28c362",
          "title": "evita el placeholder de hidratación al volver con la misma identidad",
          "full_title": "evita el placeholder de hidratación al volver con la misma identidad",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-routine-calendar-weeks.test.tsx",
      "type": "unit",
      "sha256": "20db8092c3f59082b6d5ea88d947705ed65751debc9ab07744622411bf074026",
      "tests": [
        {
          "id": "58ef1d94496f6ebe8e58",
          "title": "limita el historial a ocho semanas y conserva la última incluso con el mismo reloj",
          "full_title": "limita el historial a ocho semanas y conserva la última incluso con el mismo reloj",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "09eb96d7d66358486559",
          "title": "pide sólo la semana visible; al avanzar solicita una semana nueva y conserva las anteriores",
          "full_title": "pide sólo la semana visible; al avanzar solicita una semana nueva y conserva las anteriores",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c4d4f78ebdc4ae793f60",
          "title": "muestra la semana conservada y la actualiza al volver después de treinta segundos",
          "full_title": "muestra la semana conservada y la actualiza al volver después de treinta segundos",
          "line": 82,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ff16366af05f14bef559",
          "title": "si falla la primera semana, reintenta sólo esa semana",
          "full_title": "si falla la primera semana, reintenta sólo esa semana",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d6283a8f758ca1d56c27",
          "title": "al actualizar pide únicamente la semana visible aunque haya otras semanas guardadas",
          "full_title": "al actualizar pide únicamente la semana visible aunque haya otras semanas guardadas",
          "line": 108,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8e1292a4bd1cd9b5dba3",
          "title": "al volver enseña la semana guardada sin consulta duplicada y revalida si ya venció",
          "full_title": "al volver enseña la semana guardada sin consulta duplicada y revalida si ya venció",
          "line": 124,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-server-form-guard.test.tsx",
      "type": "unit",
      "sha256": "9cc01382f31c3149815c0870eff0a5ffa0aa438c3a7fc3f93eeb0b522d959aec",
      "tests": [
        {
          "id": "46bfa7eae2991506ad05",
          "title": "refreshes a pristine form, but never replaces an edited field",
          "full_title": "refreshes a pristine form, but never replaces an edited field",
          "line": 7,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-sesion-activa.test.tsx",
      "type": "unit",
      "sha256": "eee18fab5cb792cc2d46c6109f116e5078c68399d841cbcba72a7059d018bba5",
      "tests": [
        {
          "id": "b0cf82747a43b39185ba",
          "title": "mantiene las identidades de series al reordenar y recupera extras sin duplicarlos",
          "full_title": "active session draft > mantiene las identidades de series al reordenar y recupera extras sin duplicarlos",
          "line": 94,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c460ecc81e58209036fd",
          "title": "conserva la nota del coach al guardar la preparación y mantiene aparte la del paciente",
          "full_title": "active session draft > conserva la nota del coach al guardar la preparación y mantiene aparte la del paciente",
          "line": 106,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3dbf3a1e5067d2b1b78c",
          "title": "arma la prescripción de la sesión sin nulos que el servidor rechaza",
          "full_title": "active session draft > arma la prescripción de la sesión sin nulos que el servidor rechaza",
          "line": 113,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ccd509d980dd097c4cc3",
          "title": "separa prescripción de resultados y deja los inputs reales vacíos",
          "full_title": "active session draft > separa prescripción de resultados y deja los inputs reales vacíos",
          "line": 125,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a457430136eb24b8306f",
          "title": "una serie extra tampoco inventa resultados desde defaults",
          "full_title": "active session draft > una serie extra tampoco inventa resultados desde defaults",
          "line": 138,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "404b50c6e7d6d8bbc0a2",
          "title": "renderiza la prescripción fijada por la sesión aunque la rutina vigente ya cambió",
          "full_title": "active session draft > renderiza la prescripción fijada por la sesión aunque la rutina vigente ya cambió",
          "line": 147,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "741a0c3791f40ee40460",
          "title": "consultar la pantalla sin registrar nada no crea ni fija una sesión",
          "full_title": "active session draft > consultar la pantalla sin registrar nada no crea ni fija una sesión",
          "line": 160,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9fd3ccf37fa27697fdb9",
          "title": "conserva el entrenamiento ante la confirmación de una prescripción idéntica",
          "full_title": "active session draft > conserva el entrenamiento ante la confirmación de una prescripción idéntica",
          "line": 179,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2f7c143e80eedcfae6f0",
          "title": "la hidratación del catálogo no reinicia la misma versión prescrita",
          "full_title": "active session draft > la hidratación del catálogo no reinicia la misma versión prescrita",
          "line": 193,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "40fcd79ddb48c449ee01",
          "title": "permite reintentar la lectura inicial fallida sin crear una sesión",
          "full_title": "active session draft > permite reintentar la lectura inicial fallida sin crear una sesión",
          "line": 206,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6875014f3ca24684b402",
          "title": "una sesión sin empezar recibe hoy el cambio publicado después de abrir la pantalla",
          "full_title": "active session draft > una sesión sin empezar recibe hoy el cambio publicado después de abrir la pantalla",
          "line": 218,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7e5351b89d94010bb0e8",
          "title": "el lector cablea el snapshot de una sesión existente en lugar de la rutina vigente",
          "full_title": "active session draft > el lector cablea el snapshot de una sesión existente en lugar de la rutina vigente",
          "line": 246,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ec313394914b47bdc1bf",
          "title": "fija la versión vigente sólo al guardar el primer set",
          "full_title": "active session draft > fija la versión vigente sólo al guardar el primer set",
          "line": 287,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "570291a16096af8e64a9",
          "title": "no recupera duración objetivo al confirmar realizado sin datos y refleja el origen inmediatamente",
          "full_title": "active session draft > no recupera duración objetivo al confirmar realizado sin datos y refleja el origen inmediatamente",
          "line": 337,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e7b84677e168ef684ee8",
          "title": "pinta la serie antes de que responda el guardado y la confirma al llegar",
          "full_title": "active session draft > pinta la serie antes de que responda el guardado y la confirma al llegar",
          "line": 357,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3c10f3639395a29efea7",
          "title": "conserva la serie y la copia local si el guardado falla por la red",
          "full_title": "active session draft > conserva la serie y la copia local si el guardado falla por la red",
          "line": 400,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bab7611a6604f4713318",
          "title": "revierte la serie si el servidor rechaza el registro",
          "full_title": "active session draft > revierte la serie si el servidor rechaza el registro",
          "line": 420,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d6253c7e921cb87c2d2e",
          "title": "desmarcar una serie pendiente no llama a removeSet",
          "full_title": "active session draft > desmarcar una serie pendiente no llama a removeSet",
          "line": 464,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f45910d63da30a219838",
          "title": "permite guardar una sesión omitida con cero sets sin anclarla al abrir",
          "full_title": "active session draft > permite guardar una sesión omitida con cero sets sin anclarla al abrir",
          "line": 501,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2fe7f50ca6dd7c689c53",
          "title": "permite intercambiar aunque ya haya series registradas y conserva el progreso",
          "full_title": "active session draft > permite intercambiar aunque ya haya series registradas y conserva el progreso",
          "line": 544,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9ec2a878cc9f5d964298",
          "title": "inicia en el orden de bloques aunque la lista plana tenga otro orden",
          "full_title": "inicia en el orden de bloques aunque la lista plana tenga otro orden",
          "line": 633,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ea51e594f20e7fa3d944",
          "title": "converts every set weight and keeps non-weight exercises unchanged",
          "full_title": "converts every set weight and keeps non-weight exercises unchanged",
          "line": 650,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ae013c5877cdb0cf790b",
          "title": "keeps an unrecorded prescribed exercise removed on reload and restores converted units",
          "full_title": "keeps an unrecorded prescribed exercise removed on reload and restores converted units",
          "line": 662,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4f665948eba26018adfc",
          "title": "reorders whole series without changing their values or recorded identity",
          "full_title": "reorders whole series without changing their values or recorded identity",
          "line": 671,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/hooks/use-training-checklist.test.tsx",
      "type": "unit",
      "sha256": "a2bbf1744835e645714efb70c575331eaa9ae6828ab52b1276d7ab2ab69c5271",
      "tests": [
        {
          "id": "20d99893e6ad2f710092",
          "title": "starts from actual history but never resolves a prescribed range to an invented rep count",
          "full_title": "starts from actual history but never resolves a prescribed range to an invented rep count",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "593a5fdf0fc1a5fd1764",
          "title": "converts distance from meters to kilometers",
          "full_title": "converts distance from meters to kilometers",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "12b23409729bdf0fc2ad",
          "title": "persists cleared metrics and advanced drafts across reload without marking anything completed",
          "full_title": "persists cleared metrics and advanced drafts across reload without marking anything completed",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/auth/error-navigation.test.ts",
      "type": "unit",
      "sha256": "cb3ab86dea171935272dba0307b88f3728d70b61d08cc09cef47db4c3dd6e9f5",
      "tests": [
        {
          "id": "d875c67a399e218ee914",
          "title": "nunca envía un paciente al dashboard staff",
          "full_title": "error navigation > nunca envía un paciente al dashboard staff",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "51278974474944dcaf5c",
          "title": "usa un inicio neutral cuando todavía no conoce el rol",
          "full_title": "error navigation > usa un inicio neutral cuando todavía no conoce el rol",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "351fad8c552a4ba2b031",
          "title": "conserva destinos staff y comercial cuando el rol está confirmado",
          "full_title": "error navigation > conserva destinos staff y comercial cuando el rol está confirmado",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/auth/landing-and-projection.test.ts",
      "type": "unit",
      "sha256": "665375619cec136b92b158cf52c9cfb180fadf2f85c625b8a47fbdc24a8d81f7",
      "tests": [
        {
          "id": "12f202b1b37b5feb4853",
          "title": "basta con cumplir uno de los permisos pedidos",
          "full_title": "hasAnyPermission > basta con cumplir uno de los permisos pedidos",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "27d274907659713b5feb",
          "title": "sin ninguno de los permisos devuelve false",
          "full_title": "hasAnyPermission > sin ninguno de los permisos devuelve false",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0692fe60d27768d3daac",
          "title": "se activa con ver_pipeline y sin ver_pacientes",
          "full_title": "usesSanitizedPipeline > se activa con ver_pipeline y sin ver_pacientes",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3340e73c0c99e9011544",
          "title": "un rol clinico recibe la vista completa, no la proyeccion",
          "full_title": "usesSanitizedPipeline > un rol clinico recibe la vista completa, no la proyeccion",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "43f7b5301a32e6dff2a2",
          "title": "un rol propio del tenant con el mismo alcance recibe la proyeccion",
          "full_title": "usesSanitizedPipeline > un rol propio del tenant con el mismo alcance recibe la proyeccion",
          "line": 62,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eb43ea26509a422dbe7a",
          "title": "encenderle ver_pacientes lo saca de la proyeccion",
          "full_title": "usesSanitizedPipeline > encenderle ver_pacientes lo saca de la proyeccion",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9b00553b82cfe9dd4b22",
          "title": "un rol de equipo aterriza en el dashboard",
          "full_title": "resolveLandingRoute > un rol de equipo aterriza en el dashboard",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4cf8d03a25e92305db39",
          "title": "comercial aterriza en pacientes por su permiso, no por su nombre",
          "full_title": "resolveLandingRoute > comercial aterriza en pacientes por su permiso, no por su nombre",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c0700fc4c682aae6018c",
          "title": "un rol propio del tenant aterriza donde le alcanzan sus permisos",
          "full_title": "resolveLandingRoute > un rol propio del tenant aterriza donde le alcanzan sus permisos",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4ec480c9ce1069cbcec4",
          "title": "un rol propio SIN declarar area nace dentro del equipo",
          "full_title": "resolveLandingRoute > un rol propio SIN declarar area nace dentro del equipo",
          "line": 93,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "af4104bc763b60ed63c0",
          "title": "el paciente va al portal: es un area, no un permiso",
          "full_title": "resolveLandingRoute > el paciente va al portal: es un area, no un permiso",
          "line": 106,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5999e972a379934d833e",
          "title": "sin sesion, al login",
          "full_title": "resolveLandingRoute > sin sesion, al login",
          "line": 110,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f41fb5354adac5e77548",
          "title": "un rol sin ninguna capacidad cae en el dashboard, que explica el bloqueo",
          "full_title": "resolveLandingRoute > un rol sin ninguna capacidad cae en el dashboard, que explica el bloqueo",
          "line": 114,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "72fd03223974ac35964c",
          "title": "control negativo: quitarle ver_pipeline a comercial cambia su aterrizaje",
          "full_title": "resolveLandingRoute > control negativo: quitarle ver_pipeline a comercial cambia su aterrizaje",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/auth/login-navigation.test.ts",
      "type": "unit",
      "sha256": "41edd0ee6876a208a6249007a86a2bdf7efe5ec08ff5e2016e45211785453071",
      "tests": [
        {
          "id": "e079659f188d62cf951c",
          "title": "resuelve el destino permitido para %s",
          "full_title": "login navigation > resuelve el destino permitido para %s",
          "line": 22,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "97e54cd6d712a8dc5c79",
          "title": "rechaza redirecciones incompatibles o externas para %s",
          "full_title": "login navigation > rechaza redirecciones incompatibles o externas para %s",
          "line": 33,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "f7e529892122ed96f365",
          "title": "un rol staff sin permiso para la ruta pedida ya no se desvia aqui",
          "full_title": "login navigation > un rol staff sin permiso para la ruta pedida ya no se desvia aqui",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9d63fdd1392a8ccd541a",
          "title": "un paciente nunca sale del portal, sea cual sea su landing",
          "full_title": "login navigation > un paciente nunca sale del portal, sea cual sea su landing",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "967ddf6ae4973eb00c8a",
          "title": "permite reclamar una sola navegación aunque perfil y submit compitan",
          "full_title": "login navigation > permite reclamar una sola navegación aunque perfil y submit compitan",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d8e603bfbe4b0202319c",
          "title": "espera user y profile antes de montar el destino protegido",
          "full_title": "login navigation > espera user y profile antes de montar el destino protegido",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/auth/logout-navigation.test.ts",
      "type": "unit",
      "sha256": "dc3ae928bddc6fce6250167f7dcd3a7798e676904d65e249e18eabd070139923",
      "tests": [
        {
          "id": "05cd85ec522a55de9845",
          "title": "usa una ruta pública distinta para reconstruir Firestore",
          "full_title": "logout navigation > usa una ruta pública distinta para reconstruir Firestore",
          "line": 9,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "44aedffc56bdd5d92b98",
          "title": "no reinicia Firestore durante un login normal",
          "full_title": "logout navigation > no reinicia Firestore durante un login normal",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/auth/patient-access-policy.test.ts",
      "type": "unit",
      "sha256": "ffd69a258f27570b20568218e9c7d8eed0c5cd6ccc7cbaa6c64f2273c125b5ba",
      "tests": [
        {
          "id": "0d881463e7a724c9f177",
          "title": "bloquea en HIXION por estado canónico o respaldo legacy",
          "full_title": "isPatientAccessSuspended > bloquea en HIXION por estado canónico o respaldo legacy",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6eb7ef1114d1ca7840db",
          "title": "preserva otros tenants hasta una migración explícita",
          "full_title": "isPatientAccessSuspended > preserva otros tenants hasta una migración explícita",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b211075987ca661e4e53",
          "title": "no acepta un documento de otro workspace",
          "full_title": "isPatientAccessSuspended > no acepta un documento de otro workspace",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/auth/permissions-rules-parity.test.ts",
      "type": "unit",
      "sha256": "0b6f9caaafdb181dabd37295c02860ef2a4971a8e5cabc9bdbf2aaee2ccf3c35",
      "tests": [
        {
          "id": "0b16715cb44fc76c5fdf",
          "title": "%s concede los mismos roles en TS y en las rules",
          "full_title": "paridad entre ROLES_PREDEFINIDOS y hasDefaultPermission > %s concede los mismos roles en TS y en las rules",
          "line": 57,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "9751c83949c4bda3145b",
          "title": "las rules no mencionan un permiso que no existe en PERMISOS_DISPONIBLES",
          "full_title": "paridad entre ROLES_PREDEFINIDOS y hasDefaultPermission > las rules no mencionan un permiso que no existe en PERMISOS_DISPONIBLES",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "377641cad94a3748c5f6",
          "title": "admin conserva todos los permisos",
          "full_title": "paridad entre ROLES_PREDEFINIDOS y hasDefaultPermission > admin conserva todos los permisos",
          "line": 69,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a5c65d92481916d8c196",
          "title": "comercial nace solo con el pipeline, aunque se le pueda ampliar",
          "full_title": "paridad entre ROLES_PREDEFINIDOS y hasDefaultPermission > comercial nace solo con el pipeline, aunque se le pueda ampliar",
          "line": 77,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/auth/permissions.test.ts",
      "type": "unit",
      "sha256": "2f69cd89f06a1a8afc6bb3386953e2f936ab9aa2909c01126b8cfb7d89315182",
      "tests": [
        {
          "id": "3645fcad51bcdeb82d86",
          "title": "aplica overrides del tenant sobre los defaults de UI y servidor",
          "full_title": "matriz RBAC compartida > aplica overrides del tenant sobre los defaults de UI y servidor",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ff99ceb3ac7cce7b0a17",
          "title": "serializa una proyección estable que pueden consumir Firestore y Storage",
          "full_title": "matriz RBAC compartida > serializa una proyección estable que pueden consumir Firestore y Storage",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "73286ef846f741268ca2",
          "title": "paciente y rol desconocido no reciben permisos de equipo",
          "full_title": "matriz RBAC compartida > paciente y rol desconocido no reciben permisos de equipo",
          "line": 38,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/auth/portal-patient-binding.test.ts",
      "type": "unit",
      "sha256": "fdc2a00e3dfc99ba5f94f57b9c3fda6b8f2e4c6daaab430d54fc4e90a49a979d",
      "tests": [
        {
          "id": "4bb5607c974e55496c66",
          "title": "derives the clinical id only from an authenticated patient profile",
          "full_title": "portal patient identity binding > derives the clinical id only from an authenticated patient profile",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "148e442314d3e5611fed",
          "title": "invalidates a session as soon as its patient id diverges",
          "full_title": "portal patient identity binding > invalidates a session as soon as its patient id diverges",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "43f35bc6bfe9014a4cb2",
          "title": "does not persist clinical patient objects or fall back to email matching",
          "full_title": "portal patient identity binding > does not persist clinical patient objects or fall back to email matching",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/auth/roles-custom.test.ts",
      "type": "unit",
      "sha256": "1816a2a00ed01f437937e0dd9f821a3676155cd401ac750879156dc1d66766c7",
      "tests": [
        {
          "id": "0a020828731dd373496b",
          "title": "rechaza el id reservado %s",
          "full_title": "validateRoleId > rechaza el id reservado %s",
          "line": 30,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "6569d8088eec6a509699",
          "title": "rechaza espacios, acentos y longitudes fuera de rango",
          "full_title": "validateRoleId > rechaza espacios, acentos y longitudes fuera de rango",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fd5258b0844b6dfa12bf",
          "title": "acepta un id bien formado y lo normaliza",
          "full_title": "validateRoleId > acepta un id bien formado y lo normaliza",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "60cf54322cfc62a0213b",
          "title": "rechaza un id que ya existe, incluido un rol del sistema",
          "full_title": "validateRoleId > rechaza un id que ya existe, incluido un rol del sistema",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0a84b149b8b05aa5a0fd",
          "title": "deriva un id válido del nombre visible",
          "full_title": "slugifyRoleId > deriva un id válido del nombre visible",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ccd22bc9b324536c5146",
          "title": "retira gestionar_roles de un rol propio",
          "full_title": "stripRoleEscalation > retira gestionar_roles de un rol propio",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4f205b1570d02261738e",
          "title": "no toca un rol del sistema",
          "full_title": "stripRoleEscalation > no toca un rol del sistema",
          "line": 77,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "67f9f1bbfea30a563b9c",
          "title": "es idempotente y conserva la identidad de lo que no cambia",
          "full_title": "stripRoleEscalation > es idempotente y conserva la identidad de lo que no cambia",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2c7f151c5f194293a07a",
          "title": "los siete roles de la plataforma pertenecen al equipo",
          "full_title": "área de staff > los siete roles de la plataforma pertenecen al equipo",
          "line": 96,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2f6b5d4636004ac0c481",
          "title": "un rol propio entra al equipo sin tocar código",
          "full_title": "área de staff > un rol propio entra al equipo sin tocar código",
          "line": 102,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3849714e64a01d1d3f0d",
          "title": "un rol propio declarado fuera del área no entra",
          "full_title": "área de staff > un rol propio declarado fuera del área no entra",
          "line": 108,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a3794a5cb2778f814f78",
          "title": "el tenant puede meter a comercial al equipo desde su matriz",
          "full_title": "área de staff > el tenant puede meter a comercial al equipo desde su matriz",
          "line": 113,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b7e808e0225bc111364f",
          "title": "un rol desconocido que no está en la matriz no es del equipo",
          "full_title": "área de staff > un rol desconocido que no está en la matriz no es del equipo",
          "line": 120,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "206a857e5d00a582c719",
          "title": "superadmin es del equipo aunque no figure en la matriz del tenant",
          "full_title": "área de staff > superadmin es del equipo aunque no figure en la matriz del tenant",
          "line": 124,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fffe44770768aa16740e",
          "title": "salen de su definición, no de ningún default",
          "full_title": "permisos de un rol propio > salen de su definición, no de ningún default",
          "line": 130,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8155c37f4130a549cbbe",
          "title": "un id que no existe en la matriz no obtiene nada",
          "full_title": "permisos de un rol propio > un id que no existe en la matriz no obtiene nada",
          "line": 144,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6e64cdfb951938750330",
          "title": "retira las claves sin valor antes de escribir en Firestore",
          "full_title": "dropUndefinedFields > retira las claves sin valor antes de escribir en Firestore",
          "line": 150,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "689abea128b0b57547b7",
          "title": "no toca un rol que ya está limpio",
          "full_title": "dropUndefinedFields > no toca un rol que ya está limpio",
          "line": 168,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0f5260f7a701a20568f4",
          "title": "un rol `paciente` marcado como staff en la matriz NO entra al equipo",
          "full_title": "ids que nunca entran al área de staff > un rol `paciente` marcado como staff en la matriz NO entra al equipo",
          "line": 175,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "157e6d0562021ad4f81e",
          "title": "admin y superadmin sí son del equipo pese a ser ids reservados",
          "full_title": "ids que nunca entran al área de staff > admin y superadmin sí son del equipo pese a ser ids reservados",
          "line": 189,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4f427d7f94e980759461",
          "title": "comercial recibe lo que la matriz le concede",
          "full_title": "la matriz manda para todos los roles > comercial recibe lo que la matriz le concede",
          "line": 211,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c4b5f640a4d82ba869ca",
          "title": "comercial pertenece al equipo, como cualquier otro rol",
          "full_title": "la matriz manda para todos los roles > comercial pertenece al equipo, como cualquier otro rol",
          "line": 219,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0503e4337adde777170f",
          "title": "de fábrica sólo puede ver y operar el pipeline",
          "full_title": "la matriz manda para todos los roles > de fábrica sólo puede ver y operar el pipeline",
          "line": 226,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1ba18e4bf5a42eb3cc03",
          "title": "la proyección sanitizada sigue siendo consecuencia de la matriz",
          "full_title": "la matriz manda para todos los roles > la proyección sanitizada sigue siendo consecuencia de la matriz",
          "line": 234,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "585ed6c3e494778d8da1",
          "title": "un rol propio fuera del área conserva sus permisos igualmente",
          "full_title": "la matriz manda para todos los roles > un rol propio fuera del área conserva sus permisos igualmente",
          "line": 240,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/auth/server-route-access.test.ts",
      "type": "unit",
      "sha256": "d1f0bfaa245b0af5a96cb5176ef5c1cd379ddcc6bf94722b6ac9c91e40660320",
      "tests": [
        {
          "id": "ec4a37064eaad074d01a",
          "title": "permite a %s entrar al área staff",
          "full_title": "autorización server-side por área > permite a %s entrar al área staff",
          "line": 8,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "c20a28d0d191663f16ff",
          "title": "redirige al paciente antes de renderizar una ruta staff",
          "full_title": "autorización server-side por área > redirige al paciente antes de renderizar una ruta staff",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "44ca46dbd33f0629d4e2",
          "title": "reserva el portal exclusivamente al paciente",
          "full_title": "autorización server-side por área > reserva el portal exclusivamente al paciente",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "98be5c4d79f7fab138f2",
          "title": "rechaza perfiles ausentes",
          "full_title": "autorización server-side por área > rechaza perfiles ausentes",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/auth/server-route-guard.test.ts",
      "type": "unit",
      "sha256": "6d95c85013d47cf6b0bce99a4abe9e8dbeddd21f0636fce8888d4d023a0412ce",
      "tests": [
        {
          "id": "c825c8e314933e7a9c14",
          "title": "rechaza antes de consultar Firebase cuando no existe cookie",
          "full_title": "requireProtectedRouteArea > rechaza antes de consultar Firebase cuando no existe cookie",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b8d5538394ae883582fd",
          "title": "rechaza un token inválido",
          "full_title": "requireProtectedRouteArea > rechaza un token inválido",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "19e8776b00b9ecd7978f",
          "title": "redirige al paciente fuera del área staff antes de renderizar",
          "full_title": "requireProtectedRouteArea > redirige al paciente fuera del área staff antes de renderizar",
          "line": 60,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7ef832ea13fcd1e783da",
          "title": "permite al paciente entrar al portal",
          "full_title": "requireProtectedRouteArea > permite al paciente entrar al portal",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0514e305d93945aa689c",
          "title": "bloquea el portal desde servidor para un paciente HIXION suspendido",
          "full_title": "requireProtectedRouteArea > bloquea el portal desde servidor para un paciente HIXION suspendido",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "94c8a5b13b5cc0272f08",
          "title": "permite el portal HIXION después de un restablecimiento controlado",
          "full_title": "requireProtectedRouteArea > permite el portal HIXION después de un restablecimiento controlado",
          "line": 83,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7269e4175bdaa259c0ec",
          "title": "redirige a un miembro del equipo fuera del portal",
          "full_title": "requireProtectedRouteArea > redirige a un miembro del equipo fuera del portal",
          "line": 94,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "32bfe283cf3c0686a608",
          "title": "permite a un miembro activo del equipo entrar al área staff",
          "full_title": "requireProtectedRouteArea > permite a un miembro activo del equipo entrar al área staff",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6747df343193959bf4a0",
          "title": "rechaza perfiles inactivos o inexistentes",
          "full_title": "requireProtectedRouteArea > rechaza perfiles inactivos o inexistentes",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/acompanamientos-sugeridos.test.ts",
      "type": "unit",
      "sha256": "3aafe89f133ee47d68e2a9f7426f05cb6a8df8e0519d58350b04edd0f5f30f9d",
      "tests": [
        {
          "id": "cf656455994f2ba1fbc3",
          "title": "devuelve [] si la receta no es plato principal",
          "full_title": "getAcompanamientosSugeridos > devuelve [] si la receta no es plato principal",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "43988b613453cd1c1d28",
          "title": "solo sugiere candidatos con rol acompañamiento o bebida de cocina compatible",
          "full_title": "getAcompanamientosSugeridos > solo sugiere candidatos con rol acompañamiento o bebida de cocina compatible",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e6ff012d7a3a6a187fbb",
          "title": "excluye la receta misma del resultado",
          "full_title": "getAcompanamientosSugeridos > excluye la receta misma del resultado",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1e461b454b0de6ef789c",
          "title": "prioriza misma cocina sobre cocina relacionada y acompañamiento sobre bebida",
          "full_title": "getAcompanamientosSugeridos > prioriza misma cocina sobre cocina relacionada y acompañamiento sobre bebida",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0f1533a0a0a0df5a96db",
          "title": "con plato alto en proteína (>25g) da boost a acompañamientos con más CHO que proteína",
          "full_title": "getAcompanamientosSugeridos > con plato alto en proteína (>25g) da boost a acompañamientos con más CHO que proteína",
          "line": 53,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "401bcb10116e15d368be",
          "title": "respeta el límite de resultados",
          "full_title": "getAcompanamientosSugeridos > respeta el límite de resultados",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ab81bc786ee21e43f8c8",
          "title": "el rol legacy 'cafe' se trata como bebida",
          "full_title": "getAcompanamientosSugeridos > el rol legacy 'cafe' se trata como bebida",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/adherencia-fitness.test.ts",
      "type": "unit",
      "sha256": "3f7ac3349e5be5eca4d309dd6d1d5d4336126a407c1d655bb259b210a2fe587b",
      "tests": [
        {
          "id": "f2aa2c951e9f0e24bbd9",
          "title": "calcula porcentaje sobre sesiones planificadas: ceil(ventana/7) × diasSemana",
          "full_title": "calcularAdherencia > calcula porcentaje sobre sesiones planificadas: ceil(ventana/7) × diasSemana",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1e2b08a470563e15e92e",
          "title": "ignora sesiones de otras rutinas y sesiones no completadas",
          "full_title": "calcularAdherencia > ignora sesiones de otras rutinas y sesiones no completadas",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a5ade0273d159e38d698",
          "title": "excluye sesiones fuera de la ventana",
          "full_title": "calcularAdherencia > excluye sesiones fuera de la ventana",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "78c226803c09f8f34783",
          "title": "usa exactamente los últimos N días incluyendo hoy",
          "full_title": "calcularAdherencia > usa exactamente los últimos N días incluyendo hoy",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "55549295d2cc85226a44",
          "title": "suma el denominador de múltiples rutinas asignadas",
          "full_title": "calcularAdherencia > suma el denominador de múltiples rutinas asignadas",
          "line": 75,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b9a1a4ac53d3f267140e",
          "title": "cuenta los días programados tras cambios y excluye descanso de vacaciones",
          "full_title": "calcularAdherencia > cuenta los días programados tras cambios y excluye descanso de vacaciones",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "03930f4b161c1a9972b4",
          "title": "cuenta como 0 un descanso declarado después de una rutina prevista",
          "full_title": "calcularAdherencia > cuenta como 0 un descanso declarado después de una rutina prevista",
          "line": 96,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2e6c05b803ac0861b3e0",
          "title": "el porcentaje se capea en 100",
          "full_title": "calcularAdherencia > el porcentaje se capea en 100",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b22b4e8a785506f5025a",
          "title": "ventana de 0 días produce 0 planificadas y 0%",
          "full_title": "calcularAdherencia > ventana de 0 días produce 0 planificadas y 0%",
          "line": 116,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "96cfa0c99eb8f96ebfcd",
          "title": "cuenta streak de semanas ISO consecutivas que cumplen diasSemana",
          "full_title": "calcularAdherencia > cuenta streak de semanas ISO consecutivas que cumplen diasSemana",
          "line": 122,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b39b07c1889e8a9e7f1b",
          "title": "streak es 0 si la semana actual no alcanza el objetivo",
          "full_title": "calcularAdherencia > streak es 0 si la semana actual no alcanza el objetivo",
          "line": 137,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cd8111605651f48a462a",
          "title": "el streak cruza el cambio de año usando semanas ISO",
          "full_title": "calcularAdherencia > el streak cruza el cambio de año usando semanas ISO",
          "line": 148,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/distribucion-tope.test.ts",
      "type": "unit",
      "sha256": "3e61cc06599f36a724d3970012d560de10fb4e4f582aab06693cbe6f1763c265",
      "tests": [
        {
          "id": "60805e1e50391dae2600",
          "title": "sin menús u override devuelve la distribución base del plan",
          "full_title": "getDistribucionEfectivaDelMenu > sin menús u override devuelve la distribución base del plan",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7d5e87ddad3d87ecdfdc",
          "title": "aplica el override del menú slot a slot encima de la base",
          "full_title": "getDistribucionEfectivaDelMenu > aplica el override del menú slot a slot encima de la base",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "54257c3f6ca19ef66616",
          "title": "empareja overrides explícitos por tiempo aunque lleguen reordenados",
          "full_title": "getDistribucionEfectivaDelMenu > empareja overrides explícitos por tiempo aunque lleguen reordenados",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9f7f8e851aad434dcd66",
          "title": "un override vacío equivale a no tener override",
          "full_title": "getDistribucionEfectivaDelMenu > un override vacío equivale a no tener override",
          "line": 76,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "21999b63af28c6a83eae",
          "title": "devuelve las porciones del grupo en el slot",
          "full_title": "getTopePorcionesEnTiempo > devuelve las porciones del grupo en el slot",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "56864a654890822c033c",
          "title": "suma las porciones de los grupos aliados configurados en intercambiosGrupos",
          "full_title": "getTopePorcionesEnTiempo > suma las porciones de los grupos aliados configurados en intercambiosGrupos",
          "line": 90,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5f5e672ab92a29e7c09b",
          "title": "devuelve 0 si el tiempo de comida no existe en la distribución",
          "full_title": "getTopePorcionesEnTiempo > devuelve 0 si el tiempo de comida no existe en la distribución",
          "line": 96,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "da2e9637301e08f4642b",
          "title": "mantiene topes separados para azúcares y grasas con proteína",
          "full_title": "getTopePorcionesEnTiempo > mantiene topes separados para azúcares y grasas con proteína",
          "line": 100,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "729419051809fafba763",
          "title": "interpreta intercambios legacy en ambas direcciones",
          "full_title": "getTopePorcionesEnTiempo > interpreta intercambios legacy en ambas direcciones",
          "line": 116,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d96bfb847583eb7852d2",
          "title": "suma solo las porciones del grupo pedido",
          "full_title": "getPorcionesUsadasEnReceta > suma solo las porciones del grupo pedido",
          "line": 130,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "df821f7527d96c43a22a",
          "title": "incluye a los aliados del grupo en la suma",
          "full_title": "getPorcionesUsadasEnReceta > incluye a los aliados del grupo en la suma",
          "line": 134,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a80a9a7460349f3a961f",
          "title": "excluye el ingrediente en edición con excludeIdx",
          "full_title": "getPorcionesUsadasEnReceta > excluye el ingrediente en edición con excludeIdx",
          "line": 138,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aa511cab9822351f22d4",
          "title": "ingredientes sin porciones cuentan como 0",
          "full_title": "getPorcionesUsadasEnReceta > ingredientes sin porciones cuentan como 0",
          "line": 142,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "23076cb0ae5201dc5e2e",
          "title": "resta lo consumido al tope del slot",
          "full_title": "getRemanentePorcionesEnTiempo > resta lo consumido al tope del slot",
          "line": 149,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "789898552ec3607c53d3",
          "title": "nunca devuelve negativo aunque la receta exceda el tope",
          "full_title": "getRemanentePorcionesEnTiempo > nunca devuelve negativo aunque la receta exceda el tope",
          "line": 155,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3051e5e83f72bb3c4924",
          "title": "usa los intercambios del plan tanto para el tope como para lo consumido",
          "full_title": "getRemanentePorcionesEnTiempo > usa los intercambios del plan tanto para el tope como para lo consumido",
          "line": 161,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "02470644d18b97173b16",
          "title": "excludeIdx libera las porciones del ingrediente en edición",
          "full_title": "getRemanentePorcionesEnTiempo > excludeIdx libera las porciones del ingrediente en edición",
          "line": 171,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/equivalencias-fitness.test.ts",
      "type": "unit",
      "sha256": "bac09baac9fe8917e6f9870e7a89a3321693135472a57cb60f9364304f9cac39",
      "tests": [
        {
          "id": "cd7de2fe51707dc2ac40",
          "title": "un candidato idéntico en músculo/patrón/dificultad puntúa 0.925 con los pesos default",
          "full_title": "calcularEquivalentes > un candidato idéntico en músculo/patrón/dificultad puntúa 0.925 con los pesos default",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f6d9f7b59d11a5f7fb6c",
          "title": "excluye candidatos por debajo del threshold",
          "full_title": "calcularEquivalentes > excluye candidatos por debajo del threshold",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9c61b57028c3d842b910",
          "title": "descarta candidatos cuyo equipo no está disponible",
          "full_title": "calcularEquivalentes > descarta candidatos cuyo equipo no está disponible",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a8129f9482a2d4e80362",
          "title": "sin equipo configurado no aplica filtro duro de equipo",
          "full_title": "calcularEquivalentes > sin equipo configurado no aplica filtro duro de equipo",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "135eb7098f797d032455",
          "title": "los ejercicios de peso corporal siempre pasan el filtro de equipo",
          "full_title": "calcularEquivalentes > los ejercicios de peso corporal siempre pasan el filtro de equipo",
          "line": 60,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9eaf6f0dc56e572e13e0",
          "title": "descarta músculos incompatibles y patrones opuestos",
          "full_title": "calcularEquivalentes > descarta músculos incompatibles y patrones opuestos",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "62f41d4d61fe22e874de",
          "title": "el patrón de la misma familia (empuje H ↔ V) puntúa 0.5",
          "full_title": "calcularEquivalentes > el patrón de la misma familia (empuje H ↔ V) puntúa 0.5",
          "line": 76,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9f3cb6e20673dc718100",
          "title": "no incluye el ejercicio base en los resultados",
          "full_title": "calcularEquivalentes > no incluye el ejercicio base en los resultados",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7d010e866b0de4c3fa90",
          "title": "las relaciones curadas de ExerciseDB entran primero con score por confianza",
          "full_title": "calcularEquivalentes > las relaciones curadas de ExerciseDB entran primero con score por confianza",
          "line": 91,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "96ff7823994e9db8dabc",
          "title": "las relaciones low no se ofrecen aunque existan en catálogo",
          "full_title": "calcularEquivalentes > las relaciones low no se ofrecen aunque existan en catálogo",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0109225fdba4f581252e",
          "title": "las relaciones con missingTarget o archivadas se ignoran",
          "full_title": "calcularEquivalentes > las relaciones con missingTarget o archivadas se ignoran",
          "line": 116,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ff6738f8230f07f93620",
          "title": "ordena por score descendente y limita a 10 resultados",
          "full_title": "calcularEquivalentes > ordena por score descendente y limita a 10 resultados",
          "line": 128,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/equivalencias.test.ts",
      "type": "unit",
      "sha256": "e10599efa79726a433c4cfeb4bca4d7be2b850f118343f4c2841781965b0154a",
      "tests": [
        {
          "id": "25825a2161cb75eb6b79",
          "title": "devuelve solo ingredientes del mismo grupo SMAE, excluyendo al propio ingrediente",
          "full_title": "getEquivalentes > devuelve solo ingredientes del mismo grupo SMAE, excluyendo al propio ingrediente",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2c257dcff12b2baa5ece",
          "title": "incluye grupos aliados cuando el nutriólogo definió intercambios",
          "full_title": "getEquivalentes > incluye grupos aliados cuando el nutriólogo definió intercambios",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "86f7bd1b01d19fdda32e",
          "title": "no incluye grupos aliados de otros grupos distintos al del ingrediente",
          "full_title": "getEquivalentes > no incluye grupos aliados de otros grupos distintos al del ingrediente",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "712f36aabe0e070d052f",
          "title": "devuelve lista vacía si nadie más comparte grupo",
          "full_title": "getEquivalentes > devuelve lista vacía si nadie más comparte grupo",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fbd639b2692a093a4374",
          "title": "mantiene el número de porciones SMAE: Espinaca 150g (3 porc x 50g) -> Lechuga 300g (3 porc x 100g)",
          "full_title": "calcularSwap > mantiene el número de porciones SMAE: Espinaca 150g (3 porc x 50g) -> Lechuga 300g (3 porc x 100g)",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0648bf4cb1d0e7d2d154",
          "title": "deriva las porciones de cantidad/porción del catálogo cuando la receta no las trae",
          "full_title": "calcularSwap > deriva las porciones de cantidad/porción del catálogo cuando la receta no las trae",
          "line": 60,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "07e3faec2037884316c8",
          "title": "asume 1 porción si el ingrediente no está en el catálogo y la receta no trae porciones",
          "full_title": "calcularSwap > asume 1 porción si el ingrediente no está en el catálogo y la receta no trae porciones",
          "line": 68,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4845354e5ef24c858336",
          "title": "prefiere las porciones normalizadas de la receta sobre la cantidad legada escalada",
          "full_title": "calcularSwap > prefiere las porciones normalizadas de la receta sobre la cantidad legada escalada",
          "line": 77,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d2b4a0914cd600c9158c",
          "title": "redondea cantidad y nutrición con porciones fraccionarias",
          "full_title": "calcularSwap > redondea cantidad y nutrición con porciones fraccionarias",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "da893c7375a27594bd3c",
          "title": "construye el ingrediente de receta con identidad del nuevo y porciones del viejo",
          "full_title": "buildSwappedIngrediente > construye el ingrediente de receta con identidad del nuevo y porciones del viejo",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b0ab3605864332613bd5",
          "title": "usa la cantidad del ingrediente viejo de la receta como base del cálculo",
          "full_title": "buildSwappedIngrediente > usa la cantidad del ingrediente viejo de la receta como base del cálculo",
          "line": 110,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/heath-carter.test.ts",
      "type": "unit",
      "sha256": "c908997ba61ec2bd30eccd9445f3da313e33000f83c901399f27f623be2a5566",
      "tests": [
        {
          "id": "acf78f9f3c99e2072ccc",
          "title": "calcula endo/meso/ecto con las constantes de Heath-Carter (caso a mano)",
          "full_title": "calcularSomatotipo > calcula endo/meso/ecto con las constantes de Heath-Carter (caso a mano)",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c5f57a9a305b1ff94d07",
          "title": "usa la rama intermedia de ectomorfia cuando 38.25 < IP < 40.75",
          "full_title": "calcularSomatotipo > usa la rama intermedia de ectomorfia cuando 38.25 < IP < 40.75",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "af16bfecdce67bcfd2f4",
          "title": "asigna ectomorfia mínima 0.1 cuando IP <= 38.25",
          "full_title": "calcularSomatotipo > asigna ectomorfia mínima 0.1 cuando IP <= 38.25",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a2b5b9b7fd2571fb7172",
          "title": "marca tipo combinado cuando los dos componentes más altos difieren <= 0.5",
          "full_title": "calcularSomatotipo > marca tipo combinado cuando los dos componentes más altos difieren <= 0.5",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9124784845b4cf01f885",
          "title": "lanza error con nombre de campo cuando falta una medición",
          "full_title": "calcularSomatotipo > lanza error con nombre de campo cuando falta una medición",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "89330f884a897cbd219f",
          "title": "mapea una medición completa al formato Heath-Carter",
          "full_title": "medicionToHeathCarter > mapea una medición completa al formato Heath-Carter",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a7450209d5fc530c3113",
          "title": "devuelve null cuando falta %s",
          "full_title": "medicionToHeathCarter > devuelve null cuando falta %s",
          "line": 107,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/intercambios-grupos.test.ts",
      "type": "unit",
      "sha256": "b2e3514a3922675ee3d24f586a1646837b8310a6170aa058be011e288a9bd3b8",
      "tests": [
        {
          "id": "7847333a482380c87631",
          "title": "repara relaciones legacy guardadas en un solo sentido",
          "full_title": "intercambios de grupos SMAE > repara relaciones legacy guardadas en un solo sentido",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9cf73d238f4c6b5bf101",
          "title": "agrega la relación inversa al seleccionar un aliado",
          "full_title": "intercambios de grupos SMAE > agrega la relación inversa al seleccionar un aliado",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e1c36fed01efb54c7a31",
          "title": "retira ambas direcciones al deseleccionar un aliado",
          "full_title": "intercambios de grupos SMAE > retira ambas direcciones al deseleccionar un aliado",
          "line": 26,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/macros-por-comida.test.ts",
      "type": "unit",
      "sha256": "3638a301ea86a865635d79c51e2eeda82281ec02dca18292c90da1f703dbca5a",
      "tests": [
        {
          "id": "fd2844d4c2f82b6a8519",
          "title": "devuelve [] con distribución vacía, null o undefined",
          "full_title": "calcularMacrosPorComida > devuelve [] con distribución vacía, null o undefined",
          "line": 7,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d796c6ec13ee68db7743",
          "title": "calcula kcal y porcentajes Atwater de una comida (valores SMAE a mano)",
          "full_title": "calcularMacrosPorComida > calcula kcal y porcentajes Atwater de una comida (valores SMAE a mano)",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aee053e99295bb4ea69a",
          "title": "una comida sin porciones queda marcada isEmpty con porcentajes 0",
          "full_title": "calcularMacrosPorComida > una comida sin porciones queda marcada isEmpty con porcentajes 0",
          "line": 38,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fa2c2b8dbf8c62b09058",
          "title": "procesa cada comida de la distribución de forma independiente",
          "full_title": "calcularMacrosPorComida > procesa cada comida de la distribución de forma independiente",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "07ea2ed5e366b64c1d47",
          "title": "los grupos ausentes en DistribucionComida (azúcares, grasas con proteína) se tratan como 0",
          "full_title": "calcularMacrosPorComida > los grupos ausentes en DistribucionComida (azúcares, grasas con proteína) se tratan como 0",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/menu-distribution-adjustment.test.ts",
      "type": "unit",
      "sha256": "a6d2a942fbdd1e10fecd5194b247fe802a4c4bd79bf6dcb489af37ce5d7ea5e4",
      "tests": [
        {
          "id": "d6bb5a68eb30a1f10729",
          "title": "persiste un ajuste de %s%% en distribución y porciones",
          "full_title": "scaleDistributionToTargetKcal > persiste un ajuste de %s%% en distribución y porciones",
          "line": 48,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "1c1dd0b1d4bd3a636190",
          "title": "conserva la distribución cuando no hay datos energéticos",
          "full_title": "scaleDistributionToTargetKcal > conserva la distribución cuando no hay datos energéticos",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/met.test.ts",
      "type": "unit",
      "sha256": "2a656ae396c6af1e03fa3d65357916f3600b819cd492b67a6f0453479e6a8f0d",
      "tests": [
        {
          "id": "520b916c1bc9a84a000b",
          "title": "MET %s con %s kg por %s min = %s kcal",
          "full_title": "calcularKcalMET > MET %s con %s kg por %s min = %s kcal",
          "line": 13,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "ae0f00e87fc956377750",
          "title": "devuelve 0 cuando el MET es 0 (sin actividad física)",
          "full_title": "calcularKcalMET > devuelve 0 cuando el MET es 0 (sin actividad física)",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2ac687860f8d5e119a49",
          "title": "devuelve 0 cuando los minutos son 0",
          "full_title": "calcularKcalMET > devuelve 0 cuando los minutos son 0",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "791854f5268c4f07362b",
          "title": "encuentra una actividad existente con su MET de tabla",
          "full_title": "getActividadById > encuentra una actividad existente con su MET de tabla",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "23ff32a27b8cc27e2063",
          "title": "devuelve undefined para un id inexistente",
          "full_title": "getActividadById > devuelve undefined para un id inexistente",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "497f5f1fe83e3e3112b9",
          "title": "agrupa todas las actividades sin perder ninguna",
          "full_title": "getActividadesPorCategoria > agrupa todas las actividades sin perder ninguna",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e617123a32e07c0c3600",
          "title": "cada categoría del resultado tiene nombre en español definido",
          "full_title": "getActividadesPorCategoria > cada categoría del resultado tiene nombre en español definido",
          "line": 53,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "71bda4eac4ab6d9c12bc",
          "title": "las pesas quedan en la categoría fuerza",
          "full_title": "getActividadesPorCategoria > las pesas quedan en la categoría fuerza",
          "line": 60,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/nutricion.test.ts",
      "type": "unit",
      "sha256": "48e8639a3a8ae342dc425fa7cd2949918b5c0a6ada01846c688aa497ee8e7af6",
      "tests": [
        {
          "id": "4b8c0b34bdd6184caca3",
          "title": "suma macros multiplicando por el factor de porciones de cada ingrediente",
          "full_title": "calcularNutricion > suma macros multiplicando por el factor de porciones de cada ingrediente",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3c33c1776f285a3f1991",
          "title": "suma micronutrientes con factor y redondeo a 2 decimales",
          "full_title": "calcularNutricion > suma micronutrientes con factor y redondeo a 2 decimales",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "db3bd52e4fd0b9d0d275",
          "title": "calcula índice glicémico ponderado por gramos de CHO",
          "full_title": "calcularNutricion > calcula índice glicémico ponderado por gramos de CHO",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b827b824fd818ef8f5f5",
          "title": "ignora ingredientes que no existen en el catálogo",
          "full_title": "calcularNutricion > ignora ingredientes que no existen en el catálogo",
          "line": 60,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eb37481a0e0ccb2efdf8",
          "title": "omite la llave micronutrientes cuando ningún ingrediente aporta micros",
          "full_title": "calcularNutricion > omite la llave micronutrientes cuando ningún ingrediente aporta micros",
          "line": 69,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "03df95cb0a16387b4a4e",
          "title": "lista vacía devuelve todo en cero",
          "full_title": "calcularNutricion > lista vacía devuelve todo en cero",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7467023781716c86f58f",
          "title": "usa la nutrición embebida de cada ingrediente multiplicada por porciones",
          "full_title": "calcularNutricionDesdeIngredientesEmbebidos > usa la nutrición embebida de cada ingrediente multiplicada por porciones",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3473c20552bef9c90c74",
          "title": "asume factor 1 cuando porciones es undefined",
          "full_title": "calcularNutricionDesdeIngredientesEmbebidos > asume factor 1 cuando porciones es undefined",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d17c14a941791838dcd4",
          "title": "trata nutrición embebida ausente como cero sin fallar",
          "full_title": "calcularNutricionDesdeIngredientesEmbebidos > trata nutrición embebida ausente como cero sin fallar",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5e6dc56909431ca6c481",
          "title": "pondera el índice glicémico embebido con los CHO de cada ingrediente",
          "full_title": "calcularNutricionDesdeIngredientesEmbebidos > pondera el índice glicémico embebido con los CHO de cada ingrediente",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/nutrition-calorie-comparison.test.ts",
      "type": "unit",
      "sha256": "9e20edeabc9ed1a6739915143e99a45aeca0310f37f3618fa57dbb773ec7bd7c",
      "tests": [
        {
          "id": "bedabc1f5d2e4066d823",
          "title": "cada incremento de 0.5 porción cambia las kcal SMAE del grupo",
          "full_title": "nutrition calorie comparison > cada incremento de 0.5 porción cambia las kcal SMAE del grupo",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7b7615cc46d5b8a88a69",
          "title": "suma distribución completa y recetas por separado",
          "full_title": "nutrition calorie comparison > suma distribución completa y recetas por separado",
          "line": 71,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dab244e34b97b5d1c4d6",
          "title": "centraliza la comparación de una sola comida",
          "full_title": "nutrition calorie comparison > centraliza la comparación de una sola comida",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "651b7b11741e619af44a",
          "title": "reporta recetas faltantes sin inventar kcal",
          "full_title": "nutrition calorie comparison > reporta recetas faltantes sin inventar kcal",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c1eab606305affad5daa",
          "title": "el mismo menú puede devolver kcal históricas distintas entre versiones",
          "full_title": "nutrition calorie comparison > el mismo menú puede devolver kcal históricas distintas entre versiones",
          "line": 93,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d16775ea741395f55d3e",
          "title": "resuelve la receta específica del día encima de la receta del menú",
          "full_title": "nutrition calorie comparison > resuelve la receta específica del día encima de la receta del menú",
          "line": 106,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/peso-ajustado.test.ts",
      "type": "unit",
      "sha256": "2ad7eafd330c7e9976ad767089cbb0487e14711a5dd9dfed88a1b3300a234f21",
      "tests": [
        {
          "id": "04e4e3762693447d5f07",
          "title": "IMC de %s kg y %s cm",
          "full_title": "calcularIMC > IMC de %s kg y %s cm",
          "line": 11,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "54929d026e141f07c08b",
          "title": "devuelve 0 con inputs inválidos (%s kg, %s cm)",
          "full_title": "calcularIMC > devuelve 0 con inputs inválidos (%s kg, %s cm)",
          "line": 20,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "54df464033fa7bf5a7b2",
          "title": "IMC %s = %s (%s)",
          "full_title": "clasificarIMC > IMC %s = %s (%s)",
          "line": 31,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "5e90b4fb152747ad5ce8",
          "title": "hombre: PI = E - 100 - (E-150)/4",
          "full_title": "calcularPesoIdeal > hombre: PI = E - 100 - (E-150)/4",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5d766c1038524d626b6d",
          "title": "mujer: PI = E - 100 - (E-150)/2.5",
          "full_title": "calcularPesoIdeal > mujer: PI = E - 100 - (E-150)/2.5",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c6f0ceaf783f0c96c19a",
          "title": "redondea a 1 decimal",
          "full_title": "calcularPesoIdeal > redondea a 1 decimal",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0fe5558392e4b03abee7",
          "title": "devuelve 0 con estatura inválida",
          "full_title": "calcularPesoIdeal > devuelve 0 con estatura inválida",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4f8f4b11161c3ccb0965",
          "title": "aplica PA = PI + 0.25 × (PR - PI) por defecto",
          "full_title": "calcularPesoAjustado > aplica PA = PI + 0.25 × (PR - PI) por defecto",
          "line": 68,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "40ef3116e993a8007fbe",
          "title": "acepta el factor permisivo 0.40",
          "full_title": "calcularPesoAjustado > acepta el factor permisivo 0.40",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cce441a07d61129631f9",
          "title": "devuelve null cuando el peso real no supera el ideal",
          "full_title": "calcularPesoAjustado > devuelve null cuando el peso real no supera el ideal",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8161093fc37808933af0",
          "title": "devuelve null para peso normal o sobrepeso aunque supere el peso ideal",
          "full_title": "calcularPesoAjustado > devuelve null para peso normal o sobrepeso aunque supere el peso ideal",
          "line": 83,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9d1619d1a2b5b12175f5",
          "title": "devuelve null con inputs inválidos",
          "full_title": "calcularPesoAjustado > devuelve null con inputs inválidos",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/porciones-sugeridas.test.ts",
      "type": "unit",
      "sha256": "63d11ef1e70e8e2bd6241f9cc5c2330177f77705f718c765ce626de72900c3dd",
      "tests": [
        {
          "id": "a15a1d01db9eb3fa748a",
          "title": "sin objetivo calórico devuelve porciones vacías y un warning accionable",
          "full_title": "sugerirPorciones — validación de entrada > sin objetivo calórico devuelve porciones vacías y un warning accionable",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "075aa29025f75cbc7665",
          "title": "para objetivo %s el total queda dentro de ±5%% de las kcal objetivo",
          "full_title": "sugerirPorciones — solver por objetivo clínico > para objetivo %s el total queda dentro de ±5%% de las kcal objetivo",
          "line": 43,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "aba807a028c6d48b1e36",
          "title": "solo devuelve porciones enteras y no negativas",
          "full_title": "sugerirPorciones — solver por objetivo clínico > solo devuelve porciones enteras y no negativas",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "04785118f873c8f94e61",
          "title": "resuelve el objetivo por label legacy sin marcar fallback",
          "full_title": "sugerirPorciones — solver por objetivo clínico > resuelve el objetivo por label legacy sin marcar fallback",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "757a3a731071f1f3c87d",
          "title": "cae a Salud general con warning cuando el objetivo es desconocido",
          "full_title": "sugerirPorciones — solver por objetivo clínico > cae a Salud general con warning cuando el objetivo es desconocido",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6441b770536b22ade2dd",
          "title": "hipertensión bloquea AOA alto en el resultado final",
          "full_title": "sugerirPorciones — restricciones y condiciones clínicas > hipertensión bloquea AOA alto en el resultado final",
          "line": 83,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d4ef998d2b480a51d74d",
          "title": "en un menú bajo con hipertensión prefiere quedar pendiente antes que usar AOA alto",
          "full_title": "sugerirPorciones — restricciones y condiciones clínicas > en un menú bajo con hipertensión prefiere quedar pendiente antes que usar AOA alto",
          "line": 102,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "15f9dd4982602b2b7608",
          "title": "usa las kcal efectivas del menú ajustado como objetivo de sugerencia",
          "full_title": "sugerirPorciones — restricciones y condiciones clínicas > usa las kcal efectivas del menú ajustado como objetivo de sugerencia",
          "line": 131,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e57841d956267c81d69c",
          "title": "nefropatía bloquea AOA moderado y AOA alto",
          "full_title": "sugerirPorciones — restricciones y condiciones clínicas > nefropatía bloquea AOA moderado y AOA alto",
          "line": 153,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0ead3a633e561efc5b21",
          "title": "diabetes deja azúcares en 0",
          "full_title": "sugerirPorciones — restricciones y condiciones clínicas > diabetes deja azúcares en 0",
          "line": 162,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ce7c29ddb0c5a836946b",
          "title": "restricción sin lactosa deja todos los lácteos en 0 y compensa las kcal",
          "full_title": "sugerirPorciones — restricciones y condiciones clínicas > restricción sin lactosa deja todos los lácteos en 0 y compensa las kcal",
          "line": 168,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8fc4a1d97011d4321d14",
          "title": "restricción vegana deja AOA y lácteos en 0",
          "full_title": "sugerirPorciones — restricciones y condiciones clínicas > restricción vegana deja AOA y lácteos en 0",
          "line": 176,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "810b07bc0b6060b1f8e9",
          "title": "anemia + vegano genera warning clínico de hierro/B12",
          "full_title": "sugerirPorciones — restricciones y condiciones clínicas > anemia + vegano genera warning clínico de hierro/B12",
          "line": 184,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "35fdc4f714df9818ad05",
          "title": "el rechazo total por preferencias reduce el grupo pero no lo bloquea clínicamente",
          "full_title": "sugerirPorciones — preferencias de ingredientes > el rechazo total por preferencias reduce el grupo pero no lo bloquea clínicamente",
          "line": 201,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "36941f490f4b173b8984",
          "title": "las preferencias positivas se reportan en la explicación sin romper el presupuesto calórico",
          "full_title": "sugerirPorciones — preferencias de ingredientes > las preferencias positivas se reportan en la explicación sin romper el presupuesto calórico",
          "line": 222,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/porciones.test.ts",
      "type": "unit",
      "sha256": "488dfc186ff63ad1a2b533d159c43ba8afd80b3c18ec91e69cdde7463d2ff2c4",
      "tests": [
        {
          "id": "d05d4caa911b593c3647",
          "title": "cubre los 15 sub-grupos SMAE del sistema de porciones",
          "full_title": "VALORES_PORCIONES (tabla SMAE) > cubre los 15 sub-grupos SMAE del sistema de porciones",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3bd9ef3e83d54408ba62",
          "title": "devuelve 0 en todo con cero porciones",
          "full_title": "calcularTotalesPorciones > devuelve 0 en todo con cero porciones",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "95c11da160b926c2bf70",
          "title": "2 porciones de %s aportan %s kcal, %s CHO, %s PROT, %s grasas",
          "full_title": "calcularTotalesPorciones > 2 porciones de %s aportan %s kcal, %s CHO, %s PROT, %s grasas",
          "line": 57,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "c7ebd5f110bc0d63d31c",
          "title": "suma un menú combinado tipo desayuno (verduras + frutas + cereales + AOA bajo + grasas)",
          "full_title": "calcularTotalesPorciones > suma un menú combinado tipo desayuno (verduras + frutas + cereales + AOA bajo + grasas)",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0430756592b38d04c65b",
          "title": "acepta medias porciones y redondea cada total al final",
          "full_title": "calcularTotalesPorciones > acepta medias porciones y redondea cada total al final",
          "line": 76,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e45cf40b1c9935d7ebce",
          "title": "redondea el acumulado, no cada grupo por separado",
          "full_title": "calcularTotalesPorciones > redondea el acumulado, no cada grupo por separado",
          "line": 82,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "be85d19f4735f7c8647b",
          "title": "no cuenta CHO para AOA ni grasas, ni PROT para frutas/azúcares (convención SMAE)",
          "full_title": "calcularTotalesPorciones > no cuenta CHO para AOA ni grasas, ni PROT para frutas/azúcares (convención SMAE)",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/portal-mutation-contract.test.ts",
      "type": "unit",
      "sha256": "6789e5fda3f1bf4f72d5f950ef0caaa2faf680086a443c59ef4793b982106c7b",
      "tests": [
        {
          "id": "7d9d50fe56359dd4999b",
          "title": "el cliente acepta un ajuste que deja porciones libres",
          "full_title": "contrato de mutación del portal > el cliente acepta un ajuste que deja porciones libres",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "710a90af38ea8283a919",
          "title": "el cliente sí bloquea cuando el ajuste excede el tope",
          "full_title": "contrato de mutación del portal > el cliente sí bloquea cuando el ajuste excede el tope",
          "line": 63,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bc993442ffc6bc612502",
          "title": "elegir OTRO platillo sí debe encajar en la distribución",
          "full_title": "contrato de mutación del portal > elegir OTRO platillo sí debe encajar en la distribución",
          "line": 69,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9df95b75e2c75b2f406e",
          "title": "el servidor debe aceptar el mismo ajuste de ingredientes que aceptó el cliente",
          "full_title": "contrato de mutación del portal > el servidor debe aceptar el mismo ajuste de ingredientes que aceptó el cliente",
          "line": 75,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5f61a45c1065cd494041",
          "title": "el servidor sigue bloqueando lo que excede el tope",
          "full_title": "contrato de mutación del portal > el servidor sigue bloqueando lo que excede el tope",
          "line": 82,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7e720041bd089ac6f841",
          "title": "el servidor bloquea un grupo ajeno a la distribución del tiempo",
          "full_title": "contrato de mutación del portal > el servidor bloquea un grupo ajeno a la distribución del tiempo",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ab6b2df231e0e2981b5c",
          "title": "con equivalencias, el aliado consume el cupo compartido sin excederlo",
          "full_title": "contrato de mutación del portal > con equivalencias, el aliado consume el cupo compartido sin excederlo",
          "line": 92,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1d94d9e2790db4da51be",
          "title": "con equivalencias sigue bloqueando si el conjunto supera el cupo compartido",
          "full_title": "contrato de mutación del portal > con equivalencias sigue bloqueando si el conjunto supera el cupo compartido",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/portion-groups.test.ts",
      "type": "unit",
      "sha256": "d83007791670852521cdbb958025da52b00f075830c727d2dc6b0ddfd5ca500b",
      "tests": [
        {
          "id": "fb19ebad4f7ff4f205e0",
          "title": "sin equivalencias emite una familia por grupo establecido",
          "full_title": "buildPortionFamilies > sin equivalencias emite una familia por grupo establecido",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5e1ab5798c746e11981c",
          "title": "colapsa los grupos intercambiables en una sola familia y no duplica el pool",
          "full_title": "buildPortionFamilies > colapsa los grupos intercambiables en una sola familia y no duplica el pool",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "19257253602747f843ad",
          "title": "el permitido de la familia coincide con el tope de dominio",
          "full_title": "buildPortionFamilies > el permitido de la familia coincide con el tope de dominio",
          "line": 68,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b4ecc57c353e000f4e47",
          "title": "titula la familia con el grupo que tiene más porciones asignadas",
          "full_title": "buildPortionFamilies > titula la familia con el grupo que tiene más porciones asignadas",
          "line": 82,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5dccaf9377fc5d801782",
          "title": "atribuye cada ingrediente a una sola familia, con su índice original",
          "full_title": "buildPortionFamilies > atribuye cada ingrediente a una sola familia, con su índice original",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f4f6170455e793ff4ef2",
          "title": "atribuye un ingrediente sustituido a la familia del grupo establecido",
          "full_title": "buildPortionFamilies > atribuye un ingrediente sustituido a la familia del grupo establecido",
          "line": 109,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "725482ecd357fd913e49",
          "title": "separa como huérfano lo que no pertenece a ningún grupo del slot",
          "full_title": "buildPortionFamilies > separa como huérfano lo que no pertenece a ningún grupo del slot",
          "line": 130,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9d785ef62898c4797d0e",
          "title": "agrupa cadenas transitivas sin sobre-reportar el tope",
          "full_title": "buildPortionFamilies > agrupa cadenas transitivas sin sobre-reportar el tope",
          "line": 142,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ac07f14fb81d94c7486d",
          "title": "devuelve vacío si el tiempo no existe en la distribución",
          "full_title": "buildPortionFamilies > devuelve vacío si el tiempo no existe en la distribución",
          "line": 156,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0a86f7f0953c0adcf47d",
          "title": "libresTotal no cuenta dos veces el pool compartido",
          "full_title": "buildPortionFamilies > libresTotal no cuenta dos veces el pool compartido",
          "line": 167,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "31d382dbd1906442c475",
          "title": "no reporta ruptura mientras la equivalencia sigue activa",
          "full_title": "getPortionContractBreach > no reporta ruptura mientras la equivalencia sigue activa",
          "line": 185,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cac07b601d02c536199e",
          "title": "detecta al ingrediente que se queda sin grupo al retirar la equivalencia",
          "full_title": "getPortionContractBreach > detecta al ingrediente que se queda sin grupo al retirar la equivalencia",
          "line": 196,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "77c7a9d6ccc11a53bee4",
          "title": "detecta el sobrecupo cuando el grupo se encoge pero conserva cupo",
          "full_title": "getPortionContractBreach > detecta el sobrecupo cuando el grupo se encoge pero conserva cupo",
          "line": 213,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f116bd63c2437382ac69",
          "title": "no reporta ruptura por errores de redondeo",
          "full_title": "getPortionContractBreach > no reporta ruptura por errores de redondeo",
          "line": 230,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/pr-detection.test.ts",
      "type": "unit",
      "sha256": "118496e1e169c41c8222cd1fc3870486ac7cd40a1b289bfc093439da63db8c59",
      "tests": [
        {
          "id": "5934b45ac579ba95838d",
          "title": "un set incompleto o sin datos nunca es PR",
          "full_title": "detectarPR > un set incompleto o sin datos nunca es PR",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eb9cd30d82c89447d3d6",
          "title": "el primer set registrado establece peso, reps y volumen",
          "full_title": "detectarPR > el primer set registrado establece peso, reps y volumen",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a963abe9ec04f01ce1ba",
          "title": "el primer set de carga asistida establece peso y reps",
          "full_title": "detectarPR > el primer set de carga asistida establece peso y reps",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8d4c1b02f4d4d49d3477",
          "title": "detecta PR de peso cuando supera el máximo histórico",
          "full_title": "detectarPR > detecta PR de peso cuando supera el máximo histórico",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fd23043c02f0683c9fd3",
          "title": "detecta PR de reps y volumen al mismo peso",
          "full_title": "detectarPR > detecta PR de reps y volumen al mismo peso",
          "line": 67,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5ad030244fb40f9b19d1",
          "title": "normaliza libras a kg antes de comparar",
          "full_title": "detectarPR > normaliza libras a kg antes de comparar",
          "line": 76,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f6ce6ae4802283b75862",
          "title": "en carga asistida el PR es usar MENOS peso de asistencia",
          "full_title": "detectarPR > en carga asistida el PR es usar MENOS peso de asistencia",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "49938a6e6fad0e4d295b",
          "title": "ignora históricos de otro ejercicio o de otro tipo de carga",
          "full_title": "detectarPR > ignora históricos de otro ejercicio o de otro tipo de carga",
          "line": 106,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "03ee1eef669574189c5b",
          "title": "igualar el récord no cuenta como PR",
          "full_title": "detectarPR > igualar el récord no cuenta como PR",
          "line": 117,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/progresion.test.ts",
      "type": "unit",
      "sha256": "9d608b6d01d4325edc9b47be13a800ca5715119baaaa40e8798841ebcf38dc5e",
      "tests": [
        {
          "id": "36d4363d41ac6432cc62",
          "title": "fase carga semana 1 no modifica el peso (1.05^0 = 1)",
          "full_title": "aplicarProgresion > fase carga semana 1 no modifica el peso (1.05^0 = 1)",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "425136c2f51534cbc666",
          "title": "fase carga compone +5% por semana y redondea a 0.5 kg",
          "full_title": "aplicarProgresion > fase carga compone +5% por semana y redondea a 0.5 kg",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cc66fa70c0c4f5d606ae",
          "title": "fase descarga reduce peso y volumen 20%",
          "full_title": "aplicarProgresion > fase descarga reduce peso y volumen 20%",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5d3baed5a05289cb06e0",
          "title": "fase mantenimiento no cambia nada",
          "full_title": "aplicarProgresion > fase mantenimiento no cambia nada",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "83801143cba4d96b90b9",
          "title": "fase peaking sube intensidad 10% y baja volumen 30%",
          "full_title": "aplicarProgresion > fase peaking sube intensidad 10% y baja volumen 30%",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "129533154acbc875ff8f",
          "title": "ejercicio sin peso (tiempo) no recibe pesoAjustado pero sí ajuste de series",
          "full_title": "aplicarProgresion > ejercicio sin peso (tiempo) no recibe pesoAjustado pero sí ajuste de series",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "468035854490e967002b",
          "title": "las series nunca bajan de 1",
          "full_title": "aplicarProgresion > las series nunca bajan de 1",
          "line": 68,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e1542f7d32f75d4a4fab",
          "title": "el microciclo con intensidadPct anula la composición semanal de carga",
          "full_title": "aplicarProgresion > el microciclo con intensidadPct anula la composición semanal de carga",
          "line": 76,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c62620d413d940584869",
          "title": "el microciclo puede cambiar la fase y aportar notas propias",
          "full_title": "aplicarProgresion > el microciclo puede cambiar la fase y aportar notas propias",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7a27588c16399fcbbe69",
          "title": "el microciclo con volumenPct anula el factor de volumen de la fase",
          "full_title": "aplicarProgresion > el microciclo con volumenPct anula el factor de volumen de la fase",
          "line": 97,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/receta-distribucion.test.ts",
      "type": "unit",
      "sha256": "30b75e49825e7fd467d533b7358c1c71d7363f1c8247c44038b59a837521dd11",
      "tests": [
        {
          "id": "288b9bb66d8594880f64",
          "title": "devuelve lista vacía sin slot de distribución",
          "full_title": "getPorcionesDeltas > devuelve lista vacía sin slot de distribución",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5dffeeb760f74bfd22ad",
          "title": "calcula objetivo, real y delta por grupo colapsado, omitiendo grupos en cero",
          "full_title": "getPorcionesDeltas > calcula objetivo, real y delta por grupo colapsado, omitiendo grupos en cero",
          "line": 62,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a35a31cf2399604371e3",
          "title": "colapsa sub-tipos de AOA y lácteos en un solo bucket",
          "full_title": "getPorcionesDeltas > colapsa sub-tipos de AOA y lácteos en un solo bucket",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c48e468a233b1da64826",
          "title": "marca sobran cuando el real excede el objetivo más allá de 0.1",
          "full_title": "getPorcionesDeltas > marca sobran cuando el real excede el objetivo más allá de 0.1",
          "line": 91,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "db2bf6a37a95967d773d",
          "title": "detecta mismatch cuando alguna fila no está en ok",
          "full_title": "hasPorcionesMismatch y recetaRespetaIngredientes > detecta mismatch cuando alguna fila no está en ok",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "af0c619b5b30d3d2aa12",
          "title": "recetaRespetaIngredientes es true sin slot y refleja el mismatch con slot",
          "full_title": "hasPorcionesMismatch y recetaRespetaIngredientes > recetaRespetaIngredientes es true sin slot y refleja el mismatch con slot",
          "line": 111,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9cc7a81193f4b02a2281",
          "title": "sin slot devuelve la receta intacta",
          "full_title": "ajustarRecetaADistribucion > sin slot devuelve la receta intacta",
          "line": 131,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "caaebeb5548797f45ffc",
          "title": "receta frozen queda congelada y receta auto sigue la distribución",
          "full_title": "ajustarRecetaADistribucion > receta frozen queda congelada y receta auto sigue la distribución",
          "line": 137,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0783e19179483852407b",
          "title": "reparte el objetivo del grupo entre sus ingredientes y compensa el residuo en el último",
          "full_title": "ajustarRecetaADistribucion > reparte el objetivo del grupo entre sus ingredientes y compensa el residuo en el último",
          "line": 157,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "46dc84aa056f05b0b8da",
          "title": "elimina ingredientes de grupos con objetivo 0 y conserva grupos no mapeados",
          "full_title": "ajustarRecetaADistribucion > elimina ingredientes de grupos con objetivo 0 y conserva grupos no mapeados",
          "line": 171,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0f7d937792b70de9e08f",
          "title": "recalcula nutrición cuando todos los ingredientes traen nutrición embebida",
          "full_title": "ajustarRecetaADistribucion > recalcula nutrición cuando todos los ingredientes traen nutrición embebida",
          "line": 183,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6e2aeeb3096d65c5eb6e",
          "title": "conserva la nutrición original si algún ingrediente no trae nutrición embebida",
          "full_title": "ajustarRecetaADistribucion > conserva la nutrición original si algún ingrediente no trae nutrición embebida",
          "line": 197,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "90577b75e1b4d3e18dbc",
          "title": "escribe metadata de ajuste con fecha inyectada por reloj y diagnóstico de porciones",
          "full_title": "ajustarRecetaADistribucion > escribe metadata de ajuste con fecha inyectada por reloj y diagnóstico de porciones",
          "line": 203,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ba521697b50430b4871d",
          "title": "sin meta hereda tiempo del slot y menuIndex 0, con diagnóstico ok cuando todo cuadra",
          "full_title": "ajustarRecetaADistribucion > sin meta hereda tiempo del slot y menuIndex 0, con diagnóstico ok cuando todo cuadra",
          "line": 223,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "17eb62bda77168f1f38c",
          "title": "fijar conserva ingredientes, porciones y kcal después de cambiar la distribución",
          "full_title": "ajustarRecetaADistribucion > fijar conserva ingredientes, porciones y kcal después de cambiar la distribución",
          "line": 231,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d3f5eb0aa05c9070d5d1",
          "title": "usar distribución conserva la composición actual y ajusta inmediatamente",
          "full_title": "ajustarRecetaADistribucion > usar distribución conserva la composición actual y ajusta inmediatamente",
          "line": 244,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1a72ddfa4ec32ceaf22e",
          "title": "un guardado manual no se congela y espera a un cambio real de firma",
          "full_title": "ajustarRecetaADistribucion > un guardado manual no se congela y espera a un cambio real de firma",
          "line": 256,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0b0ea5d9009500138b02",
          "title": "la adaptación materializada es idempotente y conserva referencia",
          "full_title": "ajustarRecetaADistribucion > la adaptación materializada es idempotente y conserva referencia",
          "line": 271,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/receta-match-status.test.ts",
      "type": "unit",
      "sha256": "deb216e89cd9d0fc537b5bedad1054d37ce9c61a24c3cfc461d4f5377a846200",
      "tests": [
        {
          "id": "abeb65e14b002a973979",
          "title": "acepta el ±5% del objetivo",
          "full_title": "getKcalRangeStatus > acepta el ±5% del objetivo",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3f18f61b018b3aa16593",
          "title": "rechaza fuera del rango, por arriba y por abajo",
          "full_title": "getKcalRangeStatus > rechaza fuera del rango, por arriba y por abajo",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4cb14ce6f038dc1fd306",
          "title": "sin objetivo definido basta con que la receta aporte algo",
          "full_title": "getKcalRangeStatus > sin objetivo definido basta con que la receta aporte algo",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f1fa8df904a79f994d25",
          "title": "reporta desviación y porcentaje para que la UI los pueda mostrar",
          "full_title": "getKcalRangeStatus > reporta desviación y porcentaje para que la UI los pueda mostrar",
          "line": 76,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eab34e25968450c2bd58",
          "title": "los tres estados salen de las dos condiciones",
          "full_title": "getRecetaMatchStatus y su etiqueta > los tres estados salen de las dos condiciones",
          "line": 84,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a4c23c556e5800fde543",
          "title": "la etiqueta distingue cuál de las dos falla, no sólo que algo falla",
          "full_title": "getRecetaMatchStatus y su etiqueta > la etiqueta distingue cuál de las dos falla, no sólo que algo falla",
          "line": 91,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dd4fb53c713b58022373",
          "title": "sin recetas no hay estado que mostrar",
          "full_title": "getRecetasAggregateStatus > sin recetas no hay estado que mostrar",
          "line": 100,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b5ce3a123538c300e3f2",
          "title": "gana el peor: una receta en danger tiñe el tiempo entero",
          "full_title": "getRecetasAggregateStatus > gana el peor: una receta en danger tiñe el tiempo entero",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "12804ce090d2a051240c",
          "title": "con cero recetas devuelve un resumen vacío sin estado",
          "full_title": "buildRecetasResumen > con cero recetas devuelve un resumen vacío sin estado",
          "line": 113,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b92f54aed43ddbb19545",
          "title": "con una receta dentro del objetivo el tiempo queda en success",
          "full_title": "buildRecetasResumen > con una receta dentro del objetivo el tiempo queda en success",
          "line": 125,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0e626ea0b4ef407b5d80",
          "title": "conserva las N recetas del tiempo, no sólo la principal",
          "full_title": "buildRecetasResumen > conserva las N recetas del tiempo, no sólo la principal",
          "line": 139,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "96833560f2a130b8cb8a",
          "title": "una sola receta fuera de rango degrada el estado del tiempo completo",
          "full_title": "buildRecetasResumen > una sola receta fuera de rango degrada el estado del tiempo completo",
          "line": 150,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bb9034ab13be00a2d0a5",
          "title": "sin objetivo de kcal, una receta que aporta calorías no puede dar success",
          "full_title": "buildRecetasResumen > sin objetivo de kcal, una receta que aporta calorías no puede dar success",
          "line": 162,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1e63cad15f008a7e6c19",
          "title": "degrada a warning cuando las kcal cuadran pero falta un grupo del tiempo",
          "full_title": "buildRecetasResumen > degrada a warning cuando las kcal cuadran pero falta un grupo del tiempo",
          "line": 178,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a115ed0625f794cbe2d3",
          "title": "reescala un grupo que la receta sí contiene, así que eso no degrada nada",
          "full_title": "buildRecetasResumen > reescala un grupo que la receta sí contiene, así que eso no degrada nada",
          "line": 189,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9ba60be3d1ee5c51ed4f",
          "title": "una receta frozen se presenta como custom sin ocultar otra problemática",
          "full_title": "buildRecetasResumen > una receta frozen se presenta como custom sin ocultar otra problemática",
          "line": 200,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/recipe-matching.test.ts",
      "type": "unit",
      "sha256": "c5bab1c9d0b23a0b454bd544dfaa576842e2db6a43009cf1346aa3fc99eab5d8",
      "tests": [
        {
          "id": "e81dcbd623cbabd51512",
          "title": "junta candidatas de menú, otros días y catálogo en ese orden, sin duplicar",
          "full_title": "getCandidatasParaSwap > junta candidatas de menú, otros días y catálogo en ese orden, sin duplicar",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7f673b841aac1c517cf8",
          "title": "excluye la receta actual por nombre",
          "full_title": "getCandidatasParaSwap > excluye la receta actual por nombre",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fa2fc2d384638090df64",
          "title": "los slots multi-receta del menú aportan todas sus opciones",
          "full_title": "getCandidatasParaSwap > los slots multi-receta del menú aportan todas sus opciones",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a3f4f8238bc0f070d6c8",
          "title": "el catálogo solo muestra recetas SMAE-strict del mismo tiempo de comida",
          "full_title": "getCandidatasParaSwap > el catálogo solo muestra recetas SMAE-strict del mismo tiempo de comida",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8f0dffe212df86d826f8",
          "title": "con el menú sí marca smaeMatch false en vez de excluir",
          "full_title": "getCandidatasParaSwap > con el menú sí marca smaeMatch false en vez de excluir",
          "line": 82,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a1092a4ea8278f16fe81",
          "title": "intercambiosGrupos habilita candidatas por equivalencia grupo-a-grupo",
          "full_title": "getCandidatasParaSwap > intercambiosGrupos habilita candidatas por equivalencia grupo-a-grupo",
          "line": 90,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cee715860d89d467f424",
          "title": "fuentesPermitidas restringe de dónde salen las candidatas",
          "full_title": "getCandidatasParaSwap > fuentesPermitidas restringe de dónde salen las candidatas",
          "line": 114,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/recipe-smae.test.ts",
      "type": "unit",
      "sha256": "622efe01340ac0d9f070938daa0f889d161ba243bc1015d295e8fd5afa52e50b",
      "tests": [
        {
          "id": "fb243c5a5d455a5c3195",
          "title": "devuelve todo en 0 para receta null",
          "full_title": "calcularPorcionesReceta > devuelve todo en 0 para receta null",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e7fd80057ed72eb4dca0",
          "title": "suma porciones por grupo colapsando sub-tipos de AOA y Lácteos",
          "full_title": "calcularPorcionesReceta > suma porciones por grupo colapsando sub-tipos de AOA y Lácteos",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a9ecdf8f74bff9ef20da",
          "title": "reconoce 'Origen animal' como AOA y es insensible a mayúsculas",
          "full_title": "calcularPorcionesReceta > reconoce 'Origen animal' como AOA y es insensible a mayúsculas",
          "line": 63,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c18bd2e7ddac4b55087f",
          "title": "ignora grupos desconocidos (ej. Suplementos)",
          "full_title": "calcularPorcionesReceta > ignora grupos desconocidos (ej. Suplementos)",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "622638fa5162935d3cd2",
          "title": "mantiene separados azúcares y grasas con proteína del bucket grasas",
          "full_title": "calcularPorcionesReceta > mantiene separados azúcares y grasas con proteína del bucket grasas",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b2db80961fd2f3c2026e",
          "title": "suma los sub-tipos de AOA y Lácteos en un solo bucket cada uno",
          "full_title": "colapsarDistribucion > suma los sub-tipos de AOA y Lácteos en un solo bucket cada uno",
          "line": 106,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "42dd7dadba422b9dd250",
          "title": "acepta receta con diferencia de exactamente 1 porción por grupo (tolerancia default)",
          "full_title": "recetaMatcheaDistribucion > acepta receta con diferencia de exactamente 1 porción por grupo (tolerancia default)",
          "line": 136,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "676572db898ed338951b",
          "title": "rechaza receta que se pasa por más de 1 porción en un grupo activo",
          "full_title": "recetaMatcheaDistribucion > rechaza receta que se pasa por más de 1 porción en un grupo activo",
          "line": 145,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "34262667c2e88fd94b99",
          "title": "rechaza receta que no aporta nada a un grupo activo con más de 1 porción",
          "full_title": "recetaMatcheaDistribucion > rechaza receta que no aporta nada a un grupo activo con más de 1 porción",
          "line": 153,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1c3fe4887db88c4dc6ce",
          "title": "ignora grupos en 0 en la distribución aunque la receta traiga porciones extra",
          "full_title": "recetaMatcheaDistribucion > ignora grupos en 0 en la distribución aunque la receta traiga porciones extra",
          "line": 159,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d23ac96cde85f3e765b1",
          "title": "respeta una tolerancia custom más estricta",
          "full_title": "recetaMatcheaDistribucion > respeta una tolerancia custom más estricta",
          "line": 169,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "29b0eb5a1e53ddbe645f",
          "title": "expone solo los grupos con porciones > 0, con sus nombres SMAE finos",
          "full_title": "distribucionFina > expone solo los grupos con porciones > 0, con sus nombres SMAE finos",
          "line": 179,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5a13cae98547bac95cf4",
          "title": "incluye azúcares y grasas con proteína con sus llaves finas",
          "full_title": "distribucionFina > incluye azúcares y grasas con proteína con sus llaves finas",
          "line": 188,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "79b9a8ed8338a790228a",
          "title": "acepta receta que cubre exactamente cada grupo del slot",
          "full_title": "recetaCumpleIntercambios > acepta receta que cubre exactamente cada grupo del slot",
          "line": 205,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7212c049f7c6715eeb35",
          "title": "acepta cubrir un grupo con su aliado permitido por el nutriólogo",
          "full_title": "recetaCumpleIntercambios > acepta cubrir un grupo con su aliado permitido por el nutriólogo",
          "line": 214,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "81f71940cbaffc3a9405",
          "title": "rechaza el mismo reemplazo si el intercambio no está permitido",
          "full_title": "recetaCumpleIntercambios > rechaza el mismo reemplazo si el intercambio no está permitido",
          "line": 223,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "86a413f6bba2437e1732",
          "title": "rechaza porciones extra de grupos ausentes en el slot",
          "full_title": "recetaCumpleIntercambios > rechaza porciones extra de grupos ausentes en el slot",
          "line": 234,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8a5f18344c5327babe74",
          "title": "tolera hasta 0.5 porciones de desviación por grupo (default)",
          "full_title": "recetaCumpleIntercambios > tolera hasta 0.5 porciones de desviación por grupo (default)",
          "line": 243,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "834a2c758bf0c7379a79",
          "title": "normaliza nombres de grupo con acentos y sinónimos ('leche entera' -> Lácteos entero)",
          "full_title": "recetaCumpleIntercambios > normaliza nombres de grupo con acentos y sinónimos ('leche entera' -> Lácteos entero)",
          "line": 249,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5ae0f2d428be07388513",
          "title": "trata 'AOA' sin calificador como AOA alto",
          "full_title": "recetaCumpleIntercambios > trata 'AOA' sin calificador como AOA alto",
          "line": 255,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b5075d03a267f87964d1",
          "title": "trata 'yogurt' genérico como Lácteos descremado",
          "full_title": "recetaCumpleIntercambios > trata 'yogurt' genérico como Lácteos descremado",
          "line": 261,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "68c0be28647557acbb1b",
          "title": "no permite reutilizar las mismas porciones para dos grupos del slot",
          "full_title": "recetaCumpleIntercambios > no permite reutilizar las mismas porciones para dos grupos del slot",
          "line": 267,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a0a9b54f4c8d1de759a7",
          "title": "acepta un intercambio legacy desde cualquiera de sus dos direcciones",
          "full_title": "recetaCumpleIntercambios > acepta un intercambio legacy desde cualquiera de sus dos direcciones",
          "line": 275,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/reglas-nutricionales.test.ts",
      "type": "unit",
      "sha256": "b1469677700a4d91d34e93788c8a6d3bf2210acce07b8a6607c7945e7fbcef45",
      "tests": [
        {
          "id": "b0b9fbe4d4d0f3317a7e",
          "title": "sin restricciones devuelve los ratios base intactos",
          "full_title": "aplicarFiltros > sin restricciones devuelve los ratios base intactos",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3c845da640af81b2aaf7",
          "title": "vegano elimina AOA y lácteos redistribuyéndolos",
          "full_title": "aplicarFiltros > vegano elimina AOA y lácteos redistribuyéndolos",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a21e43817a6ae74c4562",
          "title": "vegano tiene precedencia sobre vegetariano",
          "full_title": "aplicarFiltros > vegano tiene precedencia sobre vegetariano",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3827535556dff30af645",
          "title": "vegetariano redistribuye AOA hacia leguminosas, lácteos y grasas con proteína",
          "full_title": "aplicarFiltros > vegetariano redistribuye AOA hacia leguminosas, lácteos y grasas con proteína",
          "line": 60,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7c6ee869c4712f39bb67",
          "title": "sin lactosa mueve los lácteos hacia AOA",
          "full_title": "aplicarFiltros > sin lactosa mueve los lácteos hacia AOA",
          "line": 68,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cb0082a8948c2aaba7fa",
          "title": "halal se anota para trazabilidad sin cambiar la distribución",
          "full_title": "aplicarFiltros > halal se anota para trazabilidad sin cambiar la distribución",
          "line": 75,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f73e446469ff1526478e",
          "title": "alergia a frutos secos mueve grasas con proteína hacia grasas",
          "full_title": "aplicarFiltros > alergia a frutos secos mueve grasas con proteína hacia grasas",
          "line": 82,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8182975e0942aa224a5e",
          "title": "diabetes elimina azúcares y reduce cereales 20% hacia verduras y leguminosas",
          "full_title": "aplicarFiltros > diabetes elimina azúcares y reduce cereales 20% hacia verduras y leguminosas",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6d6e1d03e07b5103a91e",
          "title": "dislipidemia transfiere 30% de grasas hacia verduras",
          "full_title": "aplicarFiltros > dislipidemia transfiere 30% de grasas hacia verduras",
          "line": 100,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "edc8da1497977838a6fb",
          "title": "hipertensión y nefropatía solo se anotan (el split fino se maneja aparte)",
          "full_title": "aplicarFiltros > hipertensión y nefropatía solo se anotan (el split fino se maneja aparte)",
          "line": 107,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a617b71d76ff256817bb",
          "title": "acumula varios filtros en una sola pasada",
          "full_title": "aplicarFiltros > acumula varios filtros en una sola pasada",
          "line": 118,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1a8bc62c4adf0ea2c0ab",
          "title": "el match es insensible a mayúsculas",
          "full_title": "aplicarFiltros > el match es insensible a mayúsculas",
          "line": 133,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/restriction-matcher.test.ts",
      "type": "unit",
      "sha256": "22ead5a0288a32f2d426e999826f070123511d618b97e3bcfd4d280c6fee5d22",
      "tests": [
        {
          "id": "383cdd795b84cb090ab8",
          "title": "devuelve false para EMPTY_TAG_FILTERS",
          "full_title": "hasActiveFilters > devuelve false para EMPTY_TAG_FILTERS",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dbfa9c86098ab22ca0b8",
          "title": "devuelve true cuando hay filtro activo de %s",
          "full_title": "hasActiveFilters > devuelve true cuando hay filtro activo de %s",
          "line": 47,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "c528647e1651d4d1b778",
          "title": "devuelve todas las recetas cuando no hay filtros activos",
          "full_title": "filterByTags > devuelve todas las recetas cuando no hay filtros activos",
          "line": 67,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e6f04624c216dd23452b",
          "title": "excluye recetas sin tags cuando hay cualquier filtro activo",
          "full_title": "filterByTags > excluye recetas sin tags cuando hay cualquier filtro activo",
          "line": 71,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "be51bdcee93d56a12620",
          "title": "restricciones exige que la receta tenga TODAS las seleccionadas",
          "full_title": "filterByTags > restricciones exige que la receta tenga TODAS las seleccionadas",
          "line": 76,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2717e2f7c41e9626aba1",
          "title": "patologias exige al menos UNA coincidencia",
          "full_title": "filterByTags > patologias exige al menos UNA coincidencia",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f207c23b71f2b4a11329",
          "title": "timing exige al menos UNA coincidencia y excluye recetas sin timing",
          "full_title": "filterByTags > timing exige al menos UNA coincidencia y excluye recetas sin timing",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "568e48f58584e21ea32c",
          "title": "dificultad es match exacto",
          "full_title": "filterByTags > dificultad es match exacto",
          "line": 100,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8e3e7b1aff0ab1aa259b",
          "title": "combina categorías con AND",
          "full_title": "filterByTags > combina categorías con AND",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "49ca701374c46ede24ca",
          "title": "devuelve todo si no hay restricciones",
          "full_title": "filterByRestrictions > devuelve todo si no hay restricciones",
          "line": 126,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "159a00a5a16a1b9ccbe1",
          "title": "ignora restricciones de severidad preferencia (solo generan warnings)",
          "full_title": "filterByRestrictions > ignora restricciones de severidad preferencia (solo generan warnings)",
          "line": 130,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "05f4239c14d57b5a799c",
          "title": "excluye recetas con ingredientes prohibidos por conviccion (case-insensitive, por substring)",
          "full_title": "filterByRestrictions > excluye recetas con ingredientes prohibidos por conviccion (case-insensitive, por substring)",
          "line": 135,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5ed5529c3fa30d183c5f",
          "title": "no excluye nada si la conviccion no lista alimentos prohibidos",
          "full_title": "filterByRestrictions > no excluye nada si la conviccion no lista alimentos prohibidos",
          "line": 140,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8578ae3fe611a72e90c4",
          "title": "genera warning por cada restriccion preferencia con ingredientes encontrados",
          "full_title": "getRestrictionWarnings > genera warning por cada restriccion preferencia con ingredientes encontrados",
          "line": 155,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6702ea0e2598d3783a90",
          "title": "no genera warnings para restricciones conviccion ni sin coincidencias",
          "full_title": "getRestrictionWarnings > no genera warnings para restricciones conviccion ni sin coincidencias",
          "line": 164,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/rpe-rir.test.ts",
      "type": "unit",
      "sha256": "2369a75e7a7f5e2f1c48367be7d1f656a922ccb0a811c037437f5f65556d9740",
      "tests": [
        {
          "id": "7ff9c0516e00ac8589b4",
          "title": "RPE %s = RIR %s",
          "full_title": "calcularRirDesdeRpe > RPE %s = RIR %s",
          "line": 12,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "cdf42167201ea13c5567",
          "title": "clampa RPE fuera de rango antes de convertir",
          "full_title": "calcularRirDesdeRpe > clampa RPE fuera de rango antes de convertir",
          "line": 26,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cdcad7929792b29a82ed",
          "title": "redondea RPE decimal al entero más cercano",
          "full_title": "calcularRirDesdeRpe > redondea RPE decimal al entero más cercano",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "af359e8c28587388163c",
          "title": "devuelve undefined con %s",
          "full_title": "calcularRirDesdeRpe > devuelve undefined con %s",
          "line": 36,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "dd4230cccf7389f54807",
          "title": "clampa al rango 1-10 y redondea",
          "full_title": "normalizeRpe > clampa al rango 1-10 y redondea",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e220f4450c34ef31172b",
          "title": "devuelve undefined con null/NaN",
          "full_title": "normalizeRpe > devuelve undefined con null/NaN",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d3016172259e25fb3255",
          "title": "clampa al rango 0-5 y redondea",
          "full_title": "normalizeRir > clampa al rango 0-5 y redondea",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d98c6bdb842d189bb36d",
          "title": "devuelve undefined con null/NaN",
          "full_title": "normalizeRir > devuelve undefined con null/NaN",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cf1311a21d6fbfb95b7a",
          "title": "RPE %s → \"%s\"",
          "full_title": "describirRpe > RPE %s → \"%s\"",
          "line": 68,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "684984f153b5e3148813",
          "title": "RPE sin registrar cuando el valor es null",
          "full_title": "describirRpe > RPE sin registrar cuando el valor es null",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a4ab5cd82cd09e6ec0e6",
          "title": "devuelve las 5 opciones estándar (RPE 6-10) con RIR coherente",
          "full_title": "getRpeOptions > devuelve las 5 opciones estándar (RPE 6-10) con RIR coherente",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/sugerir-distribucion-bloqueo.test.ts",
      "type": "unit",
      "sha256": "33bc5bcc4cd0222f2eda0b6e15f73de8cf927977eb8f11ac3a5f2bf8a77c9a83",
      "tests": [
        {
          "id": "be30fe36441bfc31bb2d",
          "title": "nunca bloquea el primer menú: sus porciones son las del plan",
          "full_title": "resolverMotivoSugerirBloqueado > nunca bloquea el primer menú: sus porciones son las del plan",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5452f1f5bf0a18f2266b",
          "title": "no bloquea un menú secundario con porciones propias en rango",
          "full_title": "resolverMotivoSugerirBloqueado > no bloquea un menú secundario con porciones propias en rango",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bcd13b86efccccf0411f",
          "title": "bloquea cuando el menú hereda las porciones del plan",
          "full_title": "resolverMotivoSugerirBloqueado > bloquea cuando el menú hereda las porciones del plan",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7745cb4b7a120d41b1b0",
          "title": "bloquea por encima del 105% e informa las cifras reales",
          "full_title": "resolverMotivoSugerirBloqueado > bloquea por encima del 105% e informa las cifras reales",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "88e7ea51ee1749f6f1e7",
          "title": "bloquea también por debajo del 95%",
          "full_title": "resolverMotivoSugerirBloqueado > bloquea también por debajo del 95%",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "122501681124113c272e",
          "title": "la falta de porciones propias manda sobre el rango: es lo que hay que arreglar primero",
          "full_title": "resolverMotivoSugerirBloqueado > la falta de porciones propias manda sobre el rango: es lo que hay que arreglar primero",
          "line": 68,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2bd4063faced2f9bca5b",
          "title": "redondea el porcentaje para no mostrar decimales largos",
          "full_title": "resolverMotivoSugerirBloqueado > redondea el porcentaje para no mostrar decimales largos",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/tiempos-por-menu.test.ts",
      "type": "unit",
      "sha256": "7ce4d74d72043040252634bf17907df0424bc5415c7a44a96c387ab74e32ed74",
      "tests": [
        {
          "id": "87bb7c48958e5d8ad5a2",
          "title": "prioridad 1: usa el tiemposOverride propio del menú",
          "full_title": "getTiemposParaMenu > prioridad 1: usa el tiemposOverride propio del menú",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "514fa09e4c9cb1676c2f",
          "title": "prioridad 2: hereda el override del menú base cuando el menú target no tiene",
          "full_title": "getTiemposParaMenu > prioridad 2: hereda el override del menú base cuando el menú target no tiene",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "df3e698ebd5b555b47f4",
          "title": "prioridad 3: cae al sistema legacy plan.distribucion con tiemposCustom",
          "full_title": "getTiemposParaMenu > prioridad 3: cae al sistema legacy plan.distribucion con tiemposCustom",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b3d595d13ff5cc25de9f",
          "title": "prioridad 4: plan totalmente vacío devuelve los defaults globales",
          "full_title": "getTiemposParaMenu > prioridad 4: plan totalmente vacío devuelve los defaults globales",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4ca7f0a87d45dc8f5652",
          "title": "un override vacío no cuenta como override",
          "full_title": "getTiemposParaMenu > un override vacío no cuenta como override",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5cf39ce65f649e885364",
          "title": "índice fuera de rango cae a la herencia del menú base",
          "full_title": "getTiemposParaMenu > índice fuera de rango cae a la herencia del menú base",
          "line": 69,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0bb10a6906da38bcc184",
          "title": "true solo cuando el menú tiene override propio no vacío",
          "full_title": "tieneTiemposPersonalizados > true solo cuando el menú tiene override propio no vacío",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/tmb.test.ts",
      "type": "unit",
      "sha256": "259381254637674ac7d202ab9793fba3fbc3dc05986098e9c07e2489fa01df61",
      "tests": [
        {
          "id": "e32d381fab61cbf2143f",
          "title": "calcula %s para %s de %skg, %scm, %s años = %s kcal",
          "full_title": "calcularTMB > calcula %s para %s de %skg, %scm, %s años = %s kcal",
          "line": 38,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "40eaa8d0c051f6ef4da0",
          "title": "usa Mifflin-St Jeor como fórmula por defecto",
          "full_title": "calcularTMB > usa Mifflin-St Jeor como fórmula por defecto",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "390a4cdbadfca77cf4ba",
          "title": "trata género 'Otro' con la rama de mujer en todas las fórmulas",
          "full_title": "calcularTMB > trata género 'Otro' con la rama de mujer en todas las fórmulas",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e15a868d81419fb2ce18",
          "title": "calcula Katch-McArdle y Cunningham desde masa libre de grasa",
          "full_title": "calcularTMB > calcula Katch-McArdle y Cunningham desde masa libre de grasa",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "47ce8499851d1b91a9bc",
          "title": "no calcula fórmulas de masa libre de grasa sin un porcentaje válido",
          "full_title": "calcularTMB > no calcula fórmulas de masa libre de grasa sin un porcentaje válido",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bed35156d9ccc3a7e342",
          "title": "FAO/OMS respeta la frontera etaria a los %s años",
          "full_title": "calcularTMB > FAO/OMS respeta la frontera etaria a los %s años",
          "line": 66,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "20216fb1d0aa0330c593",
          "title": "GET de TMB %s con factor %s = %s kcal",
          "full_title": "calcularGET > GET de TMB %s con factor %s = %s kcal",
          "line": 78,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "d8f10c70c6ccfc6070e4",
          "title": "calcula el 10% del GET redondeado",
          "full_title": "calcularETA > calcula el 10% del GET redondeado",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cfdf68f69c920320f824",
          "title": "convierte porcentajes a gramos con 4 kcal/g (CHO y PROT) y 9 kcal/g (grasas)",
          "full_title": "calcularMacros > convierte porcentajes a gramos con 4 kcal/g (CHO y PROT) y 9 kcal/g (grasas)",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "88a4dd4afe6662713a7b",
          "title": "no reporta faltantes cuando el paciente tiene todos los datos",
          "full_title": "verificarCamposFaltantes > no reporta faltantes cuando el paciente tiene todos los datos",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aebe80e4f8b38abf48df",
          "title": "reporta peso, talla, edad y sexo faltantes para Mifflin con paciente vacío",
          "full_title": "verificarCamposFaltantes > reporta peso, talla, edad y sexo faltantes para Mifflin con paciente vacío",
          "line": 110,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7a7cd527d791f42ed31e",
          "title": "trata valores cero o negativos como faltantes",
          "full_title": "verificarCamposFaltantes > trata valores cero o negativos como faltantes",
          "line": 114,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b9264768112588c05e9d",
          "title": "rechaza porcentajes de grasa fuera del rango clínico",
          "full_title": "verificarCamposFaltantes > rechaza porcentajes de grasa fuera del rango clínico",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4f584f441783301ea238",
          "title": "Owen solo exige peso y sexo (no talla ni edad)",
          "full_title": "verificarCamposFaltantes > Owen solo exige peso y sexo (no talla ni edad)",
          "line": 124,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "80e9ed07f6f136a0c02e",
          "title": "con solo peso y sexo únicamente Owen está disponible",
          "full_title": "getFormulasDisponibles > con solo peso y sexo únicamente Owen está disponible",
          "line": 130,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4cba932ac7c48595af18",
          "title": "con datos antropométricos básicos quedan disponibles las 5 fórmulas generales",
          "full_title": "getFormulasDisponibles > con datos antropométricos básicos quedan disponibles las 5 fórmulas generales",
          "line": 134,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9d8d9108fc28f4e161ff",
          "title": "habilita las fórmulas basadas en masa libre de grasa cuando existe el porcentaje",
          "full_title": "getFormulasDisponibles > habilita las fórmulas basadas en masa libre de grasa cuando existe el porcentaje",
          "line": 139,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "127520bfa5e87c5bcf78",
          "title": "sin talla quedan disponibles las fórmulas que no la requieren",
          "full_title": "getFormulasDisponibles > sin talla quedan disponibles las fórmulas que no la requieren",
          "line": 151,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bfa64cd16ea30ed534eb",
          "title": "recomienda FAO/OMS para adulto mayor por tipo",
          "full_title": "recomendarFormula > recomienda FAO/OMS para adulto mayor por tipo",
          "line": 161,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5060dcd1e9d3f1dd78b3",
          "title": "recomienda FAO/OMS por edad >= 60 aunque el tipo sea otro",
          "full_title": "recomendarFormula > recomienda FAO/OMS por edad >= 60 aunque el tipo sea otro",
          "line": 165,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d39c9dee96eff37e4c56",
          "title": "recomienda Mifflin-St Jeor para atleta de alto rendimiento",
          "full_title": "recomendarFormula > recomienda Mifflin-St Jeor para atleta de alto rendimiento",
          "line": 169,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "92ee8bac274effa52661",
          "title": "recomienda Mifflin-St Jeor para población general con Valencia como alternativa",
          "full_title": "recomendarFormula > recomienda Mifflin-St Jeor para población general con Valencia como alternativa",
          "line": 173,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/training-nutrition-sync.test.ts",
      "type": "unit",
      "sha256": "0e1b30b30a7d98592fb0d5d5946e382f42d1fe4c309fc92343dbe1dd976a8943",
      "tests": [
        {
          "id": "aa4051a9f10270247cd1",
          "title": "genera 7 días: lunes-viernes entrenamiento, fin de semana descanso",
          "full_title": "generateDefaultSchedule > genera 7 días: lunes-viernes entrenamiento, fin de semana descanso",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "42d77559005ebb7457e8",
          "title": "%s = %s%%",
          "full_title": "getAjusteForTraining > %s = %s%%",
          "line": 24,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "aad65c372d611eaaf02c",
          "title": "una intensidad desconocida cae al ajuste default de entrenamiento",
          "full_title": "getAjusteForTraining > una intensidad desconocida cae al ajuste default de entrenamiento",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bb860fa6e0974d314dec",
          "title": "la intensidad se ignora en días de descanso y competencia",
          "full_title": "getAjusteForTraining > la intensidad se ignora en días de descanso y competencia",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ce6093181cadaed02573",
          "title": "mapea cada día a su ajuste calórico",
          "full_title": "syncTrainingWithPlan > mapea cada día a su ajuste calórico",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/valida-mutacion.test.ts",
      "type": "unit",
      "sha256": "b26b9cecabce0cd0f4b23d8e1963cf61633d47b2d7b7940dd7b4bb50e11c7eb8",
      "tests": [
        {
          "id": "33894fc8150460592f6e",
          "title": "bloquea con error cuando usado + propuesto excede el permitido",
          "full_title": "validarTopePorciones > bloquea con error cuando usado + propuesto excede el permitido",
          "line": 90,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4692e6723c38d6ba4504",
          "title": "acepta cuando el total queda exactamente en el permitido",
          "full_title": "validarTopePorciones > acepta cuando el total queda exactamente en el permitido",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "962771900ad84a7895f3",
          "title": "tolera excesos de punto flotante menores o iguales a 0.001",
          "full_title": "validarTopePorciones > tolera excesos de punto flotante menores o iguales a 0.001",
          "line": 111,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "18a0c1fca4d6b8aaccdd",
          "title": "acepta receta que aporta exactamente las porciones del slot",
          "full_title": "validarSwapReceta > acepta receta que aporta exactamente las porciones del slot",
          "line": 126,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8ab1dc164ca2c962e0c1",
          "title": "reporta warning por default cuando la receta no respeta la distribución",
          "full_title": "validarSwapReceta > reporta warning por default cuando la receta no respeta la distribución",
          "line": 135,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "12504edc9eb75e89d1ec",
          "title": "escala a error cuando el contexto pide bloquear",
          "full_title": "validarSwapReceta > escala a error cuando el contexto pide bloquear",
          "line": 148,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4682bfb3f7e878b4e677",
          "title": "acepta porciones cubiertas por un grupo aliado del mapping de intercambios",
          "full_title": "validarSwapReceta > acepta porciones cubiertas por un grupo aliado del mapping de intercambios",
          "line": 156,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "67b3f1fe5439a0a85b40",
          "title": "devuelve ok si el tiempo de comida no existe en la distribución",
          "full_title": "validarSwapReceta > devuelve ok si el tiempo de comida no existe en la distribución",
          "line": 167,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "233f17a7a4e5ac248c29",
          "title": "acepta cuando el total del grupo queda dentro del tope del slot",
          "full_title": "validarEdicionCantidad > acepta cuando el total del grupo queda dentro del tope del slot",
          "line": 176,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "30774e9fda8fdbe1b0dc",
          "title": "reporta warning con delta y mensaje plural cuando excede el tope",
          "full_title": "validarEdicionCantidad > reporta warning con delta y mensaje plural cuando excede el tope",
          "line": 189,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ece17ae794858b8f278d",
          "title": "escala a error con resumen de bloqueo cuando ctx.bloquear es true",
          "full_title": "validarEdicionCantidad > escala a error con resumen de bloqueo cuando ctx.bloquear es true",
          "line": 208,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "58d1209f27f4506c8016",
          "title": "suma porciones de grupos aliados al calcular lo usado y el tope",
          "full_title": "validarEdicionCantidad > suma porciones de grupos aliados al calcular lo usado y el tope",
          "line": 222,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "14ee8f5294ce1cd3877f",
          "title": "usa el distribucionOverride del menú al resolver el tope",
          "full_title": "validarEdicionCantidad > usa el distribucionOverride del menú al resolver el tope",
          "line": 239,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8ea113c0e64914ce36bd",
          "title": "valida las porciones del ingrediente nuevo contra el remanente del grupo",
          "full_title": "validarSwapIngrediente > valida las porciones del ingrediente nuevo contra el remanente del grupo",
          "line": 259,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "613093fb592fb611a492",
          "title": "trata porciones ausentes del ingrediente nuevo como 0",
          "full_title": "validarSwapIngrediente > trata porciones ausentes del ingrediente nuevo como 0",
          "line": 276,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/volumen-fitness.test.ts",
      "type": "unit",
      "sha256": "647017a47f8072350b4c2011a7b19718cf62c6dea27c9a42c92067dcdcccfc74",
      "tests": [
        {
          "id": "e42ec82ebdaec68741a8",
          "title": "tonelaje = peso × reps de un set completado",
          "full_title": "calcularTonelajeSet > tonelaje = peso × reps de un set completado",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f73875b629604544459d",
          "title": "devuelve 0 con set %s",
          "full_title": "calcularTonelajeSet > devuelve 0 con set %s",
          "line": 47,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "b15c68b93ca07513a750",
          "title": "los sets de calentamiento no cuentan como volumen efectivo",
          "full_title": "calcularSetEfectivo > los sets de calentamiento no cuentan como volumen efectivo",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "308dbe12372f70b66acb",
          "title": "el tipo de serie %s cuenta como 1 set efectivo",
          "full_title": "calcularSetEfectivo > el tipo de serie %s cuenta como 1 set efectivo",
          "line": 62,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "4b22e896e9ffcf2f3a4f",
          "title": "sin tipoSerie usa el default efectiva (1) y los legacy 'normal' también",
          "full_title": "calcularSetEfectivo > sin tipoSerie usa el default efectiva (1) y los legacy 'normal' también",
          "line": 69,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9701f547b14851df985f",
          "title": "un set no completado nunca es efectivo",
          "full_title": "calcularSetEfectivo > un set no completado nunca es efectivo",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "47b7e7b0dd977642cc1c",
          "title": "combina tonelaje y sets efectivos",
          "full_title": "calcularVolumenSet > combina tonelaje y sets efectivos",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1a5233e1d5c84641b892",
          "title": "acredita 1 set al músculo primario y 0.5 a los secundarios (taxonomía fina)",
          "full_title": "calcularVolumenEfectivoPorMusculo > acredita 1 set al músculo primario y 0.5 a los secundarios (taxonomía fina)",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ea8c09e999556d1217d2",
          "title": "sin taxonomía fina cae a musculoPrincipal/musculoSecundario crudos",
          "full_title": "calcularVolumenEfectivoPorMusculo > sin taxonomía fina cae a musculoPrincipal/musculoSecundario crudos",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c4f1c3e5e6811e37cac0",
          "title": "ignora sets de calentamiento y de ejercicios desconocidos",
          "full_title": "calcularVolumenEfectivoPorMusculo > ignora sets de calentamiento y de ejercicios desconocidos",
          "line": 111,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/calculations/water.test.ts",
      "type": "unit",
      "sha256": "111922a7d220f9d454f01bc710ec760efbc73d766a8efa4688d3d4edfe9d3df7",
      "tests": [
        {
          "id": "e345a75e38c7cf4e910e",
          "title": "usa el factor default de 35 ml/kg",
          "full_title": "calcularAguaRecomendada > usa el factor default de 35 ml/kg",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b90fc0dfda7f6da7cf92",
          "title": "peso %s kg con factor %s = %s ml",
          "full_title": "calcularAguaRecomendada > peso %s kg con factor %s = %s ml",
          "line": 22,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "fbc6ea47f8d3146351a2",
          "title": "peso inválido (%s) produce 0 ml",
          "full_title": "calcularAguaRecomendada > peso inválido (%s) produce 0 ml",
          "line": 34,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "30bab4dbfc7ddad2d5f2",
          "title": "factor inválido cae al default de 35 ml/kg",
          "full_title": "calcularAguaRecomendada > factor inválido cae al default de 35 ml/kg",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1c55465cd0fdb1824d0b",
          "title": "los presets clínicos producen recomendaciones crecientes",
          "full_title": "calcularAguaRecomendada > los presets clínicos producen recomendaciones crecientes",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/captacion/config.test.ts",
      "type": "unit",
      "sha256": "c7fd754fbf07077cc9a6b573d60020f13c622dc874b4dab959b3be695d114645",
      "tests": [
        {
          "id": "55079d24e1fea982cf45",
          "title": "deja la página 1 para Gera y cualquier configuración anterior",
          "full_title": "normalizeCaptacionPdfPreviewPage > deja la página 1 para Gera y cualquier configuración anterior",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "38855536110a84919ea2",
          "title": "conserva enteros válidos y acota valores fuera de rango",
          "full_title": "normalizeCaptacionPdfPreviewPage > conserva enteros válidos y acota valores fuera de rango",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/captacion/lead-notification.test.ts",
      "type": "unit",
      "sha256": "88226799df5539aebee6963c5a59113ceef2d4b9fb1a20509565e91a83b6a688",
      "tests": [
        {
          "id": "c4f227c8c5074576c450",
          "title": "abre el Kanban filtrado en la etapa registrada",
          "full_title": "notificación de nuevo lead > abre el Kanban filtrado en la etapa registrada",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f70398a6bfba919c2561",
          "title": "mantiene una descripción útil cuando no se capturó la fuente",
          "full_title": "notificación de nuevo lead > mantiene una descripción útil cuando no se capturó la fuente",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/captacion/media.test.ts",
      "type": "unit",
      "sha256": "03cc28a8de2e53366c7f8c05c96017538d38a8909a612685df5f82d747fa7cef",
      "tests": [
        {
          "id": "d6499a71b72941a2628f",
          "title": "solo admite https",
          "full_title": "isSafeUrl > solo admite https",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "12ae718266475cf6ef50",
          "title": "admite HTTP únicamente para el servidor local fuera de producción",
          "full_title": "isSafeUrl > admite HTTP únicamente para el servidor local fuera de producción",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9c0f00b2fbc689a78a2e",
          "title": "reconoce archivos servidos directamente, con o sin query",
          "full_title": "isArchivoDeVideo > reconoce archivos servidos directamente, con o sin query",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "97cf2fc2a6bb34894137",
          "title": "no se queja de un campo vacío",
          "full_title": "diagnosticarVideo > no se queja de un campo vacío",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d46dac383317073dc2e7",
          "title": "acepta el enlace de inserción de YouTube",
          "full_title": "diagnosticarVideo > acepta el enlace de inserción de YouTube",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b9b7cfa612b5b431614a",
          "title": "avisa cuando se pegó la URL de la barra de YouTube",
          "full_title": "diagnosticarVideo > avisa cuando se pegó la URL de la barra de YouTube",
          "line": 38,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2d1e360515429268d6fe",
          "title": "avisa con los enlaces cortos youtu.be",
          "full_title": "diagnosticarVideo > avisa con los enlaces cortos youtu.be",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "57de7f8132693824ad29",
          "title": "acepta el reproductor de Vimeo y avisa del enlace normal",
          "full_title": "diagnosticarVideo > acepta el reproductor de Vimeo y avisa del enlace normal",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "362778b0e3fbec7d4fc2",
          "title": "reconoce un archivo de vídeo propio",
          "full_title": "diagnosticarVideo > reconoce un archivo de vídeo propio",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7dd18b4f00b14e0c9824",
          "title": "rechaza lo que no sea https",
          "full_title": "diagnosticarVideo > rechaza lo que no sea https",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9f6e5aacda05ce00e1d3",
          "title": "no da por bueno un proveedor desconocido",
          "full_title": "diagnosticarVideo > no da por bueno un proveedor desconocido",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/captacion/pdf-assets.test.ts",
      "type": "unit",
      "sha256": "df727d8473ce8cd64d809cc2fe77585b17176770ab0558dc530e3f2ba53e0ccd",
      "tests": [
        {
          "id": "4366f676ed8b7c895aaf",
          "title": "genera rutas públicas estables por tenant y tipo",
          "full_title": "PDFs administrados de Captación > genera rutas públicas estables por tenant y tipo",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7c963bdd8112e02bc236",
          "title": "acepta únicamente la firma binaria PDF",
          "full_title": "PDFs administrados de Captación > acepta únicamente la firma binaria PDF",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3a58828187e395408704",
          "title": "solo considera administrables rutas del tenant autenticado",
          "full_title": "PDFs administrados de Captación > solo considera administrables rutas del tenant autenticado",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/captacion/prefill-token.test.ts",
      "type": "unit",
      "sha256": "d01e7cc0a53eb4dc4de587b768635d18669bf3a906d18817d5e16c16ed5a34c2",
      "tests": [
        {
          "id": "0bf6e2cc590db190743a",
          "title": "expone sólo los datos comerciales seguros que necesita el formulario",
          "full_title": "commercial prefill > expone sólo los datos comerciales seguros que necesita el formulario",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e3cf8732125bb1a09cce",
          "title": "omite valores vacíos o edades inválidas",
          "full_title": "commercial prefill > omite valores vacíos o edades inválidas",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f56b188c5ba1cd34eafb",
          "title": "omite campos opcionales ausentes al persistir el token",
          "full_title": "commercial prefill > omite campos opcionales ausentes al persistir el token",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "44d222268c0d8a5e38f6",
          "title": "conserva los campos opcionales cuando están presentes",
          "full_title": "commercial prefill > conserva los campos opcionales cuando están presentes",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/captacion/public-entry.test.ts",
      "type": "unit",
      "sha256": "c134f010d3011cb6c8eb44c883afb542015737df54599fbe4c1cd493e2da221c",
      "tests": [
        {
          "id": "853095fd86ac90356dfa",
          "title": "abre el cuestionario cuando el sitio pide saltar planes",
          "full_title": "resolvePublicCaptacionEntryFase > abre el cuestionario cuando el sitio pide saltar planes",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fcdf2a250caf6707b0e0",
          "title": "conserva la vista de video y PDF si no hay parámetro",
          "full_title": "resolvePublicCaptacionEntryFase > conserva la vista de video y PDF si no hay parámetro",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "537bf8800a9e1f9e931f",
          "title": "cae al formulario cuando no hay video seguro",
          "full_title": "resolvePublicCaptacionEntryFase > cae al formulario cuando no hay video seguro",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/captacion/public-plan-catalog.test.ts",
      "type": "unit",
      "sha256": "1e58e0f89b907d11672b7523e5d201568a28cddc173a06ce58bb6748cf7e1f94",
      "tests": [
        {
          "id": "c09e6f083379170d1515",
          "title": "expone sólo los campos comerciales de un plan activo",
          "full_title": "catálogo público de captación > expone sólo los campos comerciales de un plan activo",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1c30a38f46dc70e819d1",
          "title": "descarta planes inactivos o sin identidad comercial",
          "full_title": "catálogo público de captación > descarta planes inactivos o sin identidad comercial",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e4a18229db78c585e5ae",
          "title": "calcula en centavos el descuento autoritativo de 5% para pago completo",
          "full_title": "catálogo público de captación > calcula en centavos el descuento autoritativo de 5% para pago completo",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d6ef690af32644acab05",
          "title": "la consulta VIP incluye origen, variante y todos los datos disponibles",
          "full_title": "catálogo público de captación > la consulta VIP incluye origen, variante y todos los datos disponibles",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7c500473986325e2d4b7",
          "title": "la ayuda general sin respuestas abre WhatsApp sin datos personales",
          "full_title": "catálogo público de captación > la ayuda general sin respuestas abre WhatsApp sin datos personales",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0f23a8e58279fa2774f5",
          "title": "la ayuda general incluye los datos del formulario y el plan de interés",
          "full_title": "catálogo público de captación > la ayuda general incluye los datos del formulario y el plan de interés",
          "line": 109,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9ed526a449a69d5fc058",
          "title": "omite las líneas de los datos que el prospecto no llenó",
          "full_title": "catálogo público de captación > omite las líneas de los datos que el prospecto no llenó",
          "line": 133,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/captacion/server.test.ts",
      "type": "unit",
      "sha256": "bc5fe94e5b0d988295c2da7ae169615e1f6e3c10583589490096f450b08242e0",
      "tests": [
        {
          "id": "d5531f15e9c1c71c4fc8",
          "title": "completa únicamente campos vacíos del paciente existente",
          "full_title": "consolidación de captación > completa únicamente campos vacíos del paciente existente",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b7dba05a3e44a7111d7d",
          "title": "no pisa información ya consolidada",
          "full_title": "consolidación de captación > no pisa información ya consolidada",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/captacion/whatsapp-redirect.test.ts",
      "type": "unit",
      "sha256": "0f25f9777dc8da53b1c53c166d181675b376c6697d144178520c6be072859b5a",
      "tests": [
        {
          "id": "6e7273e68cfceff86e09",
          "title": "abre WhatsApp sólo después de pedir más información",
          "full_title": "shouldRedirectInquiryToWhatsapp > abre WhatsApp sólo después de pedir más información",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "96f794e9ea59243a21c1",
          "title": "no interrumpe Quiero pagar ni un envío sin liga segura",
          "full_title": "shouldRedirectInquiryToWhatsapp > no interrumpe Quiero pagar ni un envío sin liga segura",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "254f4616868d26fff631",
          "title": "espera a que se lea la confirmación antes de redirigir",
          "full_title": "scheduleCaptacionWhatsappRedirect > espera a que se lea la confirmación antes de redirigir",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ad345ac8d95757e5b8ab",
          "title": "cancela el salto si la persona deja la pantalla de confirmación",
          "full_title": "scheduleCaptacionWhatsappRedirect > cancela el salto si la persona deja la pantalla de confirmación",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/chat/attachment-metadata.test.ts",
      "type": "unit",
      "sha256": "b490145e211d234df69c32c940e78f788c8a6ef3fdfe4784889387fdfc45ea3f",
      "tests": [
        {
          "id": "70173f42eaf9edcb9acf",
          "title": "conserva un tamaño legible sin inventar metadata ausente",
          "full_title": "attachment metadata > conserva un tamaño legible sin inventar metadata ausente",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "754f275f272e6ca3e3a6",
          "title": "combina MIME y tamaño para el render bidireccional",
          "full_title": "attachment metadata > combina MIME y tamaño para el render bidireccional",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/chat/conversation-operational-state.test.ts",
      "type": "unit",
      "sha256": "0992e6720a26b41e8edd61623af66ebce6074ee8d0a22ca21a3eb04ab287411e",
      "tests": [
        {
          "id": "84deecbb0e7566583249",
          "title": "separa lectura de necesidad de respuesta",
          "full_title": "conversation operational state > separa lectura de necesidad de respuesta",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a40d156abba58e49ee7d",
          "title": "mantiene compatibilidad con conversaciones anteriores",
          "full_title": "conversation operational state > mantiene compatibilidad con conversaciones anteriores",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4ca8c8c0d409f35a6c48",
          "title": "abre y cierra el reloj de respuesta según el remitente",
          "full_title": "conversation operational state > abre y cierra el reloj de respuesta según el remitente",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/chat/conversation-routing.test.ts",
      "type": "unit",
      "sha256": "e2763c6ec95d0f5f58867c7acdb349d776de435470d0672cc2bc9b72080b3070",
      "tests": [
        {
          "id": "57a4a3157e8eaf707fb5",
          "title": "usa al especialista asignado aunque otro miembro staff abra el chat",
          "full_title": "conversation routing > usa al especialista asignado aunque otro miembro staff abra el chat",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e9809d4c616226284e38",
          "title": "rechaza crear una llave arbitraria cuando el paciente no está asignado",
          "full_title": "conversation routing > rechaza crear una llave arbitraria cuando el paciente no está asignado",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "505772ebd29288aa127a",
          "title": "normaliza espacios del tenant y del especialista",
          "full_title": "conversation routing > normaliza espacios del tenant y del especialista",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "310c2bccb86af6102e24",
          "title": "prefiere la conversación determinista y descarta el hilo paralelo del operador",
          "full_title": "conversation routing > prefiere la conversación determinista y descarta el hilo paralelo del operador",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b455f63b9c04125f329d",
          "title": "usa sólo el legacy del especialista asignado mientras se reconcilia",
          "full_title": "conversation routing > usa sólo el legacy del especialista asignado mientras se reconcilia",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/chat/unread-state.test.ts",
      "type": "unit",
      "sha256": "d6b2e4504d4a09a87f14800448efd97d1362552f0b9783ea4e5fd6eb64457297",
      "tests": [
        {
          "id": "7f6985e99e1cbc3542db",
          "title": "incrementa sólo el lado receptor y conserva el alias staff",
          "full_title": "chat unread state > incrementa sólo el lado receptor y conserva el alias staff",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "00cdf602987512cea7b1",
          "title": "marcar leído reinicia de forma idempotente el contador del lector",
          "full_title": "chat unread state > marcar leído reinicia de forma idempotente el contador del lector",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/checkins/patient-checkin-summary.test.ts",
      "type": "unit",
      "sha256": "5fc331aaa38eb2fcd8f50e3c0ac7e8113107b98614b4d10dbf16f47ce2f0d8dd",
      "tests": [
        {
          "id": "a0a11cc2dc6b99db1730",
          "title": "conserva sólo los siete registros recientes y omite contenido clínico libre",
          "full_title": "patient check-in summary > conserva sólo los siete registros recientes y omite contenido clínico libre",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4e345d6c78856161bc1d",
          "title": "produce una versión estable y cambia cuando cambia la fuente",
          "full_title": "patient check-in summary > produce una versión estable y cambia cuando cambia la fuente",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aebdfb3d454267a2515d",
          "title": "descarta entradas inválidas sin contaminar indicadores",
          "full_title": "patient check-in summary > descarta entradas inválidas sin contaminar indicadores",
          "line": 77,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b55e97c706b0a896774c",
          "title": "mantiene paridad exacta entre la app, el trigger y el backfill",
          "full_title": "patient check-in summary > mantiene paridad exacta entre la app, el trigger y el backfill",
          "line": 91,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "71bae5a1e70102cccf8c",
          "title": "hace el backfill repetible sin reescribir una proyecciÃ³n equivalente",
          "full_title": "patient check-in summary > hace el backfill repetible sin reescribir una proyecciÃ³n equivalente",
          "line": 108,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/citas/agenda-policy.test.ts",
      "type": "unit",
      "sha256": "e2c23dab4a1947d1cdf93c10ebe191f9405c9a5800ba76d45421da909c2a80c2",
      "tests": [
        {
          "id": "de1f26fd13475e70b179",
          "title": "convierte en ambos sentidos",
          "full_title": "horaAMinutos / minutosAHora > convierte en ambos sentidos",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cb1b3469b916bbc72980",
          "title": "rechaza formatos y rangos inválidos en vez de devolver NaN",
          "full_title": "horaAMinutos / minutosAHora > rechaza formatos y rangos inválidos en vez de devolver NaN",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "70a875012c6acf9737c1",
          "title": "devuelve los defaults cuando el documento no existe",
          "full_title": "normalizeAgendaPolicy > devuelve los defaults cuando el documento no existe",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5696a2e37f65abda324b",
          "title": "descarta bloques cuyo fin no supera el inicio",
          "full_title": "normalizeAgendaPolicy > descarta bloques cuyo fin no supera el inicio",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5a4d9e9765dd44c79c29",
          "title": "fusiona bloques solapados para no duplicar slots",
          "full_title": "normalizeAgendaPolicy > fusiona bloques solapados para no duplicar slots",
          "line": 53,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4c672b850afd0f1a5d6d",
          "title": "ordena los bloques recibidos fuera de orden",
          "full_title": "normalizeAgendaPolicy > ordena los bloques recibidos fuera de orden",
          "line": 60,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "80dc5eeb658790a5fcc0",
          "title": "cae al default cuando la zona horaria no es válida",
          "full_title": "normalizeAgendaPolicy > cae al default cuando la zona horaria no es válida",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "11791b26ba166ffb9438",
          "title": "nunca produce las 24:00, que el input de hora no sabe pintar",
          "full_title": "normalizeAgendaPolicy > nunca produce las 24:00, que el input de hora no sabe pintar",
          "line": 75,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "62b68084fac4c7489135",
          "title": "acota los márgenes al rango permitido",
          "full_title": "normalizeAgendaPolicy > acota los márgenes al rango permitido",
          "line": 83,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4ab87258284c7ebe392b",
          "title": "acepta una cita que cabe completa en un bloque",
          "full_title": "estaDentroDelHorario > acepta una cita que cabe completa en un bloque",
          "line": 93,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "05e77e507c130d050a05",
          "title": "rechaza una cita que empieza dentro pero termina fuera",
          "full_title": "estaDentroDelHorario > rechaza una cita que empieza dentro pero termina fuera",
          "line": 97,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "60783818c9a846df1396",
          "title": "rechaza la hora de la comida, que cae entre dos bloques",
          "full_title": "estaDentroDelHorario > rechaza la hora de la comida, que cae entre dos bloques",
          "line": 102,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c1dbe04dee8f5434781a",
          "title": "rechaza la madrugada — el caso que el servidor aceptaba",
          "full_title": "estaDentroDelHorario > rechaza la madrugada — el caso que el servidor aceptaba",
          "line": 106,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "01fd0c9e55dd1d2232fd",
          "title": "rechaza un día sin atención",
          "full_title": "estaDentroDelHorario > rechaza un día sin atención",
          "line": 110,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ff5aac547de988b7efc0",
          "title": "acepta cualquier hora cuando se permite agendar fuera de horario",
          "full_title": "estaDentroDelHorario > acepta cualquier hora cuando se permite agendar fuera de horario",
          "line": 114,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7c09efc84f0afd3a33d5",
          "title": "interpreta la hora en la zona del workspace, no en la del servidor",
          "full_title": "estaDentroDelHorario > interpreta la hora en la zona del workspace, no en la del servidor",
          "line": 120,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "31bcaddbc0a57269f700",
          "title": "genera slots de 15 minutos que caben completos en el bloque",
          "full_title": "slotsDelDia > genera slots de 15 minutos que caben completos en el bloque",
          "line": 131,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "af95efbaebd6ff8606c5",
          "title": "una duración mayor reduce los slots ofrecidos",
          "full_title": "slotsDelDia > una duración mayor reduce los slots ofrecidos",
          "line": 139,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3b5347591e01bb5ff369",
          "title": "no ofrece nada en un día sin atención",
          "full_title": "slotsDelDia > no ofrece nada en un día sin atención",
          "line": 145,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3cc096b6c566d10e911b",
          "title": "ofrece una jornada amplia en día cerrado si se permite fuera de horario",
          "full_title": "slotsDelDia > ofrece una jornada amplia en día cerrado si se permite fuera de horario",
          "line": 149,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7d64bed0dd8285d0d7f3",
          "title": "alinea al intervalo un bloque que empieza a deshora",
          "full_title": "slotsDelDia > alinea al intervalo un bloque que empieza a deshora",
          "line": 156,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/citas/calendar-origin.test.ts",
      "type": "unit",
      "sha256": "e02842a87a4333f8a0c4d2fe1cf7122d52334331f1af8172dba75c8e6e494cb7",
      "tests": [
        {
          "id": "08bfec7954ae74dbd811",
          "title": "distingue plataforma, sólo Google y Google vinculado",
          "full_title": "calendar appointment origin > distingue plataforma, sólo Google y Google vinculado",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bcdd125a2c7b87a3a870",
          "title": "reconoce una cita Google vinculada creada antes del marcador dedicado",
          "full_title": "calendar appointment origin > reconoce una cita Google vinculada creada antes del marcador dedicado",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0609793c80250536da85",
          "title": "muestra el correo que produjo el match sin inventarlo",
          "full_title": "calendar appointment origin > muestra el correo que produjo el match sin inventarlo",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/citas/derivar-defaults.test.ts",
      "type": "unit",
      "sha256": "2c347a1986371c07be041057dda93cfb52c8978ff057ca1be36cc28a1afcf7d0",
      "tests": [
        {
          "id": "259cde6101fdac7ebbcf",
          "title": "acepta una contratación activa con sesiones restantes",
          "full_title": "esContratacionAgendable > acepta una contratación activa con sesiones restantes",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "39ffa35d8ffade8692bb",
          "title": "rechaza una agotada aunque el hook de cliente la considerara activa",
          "full_title": "esContratacionAgendable > rechaza una agotada aunque el hook de cliente la considerara activa",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f2a7316ea0f763222c55",
          "title": "rechaza una activa sin sesiones restantes",
          "full_title": "esContratacionAgendable > rechaza una activa sin sesiones restantes",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e91bbd25a1c3c5ad5289",
          "title": "rechaza canceladas y archivadas",
          "full_title": "esContratacionAgendable > rechaza canceladas y archivadas",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3d37441349fa3316c9e8",
          "title": "elige la más antigua utilizable, igual que el servidor al consumir",
          "full_title": "seleccionarContratacionParaAgendar > elige la más antigua utilizable, igual que el servidor al consumir",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d4a121d4b58228cb14fe",
          "title": "prefiere fechaInicio sobre fechaCreacion",
          "full_title": "seleccionarContratacionParaAgendar > prefiere fechaInicio sobre fechaCreacion",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "09fc1cc59809c8add4d6",
          "title": "ignora las no agendables aunque sean las más antiguas",
          "full_title": "seleccionarContratacionParaAgendar > ignora las no agendables aunque sean las más antiguas",
          "line": 62,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f3b18ee2609f2911f5ba",
          "title": "devuelve null cuando no hay ninguna utilizable",
          "full_title": "seleccionarContratacionParaAgendar > devuelve null cuando no hay ninguna utilizable",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d3364af8a6db87b293c0",
          "title": "el plan manda cuando es inequívoco",
          "full_title": "derivarModalidad > el plan manda cuando es inequívoco",
          "line": 77,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3eac9677c98cd4ddae65",
          "title": "un plan híbrido cede al tipo de cita: el paquete admite ambas",
          "full_title": "derivarModalidad > un plan híbrido cede al tipo de cita: el paquete admite ambas",
          "line": 82,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f200e61572abb5ba8c8d",
          "title": "`no_aplica` tampoco impone modalidad",
          "full_title": "derivarModalidad > `no_aplica` tampoco impone modalidad",
          "line": 91,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "31af5d8799327c2f170d",
          "title": "`heredar` en el tipo de cita deja pasar al siguiente nivel",
          "full_title": "derivarModalidad > `heredar` en el tipo de cita deja pasar al siguiente nivel",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3275eb2e48b6ef27bf0c",
          "title": "sin plan ni tipo, presencial — el comportamiento previo",
          "full_title": "derivarModalidad > sin plan ni tipo, presencial — el comportamiento previo",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b29a02c84cc68292f7ab",
          "title": "una contratación sin modalidad en el snapshot no impone nada",
          "full_title": "derivarModalidad > una contratación sin modalidad en el snapshot no impone nada",
          "line": 108,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f090f638091adbe079a1",
          "title": "no avisa cuando coincide con lo derivado",
          "full_title": "avisoModalidadSobrescrita > no avisa cuando coincide con lo derivado",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e097ae609a09c3395777",
          "title": "avisa al contradecir el plan contratado",
          "full_title": "avisoModalidadSobrescrita > avisa al contradecir el plan contratado",
          "line": 124,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "50e7214a2b65a4e3b85e",
          "title": "no avisa cuando el origen era sólo la omisión",
          "full_title": "avisoModalidadSobrescrita > no avisa cuando el origen era sólo la omisión",
          "line": 129,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/citas/entitlements.test.ts",
      "type": "unit",
      "sha256": "3b92f4c9651182a5e040d8dbe8b85fcf0cab696c5bec96494de91e377bdc98dc",
      "tests": [
        {
          "id": "687dabb91b51c79bd102",
          "title": "deriva el saldo canónico de contratos legacy",
          "full_title": "appointment session entitlements > deriva el saldo canónico de contratos legacy",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "00c6e51c11b064817a8f",
          "title": "resta reservadas y consumidas sin permitir saldo negativo",
          "full_title": "appointment session entitlements > resta reservadas y consumidas sin permitir saldo negativo",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "474b1f9bfd18c4668130",
          "title": "tolera arrays legacy incompletos sin reducir los contadores existentes",
          "full_title": "appointment session entitlements > tolera arrays legacy incompletos sin reducir los contadores existentes",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4a02fef3a4f2f5f7b345",
          "title": "no revive un contrato cancelada al completar una cita reservada",
          "full_title": "estadoContratoTrasMovimientoDeSaldo > no revive un contrato cancelada al completar una cita reservada",
          "line": 60,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4fca2703a51c587eada3",
          "title": "completa una cita ya ligada en el contrato cerrado, no en el nuevo",
          "full_title": "selectConsumableContract > completa una cita ya ligada en el contrato cerrado, no en el nuevo",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7d862347f785e12c315d",
          "title": "las citas nuevas solo eligen un contrato activa",
          "full_title": "selectConsumableContract > las citas nuevas solo eligen un contrato activa",
          "line": 83,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/citas/google-event-key.test.ts",
      "type": "unit",
      "sha256": "cadc135ecaa04cc127790efb1d526751e7f7701b6d8efc27dfe64347e068e05c",
      "tests": [
        {
          "id": "f57efdc321ffdf7c4972",
          "title": "es determinista: el mismo evento siempre da el mismo id",
          "full_title": "googleEventDocId > es determinista: el mismo evento siempre da el mismo id",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ef7c4944be2ced55e32e",
          "title": "distingue tenant, calendario y evento",
          "full_title": "googleEventDocId > distingue tenant, calendario y evento",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e5a3753ed27aff7771c3",
          "title": "no confunde dos eventos que solo difieren en donde corta el separador",
          "full_title": "googleEventDocId > no confunde dos eventos que solo difieren en donde corta el separador",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1ec0a7f04ba29b26e983",
          "title": "produce un id valido para Firestore",
          "full_title": "googleEventDocId > produce un id valido para Firestore",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b6551bbae9ab61b1c909",
          "title": "importa un evento futuro con paciente identificado por correo",
          "full_title": "evaluarVinculoImportable > importa un evento futuro con paciente identificado por correo",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9cf6c0675f418da12550",
          "title": "importa cuando el paciente viene declarado en el propio evento",
          "full_title": "evaluarVinculoImportable > importa cuando el paciente viene declarado en el propio evento",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c37a252bb18cc0f5ba70",
          "title": "no importa sin paciente resuelto",
          "full_title": "evaluarVinculoImportable > no importa sin paciente resuelto",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3d655c2922c9e142bde1",
          "title": "no importa por coincidencia de titulo: el nombre no identifica",
          "full_title": "evaluarVinculoImportable > no importa por coincidencia de titulo: el nombre no identifica",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6178af6bcbe51e2acf30",
          "title": "no importa una reunion con muchos invitados donde alguien es paciente",
          "full_title": "evaluarVinculoImportable > no importa una reunion con muchos invitados donde alguien es paciente",
          "line": 69,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "58b67b540d4f43603f9f",
          "title": "no importa historico: el paciente ve sus citas en el portal",
          "full_title": "evaluarVinculoImportable > no importa historico: el paciente ve sus citas en el portal",
          "line": 75,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f4f8d45e3ca4fa6e67b1",
          "title": "no importa un evento cancelado en Google",
          "full_title": "evaluarVinculoImportable > no importa un evento cancelado en Google",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/citas/import-google-event.test.ts",
      "type": "unit",
      "sha256": "1ae424f4a9a7a634af63c38e011e11f6c919a5f60323926d7e48e9bf460049b9",
      "tests": [
        {
          "id": "c755c286110b3e059a13",
          "title": "crea la cita con id determinista y la marca como nacida en Google",
          "full_title": "importarEventoGoogleComoCita > crea la cita con id determinista y la marca como nacida en Google",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7d019bede0ac09322f6d",
          "title": "deja pendiente la decision de descontar del plan, sin consumirlo solo",
          "full_title": "importarEventoGoogleComoCita > deja pendiente la decision de descontar del plan, sin consumirlo solo",
          "line": 90,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fb714244f99212667c90",
          "title": "nunca escribe undefined: Firestore rechaza el documento entero",
          "full_title": "importarEventoGoogleComoCita > nunca escribe undefined: Firestore rechaza el documento entero",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "10adb71377315e4ac8e6",
          "title": "no duplica cuando otra cita ya reclama ese evento",
          "full_title": "importarEventoGoogleComoCita > no duplica cuando otra cita ya reclama ese evento",
          "line": 114,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "77822f58caeef20796c1",
          "title": "trata la colision de id como ya existente, no como error",
          "full_title": "importarEventoGoogleComoCita > trata la colision de id como ya existente, no como error",
          "line": 130,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ac4e24e6a3f2f5e9dee3",
          "title": "propaga cualquier otro fallo de escritura",
          "full_title": "importarEventoGoogleComoCita > propaga cualquier otro fallo de escritura",
          "line": 138,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "938fb92aa65d03ce52b5",
          "title": "omite lo que no cumple el criterio, sin escribir nada",
          "full_title": "importarEventoGoogleComoCita > omite lo que no cumple el criterio, sin escribir nada",
          "line": 144,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "914dba01e22ebe106313",
          "title": "omite el historico aunque el paciente este bien identificado",
          "full_title": "importarEventoGoogleComoCita > omite el historico aunque el paciente este bien identificado",
          "line": 151,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6d35f46cb2942dc9153c",
          "title": "refleja la hora nueva del evento: Google manda en cuando ocurre",
          "full_title": "reconciliarCitaImportada > refleja la hora nueva del evento: Google manda en cuando ocurre",
          "line": 173,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b911b8c302d0f3c0430c",
          "title": "no escribe si nada cambio",
          "full_title": "reconciliarCitaImportada > no escribe si nada cambio",
          "line": 185,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "02bb7a8ad9909e14d202",
          "title": "no reescribe una cita ya cerrada (%s): corromperia el expediente",
          "full_title": "reconciliarCitaImportada > no reescribe una cita ya cerrada (%s): corromperia el expediente",
          "line": 196,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        }
      ]
    },
    {
      "path": "tests/unit/lib/citas/lifecycle.test.ts",
      "type": "unit",
      "sha256": "e297d11ef55c184dfe5e33a8ab13b6a6a78ba712965821805a464fdc1cc27ab7",
      "tests": [
        {
          "id": "52adab7c5fd7be94c6cd",
          "title": "normaliza reagendada como evento y repara estado/completada contradictorios",
          "full_title": "appointment lifecycle > normaliza reagendada como evento y repara estado/completada contradictorios",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3db3c05ab3e5cc67dad9",
          "title": "expone únicamente las transiciones canónicas permitidas",
          "full_title": "appointment lifecycle > expone únicamente las transiciones canónicas permitidas",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7017b6cd22bc59a25b41",
          "title": "rechaza saltos y cambios desde estados terminales",
          "full_title": "appointment lifecycle > rechaza saltos y cambios desde estados terminales",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9513c0422b1a1b666768",
          "title": "proyecta el estado compatible de la consulta",
          "full_title": "appointment lifecycle > proyecta el estado compatible de la consulta",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ee186444c15223cce125",
          "title": "mantiene el acceso desde 30 minutos antes hasta fin más 15 minutos, inclusivo",
          "full_title": "appointment lifecycle > mantiene el acceso desde 30 minutos antes hasta fin más 15 minutos, inclusivo",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "efa19deac87e22c4a2c6",
          "title": "aplica la ventana configurable de acceso y conserva defaults para citas legacy",
          "full_title": "appointment lifecycle > aplica la ventana configurable de acceso y conserva defaults para citas legacy",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1b73211e0be53c47587f",
          "title": "mantiene visible una cita iniciada hasta fin+tolerancia y excluye estados terminales",
          "full_title": "appointment lifecycle > mantiene visible una cita iniciada hasta fin+tolerancia y excluye estados terminales",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "352cc5d94df219d5ae88",
          "title": "construye el CTA canónico por citaId",
          "full_title": "appointment lifecycle > construye el CTA canónico por citaId",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/citas/marca-confirmacion.test.ts",
      "type": "unit",
      "sha256": "4ecd520485430bcbc78c25719e5127326e948e0f24b9f0096a81ec34e5ea4201",
      "tests": [
        {
          "id": "c2f30e0378710eb7904a",
          "title": "reconoce el alfabeto de la clinica",
          "full_title": "parseMarcaConfirmacion > reconoce el alfabeto de la clinica",
          "line": 17,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "9b9bd4aea527bdafbbd5",
          "title": "encuentra la marca aunque no vaya al inicio del titulo",
          "full_title": "parseMarcaConfirmacion > encuentra la marca aunque no vaya al inicio del titulo",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c8603207325585c9c0e3",
          "title": "no marca nada cuando el titulo no trae senal",
          "full_title": "parseMarcaConfirmacion > no marca nada cuando el titulo no trae senal",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7c7a1fa7163c0d9f510b",
          "title": "ignora emojis de color fuera del alfabeto en vez de adivinar",
          "full_title": "parseMarcaConfirmacion > ignora emojis de color fuera del alfabeto en vez de adivinar",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f65e2bb2141311318fa0",
          "title": "con dos marcas distintas gana la primera del titulo",
          "full_title": "parseMarcaConfirmacion > con dos marcas distintas gana la primera del titulo",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7020f9b17bd435dfa5c2",
          "title": "tolera titulo vacio o ausente",
          "full_title": "parseMarcaConfirmacion > tolera titulo vacio o ausente",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "14223bf83288ddc36cb8",
          "title": "deja el titulo utilizable por el regex de identidad",
          "full_title": "stripMarcaConfirmacion > deja el titulo utilizable por el regex de identidad",
          "line": 53,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8bb72a5dc71ad114bd07",
          "title": "quita los separadores que quedan sueltos al retirar la marca",
          "full_title": "stripMarcaConfirmacion > quita los separadores que quedan sueltos al retirar la marca",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bbbf4e7308d47bb1f52a",
          "title": "quita todas las marcas, no solo la primera",
          "full_title": "stripMarcaConfirmacion > quita todas las marcas, no solo la primera",
          "line": 63,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "16fae14361d435a7b543",
          "title": "es idempotente",
          "full_title": "stripMarcaConfirmacion > es idempotente",
          "line": 67,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "db94877cac46ef77ee4c",
          "title": "no altera un titulo sin marca",
          "full_title": "stripMarcaConfirmacion > no altera un titulo sin marca",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "217baf2eea0918018138",
          "title": "devuelve cadena vacia si el titulo era solo la marca",
          "full_title": "stripMarcaConfirmacion > devuelve cadena vacia si el titulo era solo la marca",
          "line": 77,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/citas/patient-appointment-summary.test.ts",
      "type": "unit",
      "sha256": "239a4d1ba44fbfe2cb9996e575168621d19e2d16ae17c341ab4c9bbd875532e3",
      "tests": [
        {
          "id": "8bddf7d541f365f16d5c",
          "title": "devuelve vacío sin paciente o sin citas",
          "full_title": "resumirCitasDePaciente > devuelve vacío sin paciente o sin citas",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8df5135178718ee742b9",
          "title": "solo considera las citas del paciente indicado",
          "full_title": "resumirCitasDePaciente > solo considera las citas del paciente indicado",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bf266b26e7aa609f37fc",
          "title": "una cita pasada no cuenta como próxima",
          "full_title": "resumirCitasDePaciente > una cita pasada no cuenta como próxima",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f3a7cdbbaca952498671",
          "title": "una cita cancelada o no_show en el futuro tampoco cuenta",
          "full_title": "resumirCitasDePaciente > una cita cancelada o no_show en el futuro tampoco cuenta",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a891e2ffa8ac6c1fa62a",
          "title": "una cita reagendada a futuro sí respalda, porque normaliza a programada",
          "full_title": "resumirCitasDePaciente > una cita reagendada a futuro sí respalda, porque normaliza a programada",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1d7da75d8d68a015d5c7",
          "title": "elige la más cercana entre varias futuras",
          "full_title": "resumirCitasDePaciente > elige la más cercana entre varias futuras",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "97aac3755c1dece30f90",
          "title": "una cita completada en el futuro no es próxima",
          "full_title": "resumirCitasDePaciente > una cita completada en el futuro no es próxima",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e75e6049a72a0d4d832a",
          "title": "la última realizada es la más reciente, no la primera del arreglo",
          "full_title": "resumirCitasDePaciente > la última realizada es la más reciente, no la primera del arreglo",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e28d50c724787eaecf9b",
          "title": "reconoce completada por el booleano aunque el estado diga otra cosa",
          "full_title": "resumirCitasDePaciente > reconoce completada por el booleano aunque el estado diga otra cosa",
          "line": 96,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "63749a55b25ad8867108",
          "title": "detecta una sesión en curso",
          "full_title": "resumirCitasDePaciente > detecta una sesión en curso",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "54dab68f763f341ba2df",
          "title": "descarta citas con fecha ilegible en vez de propagar NaN",
          "full_title": "resumirCitasDePaciente > descarta citas con fecha ilegible en vez de propagar NaN",
          "line": 116,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d0a9d4f33394cdad66da",
          "title": "ordena el historial de la más reciente a la más antigua",
          "full_title": "resumirCitasDePaciente > ordena el historial de la más reciente a la más antigua",
          "line": 125,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/citas/tipo-cita-defaults.test.ts",
      "type": "unit",
      "sha256": "701051d92bb2d5d9fd0e289329ef1373e46e7417a5e0800d6c91868f02aab201",
      "tests": [
        {
          "id": "a4c91eb68437820da9e4",
          "title": "incluye los tipos que antes sólo existían en consultas",
          "full_title": "catálogo canónico de tipos de cita > incluye los tipos que antes sólo existían en consultas",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "308801b3691c36c9c500",
          "title": "conserva cuestionarios y preparación avanzada en el metadata del tipo",
          "full_title": "catálogo canónico de tipos de cita > conserva cuestionarios y preparación avanzada en el metadata del tipo",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e908970692e76798d74c",
          "title": "cuenta la guía dentro del máximo de archivos y aplica 10 MB totales",
          "full_title": "catálogo canónico de tipos de cita > cuenta la guía dentro del máximo de archivos y aplica 10 MB totales",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2f67d9ba8771a316f43d",
          "title": "sólo permite retirar una consulta completamente vacía",
          "full_title": "catálogo canónico de tipos de cita > sólo permite retirar una consulta completamente vacía",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/clinical/exercise-eligibility.test.ts",
      "type": "unit",
      "sha256": "acc39d5a47cc03d2a63728d825b681880739e9c999cc6da1887e8c5afaf5d81d",
      "tests": [
        {
          "id": "e69357887c9e09dd0be9",
          "title": "convierte los bloqueos de una rutina en advertencias asignables",
          "full_title": "exercise clinical eligibility > convierte los bloqueos de una rutina en advertencias asignables",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f3a0958dc9cc452c8a72",
          "title": "normaliza alias de equipo en español e inglés",
          "full_title": "exercise clinical eligibility > normaliza alias de equipo en español e inglés",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "649440a5b1f0fd172af0",
          "title": "advierte sin bloquear cuando falta equipo y el paciente sí declaró equipo disponible",
          "full_title": "exercise clinical eligibility > advierte sin bloquear cuando falta equipo y el paciente sí declaró equipo disponible",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d20fdabf62f2e2be798e",
          "title": "no muestra advertencias cuando el perfil de equipo no se ha configurado",
          "full_title": "exercise clinical eligibility > no muestra advertencias cuando el perfil de equipo no se ha configurado",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c5ca4794be674da957d5",
          "title": "no muestra advertencias cuando el paciente declaró que entrena sin equipo",
          "full_title": "exercise clinical eligibility > no muestra advertencias cuando el paciente declaró que entrena sin equipo",
          "line": 71,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "645fdce799a73c100de1",
          "title": "omite la validacion de equipo al crear una rutina global sin paciente",
          "full_title": "exercise clinical eligibility > omite la validacion de equipo al crear una rutina global sin paciente",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "93b947c658f3e804aeb4",
          "title": "normaliza el equipo de catalogo %s con el perfil %s",
          "full_title": "exercise clinical eligibility > normaliza el equipo de catalogo %s con el perfil %s",
          "line": 91,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "101baf810b703a8e0e14",
          "title": "bloquea una coincidencia entre antecedente y zona de riesgo",
          "full_title": "exercise clinical eligibility > bloquea una coincidencia entre antecedente y zona de riesgo",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a6eea16c0cbb37895695",
          "title": "incluye notas clínicas como fuente autoritativa del perfil",
          "full_title": "exercise clinical eligibility > incluye notas clínicas como fuente autoritativa del perfil",
          "line": 114,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "19558b226964639befdd",
          "title": "advierte por medicación y carga técnica sin bloquear por sí sola",
          "full_title": "exercise clinical eligibility > advierte por medicación y carga técnica sin bloquear por sí sola",
          "line": 132,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7fa3bdf5123ab4df5d94",
          "title": "bloquea un ejercicio avanzado en una rutina principiante",
          "full_title": "exercise clinical eligibility > bloquea un ejercicio avanzado en una rutina principiante",
          "line": 141,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8d00ccc04511ab5083d9",
          "title": "bloquea ejercicios marcados sólo supervisados",
          "full_title": "exercise clinical eligibility > bloquea ejercicios marcados sólo supervisados",
          "line": 150,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7a6f2775892d7699ad70",
          "title": "mantiene el bloqueo si la justificación no cumple el mínimo",
          "full_title": "exercise clinical eligibility > mantiene el bloqueo si la justificación no cumple el mínimo",
          "line": 158,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2547e366800c78c1510d",
          "title": "convierte bloqueos a advertencias con override profesional trazable",
          "full_title": "exercise clinical eligibility > convierte bloqueos a advertencias con override profesional trazable",
          "line": 168,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3d143de0dc72ac10d0b5",
          "title": "agrega el resultado de todos los ejercicios de la rutina",
          "full_title": "exercise clinical eligibility > agrega el resultado de todos los ejercicios de la rutina",
          "line": 184,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/clinical/food-eligibility.test.ts",
      "type": "unit",
      "sha256": "6e79e7fd61839842df4e9c8a48c5424d446ec1bf957e16306905c8546925beae",
      "tests": [
        {
          "id": "3dc1fffa2f438cc7693d",
          "title": "devuelve blocked y un código estable al detectar un alérgeno",
          "full_title": "motor único de elegibilidad clínica > devuelve blocked y un código estable al detectar un alérgeno",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eee1fc7c1f6a6ee3da07",
          "title": "trata veganismo declarado como convicción y bloquea carne, pescado y lácteos",
          "full_title": "motor único de elegibilidad clínica > trata veganismo declarado como convicción y bloquea carne, pescado y lácteos",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6c26bbf465e5e2d41c40",
          "title": "mantiene un ingrediente no preferido como warning configurable",
          "full_title": "motor único de elegibilidad clínica > mantiene un ingrediente no preferido como warning configurable",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "385146fc6767c7a4154d",
          "title": "deja datos insuficientes en warning para revisión",
          "full_title": "motor único de elegibilidad clínica > deja datos insuficientes en warning para revisión",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4715f295019feee4ff70",
          "title": "mantiene en revisión una condición médica sin regla alimentaria explícita",
          "full_title": "motor único de elegibilidad clínica > mantiene en revisión una condición médica sin regla alimentaria explícita",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5e3da0fc52ed1e97bca0",
          "title": "sólo aplica override profesional cuando registra autor, motivo, fecha y código",
          "full_title": "motor único de elegibilidad clínica > sólo aplica override profesional cuando registra autor, motivo, fecha y código",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/clinical/measurements.test.ts",
      "type": "unit",
      "sha256": "c1bdfcfc26db3b16d51f1fcab33197135ba23ee7b3f7cead96cc94160bda3019",
      "tests": [
        {
          "id": "556f2b86b1f64c7768eb",
          "title": "selects the latest measurement by timestamp instead of array position",
          "full_title": "canonical clinical measurements > selects the latest measurement by timestamp instead of array position",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c71689b1748484ecdf81",
          "title": "sorts without mutating the source and places invalid dates last",
          "full_title": "canonical clinical measurements > sorts without mutating the source and places invalid dates last",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9943f5d2ec7bd70f1f2d",
          "title": "uses the id as a deterministic tie breaker",
          "full_title": "canonical clinical measurements > uses the id as a deterministic tie breaker",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1b4aad1aa96cf3aded80",
          "title": "uses one concurrent writer for portal and staff and maintains the patient projection",
          "full_title": "canonical clinical measurements > uses one concurrent writer for portal and staff and maintains the patient projection",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/clinical/routine-eligibility-presentation.test.ts",
      "type": "unit",
      "sha256": "4d6ed0083dfcbe0e92003ea2f004b868f5cc37a09a0f638030cd44175c0897e0",
      "tests": [
        {
          "id": "c923b1a1be5db42af977",
          "title": "convierte la falta de perfil de equipo en información no bloqueante",
          "full_title": "getRoutineEligibilityPresentation > convierte la falta de perfil de equipo en información no bloqueante",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2625627b4f2d2f446ec0",
          "title": "presenta como advertencia la falta de equipo confirmado",
          "full_title": "getRoutineEligibilityPresentation > presenta como advertencia la falta de equipo confirmado",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "57474674f04b0a506306",
          "title": "conserva advertencias clínicas accionables como Revisar",
          "full_title": "getRoutineEligibilityPresentation > conserva advertencias clínicas accionables como Revisar",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "99f96f4cad94f7e51954",
          "title": "presenta hallazgos bloqueantes como revisión sin mostrar Bloqueada",
          "full_title": "getRoutineEligibilityPresentation > presenta hallazgos bloqueantes como revisión sin mostrar Bloqueada",
          "line": 83,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8e51be5f39efa5d35067",
          "title": "no infla el contador accionable con avisos de equipo sin configurar",
          "full_title": "getRoutineEligibilityPresentation > no infla el contador accionable con avisos de equipo sin configurar",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/comercial/appointment-adapter.test.ts",
      "type": "unit",
      "sha256": "792c249651d2a06c1a3b1c54952b7dc543c083a2026d883b6c3320c403a9fd52",
      "tests": [
        {
          "id": "f522114ef65d32507eed",
          "title": "mueve a cita agendada con correlación",
          "full_title": "appointmentCommercialChanges > mueve a cita agendada con correlación",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "95d5b08a73638000a4f5",
          "title": "no retrocede atención activa al agendar un seguimiento",
          "full_title": "appointmentCommercialChanges > no retrocede atención activa al agendar un seguimiento",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "080ef2c61f04426454bd",
          "title": "vuelve a pagado sin cita al cancelar la única reserva",
          "full_title": "appointmentCommercialChanges > vuelve a pagado sin cita al cancelar la única reserva",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5a346ebecbac7a06ceb0",
          "title": "conserva cita agendada si existe otra cita vigente",
          "full_title": "appointmentCommercialChanges > conserva cita agendada si existe otra cita vigente",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/comercial/feature-flag.test.ts",
      "type": "unit",
      "sha256": "0c0fa965630c6e94ba7b054745142c20969f080055e98253b659bc66647d18b2",
      "tests": [
        {
          "id": "e688dda40bf00463934a",
          "title": "permite HIXION y el workspace del cliente, y a nadie más",
          "full_title": "commercial flow rollout > permite HIXION y el workspace del cliente, y a nadie más",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ea28502c1343d5c157e2",
          "title": "la lista por sí sola no enciende nada: falta el documento del tenant",
          "full_title": "commercial flow rollout > la lista por sí sola no enciende nada: falta el documento del tenant",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/comercial/legacy-state.test.ts",
      "type": "unit",
      "sha256": "650f8f34f369c5be831d8c7c74377b808f8edd82848266477bb33cf7e0030f47",
      "tests": [
        {
          "id": "d11d1bfc7f198d5b1e6c",
          "title": "mapea pagado a la cola canónica sin cita",
          "full_title": "compatibilidad comercial legacy > mapea pagado a la cola canónica sin cita",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c4caac0c6edf44d05e1b",
          "title": "preserva el estado canónico y su revisión",
          "full_title": "compatibilidad comercial legacy > preserva el estado canónico y su revisión",
          "line": 9,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ed4b55f3f63595f5e6a1",
          "title": "marca estados ambiguos para revisión de migración",
          "full_title": "compatibilidad comercial legacy > marca estados ambiguos para revisión de migración",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/comercial/onboarding-access.test.ts",
      "type": "unit",
      "sha256": "c69bdaa534bd7e250eeec9529a1ec16abd1b823d7cf46259c5079704b8d9b99a",
      "tests": [
        {
          "id": "af9257e757cea3a0ec12",
          "title": "activa acceso y produce un evento auditable determinista",
          "full_title": "activación comercial al completar onboarding > activa acceso y produce un evento auditable determinista",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3a8fec678fdbde00f9da",
          "title": "rechaza activar acceso sin pago y contrato válidos",
          "full_title": "activación comercial al completar onboarding > rechaza activar acceso sin pago y contrato válidos",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/comercial/pagado-pipeline-projection.test.ts",
      "type": "unit",
      "sha256": "11878ded9239a87223b8808853777464b4b1d0856515866ec945b7d707fc8c50",
      "tests": [
        {
          "id": "654e5b651cfbd02fb000",
          "title": "el cobro autoritativo usa el mismo parche que el webhook",
          "full_title": "proyección de pagado en el pipeline > el cobro autoritativo usa el mismo parche que el webhook",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/comercial/pipeline-rollout.test.ts",
      "type": "unit",
      "sha256": "46bbf7af4a4e9ebc3029eaa4a893a11a8561403bf26cafab52729a67815e4d5c",
      "tests": [
        {
          "id": "a44e3e09ca65e6ae08ac",
          "title": "conserva las columnas legacy cuando la bandera está apagada",
          "full_title": "pipeline comercial por rollout > conserva las columnas legacy cuando la bandera está apagada",
          "line": 9,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "67cb020c62ddf7de7d47",
          "title": "activa exactamente el embudo canónico comercial en HIXION",
          "full_title": "pipeline comercial por rollout > activa exactamente el embudo canónico comercial en HIXION",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e19fc16c15dd1a1937aa",
          "title": "migra en memoria una configuración legacy sin escribirla en Gera",
          "full_title": "pipeline comercial por rollout > migra en memoria una configuración legacy sin escribirla en Gera",
          "line": 38,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/comercial/queues.test.ts",
      "type": "unit",
      "sha256": "4057340734e2b7b101aed8c493d495c799b1126b29d4882e14285378e5a42ccf",
      "tests": [
        {
          "id": "984901ebee29d861d973",
          "title": "separa pagados sin cita, checkout y seguimientos vencidos",
          "full_title": "colas operativas comerciales > separa pagados sin cita, checkout y seguimientos vencidos",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "135750fb5a7b763bc88d",
          "title": "incluye acceso pendiente sólo con pago y contrato activos",
          "full_title": "colas operativas comerciales > incluye acceso pendiente sólo con pago y contrato activos",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/comercial/state-machine.test.ts",
      "type": "unit",
      "sha256": "e833afdeda10eff4711921c7217733cbc6197a43f139e841d0cf92f3ef8fa2d4",
      "tests": [
        {
          "id": "2e48557ca76904d010d5",
          "title": "avanza varios ejes de forma atómica y revisionada",
          "full_title": "máquina de estado comercial > avanza varios ejes de forma atómica y revisionada",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0bff42c63e0dfb12dedf",
          "title": "impide confirmar pago sin cobro válido y contrato activo",
          "full_title": "máquina de estado comercial > impide confirmar pago sin cobro válido y contrato activo",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "392a4e4bddd993c0f6be",
          "title": "impide retrocesos salvo corrección explícita",
          "full_title": "máquina de estado comercial > impide retrocesos salvo corrección explícita",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b85a6521818e61e5ab0e",
          "title": "exige correlación de cita para cita agendada",
          "full_title": "máquina de estado comercial > exige correlación de cita para cita agendada",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "02d2c02b45ff402efea4",
          "title": "registra una disputa posterior sin borrar la etapa ya alcanzada",
          "full_title": "máquina de estado comercial > registra una disputa posterior sin borrar la etapa ya alcanzada",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "06c1463e1357a230ee86",
          "title": "sigue exigiendo pago y contrato al enviar acceso",
          "full_title": "máquina de estado comercial > sigue exigiendo pago y contrato al enviar acceso",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/configuracion/team-invitations.test.ts",
      "type": "unit",
      "sha256": "07b5563115401728b956f468cdb41a46afb4046af50da68db04e8b686c6962fa",
      "tests": [
        {
          "id": "01d7cc914ea17581deae",
          "title": "excluye vencidas, elimina correos duplicados y limita las más recientes",
          "full_title": "selectVisibleTeamInvitations > excluye vencidas, elimina correos duplicados y limita las más recientes",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/data/avanzar-a-pagado.test.ts",
      "type": "unit",
      "sha256": "377a22885c65df55d1413a0f41f2c90f89ae22d566d48cd37813ec696c35835a",
      "tests": [
        {
          "id": "0e96173f154369603a2e",
          "title": "avanza desde las etapas comerciales",
          "full_title": "debeAvanzarAPagado > avanza desde las etapas comerciales",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dca8b5d3b9f03d244c32",
          "title": "incluye captación, checkout y el embudo v2",
          "full_title": "debeAvanzarAPagado > incluye captación, checkout y el embudo v2",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fe9691ee3301af3dd80a",
          "title": "NO retrocede a quien ya está en atención",
          "full_title": "debeAvanzarAPagado > NO retrocede a quien ya está en atención",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "412f08797911fd29981a",
          "title": "no reactiva a quien fue cancelado ni mueve colaboradores",
          "full_title": "debeAvanzarAPagado > no reactiva a quien fue cancelado ni mueve colaboradores",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "983c321d9ad80643dfef",
          "title": "avanza cuando la ficha no tiene etapa, para que no quede escondida",
          "full_title": "debeAvanzarAPagado > avanza cuando la ficha no tiene etapa, para que no quede escondida",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7e15577db389d85065ae",
          "title": "no avanza ante una etapa personalizada desconocida",
          "full_title": "debeAvanzarAPagado > no avanza ante una etapa personalizada desconocida",
          "line": 62,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9f1c80f3b46a93ad3f0a",
          "title": "mueve a pagado desde más información, checkout y planes",
          "full_title": "pipelinePatchAfterConfirmedPayment > mueve a pagado desde más información, checkout y planes",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "399a89286189f4b2874c",
          "title": "no toca a quien ya está en atención",
          "full_title": "pipelinePatchAfterConfirmedPayment > no toca a quien ya está en atención",
          "line": 77,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/data/example-supplement-kits.test.ts",
      "type": "unit",
      "sha256": "0e6ac533af2fcd9d8a9fcde18f39ee724436728ae19ff1e2230aae390e8b824b",
      "tests": [
        {
          "id": "46e166aa05b073319339",
          "title": "define los tres kits básicos",
          "full_title": "kits de ejemplo > define los tres kits básicos",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f5c49619646737b9a9fd",
          "title": "arma los tres kits con productos del catálogo y no los duplica por nombre o id",
          "full_title": "kits de ejemplo > arma los tres kits con productos del catálogo y no los duplica por nombre o id",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c291fdcb6afd572f0a0a",
          "title": "omite un kit si no hay ningún producto que coincida",
          "full_title": "kits de ejemplo > omite un kit si no hay ningún producto que coincida",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fdfd25e09b4c5a87e748",
          "title": "reconoce proteína y creatina aunque la categoría venga con etiqueta legacy",
          "full_title": "kits de ejemplo > reconoce proteína y creatina aunque la categoría venga con etiqueta legacy",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/data/lada-telefono.test.ts",
      "type": "unit",
      "sha256": "814b717e04f6df0391b065218469054ef715aaa78b430fca8a4bac6ad22ad94e",
      "tests": [
        {
          "id": "6a17487887610395104c",
          "title": "ignora números que no empiezan con +",
          "full_title": "detectarPaisPorLada > ignora números que no empiezan con +",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "62c05c7f90f15be7b3bc",
          "title": "separa lada y número nacional de un pegado completo",
          "full_title": "detectarPaisPorLada > separa lada y número nacional de un pegado completo",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d0f58a1d8d0af573ed70",
          "title": "tolera separadores dentro del número pegado",
          "full_title": "detectarPaisPorLada > tolera separadores dentro del número pegado",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f812c2f140e11a02cc84",
          "title": "prefiere el prefijo más largo cuando dos ladas comparten inicio",
          "full_title": "detectarPaisPorLada > prefiere el prefijo más largo cuando dos ladas comparten inicio",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "91c52dc0da519092a864",
          "title": "con ladas empatadas resuelve por orden del catálogo",
          "full_title": "detectarPaisPorLada > con ladas empatadas resuelve por orden del catálogo",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "99c5ea25987936d52056",
          "title": "devuelve null cuando ninguna lada coincide",
          "full_title": "detectarPaisPorLada > devuelve null cuando ninguna lada coincide",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c0737f09fe0399c12940",
          "title": "es falso mientras exista una lada más larga con el mismo inicio",
          "full_title": "esLadaCompleta > es falso mientras exista una lada más larga con el mismo inicio",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d420b783a404350b1f62",
          "title": "es verdadero cuando ninguna otra lada extiende estos dígitos",
          "full_title": "esLadaCompleta > es verdadero cuando ninguna otra lada extiende estos dígitos",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "581f0e1858feacd088a0",
          "title": "resuelve la lada exacta sin el signo +",
          "full_title": "LADA_DIGITS_TO_PAIS > resuelve la lada exacta sin el signo +",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8541e42d473d60602945",
          "title": "separa el teléfono guardado en lada y parte nacional formateada",
          "full_title": "descomponerTelefono / componerTelefono > separa el teléfono guardado en lada y parte nacional formateada",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7aa933d7b9f502a68f62",
          "title": "no recorta dígitos cuando el guardado no trae la lada del país",
          "full_title": "descomponerTelefono / componerTelefono > no recorta dígitos cuando el guardado no trae la lada del país",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1f192975fa3efb3cb65b",
          "title": "sobrevive al viaje de ida y vuelta",
          "full_title": "descomponerTelefono / componerTelefono > sobrevive al viaje de ida y vuelta",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e0a0e418551e99f38e62",
          "title": "conserva una lada tecleada aunque no corresponda a ningún país conocido",
          "full_title": "descomponerTelefono / componerTelefono > conserva una lada tecleada aunque no corresponda a ningún país conocido",
          "line": 91,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2f54840db435dcd3c252",
          "title": "devuelve cadena vacía cuando no hay dígitos nacionales",
          "full_title": "descomponerTelefono / componerTelefono > devuelve cadena vacía cuando no hay dígitos nacionales",
          "line": 96,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d8f0a6fd937ab6e4de5e",
          "title": "ladaDePais devuelve vacío para un país desconocido",
          "full_title": "descomponerTelefono / componerTelefono > ladaDePais devuelve vacío para un país desconocido",
          "line": 101,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/data/photo-progress.test.ts",
      "type": "unit",
      "sha256": "a5ff60a8486767e766652e11b86feb87163f40a9d988c2c679a319684afd3b3b",
      "tests": [
        {
          "id": "a7cdf38830bd0951344d",
          "title": "acepta %s",
          "full_title": "photo progress mobile formats > acepta %s",
          "line": 6,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "351b239a378ba53af88c",
          "title": "expone HEIC y HEIF al selector movil",
          "full_title": "photo progress mobile formats > expone HEIC y HEIF al selector movil",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/data/workspace-catalogs-create.test.ts",
      "type": "unit",
      "sha256": "84f026316261b58ce997b96ffc434de77e1acf0577eee748d8673b4a0c8909ab",
      "tests": [
        {
          "id": "6047fa66cd51c9ebe558",
          "title": "devuelve null si la búsqueda está vacía",
          "full_title": "catalogCreateCandidate > devuelve null si la búsqueda está vacía",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0b8ed6a5620f4ad4eebb",
          "title": "devuelve null si el valor ya existe por etiqueta",
          "full_title": "catalogCreateCandidate > devuelve null si el valor ya existe por etiqueta",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "65408cf9811ef10f40fd",
          "title": "devuelve null si el valor ya existe por id",
          "full_title": "catalogCreateCandidate > devuelve null si el valor ya existe por id",
          "line": 33,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "37de969ba099ec5f1306",
          "title": "propone id y etiqueta para un valor nuevo",
          "full_title": "catalogCreateCandidate > propone id y etiqueta para un valor nuevo",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c570968c580ff5eb143f",
          "title": "acepta el id del catálogo y la etiqueta legacy",
          "full_title": "catalogValueMatches > acepta el id del catálogo y la etiqueta legacy",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2121b26c6fa63241a642",
          "title": "rechaza otro valor",
          "full_title": "catalogValueMatches > rechaza otro valor",
          "line": 53,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2f709d146ad052498451",
          "title": "pone proteína, creatina y farmacología al frente",
          "full_title": "supplement-categories clinical order > pone proteína, creatina y farmacología al frente",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b004631e2b44dcc4e955",
          "title": "migra el orden semilla viejo guardado en el workspace",
          "full_title": "supplement-categories clinical order > migra el orden semilla viejo guardado en el workspace",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "abbbf72df75daacdfbe8",
          "title": "respeta un orden custom que no coincide con la semilla vieja",
          "full_title": "supplement-categories clinical order > respeta un orden custom que no coincide con la semilla vieja",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "32a0f23090e87afc89bc",
          "title": "conserva visible un valor inactivo ya guardado",
          "full_title": "includeHistoricalCatalogItem > conserva visible un valor inactivo ya guardado",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a8907e45cb22131ca34f",
          "title": "no incluye inactivos si no hay valor seleccionado",
          "full_title": "includeHistoricalCatalogItem > no incluye inactivos si no hay valor seleccionado",
          "line": 94,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/email/patient-access-template.test.tsx",
      "type": "unit",
      "sha256": "73014616ab5b406f066040a2f9df59a7d2ed4b3d9f34e06823259b2674e20b85",
      "tests": [
        {
          "id": "0d163bec533df7ee061f",
          "title": "escapa el mensaje editable y conserva sus saltos de línea",
          "full_title": "correo de acceso > escapa el mensaje editable y conserva sus saltos de línea",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/estudio-corporal-confirmacion.test.ts",
      "type": "unit",
      "sha256": "d270c1102bdee73a398d7c6862c53555a756b09270fb07dc32ab0e926ca96432",
      "tests": [
        {
          "id": "e344a9f57b1cd72eeb08",
          "title": "solo lista lo que el estudio trajo",
          "full_title": "campos de confirmacion > solo lista lo que el estudio trajo",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d23e705858bbfd60f090",
          "title": "con incluirVacios muestra tambien los campos para capturar a mano",
          "full_title": "campos de confirmacion > con incluirVacios muestra tambien los campos para capturar a mano",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cdff5ce56d1ba5191b4c",
          "title": "agrupa los campos segun las pestanas del expediente",
          "full_title": "campos de confirmacion > agrupa los campos segun las pestanas del expediente",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5ea16bba64403628f988",
          "title": "marca la procedencia del dato para no confundirlo con captura manual",
          "full_title": "medicion confirmada > marca la procedencia del dato para no confundirlo con captura manual",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e5fa377b5790d999ad69",
          "title": "reconstruye los grupos anidados del expediente",
          "full_title": "medicion confirmada > reconstruye los grupos anidados del expediente",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "78481c4a28fb8887645c",
          "title": "omite los campos que el especialista excluyo o dejo vacios",
          "full_title": "medicion confirmada > omite los campos que el especialista excluyo o dejo vacios",
          "line": 96,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1d3b757a49068dda0a9f",
          "title": "no permite guardar sin peso confirmado",
          "full_title": "medicion confirmada > no permite guardar sin peso confirmado",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "86029e2f226dab260cf6",
          "title": "traduce la ruta tecnica a la etiqueta que ve el especialista",
          "full_title": "resumen de descartes > traduce la ruta tecnica a la etiqueta que ve el especialista",
          "line": 116,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "864a46161b8364bc2c3e",
          "title": "conserva la ruta cuando el campo no esta en el catalogo",
          "full_title": "resumen de descartes > conserva la ruta cuando el campo no esta en el catalogo",
          "line": 124,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "77bbec814a1f8bfb25c0",
          "title": "no pinta las secciones nuevas cuando el estudio no las trae",
          "full_title": "schema ampliado: segmental y bioimpedancia avanzada > no pinta las secciones nuevas cuando el estudio no las trae",
          "line": 140,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2ebfcde62caf6afd5c6d",
          "title": "las muestra en cuanto el estudio si las trae",
          "full_title": "schema ampliado: segmental y bioimpedancia avanzada > las muestra en cuanto el estudio si las trae",
          "line": 146,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ca0145addcbecf75004c",
          "title": "guarda el segmental anidado por segmento, no aplanado",
          "full_title": "schema ampliado: segmental y bioimpedancia avanzada > guarda el segmental anidado por segmento, no aplanado",
          "line": 160,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7c2ad7dcc353ec0af178",
          "title": "no agrega segmental cuando no hay ningun valor de segmento",
          "full_title": "schema ampliado: segmental y bioimpedancia avanzada > no agrega segmental cuando no hay ningun valor de segmento",
          "line": 178,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/estudio-corporal-inbody.test.ts",
      "type": "unit",
      "sha256": "0592b16b9ce6167210702377c8a595ffe13c29df46b869a9e5d86cfd4f7f03a9",
      "tests": [
        {
          "id": "0a17cbc455e72df271cb",
          "title": "declara su proveedor y lee la fecha en formato ISO",
          "full_title": "proveedor InBody > declara su proveedor y lee la fecha en formato ISO",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "51938a8c9bcfd4875091",
          "title": "mapea la bioimpedancia avanzada a los campos nuevos del expediente",
          "full_title": "proveedor InBody > mapea la bioimpedancia avanzada a los campos nuevos del expediente",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "852ec64eee8c64b7b742",
          "title": "no confunde masa muscular con masa muscular esqueletica",
          "full_title": "proveedor InBody > no confunde masa muscular con masa muscular esqueletica",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4c267cf02b794f2b3d81",
          "title": "no confunde peso con peso ideal",
          "full_title": "proveedor InBody > no confunde peso con peso ideal",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "534f14800d978741fc6a",
          "title": "lo extraido pasa el validador sin descartes",
          "full_title": "proveedor InBody > lo extraido pasa el validador sin descartes",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "11e284845084a2cfe56c",
          "title": "reporta los campos que la hoja no trae en vez de inventarlos",
          "full_title": "proveedor InBody > reporta los campos que la hoja no trae en vez de inventarlos",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "877dc189f226294cda67",
          "title": "acepta fecha latina y fecha ISO",
          "full_title": "motor generico de etiquetas > acepta fecha latina y fecha ISO",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b9f63c771e202686c258",
          "title": "un proveedor nuevo es una tabla de reglas, no otro parser",
          "full_title": "motor generico de etiquetas > un proveedor nuevo es una tabla de reglas, no otro parser",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/estudio-corporal-isak.test.ts",
      "type": "unit",
      "sha256": "a74d0772900700a30c8541a89a069f306dca16a5649f6aa753a90dc9f87feaa1",
      "tests": [
        {
          "id": "7a12576850a6306523bc",
          "title": "no filtra el nombre del paciente ni del evaluador",
          "full_title": "parser ISAK — fixture real anonimizado > no filtra el nombre del paciente ni del evaluador",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f8a8d0762fc8d2d1b112",
          "title": "lee la fecha del informe en formato ISO",
          "full_title": "parser ISAK — fixture real anonimizado > lee la fecha del informe en formato ISO",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "210d5ff2b75489303d37",
          "title": "lee las medidas basicas",
          "full_title": "parser ISAK — fixture real anonimizado > lee las medidas basicas",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "799cc5af8f03dfe17da9",
          "title": "lee los 8 pliegues del perfil restringido",
          "full_title": "parser ISAK — fixture real anonimizado > lee los 8 pliegues del perfil restringido",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "112c9a33df9314df7b8b",
          "title": "lee los perimetros como circunferencias del expediente",
          "full_title": "parser ISAK — fixture real anonimizado > lee los perimetros como circunferencias del expediente",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cf4508078707a4065322",
          "title": "lee los diametros oseos",
          "full_title": "parser ISAK — fixture real anonimizado > lee los diametros oseos",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7255d7802387d0cdd708",
          "title": "lee los perimetros ISAK dedicados",
          "full_title": "parser ISAK — fixture real anonimizado > lee los perimetros ISAK dedicados",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "83910d806d9d5dbff1ef",
          "title": "lee el fraccionamiento anatomico y el indice cintura/cadera",
          "full_title": "parser ISAK — fixture real anonimizado > lee el fraccionamiento anatomico y el indice cintura/cadera",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8ade39fb86b2bf55b0e1",
          "title": "no confunde etiquetas homonimas entre secciones",
          "full_title": "parser ISAK — fixture real anonimizado > no confunde etiquetas homonimas entre secciones",
          "line": 83,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "32c5e224398160b06f5a",
          "title": "ignora las paginas de graficas, que repiten etiquetas sin unidad",
          "full_title": "parser ISAK — fixture real anonimizado > ignora las paginas de graficas, que repiten etiquetas sin unidad",
          "line": 93,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c7a67f94a9faf26388c8",
          "title": "reporta que este informe no trae pectoral, axilar ni biacromial",
          "full_title": "parser ISAK — fixture real anonimizado > reporta que este informe no trae pectoral, axilar ni biacromial",
          "line": 101,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d385b529b2771b08f63b",
          "title": "la extraccion completa pasa el validador sin descartes",
          "full_title": "parser ISAK — fixture real anonimizado > la extraccion completa pasa el validador sin descartes",
          "line": 109,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "531421f1b52db857c6f7",
          "title": "devuelve una extraccion vacia y lista los campos ausentes",
          "full_title": "parser ISAK — texto degradado > devuelve una extraccion vacia y lista los campos ausentes",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dc08a5cff2c5e98bd229",
          "title": "tolera coma decimal",
          "full_title": "parser ISAK — texto degradado > tolera coma decimal",
          "line": 126,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8413ae1f13c3a2b36274",
          "title": "no inventa un valor cuando la etiqueta viene sin numero",
          "full_title": "parser ISAK — texto degradado > no inventa un valor cuando la etiqueta viene sin numero",
          "line": 131,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/estudio-corporal-pdf-text.test.ts",
      "type": "unit",
      "sha256": "283feaeeb93380612a4784eb64521fae754a6ee2a62a08e0fce136cf784c836c",
      "tests": [
        {
          "id": "b6629a17226a6aa27723",
          "title": "lee cadenas literales y arreglos TJ",
          "full_title": "capa de texto del PDF > lee cadenas literales y arreglos TJ",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f4c75e3e01a29d2a3eb9",
          "title": "ordena los fragmentos por posicion horizontal, no por orden de pintado",
          "full_title": "capa de texto del PDF > ordena los fragmentos por posicion horizontal, no por orden de pintado",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c88f380609304639ec75",
          "title": "compone los renglones de arriba hacia abajo",
          "full_title": "capa de texto del PDF > compone los renglones de arriba hacia abajo",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b0a11c60def9c002ea54",
          "title": "NO mezcla renglones de paginas distintas que caen a la misma altura",
          "full_title": "capa de texto del PDF > NO mezcla renglones de paginas distintas que caen a la misma altura",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f1b9f9cecae0c8ec5d26",
          "title": "decodifica cadenas hexadecimales UTF-16BE",
          "full_title": "capa de texto del PDF > decodifica cadenas hexadecimales UTF-16BE",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2435872b567d684f45f6",
          "title": "marca como vacio un PDF sin capa de texto util",
          "full_title": "capa de texto del PDF > marca como vacio un PDF sin capa de texto util",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6052695b9a103eb5febe",
          "title": "no revienta con basura binaria ni con flujos que no son Flate",
          "full_title": "capa de texto del PDF > no revienta con basura binaria ni con flujos que no son Flate",
          "line": 69,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f2df91b353660ec7f593",
          "title": "no filtra datos del paciente ni del evaluador",
          "full_title": "pasada 1 extremo a extremo sobre el informe real > no filtra datos del paciente ni del evaluador",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3c15388cd68f90420ba3",
          "title": "recupera los 27 valores del informe y ninguno queda sin encontrar",
          "full_title": "pasada 1 extremo a extremo sobre el informe real > recupera los 27 valores del informe y ninguno queda sin encontrar",
          "line": 93,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e972f8f62eb6b5ca4ab0",
          "title": "coincide con lo que produce la extraccion de referencia (pdftotext)",
          "full_title": "pasada 1 extremo a extremo sobre el informe real > coincide con lo que produce la extraccion de referencia (pdftotext)",
          "line": 132,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/estudio-corporal-reconciliacion.test.ts",
      "type": "unit",
      "sha256": "4ef9b11cb4a707d7d8e6bb69013aaf0806301dce30d8d6c04f1ef9cd3b283a72",
      "tests": [
        {
          "id": "05f04a06bb5069db4740",
          "title": "aplana los grupos y descarta lo que no es numero finito",
          "full_title": "aplanado > aplana los grupos y descarta lo que no es numero finito",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "827ab54a20aa14e28296",
          "title": "tolera null y undefined",
          "full_title": "aplanado > tolera null y undefined",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1ca0b3e90b71c3c37e44",
          "title": "marca alta confianza cuando ambas coinciden",
          "full_title": "reconciliacion de las dos pasadas > marca alta confianza cuando ambas coinciden",
          "line": 26,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7ce9fa1d77366c046bdb",
          "title": "NO promedia ni elige cuando discrepan: deja el campo sin valor",
          "full_title": "reconciliacion de las dos pasadas > NO promedia ni elige cuando discrepan: deja el campo sin valor",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dbd8f152dcc9541d6ef0",
          "title": "no aplica tolerancias generosas: 79.1 y 79.4 discrepan",
          "full_title": "reconciliacion de las dos pasadas > no aplica tolerancias generosas: 79.1 y 79.4 discrepan",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ed60e7e0e7f5ac3b8d0d",
          "title": "ignora el ruido de coma flotante",
          "full_title": "reconciliacion de las dos pasadas > ignora el ruido de coma flotante",
          "line": 62,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2f853177be3c747636a5",
          "title": "propone con confianza baja lo que solo una pasada encontro",
          "full_title": "reconciliacion de las dos pasadas > propone con confianza baja lo que solo una pasada encontro",
          "line": 67,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8922e69d65cd4bbaee44",
          "title": "si solo corre una pasada nada queda verificado",
          "full_title": "reconciliacion de las dos pasadas > si solo corre una pasada nada queda verificado",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8940b49a87e8fb10b2bd",
          "title": "toma proveedor y fecha de la pasada que los traiga",
          "full_title": "reconciliacion de las dos pasadas > toma proveedor y fecha de la pasada que los traiga",
          "line": 96,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "62e078badf92366c0e61",
          "title": "no revienta si ninguna pasada devolvio algo",
          "full_title": "reconciliacion de las dos pasadas > no revienta si ninguna pasada devolvio algo",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/estudio-corporal-schema.test.ts",
      "type": "unit",
      "sha256": "f89cb187991ca6a2b9bf93c88c9792520c14883bdde835a4ce82aeb6c13b4a02",
      "tests": [
        {
          "id": "65ec055204dcfbe4263b",
          "title": "acepta una extraccion plausible",
          "full_title": "validador de extraccion — rangos fisicos > acepta una extraccion plausible",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8144bbd56c58ec3797a4",
          "title": "descarta el campo fuera de rango sin tirar el resto del estudio",
          "full_title": "validador de extraccion — rangos fisicos > descarta el campo fuera de rango sin tirar el resto del estudio",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "35218499d9d906805a45",
          "title": "rechaza peso y porcentaje de grasa imposibles",
          "full_title": "validador de extraccion — rangos fisicos > rechaza peso y porcentaje de grasa imposibles",
          "line": 33,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bdaed1985850425242a7",
          "title": "descarta valores no numericos, infinitos y NaN",
          "full_title": "validador de extraccion — rangos fisicos > descarta valores no numericos, infinitos y NaN",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8bf42669e40cc2a8e509",
          "title": "descarta campos desconocidos en vez de propagarlos a Firestore",
          "full_title": "validador de extraccion — rangos fisicos > descarta campos desconocidos en vez de propagarlos a Firestore",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c36f81e9878da364d966",
          "title": "redondea a la precision del campo",
          "full_title": "validador de extraccion — rangos fisicos > redondea a la precision del campo",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3f3bee2eb1dc15c59da1",
          "title": "valida el formato de la fecha del estudio",
          "full_title": "validador de extraccion — rangos fisicos > valida el formato de la fecha del estudio",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ca7717ba45cdd2edc990",
          "title": "exige un proveedor conocido y un objeto",
          "full_title": "validador de extraccion — rangos fisicos > exige un proveedor conocido y un objeto",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "30a499fc9c7019b0933d",
          "title": "ignora nulos y undefined sin reportarlos como descartes",
          "full_title": "validador de extraccion — rangos fisicos > ignora nulos y undefined sin reportarlos como descartes",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/estudio-corporal-vertex.test.ts",
      "type": "unit",
      "sha256": "617ffff4392a42594ee3fb011313a027409c27a71ceffe49ff5a18597d228696",
      "tests": [
        {
          "id": "d60dace3a18883832ad8",
          "title": "le prohibe calcular, estimar y completar",
          "full_title": "prompt de la pasada de vision > le prohibe calcular, estimar y completar",
          "line": 9,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ee5671d6be9d413085cf",
          "title": "no filtra el resultado de la pasada 1: solo describe el formato de salida",
          "full_title": "prompt de la pasada de vision > no filtra el resultado de la pasada 1: solo describe el formato de salida",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a62c3f69c0e3f090fd3f",
          "title": "se adapta al proveedor",
          "full_title": "prompt de la pasada de vision > se adapta al proveedor",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e699ab845d06c149a512",
          "title": "acepta JSON limpio",
          "full_title": "saneado de la respuesta del modelo > acepta JSON limpio",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fa21360d46585f5fe8df",
          "title": "recorta cercas de markdown y texto alrededor",
          "full_title": "saneado de la respuesta del modelo > recorta cercas de markdown y texto alrededor",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9a14ce2fdf016b98f756",
          "title": "devuelve null en vez de adivinar cuando la respuesta no es un objeto",
          "full_title": "saneado de la respuesta del modelo > devuelve null en vez de adivinar cuando la respuesta no es un objeto",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/estudios-corporales.test.ts",
      "type": "unit",
      "sha256": "4c82e33c8e217fa08555590fd3f5306e66d8c13fbd7e036bf5279a6a07870574",
      "tests": [
        {
          "id": "ed29f89d08ae8a3b7ffe",
          "title": "acepta application/pdf con y sin parametros",
          "full_title": "estudios corporales — MIME permitido > acepta application/pdf con y sin parametros",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "40ac3f9d81ef93d0105b",
          "title": "rechaza imagenes, documentos de office y contenido ejecutable",
          "full_title": "estudios corporales — MIME permitido > rechaza imagenes, documentos de office y contenido ejecutable",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "65ae630254fd0ddf6f0c",
          "title": "acepta el PDF por MIME declarado",
          "full_title": "estudios corporales — validacion del archivo > acepta el PDF por MIME declarado",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f965b5b85cd20f6f402b",
          "title": "cae a la extension solo cuando el navegador no manda type",
          "full_title": "estudios corporales — validacion del archivo > cae a la extension solo cuando el navegador no manda type",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e67648fa9bd565ad3be2",
          "title": "no deja pasar un archivo cuyo MIME declarado no es PDF aunque se llame .pdf",
          "full_title": "estudios corporales — validacion del archivo > no deja pasar un archivo cuyo MIME declarado no es PDF aunque se llame .pdf",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e6115031ca81200929e9",
          "title": "valida el tamano contra el limite de Storage",
          "full_title": "estudios corporales — validacion del archivo > valida el tamano contra el limite de Storage",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "386eeccd610a196a3b75",
          "title": "reconoce los proveedores del catalogo",
          "full_title": "estudios corporales — proveedor > reconoce los proveedores del catalogo",
          "line": 63,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "90a915f4f68435151289",
          "title": "degrada a 'otro' cualquier valor desconocido",
          "full_title": "estudios corporales — proveedor > degrada a 'otro' cualquier valor desconocido",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "23b1abbe63db11bcd065",
          "title": "sanea acentos, espacios y caracteres no seguros",
          "full_title": "estudios corporales — path en Storage > sanea acentos, espacios y caracteres no seguros",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d23b98972515b893f422",
          "title": "normaliza el nombre del archivo a una sola extension .pdf",
          "full_title": "estudios corporales — path en Storage > normaliza el nombre del archivo a una sola extension .pdf",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "95d0cc7bdff4b0942e5f",
          "title": "construye un path bajo pacientes/{id}/estudios y sin traversal",
          "full_title": "estudios corporales — path en Storage > construye un path bajo pacientes/{id}/estudios y sin traversal",
          "line": 93,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f1ac92b779917c221bea",
          "title": "acepta solo YYYY-MM-DD valido",
          "full_title": "estudios corporales — fecha del estudio > acepta solo YYYY-MM-DD valido",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c9923669f67c061caa2b",
          "title": "rechaza formatos y fechas invalidas",
          "full_title": "estudios corporales — fecha del estudio > rechaza formatos y fechas invalidas",
          "line": 109,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/firebase/acceso-paciente-admin.test.ts",
      "type": "unit",
      "sha256": "d26238a5d35d2860e8bfcfd8f87015a04f1d347d6e33240197eb2206be424681",
      "tests": [
        {
          "id": "f043fb1b5b57a87c888b",
          "title": "emite token y manda el correo a quien nunca ha entrado",
          "full_title": "enviarAccesoPaciente > emite token y manda el correo a quien nunca ha entrado",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "181438973e67df66cba7",
          "title": "reutiliza el token vigente en un reintento del webhook",
          "full_title": "enviarAccesoPaciente > reutiliza el token vigente en un reintento del webhook",
          "line": 82,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "436b35de93eb8b2c1b7c",
          "title": "emite uno nuevo si el token anterior caducó",
          "full_title": "enviarAccesoPaciente > emite uno nuevo si el token anterior caducó",
          "line": 101,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "54f093aecae88d180fcc",
          "title": "emite uno nuevo si el token anterior ya se usó",
          "full_title": "enviarAccesoPaciente > emite uno nuevo si el token anterior ya se usó",
          "line": 111,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "76ac525b32cd6b3524ab",
          "title": "no reenvía onboarding a quien ya tiene cuenta",
          "full_title": "enviarAccesoPaciente > no reenvía onboarding a quien ya tiene cuenta",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f5fa707475ca74dfd73b",
          "title": "tampoco a quien ya tiene usuario de Firebase",
          "full_title": "enviarAccesoPaciente > tampoco a quien ya tiene usuario de Firebase",
          "line": 126,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c994e0c3072c83dda443",
          "title": "no toca nada si el paciente es de otro tenant",
          "full_title": "enviarAccesoPaciente > no toca nada si el paciente es de otro tenant",
          "line": 132,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "282ad85c006caf831e72",
          "title": "informa cuando el paciente no tiene correo",
          "full_title": "enviarAccesoPaciente > informa cuando el paciente no tiene correo",
          "line": 140,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "48f83e995dff3fcbca87",
          "title": "informa cuando el correo es inválido",
          "full_title": "enviarAccesoPaciente > informa cuando el correo es inválido",
          "line": 146,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "faad10d4d7bd4decac67",
          "title": "no marca el envío si el correo falla, y deja el token utilizable",
          "full_title": "enviarAccesoPaciente > no marca el envío si el correo falla, y deja el token utilizable",
          "line": 152,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "36efd41cee60ae9438ce",
          "title": "no emite token si el correo no está configurado",
          "full_title": "enviarAccesoPaciente > no emite token si el correo no está configurado",
          "line": 161,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "58dc3e5671052f02e3ca",
          "title": "no filtra datos clínicos en el prellenado público",
          "full_title": "enviarAccesoPaciente > no filtra datos clínicos en el prellenado público",
          "line": 168,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d8690360f168ec0f44b6",
          "title": "registra el origen del envío",
          "full_title": "enviarAccesoPaciente > registra el origen del envío",
          "line": 180,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "09820752156b695eea45",
          "title": "adjunta la guía vigente también en el acceso automático por pago",
          "full_title": "enviarAccesoPaciente > adjunta la guía vigente también en el acceso automático por pago",
          "line": 189,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/firebase/activar-contratacion-plan.test.ts",
      "type": "unit",
      "sha256": "570e6ea98e9dbc93c8ada195daf47e48ea5ba0a5bec450445aa86b63042d9355",
      "tests": [
        {
          "id": "a2be4dc0990d445b9eca",
          "title": "cierra la activa previa, acredita una vez y no copia citas al nuevo",
          "full_title": "activarContratacionPlan > cierra la activa previa, acredita una vez y no copia citas al nuevo",
          "line": 120,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f24690d6b0f7ec42b065",
          "title": "sin previa no cierra nada ni toca el saldo del paciente",
          "full_title": "activarContratacionPlan > sin previa no cierra nada ni toca el saldo del paciente",
          "line": 186,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6bdeaedb5647941439e5",
          "title": "no acredita dos veces si el webhook reintenta el mismo id",
          "full_title": "activarContratacionPlan > no acredita dos veces si el webhook reintenta el mismo id",
          "line": 213,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/firebase/api-auth-permissions.test.ts",
      "type": "unit",
      "sha256": "3fba915adef50184ccce0795cc876fc42583e5dd830d18d94ec98493f3c181a2",
      "tests": [
        {
          "id": "ab10d65b463058338c52",
          "title": "carga del servidor la matriz personalizada del workspace",
          "full_title": "autorización API por permisos del tenant > carga del servidor la matriz personalizada del workspace",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "796f2abbe389f1c6c320",
          "title": "rechaza una cuenta inactiva antes de resolver rol, tenant o permisos",
          "full_title": "autorización API por permisos del tenant > rechaza una cuenta inactiva antes de resolver rol, tenant o permisos",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7fe41b99a36a7780fe06",
          "title": "rechaza toda API para un paciente HIXION con acceso suspendido",
          "full_title": "autorización API por permisos del tenant > rechaza toda API para un paciente HIXION con acceso suspendido",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "19475e62e4b9f6b014dd",
          "title": "preserva otros tenants aunque aún tengan el estado de migración suspendido",
          "full_title": "autorización API por permisos del tenant > preserva otros tenants aunque aún tengan el estado de migración suspendido",
          "line": 102,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2c27c06a61788ae65903",
          "title": "falla cerrado cuando el token no tiene un perfil operativo",
          "full_title": "autorización API por permisos del tenant > falla cerrado cuando el token no tiene un perfil operativo",
          "line": 118,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a5491329e47f9982a797",
          "title": "falla cerrado cuando Firestore no puede validar el perfil",
          "full_title": "autorización API por permisos del tenant > falla cerrado cuando Firestore no puede validar el perfil",
          "line": 131,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f0f43fb6a9fada65cc63",
          "title": "combina rol permitido y capacidad explícita para una acción sensible",
          "full_title": "autorización API por permisos del tenant > combina rol permitido y capacidad explícita para una acción sensible",
          "line": 144,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b80b4ff3c075ed88335e",
          "title": "concede cuando la matriz guardada dice true",
          "full_title": "autorización API por permisos del tenant > control positivo: el valor guardado decide en ambos sentidos > concede cuando la matriz guardada dice true",
          "line": 185,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d7f2d77b49a1f62b570c",
          "title": "deniega cuando la matriz guardada dice false",
          "full_title": "autorización API por permisos del tenant > control positivo: el valor guardado decide en ambos sentidos > deniega cuando la matriz guardada dice false",
          "line": 193,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bc346a496119fc243389",
          "title": "ante un fallo de Firestore recupera los permisos predefinidos (fail-open conocido)",
          "full_title": "autorización API por permisos del tenant > ante un fallo de Firestore recupera los permisos predefinidos (fail-open conocido)",
          "line": 213,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ebadf88436ea85d2e4e0",
          "title": "%s en la lista pasa, fuera de la lista no",
          "full_title": "requireRole con roles propios del tenant > no-regresión: los roles conocidos se comportan igual > %s en la lista pasa, fuera de la lista no",
          "line": 251,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "0438d9e4dd52dc1acdba",
          "title": "%s en la lista pero sin el permiso sigue siendo 403",
          "full_title": "requireRole con roles propios del tenant > no-regresión: los roles conocidos se comportan igual > %s en la lista pero sin el permiso sigue siendo 403",
          "line": 259,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "63998c31e8f605742c0b",
          "title": "un rol fuera de la lista NO entra por permiso cuando no es staff",
          "full_title": "requireRole con roles propios del tenant > no-regresión: los roles conocidos se comportan igual > un rol fuera de la lista NO entra por permiso cuando no es staff",
          "line": 264,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "330c79be210c6dcf77ed",
          "title": "un rol propio entra por permiso aunque no esté en la lista",
          "full_title": "requireRole con roles propios del tenant > la rama nueva > un rol propio entra por permiso aunque no esté en la lista",
          "line": 271,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0d6b5006f4ebf02d2e15",
          "title": "sin el permiso, no entra",
          "full_title": "requireRole con roles propios del tenant > la rama nueva > sin el permiso, no entra",
          "line": 276,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8b8b61c278eee38b071e",
          "title": "sin `esStaff`, no entra aunque tenga el permiso",
          "full_title": "requireRole con roles propios del tenant > la rama nueva > sin `esStaff`, no entra aunque tenga el permiso",
          "line": 281,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "390bcb3c2be51d84cdaa",
          "title": "una ruta SIN permiso declarado sigue siendo sólo-lista",
          "full_title": "requireRole con roles propios del tenant > la rama nueva > una ruta SIN permiso declarado sigue siendo sólo-lista",
          "line": 286,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/firebase/auth-profile-client.test.ts",
      "type": "unit",
      "sha256": "920e60795e1cf4dab54b9c504e3629e385c1b10db63d4e9da964057a7f12cd00",
      "tests": [
        {
          "id": "57b3790b3456e079d290",
          "title": "deduplica cargas simultáneas y usa el endpoint en vez de una lectura Firestore",
          "full_title": "authenticated profile client > deduplica cargas simultáneas y usa el endpoint en vez de una lectura Firestore",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4f094b100f47473a22b3",
          "title": "convierte un vínculo clínico inválido en un error de autenticación accionable",
          "full_title": "authenticated profile client > convierte un vínculo clínico inválido en un error de autenticación accionable",
          "line": 38,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/firebase/firestore-boot-mode.test.ts",
      "type": "unit",
      "sha256": "4a0cfb4aeeb4c7c35eb403e7ba2f86bb99412159ac2ff1f2de5abe9d890b70df",
      "tests": [
        {
          "id": "c1397a457a24da06a73f",
          "title": "identifica el marcador que logout debe preservar",
          "full_title": "Firestore boot mode > identifica el marcador que logout debe preservar",
          "line": 26,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "274f401e59379e795e3d",
          "title": "clasifica únicamente los asserts que inutilizan la cola de Firestore",
          "full_title": "Firestore boot mode > clasifica únicamente los asserts que inutilizan la cola de Firestore",
          "line": 33,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1983ac3691d9e07cbee6",
          "title": "recarga una sola vez cuando la cola interna de Firestore queda dañada",
          "full_title": "Firestore boot mode > recarga una sola vez cuando la cola interna de Firestore queda dañada",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3d15e17fd93735d51173",
          "title": "usa persistencia por defecto y respeta el feature flag apagado",
          "full_title": "Firestore boot mode > usa persistencia por defecto y respeta el feature flag apagado",
          "line": 63,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9826052a3d6bba1d5a4c",
          "title": "marca modo seguro y solicita exactamente una recarga ante cuota",
          "full_title": "Firestore boot mode > marca modo seguro y solicita exactamente una recarga ante cuota",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3f5d9617a0999f4c0d1c",
          "title": "expira el marcador y vuelve a caché persistente",
          "full_title": "Firestore boot mode > expira el marcador y vuelve a caché persistente",
          "line": 84,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "034c99d7d3b6cf589ce3",
          "title": "ignora errores ajenos a cuota",
          "full_title": "Firestore boot mode > ignora errores ajenos a cuota",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2d774b579359411c4285",
          "title": "permite reintentar persistencia eliminando el marcador antes de recargar",
          "full_title": "Firestore boot mode > permite reintentar persistencia eliminando el marcador antes de recargar",
          "line": 108,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/firebase/pacientes-admin-email-resolution.test.ts",
      "type": "unit",
      "sha256": "7b3c9621bd4030eff9b3b7b1dbcdc212bfa5a5e02c3a257c72d82ab44fe1c666",
      "tests": [
        {
          "id": "237c73f3a35d1b63cd5e",
          "title": "devuelve una coincidencia activa única",
          "full_title": "resolución de pacientes por correo para Google Calendar > devuelve una coincidencia activa única",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3a2ffe96769ff5d3d929",
          "title": "no vincula silenciosamente cuando dos pacientes activos comparten correo",
          "full_title": "resolución de pacientes por correo para Google Calendar > no vincula silenciosamente cuando dos pacientes activos comparten correo",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "03651841c94a77a3b75b",
          "title": "ignora expedientes archivados al resolver automáticamente",
          "full_title": "resolución de pacientes por correo para Google Calendar > ignora expedientes archivados al resolver automáticamente",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/firebase/pacientes-service-sanitization.test.ts",
      "type": "unit",
      "sha256": "476e0ff4fda2eccb0f30ed5be2639c9eeb3f7f9bbe725e71e22a73e0eaef43c2",
      "tests": [
        {
          "id": "438e4aa83819359e13b3",
          "title": "elimina undefined también dentro de crm antes de escribir en Firestore",
          "full_title": "stripUndefined > elimina undefined también dentro de crm antes de escribir en Firestore",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/firebase/soporte-service.test.ts",
      "type": "unit",
      "sha256": "ab9fca0cf0d094d0134dc85c7f88517939a63cc9b1316090dc53a5d8f1660dbe",
      "tests": [
        {
          "id": "821e262d2e789525906f",
          "title": "no envía valores undefined a Firestore",
          "full_title": "createSoporteTicket > no envía valores undefined a Firestore",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dcdd134430290a24dce1",
          "title": "conserva los campos opcionales que sí vienen con valor",
          "full_title": "createSoporteTicket > conserva los campos opcionales que sí vienen con valor",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cdeccd8e6f6e107581b7",
          "title": "marca la eliminación pendiente y devuelve únicamente adjuntos propios",
          "full_title": "eliminación durable de tickets archivados > marca la eliminación pendiente y devuelve únicamente adjuntos propios",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5a573cf2b745312bce01",
          "title": "elimina el documento sólo después de preparar y limpiar adjuntos",
          "full_title": "eliminación durable de tickets archivados > elimina el documento sólo después de preparar y limpiar adjuntos",
          "line": 121,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ca6f60937fd64d7247fe",
          "title": "rechaza preparar el borrado si el ticket todavía no está archivado",
          "full_title": "eliminación durable de tickets archivados > rechaza preparar el borrado si el ticket todavía no está archivado",
          "line": 137,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "28589601f09460c4dbff",
          "title": "rechaza el borrado final si una recurrencia reabrió el ticket",
          "full_title": "eliminación durable de tickets archivados > rechaza el borrado final si una recurrencia reabrió el ticket",
          "line": 150,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "62330c8241c55b1aa960",
          "title": "reabre el ticket y cancela atómicamente el archivado y la eliminación pendiente",
          "full_title": "recurrencia automática durante un borrado > reabre el ticket y cancela atómicamente el archivado y la eliminación pendiente",
          "line": 172,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/fitness/exercise-alternatives.test.ts",
      "type": "unit",
      "sha256": "097fb5160b534421106b06b92dc7ec099e08d43961deb4e4ee30f5fc6e0dfbb2",
      "tests": [
        {
          "id": "a42486a725b6f37ff15d",
          "title": "prioriza equivalentes configurados y sustituciones ExerciseDB sin filtrar por equipo",
          "full_title": "buildSwapAlternatives > prioriza equivalentes configurados y sustituciones ExerciseDB sin filtrar por equipo",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "87305b497fcbb645f029",
          "title": "expone regresiones y progresiones ExerciseDB como secciones aparte",
          "full_title": "buildSwapAlternatives > expone regresiones y progresiones ExerciseDB como secciones aparte",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b85e3bac8d6e32e8a1ce",
          "title": "omite relaciones low o missingTarget como sustitución ExerciseDB",
          "full_title": "buildSwapAlternatives > omite relaciones low o missingTarget como sustitución ExerciseDB",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e382c761a6ac38d99d7f",
          "title": "expone confianza high/medium en alternativas curadas",
          "full_title": "buildSwapAlternatives > expone confianza high/medium en alternativas curadas",
          "line": 117,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "60c7dc621a8e75f9b4c8",
          "title": "omite targets legacy_review del paciente",
          "full_title": "buildSwapAlternatives > omite targets legacy_review del paciente",
          "line": 143,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "08f107796414c59fca50",
          "title": "bloquea ciclos A↔B en progresiones/regresiones y pares contradictorios",
          "full_title": "buildSwapAlternatives > bloquea ciclos A↔B en progresiones/regresiones y pares contradictorios",
          "line": 173,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/fitness/mid-session-swap.test.ts",
      "type": "unit",
      "sha256": "b4bbc247a78108e0736b95d97e375bba70acdd5f602d7b49ff5822a62df9e071",
      "tests": [
        {
          "id": "e9fa24017b66be83aca4",
          "title": "allows mid-session swap by reattributing completed sets to the replacement",
          "full_title": "decideRecordedSetOnPrepare > allows mid-session swap by reattributing completed sets to the replacement",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "968f949798d23c74244d",
          "title": "keeps matching completed sets without rewrite",
          "full_title": "decideRecordedSetOnPrepare > keeps matching completed sets without rewrite",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "45d35367e279f68f9c7c",
          "title": "blocks removing an instance that still has completed sets",
          "full_title": "decideRecordedSetOnPrepare > blocks removing an instance that still has completed sets",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c0224895ba959c1a88b7",
          "title": "blocks shrinking planned series below a completed set number",
          "full_title": "decideRecordedSetOnPrepare > blocks shrinking planned series below a completed set number",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "07daf93222915efc5c48",
          "title": "ignores incomplete sets",
          "full_title": "decideRecordedSetOnPrepare > ignores incomplete sets",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/fitness/portal-training-action.test.ts",
      "type": "unit",
      "sha256": "7915b10cfba270c4eabd21e05f891df0e5e552238659ec2dbb0ecd474526d476",
      "tests": [
        {
          "id": "1689bcdd36b7845a60ac",
          "title": "starts an in-progress session even when another card is pending",
          "full_title": "portalDayTrainingAction > starts an in-progress session even when another card is pending",
          "line": 26,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f9519bd6db7002dbfa47",
          "title": "starts the first session today that does not need a coach review",
          "full_title": "portalDayTrainingAction > starts the first session today that does not need a coach review",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "79f390dfce864015ed54",
          "title": "asks to review when every session today is blocked by a coach update",
          "full_title": "portalDayTrainingAction > asks to review when every session today is blocked by a coach update",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "009e6ba753a56f652ad3",
          "title": "does not offer start or review for a future day",
          "full_title": "portalDayTrainingAction > does not offer start or review for a future day",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/fitness/rest-timer.test.ts",
      "type": "unit",
      "sha256": "c604e11b13e0c4fa8fbc01f20de43716cbb8df334a779a1829e502b09c045677",
      "tests": [
        {
          "id": "a9c896d7b3415eb2cc6a",
          "title": "continues across locking, reloading and minimizing without ticks",
          "full_title": "rest deadline > continues across locking, reloading and minimizing without ticks",
          "line": 4,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6616a6327105b3a3be62",
          "title": "persists an explicit pause and resumes from the remaining duration",
          "full_title": "rest deadline > persists an explicit pause and resumes from the remaining duration",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7bce2a7224680c945a4d",
          "title": "changes revision on explicit extension and cancellation",
          "full_title": "rest deadline > changes revision on explicit extension and cancellation",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f6a411b4ba35bb3b0c2f",
          "title": "cannot control another session or restart an expired rest by reopening",
          "full_title": "rest deadline > cannot control another session or restart an expired rest by reopening",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2977d9fc9910afd9424b",
          "title": "recovers an offline deadline without restarting it and preserves an offline pause",
          "full_title": "rest deadline > recovers an offline deadline without restarting it and preserves an offline pause",
          "line": 63,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/fitness/routine-calendar-context.test.ts",
      "type": "unit",
      "sha256": "3d382202504ecf232ec13148c870752eb3a8edac5c92e1a89f5f212a45640c77",
      "tests": [
        {
          "id": "3502c422a498b6c81f24",
          "title": "distinguishes rest within a plan from days before and after its assignment",
          "full_title": "recorded routine history > distinguishes rest within a plan from days before and after its assignment",
          "line": 7,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ed40b26a9899a7f7a43a",
          "title": "keeps records outside the calendar window and excludes merely started sessions",
          "full_title": "recorded routine history > keeps records outside the calendar window and excludes merely started sessions",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4bfe97f9afdb059f08c6",
          "title": "includes declared block activity but does not invent a routine for extra training",
          "full_title": "recorded routine history > includes declared block activity but does not invent a routine for extra training",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/fitness/routine-lifecycle.test.ts",
      "type": "unit",
      "sha256": "edcfdd8b59336b56dc8be96375024f085636e462ca74818f0a8bb4c52c92154a",
      "tests": [
        {
          "id": "bd9d660a464e651df795",
          "title": "$label en Ciudad de México",
          "full_title": "routine assignment lifecycle > $label en Ciudad de México",
          "line": 53,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "a51c2d778206adce35b6",
          "title": "usa intervalos semiabiertos para que el reemplazo sea inequívoco",
          "full_title": "routine assignment lifecycle > usa intervalos semiabiertos para que el reemplazo sea inequívoco",
          "line": 67,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "de71af44af9b8b00f7ff",
          "title": "no activa una versión futura antes de su fecha",
          "full_title": "routine assignment lifecycle > no activa una versión futura antes de su fecha",
          "line": 75,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3adc197ec49bfc2421af",
          "title": "rechaza dos versiones vigentes en vez de escoger la más reciente",
          "full_title": "routine assignment lifecycle > rechaza dos versiones vigentes en vez de escoger la más reciente",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ebc3f6c791fd5956867f",
          "title": "omite rutinas futuras, vencidas y desasignadas",
          "full_title": "routine assignment lifecycle > omite rutinas futuras, vencidas y desasignadas",
          "line": 102,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8cbfefdc0263038f1da4",
          "title": "rechaza identidad cruzada de paciente",
          "full_title": "routine assignment lifecycle > rechaza identidad cruzada de paciente",
          "line": 116,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0dfa71c9fdb0d456e5ce",
          "title": "detecta traslape de rango con límite final exclusivo",
          "full_title": "routine assignment lifecycle > detecta traslape de rango con límite final exclusivo",
          "line": 127,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "96221a995af362290a85",
          "title": "omite intervalos legacy invertidos al leer rangos del portal",
          "full_title": "routine assignment lifecycle > omite intervalos legacy invertidos al leer rangos del portal",
          "line": 133,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "282f6c3fff271d989128",
          "title": "cierra y marca una versión sustituida sin dejarla abierta",
          "full_title": "routine assignment lifecycle > cierra y marca una versión sustituida sin dejarla abierta",
          "line": 138,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e74fe3c4b8ddd9e3fc2a",
          "title": "una versión sustituida abierta es expirada para todos los lectores de estado",
          "full_title": "routine assignment lifecycle > una versión sustituida abierta es expirada para todos los lectores de estado",
          "line": 150,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "40f6467fdb34a4a3c8fa",
          "title": "en un día civil, el calendario descarta el clon desasignado el mismo día y conserva el reemplazo",
          "full_title": "routine assignment lifecycle > en un día civil, el calendario descarta el clon desasignado el mismo día y conserva el reemplazo",
          "line": 163,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3edd263415bf563f6e57",
          "title": "conserva dos asignaciones concurrentes intencionales el mismo día civil",
          "full_title": "routine assignment lifecycle > conserva dos asignaciones concurrentes intencionales el mismo día civil",
          "line": 219,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "65ef21963506774b0b7a",
          "title": "conserva una asignación que cierra el mismo día si no hay reemplazo",
          "full_title": "routine assignment lifecycle > conserva una asignación que cierra el mismo día si no hay reemplazo",
          "line": 258,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9b5ce37a5270ce6eeb24",
          "title": "rechaza prescripciones con días o series manipuladas",
          "full_title": "routine assignment lifecycle > rechaza prescripciones con días o series manipuladas",
          "line": 286,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "04c780f04ae5e07c34e3",
          "title": "rechaza días implícitos, duplicados o sin ejercicios",
          "full_title": "routine assignment lifecycle > rechaza días implícitos, duplicados o sin ejercicios",
          "line": 308,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e2eaf1fa647f50f261d8",
          "title": "admite una prescripción de bloques libres y rechaza bloques mal referenciados",
          "full_title": "admite una prescripción de bloques libres y rechaza bloques mal referenciados",
          "line": 347,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "40c616a100ea31180ecb",
          "title": "acepta el primer ejercicio con orden cero generado por el wizard",
          "full_title": "acepta el primer ejercicio con orden cero generado por el wizard",
          "line": 353,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "31925fbc891c0f361629",
          "title": "acepta y conserva una categoría distinta tras serializar sin relajar referencias de día",
          "full_title": "acepta y conserva una categoría distinta tras serializar sin relajar referencias de día",
          "line": 360,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/fitness/routine-occurrences.test.ts",
      "type": "unit",
      "sha256": "a207564c12c57362b94754b266bbbb78597bd2262b7354032e2bc62398cf30e3",
      "tests": [
        {
          "id": "162768246ac22e76558a",
          "title": "renders whole Monday weeks independent of machine time zone",
          "full_title": "routine occurrences > renders whole Monday weeks independent of machine time zone",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0d486ca19b46cc557134",
          "title": "does not invent program length",
          "full_title": "routine occurrences > does not invent program length",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fef3cc1691e9cb75dff1",
          "title": "keeps repeated exercise appearances independent and preserves saved results",
          "full_title": "routine occurrences > keeps repeated exercise appearances independent and preserves saved results",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5c04d1b57c5f7a5628e0",
          "title": "never converts a rep range into a performed count",
          "full_title": "routine occurrences > never converts a rep range into a performed count",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d700b66d60b5eb71286f",
          "title": "declares missing values without inventing performance or PRs",
          "full_title": "routine occurrences > declares missing values without inventing performance or PRs",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ba8324ce35dd908b5603",
          "title": "keeps declared completion out of performance metrics even if legacy values are present",
          "full_title": "keeps declared completion out of performance metrics even if legacy values are present",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/fitness/session-integrity.test.ts",
      "type": "unit",
      "sha256": "cb8d26b18fbbe50041a2059c0dbb07668ee11fc186e85f12d89e41b9829c4c76",
      "tests": [
        {
          "id": "b74b58b4de015fd5f9a4",
          "title": "resuelve sólo el día y las series de la versión vigente",
          "full_title": "session-integrity > resuelve sólo el día y las series de la versión vigente",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b655c00f788d81e3586c",
          "title": "niega día, ejercicio y serie fuera de la prescripción",
          "full_title": "session-integrity > niega día, ejercicio y serie fuera de la prescripción",
          "line": 53,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d1ea4799df0b27e26df3",
          "title": "acepta resultados reales razonables y niega vacíos o valores manipulados",
          "full_title": "session-integrity > acepta resultados reales razonables y niega vacíos o valores manipulados",
          "line": 60,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "071d7718ced642dd7e5c",
          "title": "exige las métricas que corresponden al tipo de ejercicio",
          "full_title": "session-integrity > exige las métricas que corresponden al tipo de ejercicio",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "927dc37e64054e598bd3",
          "title": "cero series nunca equivale a completada y diferencia resultados terminales",
          "full_title": "session-integrity > cero series nunca equivale a completada y diferencia resultados terminales",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eff1eb38c4405eeb5669",
          "title": "impide omitir después de registrar resultados",
          "full_title": "session-integrity > impide omitir después de registrar resultados",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "06dd62993b4c1c8ed8de",
          "title": "normaliza sesiones legacy y reconoce todos los estados terminales",
          "full_title": "session-integrity > normaliza sesiones legacy y reconoce todos los estados terminales",
          "line": 110,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "49efa2c8bb035d11f9d1",
          "title": "un bloque libre no crea series prescritas",
          "full_title": "un bloque libre no crea series prescritas",
          "line": 120,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/fitness/training-reference.test.ts",
      "type": "unit",
      "sha256": "3d45db3f3000c82db9d596ccc30fe5377f31bf88af0230db2869f444fdfb6254",
      "tests": [
        {
          "id": "5c80e06b3c7961cc6cba",
          "title": "matches the same series, preserves ranges outside history, and does not choose the PR",
          "full_title": "comparable training history > matches the same series, preserves ranges outside history, and does not choose the PR",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5a339612f07dc6412339",
          "title": "excludes ambiguous older appearances when changing weight",
          "full_title": "comparable training history > excludes ambiguous older appearances when changing weight",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7f741269362c11955c46",
          "title": "does not preload an arbitrary repeated appearance",
          "full_title": "comparable training history > does not preload an arbitrary repeated appearance",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f8fb2510afca36b77218",
          "title": "normalizes units and excludes missing-data, other exercise, and incompatible load",
          "full_title": "comparable training history > normalizes units and excludes missing-data, other exercise, and incompatible load",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "32aa00504cd6df7d5c2c",
          "title": "lower assistance is the relevant load record",
          "full_title": "comparable training history > lower assistance is the relevant load record",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "79f4dee1bc7d2f0e342a",
          "title": "shows reps and weight records from the same session without changing the prior preload",
          "full_title": "shows reps and weight records from the same session without changing the prior preload",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5112cb65236b4b965ecd",
          "title": "uses the first completed set when there is no prior history and treats less assistance as better",
          "full_title": "uses the first completed set when there is no prior history and treats less assistance as better",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "378392bb658e4ff686a6",
          "title": "uses the metric of each exercise instead of weight for bodyweight and timed records",
          "full_title": "uses the metric of each exercise instead of weight for bodyweight and timed records",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/google/calendar-attendees.test.ts",
      "type": "unit",
      "sha256": "90bfe7326cea885eb7b755cfc23d4131ebffe52ecb51b0284b31f11d8c728b16",
      "tests": [
        {
          "id": "c9dd12aadc6aea2565dc",
          "title": "reagenda con patch sin sustituir los invitados existentes",
          "full_title": "updateCitaInCalendar — invitados > reagenda con patch sin sustituir los invitados existentes",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4e83d018390a08f1f6ab",
          "title": "conserva todos los invitados al mover el evento a otro calendario",
          "full_title": "updateCitaInCalendar — invitados > conserva todos los invitados al mover el evento a otro calendario",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e73fa78221e129f758d3",
          "title": "no elimina el evento anterior cuando falla la inserción en el destino",
          "full_title": "updateCitaInCalendar — invitados > no elimina el evento anterior cuando falla la inserción en el destino",
          "line": 122,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a9d5a85b58d14b7caef7",
          "title": "conserva los dos IDs para reconciliar si falla borrar el evento anterior",
          "full_title": "updateCitaInCalendar — invitados > conserva los dos IDs para reconciliar si falla borrar el evento anterior",
          "line": 134,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "189e9147e1770ba7a5e2",
          "title": "puede completar el movimiento cuando el evento anterior ya no existe",
          "full_title": "updateCitaInCalendar — invitados > puede completar el movimiento cuando el evento anterior ya no existe",
          "line": 155,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "71d463fcdedcc647d755",
          "title": "obtiene del evento real el correo invitado usado al convertirlo en cita",
          "full_title": "updateCitaInCalendar — invitados > obtiene del evento real el correo invitado usado al convertirlo en cita",
          "line": 170,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/google/calendar-health.test.ts",
      "type": "unit",
      "sha256": "2fb5ac762ba7aa88d7055ba352ea61d377b0baabcabb7ed359a7f88db207ea2b",
      "tests": [
        {
          "id": "92c4ee58b3e126f569a2",
          "title": "marca missing el calendario seleccionado que Google ya no lista, sin tumbar a los demás",
          "full_title": "diagnoseSelectedAgainstLiveList > marca missing el calendario seleccionado que Google ya no lista, sin tumbar a los demás",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8a454d1641b6ad8a841f",
          "title": "no marca error si el id seleccionado sigue en la lista viva",
          "full_title": "diagnoseSelectedAgainstLiveList > no marca error si el id seleccionado sigue en la lista viva",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6dc714f7cf133b15fe19",
          "title": "nombra el calendario y la persona, no un 'uno de los calendarios' genérico",
          "full_title": "formatCalendarFailureWarning > nombra el calendario y la persona, no un 'uno de los calendarios' genérico",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "83eec177aab07ea4009a",
          "title": "cae al correo de la cuenta Google cuando no hay nombre de miembro",
          "full_title": "formatCalendarFailureWarning > cae al correo de la cuenta Google cuando no hay nombre de miembro",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ca4d57483bd17b9b4f6b",
          "title": "atribuye el calendario roto al miembro del equipo, no a un error global",
          "full_title": "summarizeTeamCalendarIssues > atribuye el calendario roto al miembro del equipo, no a un error global",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/google/calendar-list.test.ts",
      "type": "unit",
      "sha256": "4746726fb27201773a8476ced5ee5f715db12704e7e479841b3308fc2d541a41",
      "tests": [
        {
          "id": "ef244d3e75728f856b45",
          "title": "expande selectedCalendars y cae al id legado",
          "full_title": "expandCalendarSources > expande selectedCalendars y cae al id legado",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "585b8ee2120d3dcccfc0",
          "title": "traduce el 404 crudo de Google y no lo reintenta",
          "full_title": "google calendar list errors > traduce el 404 crudo de Google y no lo reintenta",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1acbc0699e957ce5baa7",
          "title": "ignora AbortError del overlay de Next",
          "full_title": "google calendar list errors > ignora AbortError del overlay de Next",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ab830aaba94bafb26216",
          "title": "no reintenta un token Google inválido",
          "full_title": "google calendar list errors > no reintenta un token Google inválido",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/google/calendar-presentacion.test.ts",
      "type": "unit",
      "sha256": "5329e18646f1797938c8b093f7e83f7766f8ba402b55bfedfebc6c995721e853",
      "tests": [
        {
          "id": "c94fe9e1fdb5cdb61b81",
          "title": "no reescribe el titulo ni el color al editar una cita existente",
          "full_title": "updateCitaInCalendar — presentacion del evento > no reescribe el titulo ni el color al editar una cita existente",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e143570cd9214a0c213b",
          "title": "conserva el titulo y el color marcados a mano al mover la cita de calendario",
          "full_title": "updateCitaInCalendar — presentacion del evento > conserva el titulo y el color marcados a mano al mover la cita de calendario",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3d6390f384d841ea093e",
          "title": "regenera el titulo si el evento anterior ya no existe, para no dejarlo sin nombre",
          "full_title": "updateCitaInCalendar — presentacion del evento > regenera el titulo si el evento anterior ya no existe, para no dejarlo sin nombre",
          "line": 117,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c02182a54e4bc81b9d96",
          "title": "asigna el color del catalogo aunque la etiqueta venga acentuada",
          "full_title": "syncCitaToCalendar — color por tipo de cita > asigna el color del catalogo aunque la etiqueta venga acentuada",
          "line": 134,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3aa5af393c9410d95f8e",
          "title": "prefiere el id de catalogo cuando la cita ya lo trae",
          "full_title": "syncCitaToCalendar — color por tipo de cita > prefiere el id de catalogo cuando la cita ya lo trae",
          "line": 146,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2b2a5ce1ab50ad2f0cdb",
          "title": "al crear si escribe titulo y color: el evento nace con nombre",
          "full_title": "syncCitaToCalendar — color por tipo de cita > al crear si escribe titulo y color: el evento nace con nombre",
          "line": 158,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "301fea1d14a3c9e24146",
          "title": "pide a Google que notifique al crear la cita",
          "full_title": "avisos a los invitados > pide a Google que notifique al crear la cita",
          "line": 169,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2fa3e738452112ee0d37",
          "title": "pide a Google que notifique al reagendar",
          "full_title": "avisos a los invitados > pide a Google que notifique al reagendar",
          "line": 179,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a8d3ec6ab17526d98b8c",
          "title": "no avisa al retirar el evento anterior de un cambio de calendario",
          "full_title": "avisos a los invitados > no avisa al retirar el evento anterior de un cambio de calendario",
          "line": 191,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e3c5dc35facdd8f3d5e8",
          "title": "avisa al invitado cuando la cita se cancela de verdad",
          "full_title": "avisos a los invitados > avisa al invitado cuando la cita se cancela de verdad",
          "line": 203,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/navigation/support-return.test.ts",
      "type": "unit",
      "sha256": "7b5e33302ccf1cf44dc5c5c554ab744153a8c87c72bfff1333f7ba0c7d059b8c",
      "tests": [
        {
          "id": "9f90188fba11682810e8",
          "title": "conserva una ruta interna explícita desde la que se abrió Soporte",
          "full_title": "resolveSupportReturnTarget > conserva una ruta interna explícita desde la que se abrió Soporte",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "17a094e6e6a2c67aa980",
          "title": "usa el inicio del rol cuando no existe origen o es inseguro",
          "full_title": "resolveSupportReturnTarget > usa el inicio del rol cuando no existe origen o es inseguro",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/notifications/aviso-una-semana.test.ts",
      "type": "unit",
      "sha256": "54a876036524c6458c51d307bac596c072dceb9b3716a1311682a6b0ec97b780",
      "tests": [
        {
          "id": "1e59c3b5b6d1e421516f",
          "title": "viene encendido de fábrica para el paciente",
          "full_title": "aviso de cita con una semana de anticipación > viene encendido de fábrica para el paciente",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d6dc6379d54c33904fb7",
          "title": "viene encendido de fábrica para el equipo",
          "full_title": "aviso de cita con una semana de anticipación > viene encendido de fábrica para el equipo",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c7525b8a2ce59d1336db",
          "title": "se enciende a quien fue configurado antes de que existiera",
          "full_title": "aviso de cita con una semana de anticipación > se enciende a quien fue configurado antes de que existiera",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "66d7aa786725628eca91",
          "title": "respeta a quien lo apagó: no se vuelve a encender solo",
          "full_title": "aviso de cita con una semana de anticipación > respeta a quien lo apagó: no se vuelve a encender solo",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b06fd8f69e633c5c7550",
          "title": "no lo duplica si ya estaba en la lista",
          "full_title": "aviso de cita con una semana de anticipación > no lo duplica si ya estaba en la lista",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/notifications/dispatcher-email.test.ts",
      "type": "unit",
      "sha256": "51dba91dda68c86aa1fbc8fcfc2b5afbcb9c035debc1f71db03233d7c0bbcf18",
      "tests": [
        {
          "id": "c0d5d7813864e2f8e403",
          "title": "envía el aviso por correo cuando la persona lo tiene activado",
          "full_title": "canal de correo del despachador > envía el aviso por correo cuando la persona lo tiene activado",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "28f071f9a67907d28a98",
          "title": "respeta la preferencia: si el correo está apagado no escribe",
          "full_title": "canal de correo del despachador > respeta la preferencia: si el correo está apagado no escribe",
          "line": 107,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9aea4489934c6035078c",
          "title": "no intenta escribir a quien no tiene correo registrado",
          "full_title": "canal de correo del despachador > no intenta escribir a quien no tiene correo registrado",
          "line": 117,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a81b2b234db6b7d3d163",
          "title": "no envía nada si el proveedor de correo no está configurado",
          "full_title": "canal de correo del despachador > no envía nada si el proveedor de correo no está configurado",
          "line": 125,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d1671ef5dcad6347eff9",
          "title": "no escribe dos veces por el mismo aviso repetido",
          "full_title": "canal de correo del despachador > no escribe dos veces por el mismo aviso repetido",
          "line": 133,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6fb39ee510edadac3c6e",
          "title": "un fallo de correo no cancela la campanita ni el push",
          "full_title": "canal de correo del despachador > un fallo de correo no cancela la campanita ni el push",
          "line": 142,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/notifications/dispatcher.test.ts",
      "type": "unit",
      "sha256": "b191035b2a2ce92ec4c0dd68a5677b1a18fae4dd0fe51f3b156ff860d39edb37",
      "tests": [
        {
          "id": "e2e0192477636a481349",
          "title": "persiste y envía una sola vez cuando se repite el mismo notificationId",
          "full_title": "notification dispatcher idempotency > persiste y envía una sola vez cuando se repite el mismo notificationId",
          "line": 68,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ee168ae0a4c652ee6be8",
          "title": "libera la reserva de push fallida para permitir un reintento real",
          "full_title": "notification dispatcher idempotency > libera la reserva de push fallida para permitir un reintento real",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/odoo/catalog-map.test.ts",
      "type": "unit",
      "sha256": "886d5c829335be8811fd15aa81bffc5f2304e2b438efd732e427ef28a79d8030",
      "tests": [
        {
          "id": "594633fb8ec71e2f8599",
          "title": "no clasifica un dip como proteína",
          "full_title": "inferSupplementCategory > no clasifica un dip como proteína",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f3e0b86f2339e0ed4ea3",
          "title": "reconoce creatina, whey y pre-entreno",
          "full_title": "inferSupplementCategory > reconoce creatina, whey y pre-entreno",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "baa58c78f98127e23613",
          "title": "cae a otros si no hay señal clínica",
          "full_title": "inferSupplementCategory > cae a otros si no hay señal clínica",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0253b19f4818cf032e49",
          "title": "manda la categoría Odoo Anabólicos a Farmacología",
          "full_title": "inferSupplementCategory > manda la categoría Odoo Anabólicos a Farmacología",
          "line": 26,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ab0d8ca32f01cf798bea",
          "title": "detecta Blife y Dragon aunque el nombre no diga Pharma",
          "full_title": "inferSupplementBrand > detecta Blife y Dragon aunque el nombre no diga Pharma",
          "line": 33,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e0216b001433945a50d8",
          "title": "añade el sabor si no viene en el nombre",
          "full_title": "buildOdooProductName > añade el sabor si no viene en el nombre",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "67a44d495cb9b34f2cd7",
          "title": "no duplica el sabor",
          "full_title": "buildOdooProductName > no duplica el sabor",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7f25583a24418ebf9fdf",
          "title": "usa el SKU interno o un id estable",
          "full_title": "odooSku > usa el SKU interno o un id estable",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/odoo/ingredient-equivalents.test.ts",
      "type": "unit",
      "sha256": "6cc467ed6458e4425501c925634e487cab54bde7fab082d16948347c4fd777b1",
      "tests": [
        {
          "id": "3d21702320ed4074dd79",
          "title": "enlaza whey, isolate y vegana a tipos distintos",
          "full_title": "suggestIngredientEquivalentKey > enlaza whey, isolate y vegana a tipos distintos",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "83eac3c37f7767ea4860",
          "title": "no enlaza farmacología ni pre-entreno",
          "full_title": "suggestIngredientEquivalentKey > no enlaza farmacología ni pre-entreno",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "80537c7d7a8f155ce97d",
          "title": "reconoce creatina, dips y colágeno",
          "full_title": "suggestIngredientEquivalentKey > reconoce creatina, dips y colágeno",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/odoo/supplements.test.ts",
      "type": "unit",
      "sha256": "757cf9a03fd03dd0b951eaab2848967318e7de0ed4f21c108b6990f23f429a9c",
      "tests": [
        {
          "id": "e53ec697ca35f0cd3ebb",
          "title": "resuelve precio y stock exclusivamente desde Odoo",
          "full_title": "resolveSupplementCheckoutLines > resuelve precio y stock exclusivamente desde Odoo",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "60d500862d9207c2790b",
          "title": "rechaza un producto sin precio vigente",
          "full_title": "resolveSupplementCheckoutLines > rechaza un producto sin precio vigente",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d3e4de5ab97f8f766a33",
          "title": "rechaza cantidades superiores al inventario",
          "full_title": "resolveSupplementCheckoutLines > rechaza cantidades superiores al inventario",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/offline/patient-offline.test.ts",
      "type": "unit",
      "sha256": "99bafc27d7e1b8ad51dcf9e6152f784f338af0cc0ec00b724b68fba884953bf3",
      "tests": [
        {
          "id": "5756371cca5899e96b8c",
          "title": "rechaza snapshots de otro propietario, tenant, paciente o version",
          "full_title": "patient offline storage > rechaza snapshots de otro propietario, tenant, paciente o version",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6bae38e68e353d9a5cfe",
          "title": "compacta comidas por fecha/meal y conserva el ultimo valor",
          "full_title": "patient offline storage > compacta comidas por fecha/meal y conserva el ultimo valor",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e02e4ba11a91bf6aa44b",
          "title": "compacta ediciones del mismo slot y ordena start, sets y complete",
          "full_title": "patient offline storage > compacta ediciones del mismo slot y ordena start, sets y complete",
          "line": 68,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "29b6a7e6dbbaddf4d468",
          "title": "conserva sólo el último ajuste local de una ocurrencia con su propia operación",
          "full_title": "patient offline storage > conserva sólo el último ajuste local de una ocurrencia con su propia operación",
          "line": 84,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c8d72e0dfd0834034067",
          "title": "mantiene fallos para retry explicito y marca atencion al agotar intentos",
          "full_title": "patient offline storage > mantiene fallos para retry explicito y marca atencion al agotar intentos",
          "line": 90,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e1fcd15fa0c9a8d85205",
          "title": "encola sólo fallos de conectividad o servidor, no rechazos clínicos",
          "full_title": "patient offline storage > encola sólo fallos de conectividad o servidor, no rechazos clínicos",
          "line": 100,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0a2813a7e99ac46dd8c7",
          "title": "reproduce fitness en orden e inyecta la sesion remota sin duplicar pasos",
          "full_title": "patient offline storage > reproduce fitness en orden e inyecta la sesion remota sin duplicar pasos",
          "line": 107,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "98e8e616131e90990007",
          "title": "no envia complete cuando falla un set",
          "full_title": "patient offline storage > no envia complete cuando falla un set",
          "line": 131,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d758918e6516ab6e2c4c",
          "title": "detiene la sincronización si el ajuste de rutina encuentra otra revisión",
          "full_title": "patient offline storage > detiene la sincronización si el ajuste de rutina encuentra otra revisión",
          "line": 154,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a9de06be77b9468dc7d9",
          "title": "reenvía el ajuste de dieta una sola vez con su versión e identificador",
          "full_title": "patient offline storage > reenvía el ajuste de dieta una sola vez con su versión e identificador",
          "line": 171,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/pagos/checkout-parcialidades.test.ts",
      "type": "unit",
      "sha256": "7991570847b6700cb6ba17ee4529b1f10123691775bf82b51cf9736a02592701",
      "tests": [
        {
          "id": "9804af167af8e6cc9780",
          "title": "respeta lo que el equipo configuró en el plan",
          "full_title": "parcialidades del checkout público > respeta lo que el equipo configuró en el plan",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c29e1c2c7fb21753c42c",
          "title": "descarta el pago único de la lista de plazos",
          "full_title": "parcialidades del checkout público > descarta el pago único de la lista de plazos",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c1a5673ba1d769be78cf",
          "title": "cae a la regla histórica cuando el plan no configura nada",
          "full_title": "parcialidades del checkout público > cae a la regla histórica cuando el plan no configura nada",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f239e95befdcd4e7cd8f",
          "title": "usa citasIncluidas si falta consultationsIncluded",
          "full_title": "parcialidades del checkout público > usa citasIncluidas si falta consultationsIncluded",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e6ed79c8d10cdcececd3",
          "title": "una lista vacía no cuenta como configuración",
          "full_title": "parcialidades del checkout público > una lista vacía no cuenta como configuración",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "635d293670d6465c73bd",
          "title": "usa el precio del plan en Firestore, no el del catálogo en código",
          "full_title": "precio esperado contra Stripe > usa el precio del plan en Firestore, no el del catálogo en código",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "05181f5bb835f10dd766",
          "title": "cae al catálogo cuando el plan no tiene precio",
          "full_title": "precio esperado contra Stripe > cae al catálogo cuando el plan no tiene precio",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "04ca1e6a441a43a73351",
          "title": "redondea a centavos sin arrastrar decimales binarios",
          "full_title": "precio esperado contra Stripe > redondea a centavos sin arrastrar decimales binarios",
          "line": 69,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/pagos/metodo-pago.test.ts",
      "type": "unit",
      "sha256": "9fb89fde070637c9a71af7771643da1e09e0a0570d68dcb5bb739e6c8e5b325d",
      "tests": [
        {
          "id": "5c38e10c780b91e566f2",
          "title": "usa el método declarado cuando existe",
          "full_title": "metodoDeTransaccion > usa el método declarado cuando existe",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2dd4e868753a068913ce",
          "title": "deduce Stripe del origen en transacciones antiguas sin método",
          "full_title": "metodoDeTransaccion > deduce Stripe del origen en transacciones antiguas sin método",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "454697dfd4c5cb24e18c",
          "title": "clasifica como otro lo que no se puede identificar, en vez de perderlo",
          "full_title": "metodoDeTransaccion > clasifica como otro lo que no se puede identificar, en vez de perderlo",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "33001141cf5ff7c7ccdb",
          "title": "ignora un método declarado que no es de la lista",
          "full_title": "metodoDeTransaccion > ignora un método declarado que no es de la lista",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3a20fdc734c631473687",
          "title": "el método declarado gana sobre el origen",
          "full_title": "metodoDeTransaccion > el método declarado gana sobre el origen",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e70071aba6a3f1ca55fa",
          "title": "solo Stripe cuenta como cobro automatizado",
          "full_title": "esCobroManual > solo Stripe cuenta como cobro automatizado",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f31362b9ae9f5158ad09",
          "title": "agrupa lo cobrado por método",
          "full_title": "totalesPorMetodo > agrupa lo cobrado por método",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6a75be5e0c4c55f147e4",
          "title": "no suma lo que no se cobró",
          "full_title": "totalesPorMetodo > no suma lo que no se cobró",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9a64cf328a6099f4ace3",
          "title": "los reembolsos restan del total de su método",
          "full_title": "totalesPorMetodo > los reembolsos restan del total de su método",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "777ca290f2bcd6a30a0b",
          "title": "mantiene un orden estable, no el de llegada",
          "full_title": "totalesPorMetodo > mantiene un orden estable, no el de llegada",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/pagos/plan-code.test.ts",
      "type": "unit",
      "sha256": "075ba1b9ca9c904503d4155c96823b652a4a6b4464b0f73d7fa2d1d814180838",
      "tests": [
        {
          "id": "891843fc94bce26a35eb",
          "title": "acepta los tres paquetes del catálogo 2026 y un slug nuevo",
          "full_title": "esPlanCodeValido > acepta los tres paquetes del catálogo 2026 y un slug nuevo",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1a6ee776bc3d96c82c94",
          "title": "rechaza mayúsculas, espacios y un código que no es slug",
          "full_title": "esPlanCodeValido > rechaza mayúsculas, espacios y un código que no es slug",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dd9966d0393d09b9a5ef",
          "title": "quita modalidad y plazo pegados al nombre de la variante",
          "full_title": "tituloDePaquete > quita modalidad y plazo pegados al nombre de la variante",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f06dc989b49df4bbbfed",
          "title": "genera un planCode usable a partir del nombre comercial",
          "full_title": "slugDeNombre > genera un planCode usable a partir del nombre comercial",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "97f75a4ad1a8eb3ab448",
          "title": "no inventa un código cuando el nombre está vacío",
          "full_title": "slugDeNombre > no inventa un código cuando el nombre está vacío",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/pagos/saldo-cambio-paquete.test.ts",
      "type": "unit",
      "sha256": "487b16d3fb92905a96b3f527ea006d7ccfe108d5baaa0237fb576ec0897accfe",
      "tests": [
        {
          "id": "add383635c5fc6b4624f",
          "title": "Elite $6,000 / 3 citas: pagó todo y usó 1 → $4,000",
          "full_title": "calcularSaldoCambioPaquete > Elite $6,000 / 3 citas: pagó todo y usó 1 → $4,000",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5fd33c2835992835c1de",
          "title": "pagó 50% ($3,000) y usó 1 → $1,000",
          "full_title": "calcularSaldoCambioPaquete > pagó 50% ($3,000) y usó 1 → $1,000",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7aab2ae450878cfb904a",
          "title": "pagó 50% y usó 2 → $0, sin cobrar diferencia",
          "full_title": "calcularSaldoCambioPaquete > pagó 50% y usó 2 → $0, sin cobrar diferencia",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "52278cf956364714aca4",
          "title": "cuenta citas reservadas como ocupadas",
          "full_title": "calcularSaldoCambioPaquete > cuenta citas reservadas como ocupadas",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "46f4318c461fe9f7e373",
          "title": "descuenta el saldo del cargo de hoy y pide setup si queda en $0",
          "full_title": "aplicarSaldoAMontoHoy > descuenta el saldo del cargo de hoy y pide setup si queda en $0",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "55b15a3045e34f8bc538",
          "title": "un cargo menor a $1 MXN también va por setup",
          "full_title": "aplicarSaldoAMontoHoy > un cargo menor a $1 MXN también va por setup",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "40187c4f49afa0770605",
          "title": "cierra la activa previa, acredita una vez y no copia citas",
          "full_title": "planearCierrePorReemplazo > cierra la activa previa, acredita una vez y no copia citas",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6fd89c298130609045fd",
          "title": "no vuelve a acreditar un contrato que ya tiene saldoAcreditadoMonto",
          "full_title": "planearCierrePorReemplazo > no vuelve a acreditar un contrato que ya tiene saldoAcreditadoMonto",
          "line": 122,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "940775ecf904b5f314b0",
          "title": "sin previa no toca el saldo del paciente",
          "full_title": "planearCierrePorReemplazo > sin previa no toca el saldo del paciente",
          "line": 146,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1f2ba6175495ba6fd396",
          "title": "no toca las ya pagadas",
          "full_title": "cancelarParcialidadesPendientes > no toca las ya pagadas",
          "line": 159,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "989eae6deb3b296ab9c2",
          "title": "el intento cobra catalogo menos crédito",
          "full_title": "importeCheckoutTrasSaldo > el intento cobra catalogo menos crédito",
          "line": 170,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4beb2c5f8952957a5d4f",
          "title": "avisa citas congeladas, saldo a acreditar y checkout a $0",
          "full_title": "textoAvisoCambioPaquete > avisa citas congeladas, saldo a acreditar y checkout a $0",
          "line": 179,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/payments/access-eligibility.test.ts",
      "type": "unit",
      "sha256": "c7ed3feb1bbdbbbc8491ffbc9e915ca89c8193b9320eab809eec92c18ec30085",
      "tests": [
        {
          "id": "13225b7af30ed610783f",
          "title": "requires an active contract in the same tenant",
          "full_title": "resolveAccessEligibility > requires an active contract in the same tenant",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cb303866a1135c38ee8e",
          "title": "accepts a paid installment linked to the active contract",
          "full_title": "resolveAccessEligibility > accepts a paid installment linked to the active contract",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6450808feee143e69cd7",
          "title": "blocks access while the linked plan has an overdue or failed installment",
          "full_title": "resolveAccessEligibility > blocks access while the linked plan has an overdue or failed installment",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/payments/appointment-installments.test.ts",
      "type": "unit",
      "sha256": "4fc7dcc8e74de6171438e8f4419ec804baffcc67e755276e256150ce8d378753",
      "tests": [
        {
          "id": "5c402def5b172bc7b13b",
          "title": "reconstruye el mapa completo al reordenar y elimina enlaces obsoletos",
          "full_title": "mapInstallmentsToActiveAppointments > reconstruye el mapa completo al reordenar y elimina enlaces obsoletos",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cae16fe73192abf09c02",
          "title": "es idempotente y no asigna dos parcialidades duplicadas a la misma cita",
          "full_title": "mapInstallmentsToActiveAppointments > es idempotente y no asigna dos parcialidades duplicadas a la misma cita",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/payments/appointment-payment-reconciliation.test.ts",
      "type": "unit",
      "sha256": "202c3f1eb0ba8e67bad212c7eaf72b47a993d65ca78a64757af2f5cd3e125b28",
      "tests": [
        {
          "id": "3ef7e13fe906b89240b8",
          "title": "intenta todas las citas y rechaza el lote con el resumen de fallos",
          "full_title": "blockPatientAppointmentsForPayment > intenta todas las citas y rechaza el lote con el resumen de fallos",
          "line": 75,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1409e84109e42d42b170",
          "title": "expone un error tipado para que el caller pueda dejar el lote reintentable",
          "full_title": "blockPatientAppointmentsForPayment > expone un error tipado para que el caller pueda dejar el lote reintentable",
          "line": 96,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2f469e483712bbbc7f82",
          "title": "devuelve éxito sólo cuando ningún bloqueo falla",
          "full_title": "blockPatientAppointmentsForPayment > devuelve éxito sólo cuando ningún bloqueo falla",
          "line": 107,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d951d66cce7431999a92",
          "title": "conserva como no-op un workspace que todavía no tiene habilitado el flujo",
          "full_title": "blockPatientAppointmentsForPayment > conserva como no-op un workspace que todavía no tiene habilitado el flujo",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cea9d85dab36803ea5da",
          "title": "intenta todas las citas y rechaza la conciliación si una falla",
          "full_title": "reconcileBlockedAppointmentsForPayment > intenta todas las citas y rechaza la conciliación si una falla",
          "line": 157,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "caae34eed8e886b92a8a",
          "title": "expone un error tipado para que pago manual y Stripe puedan reintentar",
          "full_title": "reconcileBlockedAppointmentsForPayment > expone un error tipado para que pago manual y Stripe puedan reintentar",
          "line": 178,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "570f5a09b6a8c228fb5a",
          "title": "conserva still_blocked y too_late como resultados operativos, no como fallos técnicos",
          "full_title": "reconcileBlockedAppointmentsForPayment > conserva still_blocked y too_late como resultados operativos, no como fallos técnicos",
          "line": 185,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/payments/checkout-attempt.test.ts",
      "type": "unit",
      "sha256": "aaec74eff78ebf34f21f307e29e4da8366c0af9ecfcd18d1bfd2ff7c9daac8ae",
      "tests": [
        {
          "id": "6149dc883a40281e98f0",
          "title": "mantiene explícitos tenant, paciente, modalidad, monto y entorno",
          "full_title": "checkout attempt contract > mantiene explícitos tenant, paciente, modalidad, monto y entorno",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "054795815cd611de757d",
          "title": "incluye el crédito aplicado en el payload canónico",
          "full_title": "checkout attempt contract > incluye el crédito aplicado en el payload canónico",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/payments/commercial-payment-transition.test.ts",
      "type": "unit",
      "sha256": "1908cae29b0cd4e68715f85449b1cc12ee708193736b8e2a89abe1ab16884292",
      "tests": [
        {
          "id": "3744c5a6665f50012e96",
          "title": "falla cerrado fuera del rollout y no materializa estado comercial",
          "full_title": "sincronizacion financiera con CRM comercial > falla cerrado fuera del rollout y no materializa estado comercial",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "99516d3f179a2c1dadb0",
          "title": "avanza a pagado sin aceptar el importe o estado del navegador",
          "full_title": "sincronizacion financiera con CRM comercial > avanza a pagado sin aceptar el importe o estado del navegador",
          "line": 92,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aafe2bf99c214e52478f",
          "title": "no retrocede a quien ya tiene cita agendada",
          "full_title": "sincronizacion financiera con CRM comercial > no retrocede a quien ya tiene cita agendada",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "efb50522b032007593ec",
          "title": "reintenta sin embudo si otra transaccion avanzo al paciente",
          "full_title": "sincronizacion financiera con CRM comercial > reintenta sin embudo si otra transaccion avanzo al paciente",
          "line": 145,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7c6ddcd14720bfd0442b",
          "title": "mora a cinco dias suspende acceso y deja los otros ejes auditables",
          "full_title": "sincronizacion financiera con CRM comercial > mora a cinco dias suspende acceso y deja los otros ejes auditables",
          "line": 165,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ef3b7c2027ae1d00589c",
          "title": "reembolso parcial conserva contrato y acceso mientras abre revision",
          "full_title": "sincronizacion financiera con CRM comercial > reembolso parcial conserva contrato y acceso mientras abre revision",
          "line": 187,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/payments/installments.test.ts",
      "type": "unit",
      "sha256": "6fb586d6e822c4c70abc4355bc0aa0b8fc052326b9340281bac7579ea8cec881",
      "tests": [
        {
          "id": "67004e41fc4cb2e838ef",
          "title": "divide planes de tres citas en dos pagos exactos",
          "full_title": "parcialidades de planes GR > divide planes de tres citas en dos pagos exactos",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a947803e3221f8f1d051",
          "title": "divide planes de seis citas en tercios sin perder centavos",
          "full_title": "parcialidades de planes GR > divide planes de seis citas en tercios sin perder centavos",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ad4458d2f11d09552e68",
          "title": "divide planes de seis citas en seis pagos sin perder centavos",
          "full_title": "parcialidades de planes GR > divide planes de seis citas en seis pagos sin perder centavos",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2f60e9818bf7d0268391",
          "title": "vincula las parcialidades posteriores con las citas 2 y 3",
          "full_title": "parcialidades de planes GR > vincula las parcialidades posteriores con las citas 2 y 3",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ce4ef7985f673b347f25",
          "title": "distribuye tres exhibiciones a lo largo de un plan de seis citas",
          "full_title": "parcialidades de planes GR > distribuye tres exhibiciones a lo largo de un plan de seis citas",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f21c951645f2bbcfb9f0",
          "title": "calcula estado y saldo para cualquier número de parcialidades",
          "full_title": "parcialidades de planes GR > calcula estado y saldo para cualquier número de parcialidades",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/payments/public-checkout-status.test.ts",
      "type": "unit",
      "sha256": "1b89491b1e125466bcb74b63d2b8dd7ccfd087b6819539fdc34f3f878cf3dae8",
      "tests": [
        {
          "id": "e2cbbe91526052dc5419",
          "title": "no confía en el regreso del navegador y espera el pago persistido",
          "full_title": "public checkout status > no confía en el regreso del navegador y espera el pago persistido",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c78722d52d13d499a8e9",
          "title": "confirma cuando la parcialidad autoritativa ya quedó pagada",
          "full_title": "public checkout status > confirma cuando la parcialidad autoritativa ya quedó pagada",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "547665c714750804a9a6",
          "title": "distingue fallos, expiración e incidentes",
          "full_title": "public checkout status > distingue fallos, expiración e incidentes",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/payments/stripe-webhook-identity.test.ts",
      "type": "unit",
      "sha256": "27a2d5f404d6012efe42ce5db9bd702d01a55bb5bd4e60e089c010488329b55b",
      "tests": [
        {
          "id": "4ade52b7b5916407ffe5",
          "title": "acepta un customer legacy único de Gera y rechaza tenant, modo o cuenta incompatibles",
          "full_title": "stripe webhook identity > acepta un customer legacy único de Gera y rechaza tenant, modo o cuenta incompatibles",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "09f884bb4a2890e045d3",
          "title": "exige la cuenta exacta para HIXION",
          "full_title": "stripe webhook identity > exige la cuenta exacta para HIXION",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7dc78841d143d7e1f970",
          "title": "sólo acepta el customer singular legacy en su modo exacto",
          "full_title": "stripe webhook identity > sólo acepta el customer singular legacy en su modo exacto",
          "line": 63,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "26b208059f61b646145e",
          "title": "exige que plan, paciente y workspace de metadata sean idénticos",
          "full_title": "stripe webhook identity > exige que plan, paciente y workspace de metadata sean idénticos",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/payments/whatsapp-payment-link.test.ts",
      "type": "unit",
      "sha256": "958d3653eaa1b0b9a1a88de7f1e8c61dfa4771b2367e9fcf6f125616cf145d32",
      "tests": [
        {
          "id": "3663fc289771016ab0eb",
          "title": "elige la parcialidad pendiente más próxima del plan activo más reciente",
          "full_title": "whatsapp payment link > elige la parcialidad pendiente más próxima del plan activo más reciente",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "78e9c76d6fcdb4b78c2e",
          "title": "ignora planes liquidados y devuelve null cuando no hay cobro pendiente",
          "full_title": "whatsapp payment link > ignora planes liquidados y devuelve null cuando no hay cobro pendiente",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "988ab56b8046a9282f8a",
          "title": "normaliza teléfonos mexicanos y construye el mensaje con la liga",
          "full_title": "whatsapp payment link > normaliza teléfonos mexicanos y construye el mensaje con la liga",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b881133eebe441162bf4",
          "title": "construye el checkout público con la variante exacta",
          "full_title": "whatsapp payment link > construye el checkout público con la variante exacta",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2da7ad6f79d0f642d796",
          "title": "construye la liga CRM con precarga opaca y variante exacta",
          "full_title": "whatsapp payment link > construye la liga CRM con precarga opaca y variante exacta",
          "line": 71,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/photo-progress/mutations.test.ts",
      "type": "unit",
      "sha256": "146d868b926a91b68328c22cd4ecf86491366162a8a78bdd39d7fea361866dd6",
      "tests": [
        {
          "id": "0ed5957b1356558356aa",
          "title": "groups legacy uploads from different sessions into one daily album",
          "full_title": "photo progress albums > groups legacy uploads from different sessions into one daily album",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f0dbe458ca2aaf12ed6b",
          "title": "keeps multiple daily records inside the same named album",
          "full_title": "photo progress albums > keeps multiple daily records inside the same named album",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "633e6bbaa1c0aa2fd44e",
          "title": "excludes trashed photos and keeps the latest active version of a pose",
          "full_title": "photo progress albums > excludes trashed photos and keeps the latest active version of a pose",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eaf6e6790946345034a3",
          "title": "compares a stable custom pose across dates",
          "full_title": "photo progress albums > compares a stable custom pose across dates",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c517f19e4a5aa0e7f6ae",
          "title": "moves the previous daily pose to trash when committing a replacement",
          "full_title": "photo progress albums > moves the previous daily pose to trash when committing a replacement",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "86f7b821af7656a5c7cd",
          "title": "creates a reusable custom category and preserves its id on rename",
          "full_title": "photo progress albums > creates a reusable custom category and preserves its id on rename",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "590a076d927ac5896e72",
          "title": "trashes and restores a legacy album as a unit",
          "full_title": "photo progress albums > trashes and restores a legacy album as a unit",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d9d76df6635c669daf41",
          "title": "moves a photo to another album without changing its record date",
          "full_title": "photo progress albums > moves a photo to another album without changing its record date",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5b1cbad1219c7fbbc8cf",
          "title": "moves the conflicting target pose to trash when changing albums",
          "full_title": "photo progress albums > moves the conflicting target pose to trash when changing albums",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/photo-progress/qr-direct-upload.test.ts",
      "type": "unit",
      "sha256": "8b3d6b7ba4a16b0f706d1de5b7912c901b5bf799225817a783300b29205f3447",
      "tests": [
        {
          "id": "6ebe5fbb413529103f68",
          "title": "acepta una sesión de hasta 120 MB con archivos de hasta 12 MB",
          "full_title": "validateDirectUploadCandidates > acepta una sesión de hasta 120 MB con archivos de hasta 12 MB",
          "line": 9,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ff23948592c6bf1f74e4",
          "title": "rechaza una foto que rebasa 12 MB",
          "full_title": "validateDirectUploadCandidates > rechaza una foto que rebasa 12 MB",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d084dbcff5e858e0d84b",
          "title": "rechaza una sesión que rebasa 120 MB aunque cada archivo sea válido",
          "full_title": "validateDirectUploadCandidates > rechaza una sesión que rebasa 120 MB aunque cada archivo sea válido",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "66f3d13caaf4dea98f4c",
          "title": "rechaza ids duplicados, formatos ajenos y más fotos de las permitidas",
          "full_title": "validateDirectUploadCandidates > rechaza ids duplicados, formatos ajenos y más fotos de las permitidas",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/portal-connectivity.test.ts",
      "type": "unit",
      "sha256": "d0531a91a8167b412804bf1f0dc95c5de6a1f836d95441956a3e8ca69bf97c54",
      "tests": [
        {
          "id": "6c67af976628d2173379",
          "title": "sólo confirma conexión cuando responde la plataforma",
          "full_title": "probePortalConnection > sólo confirma conexión cuando responde la plataforma",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3370afd0f7dacaa8a5ef",
          "title": "distingue un fallo HTTP de uno de transporte",
          "full_title": "probePortalConnection > distingue un fallo HTTP de uno de transporte",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/portal-plan-mutation-version.test.ts",
      "type": "unit",
      "sha256": "25a2849d9bea34bd7839ba0d3468043b180214f51a3918bd2ca0ff3a9f0a3f01",
      "tests": [
        {
          "id": "1d146f29b14e21a9f964",
          "title": "rechaza el ajuste pendiente si cambió la versión activa o el plan fuente",
          "full_title": "rechaza el ajuste pendiente si cambió la versión activa o el plan fuente",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/portal-plan-offline.test.ts",
      "type": "unit",
      "sha256": "21b1f1ffbb55c52a8bc083df07b3a2d8241c29b7b8c47116d70522e50f4e8973",
      "tests": [
        {
          "id": "74e9a53f4edc58e82c5a",
          "title": "guarda la operación con la versión prescrita y no intenta escribir en la red",
          "full_title": "ajustes personales de dieta sin conexión > guarda la operación con la versión prescrita y no intenta escribir en la red",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9d37fc3ca4c02386bce1",
          "title": "no encola un rechazo de versión del servidor",
          "full_title": "ajustes personales de dieta sin conexión > no encola un rechazo de versión del servidor",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/portal-read-store.test.ts",
      "type": "unit",
      "sha256": "d7d26da60353aa99eea38c98c107b86959876fccb243d8238aa24261a75ee3ca",
      "tests": [
        {
          "id": "2e3840ae1b099ba05e46",
          "title": "shares equivalent reads and keeps a warm listener on return",
          "full_title": "portal read lifecycle > shares equivalent reads and keeps a warm listener on return",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "445441e8dc81bdc1bed7",
          "title": "cache-only snapshots do not claim freshness; partial failure retains data",
          "full_title": "portal read lifecycle > cache-only snapshots do not claim freshness; partial failure retains data",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "badb0b7a6a2737aa0df6",
          "title": "ignores late responses after the listener parks out and clears data on identity disposal",
          "full_title": "portal read lifecycle > ignores late responses after the listener parks out and clears data on identity disposal",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "89f25cbfbeb7ea4cb269",
          "title": "exposes disconnection without discarding data and resumes revalidation",
          "full_title": "portal read lifecycle > exposes disconnection without discarding data and resumes revalidation",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9329b5565768c3f13843",
          "title": "only reports resources consumed by the active route and clears denied data",
          "full_title": "portal read lifecycle > only reports resources consumed by the active route and clears denied data",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9a5f111450d99d97c364",
          "title": "keeps the read cache inside the offline database that sign-out clears",
          "full_title": "portal disk cache > keeps the read cache inside the offline database that sign-out clears",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e94f7c43a5c0d32e482b",
          "title": "restaura la semana confirmada de Rutina para el mismo paciente",
          "full_title": "portal disk cache > restaura la semana confirmada de Rutina para el mismo paciente",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "081ea6de8345c75c22ab",
          "title": "isolates identities, ignores another schema, and skips keys outside the four tabs",
          "full_title": "portal disk cache > isolates identities, ignores another schema, and skips keys outside the four tabs",
          "line": 122,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c87f259bae16b7961e3f",
          "title": "wipes the identity blob and memory on an access error",
          "full_title": "portal disk cache > wipes the identity blob and memory on an access error",
          "line": 154,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "76976df750fe8e905ed0",
          "title": "does not let a late disk read replace a newer live plan or a different assignment version",
          "full_title": "portal disk cache > does not let a late disk read replace a newer live plan or a different assignment version",
          "line": 181,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/portal-snapshot.test.ts",
      "type": "unit",
      "sha256": "e6233c2ee84f5b06150a82ce07acf65d96037d67559f8b456c200e447cf23bac",
      "tests": [
        {
          "id": "ffbc59de0a073bf5de72",
          "title": "comparte listeners equivalentes y confirma metadatos sin prolongar la carga por la suscripción abierta",
          "full_title": "comparte listeners equivalentes y confirma metadatos sin prolongar la carga por la suscripción abierta",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/pwa/release-notes.test.ts",
      "type": "unit",
      "sha256": "66112a8486d41f3add5bf34601f63d3352c0c6bc10daff86f0764b04f8010fe1",
      "tests": [
        {
          "id": "6c6697bb5ff043b912ce",
          "title": "separa la marca de presentación por audiencia y sesión",
          "full_title": "selectReleasePrompt > separa la marca de presentación por audiencia y sesión",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "76505afcf8ea63bea5a2",
          "title": "filtra por audiencia y no duplica avisos",
          "full_title": "selectReleasePrompt > filtra por audiencia y no duplica avisos",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4602dd233959b2db740b",
          "title": "no muestra entradas no publicadas",
          "full_title": "selectReleasePrompt > no muestra entradas no publicadas",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e71e4074f1f2d7582851",
          "title": "no anuncia a Gera una actualización aislada para HIXION",
          "full_title": "selectReleasePrompt > no anuncia a Gera una actualización aislada para HIXION",
          "line": 96,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/qa/automation-ticket.test.ts",
      "type": "unit",
      "sha256": "2d60977d113bd133840e3e34a555d2348651b78c02b781fcddf2fc5c89493318",
      "tests": [
        {
          "id": "8a0b3d793c027f80c65a",
          "title": "genera una huella estable para la misma regresión",
          "full_title": "tickets silenciosos de QA > genera una huella estable para la misma regresión",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b4568e9b223a9c41f86a",
          "title": "separa fingerprints cuando cambia el paso o navegador",
          "full_title": "tickets silenciosos de QA > separa fingerprints cuando cambia el paso o navegador",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3a7928b49d0e3b727701",
          "title": "valida HTTPS y construye etiquetas y descripción legibles",
          "full_title": "tickets silenciosos de QA > valida HTTPS y construye etiquetas y descripción legibles",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d4f308cc9b2a6b650c26",
          "title": "mantiene el endpoint automatizado separado del envío de correo",
          "full_title": "tickets silenciosos de QA > mantiene el endpoint automatizado separado del envío de correo",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/qa/golden-journey-review.test.ts",
      "type": "unit",
      "sha256": "bef717fa1d3c59cc79133973bd708a6e5858b92801da3ff35f68f1e94caf1721",
      "tests": [
        {
          "id": "10a72bb389161cdbfcfa",
          "title": "crea un checklist estable desde el catálogo canónico",
          "full_title": "registro manual de Recorridos Dorados > crea un checklist estable desde el catálogo canónico",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2d7ef6b72c5ba3583754",
          "title": "no permite aprobar un recorrido incompleto ni un contrato",
          "full_title": "registro manual de Recorridos Dorados > no permite aprobar un recorrido incompleto ni un contrato",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c10baa15d7b306ed632f",
          "title": "elimina recorridos y pasos ajenos al JSON oficial",
          "full_title": "registro manual de Recorridos Dorados > elimina recorridos y pasos ajenos al JSON oficial",
          "line": 38,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "07daf25c2b0babf1cfd8",
          "title": "calcula avance y genera un resumen accionable para agentes",
          "full_title": "registro manual de Recorridos Dorados > calcula avance y genera un resumen accionable para agentes",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/search/patient-picker.test.ts",
      "type": "unit",
      "sha256": "7a5ad13ae33316d0d9e8e54bc28aa35f8a8c63cda58ce1dc9d638b113301b87e",
      "tests": [
        {
          "id": "310658a1afe89128681e",
          "title": "excluye pacientes archivados de cualquier lista de selección",
          "full_title": "patient picker > excluye pacientes archivados de cualquier lista de selección",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d8538b928f008d8f1825",
          "title": "busca por nombre, correo y etiquetas con varios términos",
          "full_title": "patient picker > busca por nombre, correo y etiquetas con varios términos",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eb83dfd9afe776be5408",
          "title": "conserva el nombre escrito y propone el único correo invitado",
          "full_title": "patient picker > conserva el nombre escrito y propone el único correo invitado",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b5a1825ee6e28c591752",
          "title": "no adivina el correo cuando Google devuelve varios invitados",
          "full_title": "patient picker > no adivina el correo cuando Google devuelve varios invitados",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "015a7a858fb18d7c296a",
          "title": "prioriza el correo que la persona escribió en la búsqueda",
          "full_title": "patient picker > prioriza el correo que la persona escribió en la búsqueda",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "47bf8d263d4fc48b2c01",
          "title": "sugiere sólo una coincidencia activa y exacta por correo invitado",
          "full_title": "patient picker > sugiere sólo una coincidencia activa y exacta por correo invitado",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d2c652fc32ad77ee5312",
          "title": "no sugiere cuando el correo está duplicado entre pacientes activos",
          "full_title": "patient picker > no sugiere cuando el correo está duplicado entre pacientes activos",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f7ef79e2a6ea58985bff",
          "title": "no adivina entre invitados que corresponden a pacientes diferentes",
          "full_title": "patient picker > no adivina entre invitados que corresponden a pacientes diferentes",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/search/patient-visibility.test.ts",
      "type": "unit",
      "sha256": "8f1b6d82f5a76df12d640a579c32f8449b6be2858da3d7c7f1ab7ed057bbc728",
      "tests": [
        {
          "id": "1f329c6e77c5066e5ce1",
          "title": "excluye pacientes archivados antes de recomendaciones, búsqueda y filtros",
          "full_title": "isPatientVisibleInSpotlight > excluye pacientes archivados antes de recomendaciones, búsqueda y filtros",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6dfad3aa9d2350fd9d51",
          "title": "mantiene pacientes activos con identificador",
          "full_title": "isPatientVisibleInSpotlight > mantiene pacientes activos con identificador",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a1738d5978c0a7fc996b",
          "title": "descarta registros sin identificador navegable",
          "full_title": "isPatientVisibleInSpotlight > descarta registros sin identificador navegable",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/service-account.test.ts",
      "type": "unit",
      "sha256": "402a9ef138bec193ca9f814eb49e946e2e186e0ea623c2ea93615c645474ef0e",
      "tests": [
        {
          "id": "f22347a3bf888d0225a5",
          "title": "acepta el JSON con \\n escapados",
          "full_title": "service account > acepta el JSON con \\n escapados",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "61f2d8aa6bc9b7775258",
          "title": "acepta el JSON envuelto en comillas",
          "full_title": "service account > acepta el JSON envuelto en comillas",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "54bdc875c3e8eb5e718b",
          "title": "acepta saltos de linea literales dentro de private_key (formato Vercel)",
          "full_title": "service account > acepta saltos de linea literales dentro de private_key (formato Vercel)",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "10a13f857e25e7a54729",
          "title": "devuelve null sin lanzar cuando el valor no es JSON",
          "full_title": "service account > devuelve null sin lanzar cuando el valor no es JSON",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/services/atomic-assignment.test.ts",
      "type": "unit",
      "sha256": "03ec4733af6bb2089d44f53094d5ade167cd90a91eece47b40a1dc5f391f976b",
      "tests": [
        {
          "id": "3bf31713f8177ae67c7b",
          "title": "genera una clave distinta para dos acciones legítimas sobre la misma fuente",
          "full_title": "createAssignmentOperationId > genera una clave distinta para dos acciones legítimas sobre la misma fuente",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5e5c7e51379a63182857",
          "title": "conserva el nonce dentro del límite aceptado aunque los IDs sean largos",
          "full_title": "createAssignmentOperationId > conserva el nonce dentro del límite aceptado aunque los IDs sean largos",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/services/plan-assignment-sync.test.ts",
      "type": "unit",
      "sha256": "602d41a62a377aa6fe98e394d4b3f532fbe270cc538964f9e2c3387f1fdfa694",
      "tests": [
        {
          "id": "0e07cefedde36c5b40a9",
          "title": "cierra lo vigente contra el arranque de la versión nueva, no contra 'ahora'",
          "full_title": "publishActivePlanVersion > cierra lo vigente contra el arranque de la versión nueva, no contra 'ahora'",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "358ee4c7fa23ce70ead8",
          "title": "publica la versión nueva con la vigencia programada del plan",
          "full_title": "publishActivePlanVersion > publica la versión nueva con la vigencia programada del plan",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dd45e94458bc48868100",
          "title": "un plan sin fecha futura rige desde el instante del guardado",
          "full_title": "publishActivePlanVersion > un plan sin fecha futura rige desde el instante del guardado",
          "line": 77,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d52050702769c2cd1e4f",
          "title": "respalda el plan anterior antes de cerrarlo, para no dejar un hueco en el historial",
          "full_title": "publishActivePlanVersion > respalda el plan anterior antes de cerrarlo, para no dejar un hueco en el historial",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ff8a36284185fc6b5172",
          "title": "no respalda nada si el plan anterior no estaba activo",
          "full_title": "publishActivePlanVersion > no respalda nada si el plan anterior no estaba activo",
          "line": 101,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "77a0d880382c8195c19b",
          "title": "difiere al día siguiente cuando la comida del paciente ya tiene registro",
          "full_title": "publishActivePlanVersion > difiere al día siguiente cuando la comida del paciente ya tiene registro",
          "line": 113,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5e32f15e571c5d009f48",
          "title": "cierra las versiones de la fuente anterior cuando se indica",
          "full_title": "publishActivePlanVersion > cierra las versiones de la fuente anterior cuando se indica",
          "line": 128,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/services/plan-storage-atomic.test.ts",
      "type": "unit",
      "sha256": "de195c8c22d83c93bbe8849ea035aa56b1277961a23c2ad4c6986b40bfd8a035",
      "tests": [
        {
          "id": "f4925ba1a205f350a052",
          "title": "edita un plan activo existente mediante el comando canónico",
          "full_title": "savePlan activo usa el lifecycle atómico > edita un plan activo existente mediante el comando canónico",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bfe29e32295190758652",
          "title": "mantiene la edición directa sólo para borradores no efectivos",
          "full_title": "savePlan activo usa el lifecycle atómico > mantiene la edición directa sólo para borradores no efectivos",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "317f7a4cda746af7284c",
          "title": "archiva un plan activo mediante el mismo comando transaccional",
          "full_title": "savePlan activo usa el lifecycle atómico > archiva un plan activo mediante el mismo comando transaccional",
          "line": 102,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/services/routine-wizard-edit-continuation.test.ts",
      "type": "unit",
      "sha256": "76ca5695c9b484575c2a614e91137cb3da93377c1b5ce39dbfcf0cdfc7f5bf9d",
      "tests": [
        {
          "id": "ded476374a8b9dadf0d5",
          "title": "guarda otra edición después de serializar y restaurar el resultado, sin conflicto externo",
          "full_title": "continuar editando una rutina después de guardar > guarda otra edición después de serializar y restaurar el resultado, sin conflicto externo",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "572208bba8e347cbba63",
          "title": "rechaza una edición restaurada si otra publicación cambió la versión y conserva el borrador",
          "full_title": "continuar editando una rutina después de guardar > rechaza una edición restaurada si otra publicación cambió la versión y conserva el borrador",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/services/wizard-plan-conflict.test.ts",
      "type": "unit",
      "sha256": "767f800dab241e7e0c69364cced552611fb1dff848fa6b0b4a4200e279d54126",
      "tests": [
        {
          "id": "c3736872c1d29aa0263f",
          "title": "conserva el plan local y lo guarda sobre la versión remota más reciente sólo tras la acción explícita",
          "full_title": "establishWizardPlanChanges > conserva el plan local y lo guarda sobre la versión remota más reciente sólo tras la acción explícita",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "374491119041d08a134d",
          "title": "propaga una segunda colisión sin forzar ni reintentar silenciosamente",
          "full_title": "establishWizardPlanChanges > propaga una segunda colisión sin forzar ni reintentar silenciosamente",
          "line": 125,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3890e438a16eae3fd884",
          "title": "rechaza una versión que no pertenece al mismo paciente antes de guardar",
          "full_title": "establishWizardPlanChanges > rechaza una versión que no pertenece al mismo paciente antes de guardar",
          "line": 149,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f97b1b8dec3de86c3b61",
          "title": "comparte una sola escritura entre acciones simultáneas y permite guardar otra revisión después",
          "full_title": "runExclusiveWizardPlanSave > comparte una sola escritura entre acciones simultáneas y permite guardar otra revisión después",
          "line": 170,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/storage/protected-file-access.test.ts",
      "type": "unit",
      "sha256": "557db64ecec9ebe5e5fbfc9c08deb95a7cd058257322e7f36b4633d77bf9c229",
      "tests": [
        {
          "id": "b3a9d37d174a941f4129",
          "title": "permite al paciente sólo su propia ruta",
          "full_title": "canReadProtectedTarget > permite al paciente sólo su propia ruta",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b9c6262ba4617d57f273",
          "title": "permite staff clínico del tenant y niega cross-tenant",
          "full_title": "canReadProtectedTarget > permite staff clínico del tenant y niega cross-tenant",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d07c5afbd48f01025aa7",
          "title": "niega expediente clínico a roles operativos sin permiso",
          "full_title": "canReadProtectedTarget > niega expediente clínico a roles operativos sin permiso",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7f72b52380129798427f",
          "title": "permite chat al paciente dueño y a staff de chat, no a recepción",
          "full_title": "canReadProtectedTarget > permite chat al paciente dueño y a staff de chat, no a recepción",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a53185c50f4a8d0654e6",
          "title": "permite perfil propio o del mismo tenant y niega otro tenant",
          "full_title": "canReadProtectedTarget > permite perfil propio o del mismo tenant y niega otro tenant",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "280300282f1e768c0707",
          "title": "conserva el bypass de provisioning sólo para superadmin hixion",
          "full_title": "canReadProtectedTarget > conserva el bypass de provisioning sólo para superadmin hixion",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/storage/protected-file-path.test.ts",
      "type": "unit",
      "sha256": "0ffed6ca7d467255bb1d7f46a23be34edc75bd68a97a3a5087fe9d5d6857bc1e",
      "tests": [
        {
          "id": "0d23804db7629db20fde",
          "title": "clasifica %s",
          "full_title": "protected-file-path > clasifica %s",
          "line": 5,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "d44a542562edc0b4a6b6",
          "title": "rechaza una ruta fuera del perímetro: %s",
          "full_title": "protected-file-path > rechaza una ruta fuera del perímetro: %s",
          "line": 15,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "24b63e6a859f2e44b5b4",
          "title": "normaliza el nombre entregado al navegador",
          "full_title": "protected-file-path > normaliza el nombre entregado al navegador",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e5ac3606ab91d382f1f4",
          "title": "extrae una ruta de un download URL legacy y la pasa por el proxy",
          "full_title": "protected-file-path > extrae una ruta de un download URL legacy y la pasa por el proxy",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "85ceb7077e6b9d81d0b0",
          "title": "no cambia medios fuera del perímetro protegido",
          "full_title": "protected-file-path > no cambia medios fuera del perímetro protegido",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/storage/regenerable-cache.test.ts",
      "type": "unit",
      "sha256": "a44998c37408337e055ab896188d6cb7eb840b53de1a7b36476e0136bed84da6",
      "tests": [
        {
          "id": "be612fe0c4e041183c63",
          "title": "clasifica únicamente catálogos reconstruibles",
          "full_title": "regenerable cache eviction > clasifica únicamente catálogos reconstruibles",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d257b38bc8a9ec5a107e",
          "title": "elimina catálogos sin tocar sesión, borradores, snapshots o pendientes",
          "full_title": "regenerable cache eviction > elimina catálogos sin tocar sesión, borradores, snapshots o pendientes",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/stripe/canonical-price.test.ts",
      "type": "unit",
      "sha256": "f0b69fda168aa9c714589758536d9f2b9ca141ce3578b2bb69b8fd683c91470f",
      "tests": [
        {
          "id": "3e2e09bbb159c8b39f58",
          "title": "acepta el Price TEST que coincide exactamente con el plan HIXION",
          "full_title": "Stripe canonical price preflight > acepta el Price TEST que coincide exactamente con el plan HIXION",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ab446e49c9240ad327c2",
          "title": "rechaza antes del checkout un importe distinto al catalogo",
          "full_title": "Stripe canonical price preflight > rechaza antes del checkout un importe distinto al catalogo",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e759acfaaba5735e7408",
          "title": "rechaza metadata de otro tenant o variante",
          "full_title": "Stripe canonical price preflight > rechaza metadata de otro tenant o variante",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "96bee86de4bfa2f3d7c7",
          "title": "rechaza un Price recurrente en checkout de pago unico",
          "full_title": "Stripe canonical price preflight > rechaza un Price recurrente en checkout de pago unico",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/stripe/config-tenant.test.ts",
      "type": "unit",
      "sha256": "f590f89de72eddc804854775142a5bf348b2d59aacbcb97a58406ba10b77b156",
      "tests": [
        {
          "id": "7bfcee5d77fe47446b4d",
          "title": "usa la credencial TEST dedicada y nunca la llave legacy de Gera",
          "full_title": "Stripe tenant isolation > usa la credencial TEST dedicada y nunca la llave legacy de Gera",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "656a09ae4fbc7ceb2161",
          "title": "falla cerrado si HIXION recibe una credencial live",
          "full_title": "Stripe tenant isolation > falla cerrado si HIXION recibe una credencial live",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f82ae5cbaa41dac2f70b",
          "title": "no acepta la credencial genérica aunque estuviera etiquetada para HIXION",
          "full_title": "Stripe tenant isolation > no acepta la credencial genérica aunque estuviera etiquetada para HIXION",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0cb61d389d9c9759a13d",
          "title": "falla cerrado sin cuenta esperada explicita",
          "full_title": "Stripe tenant isolation > falla cerrado sin cuenta esperada explicita",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9cd7690aec23a875f943",
          "title": "mantiene la llave genérica únicamente para un tenant legacy",
          "full_title": "Stripe tenant isolation > mantiene la llave genérica únicamente para un tenant legacy",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/stripe/errors.test.ts",
      "type": "unit",
      "sha256": "5349339c2798295a98ba7b1afed23e00145edcacfd053f05c900c63e34855b98",
      "tests": [
        {
          "id": "56b5ac2d37ffc09506a3",
          "title": "elimina claves Stripe de cualquier mensaje",
          "full_title": "errores seguros de Stripe > elimina claves Stripe de cualquier mensaje",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a60a5f8f361b7f6286c7",
          "title": "convierte autenticación expirada en un error accionable sin detalle técnico",
          "full_title": "errores seguros de Stripe > convierte autenticación expirada en un error accionable sin detalle técnico",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/stripe/gera-catalog.test.ts",
      "type": "unit",
      "sha256": "870031fff1471920e1856c3af95c8c346713b1c306d977c4e97de45e8b31d0ba",
      "tests": [
        {
          "id": "5ebfbe90529dce59202c",
          "title": "contiene exactamente las diez variantes oficiales",
          "full_title": "catálogo Gerardo Reyna 2026 > contiene exactamente las diez variantes oficiales",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2ea3e06cd7fcf8f4f51b",
          "title": "publica metadata estable para Stripe y Firestore",
          "full_title": "catálogo Gerardo Reyna 2026 > publica metadata estable para Stripe y Firestore",
          "line": 26,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/stripe/seed-guards.test.ts",
      "type": "unit",
      "sha256": "5724f3fb65c3db7536c381f01837ac291a3ee92f903c8e088267638fa54b71cd",
      "tests": [
        {
          "id": "297e59ca6d0014798a35",
          "title": "falla cerrado si falta tenant, prefijo o cuenta esperada",
          "full_title": "Stripe seed HIXION guards > falla cerrado si falta tenant, prefijo o cuenta esperada",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "efdd18516386ed9fba36",
          "title": "impide sembrar Gera u otro tenant desde el seed aislado",
          "full_title": "Stripe seed HIXION guards > impide sembrar Gera u otro tenant desde el seed aislado",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fc98bdbea06289ee0010",
          "title": "solo acepta credenciales test y una cuenta explicita",
          "full_title": "Stripe seed HIXION guards > solo acepta credenciales test y una cuenta explicita",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "387668a73bea1c663bca",
          "title": "no reutiliza productos o precios sin propiedad HIXION completa",
          "full_title": "Stripe seed HIXION guards > no reutiliza productos o precios sin propiedad HIXION completa",
          "line": 33,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/support/ticket-enrichment-api.test.ts",
      "type": "unit",
      "sha256": "69f88ee5eb6ac203c125a61699b8ab3a6476b4ecf9398eac8fb9184865e65ede",
      "tests": [
        {
          "id": "c716f11d3a6620fa3859",
          "title": "rechaza a quien no tiene sesión",
          "full_title": "POST /api/soporte/enrichment > rechaza a quien no tiene sesión",
          "line": 117,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5a6c5a7d71ad42054a9e",
          "title": "rechaza a un usuario autenticado que no es de HIXION",
          "full_title": "POST /api/soporte/enrichment > rechaza a un usuario autenticado que no es de HIXION",
          "line": 126,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "257e75c5209d854f7521",
          "title": "ignora la atribución que venga en el cuerpo y firma con la identidad del servidor",
          "full_title": "POST /api/soporte/enrichment > ignora la atribución que venga en el cuerpo y firma con la identidad del servidor",
          "line": 136,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fa191c2fa43556286b54",
          "title": "responde 404 cuando el ticket ya no existe, sin escribir nada",
          "full_title": "POST /api/soporte/enrichment > responde 404 cuando el ticket ya no existe, sin escribir nada",
          "line": 160,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1dc68c6ea0f714a7c96f",
          "title": "no le entrega el diagnóstico interno a un solicitante que no es de HIXION",
          "full_title": "GET /api/soporte > no le entrega el diagnóstico interno a un solicitante que no es de HIXION",
          "line": 176,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1db2963caa0612496406",
          "title": "sí se lo entrega a HIXION",
          "full_title": "GET /api/soporte > sí se lo entrega a HIXION",
          "line": 192,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/support/ticket-enrichment.test.ts",
      "type": "unit",
      "sha256": "aa799d7149bfd547fe021c05713a42f59ad0489dc915aff917e2a363a75e81c2",
      "tests": [
        {
          "id": "7e954ed3de59ad4b6b00",
          "title": "acepta un enriquecimiento completo y enlazado al catálogo",
          "full_title": "enriquecimiento de tickets > acepta un enriquecimiento completo y enlazado al catálogo",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b7e659550546ce34149f",
          "title": "rechaza rutas y casos QA inexistentes",
          "full_title": "enriquecimiento de tickets > rechaza rutas y casos QA inexistentes",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "57741cfdd6700f47a543",
          "title": "exige needs_confirmation ante una contradicción abierta",
          "full_title": "enriquecimiento de tickets > exige needs_confirmation ante una contradicción abierta",
          "line": 62,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9ad324dfeba26e26bb8c",
          "title": "detecta un fingerprint desactualizado",
          "full_title": "enriquecimiento de tickets > detecta un fingerprint desactualizado",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b66541302ee4697edd50",
          "title": "rechaza campos de diagnóstico o solución por contrato estricto",
          "full_title": "enriquecimiento de tickets > rechaza campos de diagnóstico o solución por contrato estricto",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c6cc6f8bc241570890cd",
          "title": "sólo aprueba score de PR con 85+, CI verde y sin críticos",
          "full_title": "enriquecimiento de tickets > sólo aprueba score de PR con 85+, CI verde y sin críticos",
          "line": 91,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/support/ticket-presentation.test.ts",
      "type": "unit",
      "sha256": "e439dedcab32db7c5b8021b2d39655ecd613c4e02a3279a389362f49cf575123",
      "tests": [
        {
          "id": "6fea41c85ec259f2307a",
          "title": "ordena falla parcial entre bloqueo y área de oportunidad",
          "full_title": "presentación de tickets de soporte > ordena falla parcial entre bloqueo y área de oportunidad",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f0b528d09212af9c0c85",
          "title": "conserva una conversación estructurada existente",
          "full_title": "presentación de tickets de soporte > conserva una conversación estructurada existente",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "00c7ede2aabe54fd7ade",
          "title": "reconstruye tickets legacy con clasificación, comentario y folio",
          "full_title": "presentación de tickets de soporte > reconstruye tickets legacy con clasificación, comentario y folio",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/support/ticket-workflow.test.ts",
      "type": "unit",
      "sha256": "73e2fc179878f26ce4325eb9466842f326d3ec0482e95b2e77051630dd7de58d",
      "tests": [
        {
          "id": "346c01e461e11eaa7026",
          "title": "bloquea el reclamo obligatorio si falta enriquecimiento",
          "full_title": "flujo de responsables de tickets de soporte > bloquea el reclamo obligatorio si falta enriquecimiento",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "37258b0fc5bde5143a44",
          "title": "permite override P0 únicamente con motivo auditado",
          "full_title": "flujo de responsables de tickets de soporte > permite override P0 únicamente con motivo auditado",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "778090d1c284e3dede80",
          "title": "asigna un ticket abierto a la identidad visible que lo toma",
          "full_title": "flujo de responsables de tickets de soporte > asigna un ticket abierto a la identidad visible que lo toma",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0031e7bab6dd934ad766",
          "title": "impide que dos responsables se asignen el mismo ticket",
          "full_title": "flujo de responsables de tickets de soporte > impide que dos responsables se asignen el mismo ticket",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bfbed17ca0063c7d7a40",
          "title": "atribuye la solución al responsable asignado y la manda a revisión",
          "full_title": "flujo de responsables de tickets de soporte > atribuye la solución al responsable asignado y la manda a revisión",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d4860cb5b673f6ae932f",
          "title": "exige reclamar el ticket antes de enviarlo a revisión",
          "full_title": "flujo de responsables de tickets de soporte > exige reclamar el ticket antes de enviarlo a revisión",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "010fa7b8deee668039bf",
          "title": "exige revisión antes de cerrar un ticket",
          "full_title": "flujo de responsables de tickets de soporte > exige revisión antes de cerrar un ticket",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "486f42f6e1c3317b0e90",
          "title": "libera asignación y solución al reabrir",
          "full_title": "flujo de responsables de tickets de soporte > libera asignación y solución al reabrir",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f0cf4207ee9411de4fdf",
          "title": "sólo permite archivar un ticket resuelto y atribuye la acción",
          "full_title": "flujo de responsables de tickets de soporte > sólo permite archivar un ticket resuelto y atribuye la acción",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "045776de2900dfb33890",
          "title": "restaurar limpia el archivado sin cambiar el estado resuelto",
          "full_title": "flujo de responsables de tickets de soporte > restaurar limpia el archivado sin cambiar el estado resuelto",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6ebb8bbff484ea54d453",
          "title": "sólo considera eliminable un ticket resuelto que ya está archivado",
          "full_title": "flujo de responsables de tickets de soporte > sólo considera eliminable un ticket resuelto que ya está archivado",
          "line": 111,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/aar-alerts.test.ts",
      "type": "unit",
      "sha256": "10b5a3fed7bbd13a122f082f612ef37e8cea4408022d594cb741ac1c1d2b1cbe",
      "tests": [
        {
          "id": "5aa92f92956b5865e6ae",
          "title": "evalua la vigencia de %s",
          "full_title": "AAR alert freshness > evalua la vigencia de %s",
          "line": 8,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/aar-cockpit-kpis.test.ts",
      "type": "unit",
      "sha256": "02bc29bc5dc48abbc1a6dacfe00c11ece702c3645d3be3038d4cca0215b35b36",
      "tests": [
        {
          "id": "06e8dbf7bceed38bc7af",
          "title": "prioriza el objetivo detallado, usa el principal y explicita cuando ambos faltan",
          "full_title": "cockpit AAR acordado para la demo > prioriza el objetivo detallado, usa el principal y explicita cuando ambos faltan",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "72922e4a261d80880a3c",
          "title": "ordena adherencia y sustituye el KPI RPE del calendario por sets",
          "full_title": "cockpit AAR acordado para la demo > ordena adherencia y sustituye el KPI RPE del calendario por sets",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "07fe9cce2e2b6a6598d5",
          "title": "presenta sets completados en el KPI principal del cockpit",
          "full_title": "cockpit AAR acordado para la demo > presenta sets completados en el KPI principal del cockpit",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d0baf35b66332d7ba47f",
          "title": "muestra sobrecumplimiento sin topar el porcentaje en 100",
          "full_title": "cockpit AAR acordado para la demo > muestra sobrecumplimiento sin topar el porcentaje en 100",
          "line": 67,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/aar-competencias.test.ts",
      "type": "unit",
      "sha256": "03b3c4be67193a7ff05c2698d5f140ff493609872dc640afcf20dfe43d6595ab",
      "tests": [
        {
          "id": "df833410728372e75a79",
          "title": "el espejo apunta a la próxima por fecha, no al orden de captura",
          "full_title": "aar-competencias > conCompetenciasActualizadas > el espejo apunta a la próxima por fecha, no al orden de captura",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a36003651b4431f146b0",
          "title": "agregar un show no enciende la prep si el staff la tenía apagada",
          "full_title": "aar-competencias > conCompetenciasActualizadas > agregar un show no enciende la prep si el staff la tenía apagada",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5739b1639c1e87547e1c",
          "title": "con lista vacía borra el espejo y conserva la prep — las claves se OMITEN, no viajan vacías",
          "full_title": "aar-competencias > conCompetenciasActualizadas > con lista vacía borra el espejo y conserva la prep — las claves se OMITEN, no viajan vacías",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c9232c7885e0f22272f0",
          "title": "conPreparacionActiva solo mueve el flag",
          "full_title": "aar-competencias > conCompetenciasActualizadas > conPreparacionActiva solo mueve el flag",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "022debb1b6ceeb839784",
          "title": "fabrica la fila del espejo suelto en perfiles previos al modelo multi-competencia",
          "full_title": "aar-competencias > competenciasDelPerfil > fabrica la fila del espejo suelto en perfiles previos al modelo multi-competencia",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "81998aa32be50599a35b",
          "title": "prefiere la lista real cuando existe y devuelve vacío sin datos",
          "full_title": "aar-competencias > competenciasDelPerfil > prefiere la lista real cuando existe y devuelve vacío sin datos",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "75280705aeafefdcf0e6",
          "title": "convierte el peso de la unidad de captura a kg y descarta filas sin datos",
          "full_title": "aar-competencias > sanitizeCompetitions > convierte el peso de la unidad de captura a kg y descarta filas sin datos",
          "line": 93,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bd936541adb5764c22ef",
          "title": "ninguna clave viaja con undefined: un undefined tumba el documento entero en Firestore",
          "full_title": "aar-competencias > sanitizeCompetitions > ninguna clave viaja con undefined: un undefined tumba el documento entero en Firestore",
          "line": 105,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "36872e53ab6c879ae18a",
          "title": "conserva el kg previo cuando lo tecleado no cambió (sin deriva de redondeo)",
          "full_title": "aar-competencias > sanitizeCompetitions > conserva el kg previo cuando lo tecleado no cambió (sin deriva de redondeo)",
          "line": 117,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a521e46ea491f1cab1c1",
          "title": "conserva las tres voces de notas",
          "full_title": "aar-competencias > sanitizeCompetitions > conserva las tres voces de notas",
          "line": 127,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/aar-dashboard-layout.test.ts",
      "type": "unit",
      "sha256": "66d023c9948a4de25fb4ab4a99e42fd886d004767f8ff59e1229edbc78e722b8",
      "tests": [
        {
          "id": "cb65a71db10f6f98d471",
          "title": "preserves a complete valid custom order",
          "full_title": "AAR dashboard chart layout > preserves a complete valid custom order",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4da4d1034ad8535ebf27",
          "title": "falls back when the stored preference is incomplete or invalid",
          "full_title": "AAR dashboard chart layout > falls back when the stored preference is incomplete or invalid",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "372ad9ca11d064f31ed6",
          "title": "scopes the versioned preference to specialist and patient",
          "full_title": "AAR dashboard chart layout > scopes the versioned preference to specialist and patient",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "319687ffa30f9ef2c49f",
          "title": "acepta el formato antiguo, que era solo el orden en un array",
          "full_title": "normalizeAarDashboardPrefs > acepta el formato antiguo, que era solo el orden en un array",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e6da5c8a19e82834b4f7",
          "title": "cae a los valores por defecto ante datos corruptos",
          "full_title": "normalizeAarDashboardPrefs > cae a los valores por defecto ante datos corruptos",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ec879fe4f3cb27a21c10",
          "title": "descarta periodos no soportados y conserva los validos",
          "full_title": "normalizeAarDashboardPrefs > descarta periodos no soportados y conserva los validos",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a85355bbe815c4cb0573",
          "title": "descarta una tarjeta sin metricas porque no dibujaria nada",
          "full_title": "normalizeAarDashboardPrefs > descarta una tarjeta sin metricas porque no dibujaria nada",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a25c8cb1bf68456b3387",
          "title": "lo del paciente gana sobre lo global",
          "full_title": "resolveAarDashboardPrefs > lo del paciente gana sobre lo global",
          "line": 76,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "868f897640bc9480429f",
          "title": "lo global gana sobre los valores por defecto",
          "full_title": "resolveAarDashboardPrefs > lo global gana sobre los valores por defecto",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "859572d8acdd7567b4b2",
          "title": "rellena con los defaults lo que el paciente no fijo",
          "full_title": "resolveAarDashboardPrefs > rellena con los defaults lo que el paciente no fijo",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5cabe99eb8e543e2229b",
          "title": "un usuario sin preferencias guardadas obtiene los defaults",
          "full_title": "resolveAarDashboardPrefs > un usuario sin preferencias guardadas obtiene los defaults",
          "line": 94,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3fc541e8dbf9bdd35ebd",
          "title": "estira la tarjeta sobrante solo en el acomodo de dos columnas",
          "full_title": "getAarDashboardItemClass > estira la tarjeta sobrante solo en el acomodo de dos columnas",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c1289c944a4b1ad0790f",
          "title": "superpone varias metricas de la misma unidad, sin tope de dos",
          "full_title": "toggleTrendMetric > superpone varias metricas de la misma unidad, sin tope de dos",
          "line": 121,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "60bd69d5d3ab36b329b7",
          "title": "admite un segundo grupo de unidad, que va al eje derecho",
          "full_title": "toggleTrendMetric > admite un segundo grupo de unidad, que va al eje derecho",
          "line": 128,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "204e655e51e8213d6b7e",
          "title": "bloquea un tercer grupo de unidad en vez de soltar series en silencio",
          "full_title": "toggleTrendMetric > bloquea un tercer grupo de unidad en vez de soltar series en silencio",
          "line": 138,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4100784c5c7e6f6d8747",
          "title": "libera el eje al quitar la metrica que lo ocupaba",
          "full_title": "toggleTrendMetric > libera el eje al quitar la metrica que lo ocupaba",
          "line": 144,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cddbe31a678fb04738af",
          "title": "quita una metrica ya seleccionada",
          "full_title": "toggleTrendMetric > quita una metrica ya seleccionada",
          "line": 150,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8800ee07d2319bb3213c",
          "title": "nunca deja la grafica sin ninguna metrica",
          "full_title": "toggleTrendMetric > nunca deja la grafica sin ninguna metrica",
          "line": 154,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "338b609423fe18feb465",
          "title": "el orden de seleccion decide que unidad se queda a la izquierda",
          "full_title": "toggleTrendMetric > el orden de seleccion decide que unidad se queda a la izquierda",
          "line": 158,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/aar-fase.test.ts",
      "type": "unit",
      "sha256": "ccf82cf83a6ff56f261bf60ba9c9f7630f25a7a6cd6f10d5d1de5cb5cf2695ab",
      "tests": [
        {
          "id": "b26d4865e491a82a151a",
          "title": "mapea los días restantes a la fase",
          "full_title": "aar-fase > inferFase automática > mapea los días restantes a la fase",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "17c05d68f714b640fa61",
          "title": "la respeta mientras la competencia siga vigente, incluido el día del evento",
          "full_title": "aar-fase > caducidad de la fase manual > la respeta mientras la competencia siga vigente, incluido el día del evento",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "74b3ee32fccba9d57ec9",
          "title": "la respeta sin fecha de competencia: no hay contra qué caducar",
          "full_title": "aar-fase > caducidad de la fase manual > la respeta sin fecha de competencia: no hay contra qué caducar",
          "line": 33,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3c25791ff4faaddcc42a",
          "title": "caduca cuando la competencia ya pasó y vuelve al cálculo automático",
          "full_title": "aar-fase > caducidad de la fase manual > caduca cuando la competencia ya pasó y vuelve al cálculo automático",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4a15b49eb4caecb2968c",
          "title": "habla en días aunque falte más de una semana o un año",
          "full_title": "aar-fase > countdown en days out > habla en días aunque falte más de una semana o un año",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "428020618b1060598659",
          "title": "en pasado dice Hace N días, nunca 'días post' ni semanas",
          "full_title": "aar-fase > countdown en days out > en pasado dice Hace N días, nunca 'días post' ni semanas",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cd59eaf2995bd252fb41",
          "title": "distingue pasada, actual y próxima",
          "full_title": "aar-fase > estado de competencia > distingue pasada, actual y próxima",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a8aa217870b77a522596",
          "title": "la principal es la próxima por fecha, no la primera tecleada",
          "full_title": "aar-fase > competencia principal > la principal es la próxima por fecha, no la primera tecleada",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "69f45c4ec5f96e46939d",
          "title": "las pasadas van después de las próximas",
          "full_title": "aar-fase > competencia principal > las pasadas van después de las próximas",
          "line": 82,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b641693315dfefdf35df",
          "title": "con todo pasado, la principal es la última corrida, no la más antigua",
          "full_title": "aar-fase > competencia principal > con todo pasado, la principal es la última corrida, no la más antigua",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0f2f0436643f38b64621",
          "title": "una fecha ausente nunca encabeza la lista",
          "full_title": "aar-fase > competencia principal > una fecha ausente nunca encabeza la lista",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dbc5f4ab3c269cdc3874",
          "title": "no muta el arreglo original",
          "full_title": "aar-fase > competencia principal > no muta el arreglo original",
          "line": 100,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6316c423bcf6c77bda81",
          "title": "getPrimaryCompetition cae al espejo suelto de los perfiles previos al modelo multi-competencia",
          "full_title": "aar-fase > competencia principal > getPrimaryCompetition cae al espejo suelto de los perfiles previos al modelo multi-competencia",
          "line": 106,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3eb3a7c3e7df1f618edc",
          "title": "getNextCompetition ignora las pasadas aunque sean la más reciente",
          "full_title": "aar-fase > competencia principal > getNextCompetition ignora las pasadas aunque sean la más reciente",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "32f794636f83e325694b",
          "title": "estaEnPreparacion lee el flag del staff, no el calendario",
          "full_title": "aar-fase > competencia principal > estaEnPreparacion lee el flag del staff, no el calendario",
          "line": 134,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/aar-perfil.test.ts",
      "type": "unit",
      "sha256": "e4362cd14f48ed384e4ac964c33081e214a5a8c046d00f80008de1644a7e6d09",
      "tests": [
        {
          "id": "cabe89889edb273a5e7c",
          "title": "guardar sin tocar nada conserva notas y división del portal",
          "full_title": "buildPerfilAAR — lo capturado por el paciente sobrevive al guardado staff > guardar sin tocar nada conserva notas y división del portal",
          "line": 53,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7d99c8e3c63a44e23fed",
          "title": "cambiar de deporte limpia los detalles staff del deporte anterior sin tocar los del paciente",
          "full_title": "buildPerfilAAR — lo capturado por el paciente sobrevive al guardado staff > cambiar de deporte limpia los detalles staff del deporte anterior sin tocar los del paciente",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c8cea4575b4e57d598db",
          "title": "lo que el formulario sí gestiona sigue siendo borrable",
          "full_title": "buildPerfilAAR — lo capturado por el paciente sobrevive al guardado staff > lo que el formulario sí gestiona sigue siendo borrable",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1cc206ffa777dd015a1d",
          "title": "el interruptor de preparación activa viaja en el guardado del perfil",
          "full_title": "buildPerfilAAR — lo capturado por el paciente sobrevive al guardado staff > el interruptor de preparación activa viaja en el guardado del perfil",
          "line": 101,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9dd43c845d92cc546945",
          "title": "conserva competencias, espejo y estado competitivo como siempre",
          "full_title": "buildPerfilAAR — lo capturado por el paciente sobrevive al guardado staff > conserva competencias, espejo y estado competitivo como siempre",
          "line": 111,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4db6f92582cfb0a89087",
          "title": "ninguna clave del perfil resultante vale undefined (Firestore tumba el documento)",
          "full_title": "buildPerfilAAR — lo capturado por el paciente sobrevive al guardado staff > ninguna clave del perfil resultante vale undefined (Firestore tumba el documento)",
          "line": 122,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/aar-section-memory.test.ts",
      "type": "unit",
      "sha256": "5045c75820daacb1a743eff9f83e2016678568bca2012a64ed416a78863c1fc7",
      "tests": [
        {
          "id": "b108d05de58f28bf30c2",
          "title": "restores the last section independently for each patient",
          "full_title": "AAR section memory > restores the last section independently for each patient",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6aee7a44e1808c19832a",
          "title": "remembers the competencias section",
          "full_title": "AAR section memory > remembers the competencias section",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b709f7b6fd3516a5a457",
          "title": "falls back to dashboard when the cached value is missing or invalid",
          "full_title": "AAR section memory > falls back to dashboard when the cached value is missing or invalid",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b44e4f1bba4602c781fb",
          "title": "migrates legacy progreso and ajustes preferences",
          "full_title": "AAR section memory > migrates legacy progreso and ajustes preferences",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/active-plan-resolution.test.ts",
      "type": "unit",
      "sha256": "d33d645fd05953622ce0b121e6afe801c7cc99923062621695a70e32f97fe758",
      "tests": [
        {
          "id": "a4a97dc2e0a296709f98",
          "title": "devuelve la versión creada esta tarde cuando se consulta minutos después",
          "full_title": "resolveActiveVersionAt > devuelve la versión creada esta tarde cuando se consulta minutos después",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "07ed7e2d6c11b1b7e578",
          "title": "mantiene la versión previa antes de que arranque la nueva",
          "full_title": "resolveActiveVersionAt > mantiene la versión previa antes de que arranque la nueva",
          "line": 63,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4a84c2cf735eaccdcb42",
          "title": "no devuelve nada cuando ninguna versión está vigente",
          "full_title": "resolveActiveVersionAt > no devuelve nada cuando ninguna versión está vigente",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "72edd1dfaee676b29a4b",
          "title": "prefiere la de arranque más reciente si hubiera solapamiento",
          "full_title": "resolveActiveVersionAt > prefiere la de arranque más reciente si hubiera solapamiento",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "902c8bf650354480c259",
          "title": "omite una versión sustituida aunque conserve effectiveTo abierto",
          "full_title": "resolveActiveVersionAt > omite una versión sustituida aunque conserve effectiveTo abierto",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5ec99ac8becaa6914aae",
          "title": "expone la versión futura sin sustituir la vigente",
          "full_title": "resolveNextScheduledVersionAt > expone la versión futura sin sustituir la vigente",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5339bd287589ba5b437b",
          "title": "elige el cambio programado más próximo",
          "full_title": "resolveNextScheduledVersionAt > elige el cambio programado más próximo",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "efbfcf692705c4f52c19",
          "title": "no anuncia como próxima una versión futura sustituida",
          "full_title": "resolveNextScheduledVersionAt > no anuncia como próxima una versión futura sustituida",
          "line": 111,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6cab0d475ed8ce1b03a5",
          "title": "escucha el día local actual y todo el siguiente",
          "full_title": "currentAndNextLocalDayRangeIso > escucha el día local actual y todo el siguiente",
          "line": 121,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3073738ac734661ec107",
          "title": "deja el plan intacto cuando no hay snapshot",
          "full_title": "planFromVersion > deja el plan intacto cuando no hay snapshot",
          "line": 130,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4a8130b8f507685cb38a",
          "title": "hace ganar al snapshot congelado sobre el plan vivo",
          "full_title": "planFromVersion > hace ganar al snapshot congelado sobre el plan vivo",
          "line": 134,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3adec03f1fb7affb1436",
          "title": "conserva los suplementos vivos y no los pisa con el snapshot de nutrición",
          "full_title": "planFromVersion > conserva los suplementos vivos y no los pisa con el snapshot de nutrición",
          "line": 146,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2d3e6281f3fe113a432d",
          "title": "descarta la copia cuando la versión vigente ya es otra",
          "full_title": "shouldServeCachedPlan > descarta la copia cuando la versión vigente ya es otra",
          "line": 166,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "198958ec7e890369e0b3",
          "title": "sirve la copia mientras la versión coincide",
          "full_title": "shouldServeCachedPlan > sirve la copia mientras la versión coincide",
          "line": 170,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f30427c9a8c008d6adb0",
          "title": "sirve la copia cuando todavía no se conoce la versión vigente",
          "full_title": "shouldServeCachedPlan > sirve la copia cuando todavía no se conoce la versión vigente",
          "line": 174,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/assignment-adjustments.test.ts",
      "type": "unit",
      "sha256": "64b11ad74c0d0c23562fab02441fa4d4e06232d05de3b03037ef47f9667c4455",
      "tests": [
        {
          "id": "aed3a88fd6dcb539c77c",
          "title": "mueve sesiones sin ordenar sus días ni cambiar ejercicios y permite preparar un intercambio",
          "full_title": "borradores por asignación > mueve sesiones sin ordenar sus días ni cambiar ejercicios y permite preparar un intercambio",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "301974c63363af4f85c7",
          "title": "calcula el alcance desde todas las modificaciones y detalla un campo por serie",
          "full_title": "borradores por asignación > calcula el alcance desde todas las modificaciones y detalla un campo por serie",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bd4ee1f901c965e47212",
          "title": "acumula ajustes en dos menús y días conservando el ajuste particular del día",
          "full_title": "borradores por asignación > acumula ajustes en dos menús y días conservando el ajuste particular del día",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/assignment-patient-state.test.ts",
      "type": "unit",
      "sha256": "09a54d7b9f8e4382cab85a258f8013d5ac5197be4c9a405f09613093fd69c2b5",
      "tests": [
        {
          "id": "afee826865098c9a92d2",
          "title": "asignar dieta sin rutina previa transiciona a pendiente_rutina",
          "full_title": "buildAssignmentPatientUpdates > asignar dieta sin rutina previa transiciona a pendiente_rutina",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1ca3e59426e83fcc401c",
          "title": "asignar rutina con dieta completada transiciona a completado",
          "full_title": "buildAssignmentPatientUpdates > asignar rutina con dieta completada transiciona a completado",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cc8584760ee0630e4a9c",
          "title": "asignar dieta con rutina ya activa transiciona directo a completado",
          "full_title": "buildAssignmentPatientUpdates > asignar dieta con rutina ya activa transiciona directo a completado",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a7bd488bee46f3e8411b",
          "title": "asignar rutina sin dieta transiciona a pendiente_dieta",
          "full_title": "buildAssignmentPatientUpdates > asignar rutina sin dieta transiciona a pendiente_dieta",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c2064249871ba2652950",
          "title": "no toca estadoPaciente cuando el estado actual no es pendiente clínico",
          "full_title": "buildAssignmentPatientUpdates > no toca estadoPaciente cuando el estado actual no es pendiente clínico",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "97d6e8c6e584267311a2",
          "title": "desasignar dieta conserva la rutina y vuelve a pendiente_dieta",
          "full_title": "buildAssignmentPatientUpdates > desasignar dieta conserva la rutina y vuelve a pendiente_dieta",
          "line": 76,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8af5f003a7aeda668be4",
          "title": "desasignar dieta sin rutina deja ambas asignaciones pendientes",
          "full_title": "buildAssignmentPatientUpdates > desasignar dieta sin rutina deja ambas asignaciones pendientes",
          "line": 92,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b09387905c7d7bab2dd3",
          "title": "desasignar rutina sin dieta deja ambas asignaciones pendientes",
          "full_title": "buildAssignmentPatientUpdates > desasignar rutina sin dieta deja ambas asignaciones pendientes",
          "line": 108,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e4d622080bdb65b70f18",
          "title": "desasignar rutina con dieta pendiente conserva ambas asignaciones pendientes",
          "full_title": "buildAssignmentPatientUpdates > desasignar rutina con dieta pendiente conserva ambas asignaciones pendientes",
          "line": 123,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e38aa79375dbf4f626fa",
          "title": "desasignar no reclasifica estados comerciales",
          "full_title": "buildAssignmentPatientUpdates > desasignar no reclasifica estados comerciales",
          "line": 132,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/business-day.test.ts",
      "type": "unit",
      "sha256": "8e48173a7ea487f29e4d597d513c7be791a3dd305c6e9b5af67d26290d2c2e4d",
      "tests": [
        {
          "id": "e0587d890139ed9ca89e",
          "title": "arranca el día en la zona del negocio, no en UTC",
          "full_title": "businessDayStartIso > arranca el día en la zona del negocio, no en UTC",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2058ad04482d2d30b59f",
          "title": "respeta el horario de verano en vez de asumir un offset fijo",
          "full_title": "businessDayStartIso > respeta el horario de verano en vez de asumir un offset fijo",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "94f83748ef008d647801",
          "title": "acepta una marca de tiempo completa y toma solo su fecha civil",
          "full_title": "businessDayStartIso > acepta una marca de tiempo completa y toma solo su fecha civil",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "da572888264e2e25aef3",
          "title": "devuelve null cuando no hay una fecha civil que interpretar",
          "full_title": "businessDayStartIso > devuelve null cuando no hay una fecha civil que interpretar",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e77e0467bdd805920ec7",
          "title": "produce el mismo instante lo corra quien lo corra",
          "full_title": "businessDayStartIso > produce el mismo instante lo corra quien lo corra",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bf198486ca0bb82ec72d",
          "title": "es el milisegundo previo al arranque del día siguiente",
          "full_title": "businessDayLastInstantIso > es el milisegundo previo al arranque del día siguiente",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "16c614a235fcb33c05d0",
          "title": "devuelve la fecha civil del negocio, no la de UTC",
          "full_title": "businessDateKey > devuelve la fecha civil del negocio, no la de UTC",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4901c2729f8404bbddca",
          "title": "es la inversa de businessDayStartIso",
          "full_title": "businessDateKey > es la inversa de businessDayStartIso",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "70ebdec8efcb422344ce",
          "title": "el arranque programado no depende de dónde corra el código",
          "full_title": "alineación cliente-servidor > el arranque programado no depende de dónde corra el código",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/compatible-uuid.test.ts",
      "type": "unit",
      "sha256": "58fc37c4983b61f81c19896237dc60b86fb8bbd8126c24f2067312571d14b457",
      "tests": [
        {
          "id": "18e6e3af90c990daefd6",
          "title": "prefiere randomUUID cuando el navegador lo ofrece",
          "full_title": "createCompatibleUuid > prefiere randomUUID cuando el navegador lo ofrece",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "991b93d41143cf746a8f",
          "title": "genera un UUID v4 con getRandomValues en origenes HTTP",
          "full_title": "createCompatibleUuid > genera un UUID v4 con getRandomValues en origenes HTTP",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/contenido-sitio-encuadre.test.ts",
      "type": "unit",
      "sha256": "513d93caaff6b5d3874220e7f3fee1846b23822100e9d32fc686d07168db68a6",
      "tests": [
        {
          "id": "da83c5d318cb48672764",
          "title": "usa un marco cover centrado para atletas nuevos",
          "full_title": "encuadre de atletas del sitio > usa un marco cover centrado para atletas nuevos",
          "line": 9,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "39ab9f4f544018b3785f",
          "title": "conserva el encuadre publicado por slug aunque cambie el orden",
          "full_title": "encuadre de atletas del sitio > conserva el encuadre publicado por slug aunque cambie el orden",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "49dd23e2aa29ea060600",
          "title": "sanea valores externos antes de publicarlos",
          "full_title": "encuadre de atletas del sitio > sanea valores externos antes de publicarlos",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/diet-template-plan.test.ts",
      "type": "unit",
      "sha256": "dd22e8266e8afdd5cff5e88d00181eca435d30f1145b0dbe1fbfa0067cb10675",
      "tests": [
        {
          "id": "17ebee0b145d31f77222",
          "title": "conserva menús, recetas y cálculo al guardar desde cualquier paso",
          "full_title": "diet template plan > conserva menús, recetas y cálculo al guardar desde cualquier paso",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "469f7e1cde2b725670b3",
          "title": "elimina undefined antes de persistir la plantilla",
          "full_title": "diet template plan > elimina undefined antes de persistir la plantilla",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/diet-template-preview.test.ts",
      "type": "unit",
      "sha256": "9dd929032450e12cb3c7ad1f79967932ebc8cd7776650aa4d9418572581894c1",
      "tests": [
        {
          "id": "de9e2a33cd2c46f6a71b",
          "title": "combina el nombre del menú con la primera receta disponible",
          "full_title": "diet template preview > combina el nombre del menú con la primera receta disponible",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e04358b17bad4da38f26",
          "title": "admite plantillas legacy sin menús ni media",
          "full_title": "diet template preview > admite plantillas legacy sin menús ni media",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "52017367334fa7a1c0fd",
          "title": "devuelve un fallback neutro cuando no existe estructura de dieta",
          "full_title": "diet template preview > devuelve un fallback neutro cuando no existe estructura de dieta",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/exercise-reorder.test.ts",
      "type": "unit",
      "sha256": "0e2df9c2f2332959aee7eac6caf4834d7521a7b3726fa8bb23e62506feddf7bd",
      "tests": [
        {
          "id": "a5d4931fae8b25e88d3c",
          "title": "reordena dentro del mismo bloque",
          "full_title": "exercise-reorder > reordena dentro del mismo bloque",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4d396251f4a99e1a8d06",
          "title": "rechaza mover entre bloques distintos",
          "full_title": "exercise-reorder > rechaza mover entre bloques distintos",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2876072d9361f95f9817",
          "title": "rechaza no-ops e ids inexistentes",
          "full_title": "exercise-reorder > rechaza no-ops e ids inexistentes",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "55eb9ce082943b440336",
          "title": "renumera orden secuencial",
          "full_title": "exercise-reorder > renumera orden secuencial",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/floating-fab-position.test.ts",
      "type": "unit",
      "sha256": "b0c0c0955bd5b6207e0afd798bb66e7f14125d2b6b914146ea76b3da732f0354",
      "tests": [
        {
          "id": "c3044e60c486df5c8fff",
          "title": "keeps the default inset when no reserved control intersects the FAB lane",
          "full_title": "resolveFloatingFabBottom > keeps the default inset when no reserved control intersects the FAB lane",
          "line": 9,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c17c1e7d31069691e16b",
          "title": "moves above a full-width chat composer",
          "full_title": "resolveFloatingFabBottom > moves above a full-width chat composer",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ccd94e298d4dd679e351",
          "title": "clears a tall bottom status surface",
          "full_title": "resolveFloatingFabBottom > clears a tall bottom status surface",
          "line": 29,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d1b8dc0c577180694d24",
          "title": "uses the tallest overlapping reservation when several controls are visible",
          "full_title": "resolveFloatingFabBottom > uses the tallest overlapping reservation when several controls are visible",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d686c7c873b8930d2210",
          "title": "keeps the FAB inside the viewport when a reservation is unusually tall",
          "full_title": "resolveFloatingFabBottom > keeps the FAB inside the viewport when a reservation is unusually tall",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5e23260678d55ba0007a",
          "title": "places floating flow actions above the resolved FAB position",
          "full_title": "resolveFloatingFabBottom > places floating flow actions above the resolved FAB position",
          "line": 62,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/formatters.test.ts",
      "type": "unit",
      "sha256": "abed6141a0c35c41fbe19f584221fa784324a21ed29cd0d1502bd47521d35d4c",
      "tests": [
        {
          "id": "69094721dbd0a162b1a0",
          "title": "sin decimales por default",
          "full_title": "formatNumber > sin decimales por default",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "846e7da3244442ad7be8",
          "title": "respeta el número de decimales solicitado",
          "full_title": "formatNumber > respeta el número de decimales solicitado",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "68632a045e485bc8ceb6",
          "title": "redondea y agrega la unidad",
          "full_title": "formatKcal > redondea y agrega la unidad",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fb23e6e8cc0f8a284421",
          "title": "formatea %s g de %s como %s",
          "full_title": "formatMacro > formatea %s g de %s como %s",
          "line": 31,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "4d3270732628f24a855d",
          "title": "calcula la diferencia porcentual respecto al objetivo",
          "full_title": "calcularPorcentajeDiferencia > calcula la diferencia porcentual respecto al objetivo",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0b11df9eb5cc15311766",
          "title": "objetivo 0 retorna 0 en vez de dividir entre cero",
          "full_title": "calcularPorcentajeDiferencia > objetivo 0 retorna 0 en vez de dividir entre cero",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "410b308a1d849a0511ed",
          "title": "funciona con objetivos no redondos",
          "full_title": "calcularPorcentajeDiferencia > funciona con objetivos no redondos",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "62a12c97afc63b11e509",
          "title": "porcentaje %s → %s",
          "full_title": "getColorProgreso > porcentaje %s → %s",
          "line": 57,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/grocery-list.test.ts",
      "type": "unit",
      "sha256": "dfe34c3d5e7416b0caf8c222ce99778569f187c6f3978d9d1aa1d99b9241dc64",
      "tests": [
        {
          "id": "86e2d5ab1aee2b018290",
          "title": "agrega el mismo ingrediente (mismo nombre+unidad) sumando cantidades",
          "full_title": "buildGroceryList > agrega el mismo ingrediente (mismo nombre+unidad) sumando cantidades",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3b7078c5f32bb0da1bef",
          "title": "normaliza unidades equivalentes (gr/gramos → g) en la misma entrada",
          "full_title": "buildGroceryList > normaliza unidades equivalentes (gr/gramos → g) en la misma entrada",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "45e5133dd6537d157aac",
          "title": "NO mezcla el mismo ingrediente con unidades distintas",
          "full_title": "buildGroceryList > NO mezcla el mismo ingrediente con unidades distintas",
          "line": 71,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ffaea86fd345ea8dae40",
          "title": "mapea el grupo SMAE a la categoría de despensa",
          "full_title": "buildGroceryList > mapea el grupo SMAE a la categoría de despensa",
          "line": 83,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a21e786de201252f1408",
          "title": "descarta ingredientes sin nombre, sin unidad o con cantidad <= 0",
          "full_title": "buildGroceryList > descarta ingredientes sin nombre, sin unidad o con cantidad <= 0",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "26d5b8b490335b92fb5b",
          "title": "con ocurrencias acumula los días únicos del ingrediente",
          "full_title": "buildGroceryList > con ocurrencias acumula los días únicos del ingrediente",
          "line": 114,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7e619e338c3a8c82fc72",
          "title": "ordena categorías por número de items descendente",
          "full_title": "buildGroceryList > ordena categorías por número de items descendente",
          "line": 126,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e4e11d8bd0187a6cd4a3",
          "title": "lista vacía produce cero categorías",
          "full_title": "buildGroceryList > lista vacía produce cero categorías",
          "line": 140,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d12c3357ab4830f124ed",
          "title": "envuelve recetas con fecha/día y toma el primer tiempoComida de arreglos",
          "full_title": "recipesToGroceryOccurrences > envuelve recetas con fecha/día y toma el primer tiempoComida de arreglos",
          "line": 146,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "79978d71e3a634184c56",
          "title": "suma los items de todas las categorías",
          "full_title": "countGroceryItems > suma los items de todas las categorías",
          "line": 158,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2cbfabf1ba3bc519fbca",
          "title": "genera el texto con encabezado, conteo, categorías y días",
          "full_title": "groceryListToText > genera el texto con encabezado, conteo, categorías y días",
          "line": 174,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1858b0e0920c8ac63cbf",
          "title": "omite las líneas de opciones cuando no se pasan",
          "full_title": "groceryListToText > omite las líneas de opciones cuando no se pasan",
          "line": 191,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/grupo-total-adjustment.test.ts",
      "type": "unit",
      "sha256": "91b93c1f9be5071fa700fcb531e6c56011f92735bfbb5f2b261fe9c8e5c8b9ca",
      "tests": [
        {
          "id": "2c6f5c209ae291c8dbc9",
          "title": "reparte el aumento en pasos de 0.5 y la suma cuadra exactamente con el delta",
          "full_title": "distribuirDeltaEnTiempos > reparte el aumento en pasos de 0.5 y la suma cuadra exactamente con el delta",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4134ff6e7a1be4401afb",
          "title": "propone un reparto parejo aunque los valores iniciales sean distintos",
          "full_title": "distribuirDeltaEnTiempos > propone un reparto parejo aunque los valores iniciales sean distintos",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b6df486fe4b846e4fd2a",
          "title": "reparte en partes iguales cuando ningún tiempo tiene porciones de ese grupo",
          "full_title": "distribuirDeltaEnTiempos > reparte en partes iguales cuando ningún tiempo tiene porciones de ese grupo",
          "line": 53,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1ba5d0ef06bf4bcea029",
          "title": "incluye en la sugerencia los tiempos que estaban en cero",
          "full_title": "distribuirDeltaEnTiempos > incluye en la sugerencia los tiempos que estaban en cero",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9d1cb27f71276af9fab2",
          "title": "clasifica como remanente un delta menor al paso sin inventar cambios",
          "full_title": "distribuirDeltaEnTiempos > clasifica como remanente un delta menor al paso sin inventar cambios",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c0af79200cae25f4cdb4",
          "title": "nunca deja valores negativos y reporta lo que no pudo aplicar",
          "full_title": "distribuirDeltaEnTiempos > nunca deja valores negativos y reporta lo que no pudo aplicar",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a855a49ec099ec64692a",
          "title": "suma las porciones del grupo en todos los tiempos del menú",
          "full_title": "getTotalGrupoEnMenu > suma las porciones del grupo en todos los tiempos del menú",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "669fccf0c6b2e2507013",
          "title": "sube el total del grupo repartiéndolo solo en los tiempos elegidos",
          "full_title": "applyGrupoTotalAdjustment > sube el total del grupo repartiéndolo solo en los tiempos elegidos",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a37402416b3558330b11",
          "title": "no toca los demás grupos de los tiempos afectados",
          "full_title": "applyGrupoTotalAdjustment > no toca los demás grupos de los tiempos afectados",
          "line": 104,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c25c7ee0a3cbb7e608df",
          "title": "adapta las recetas de los tiempos afectados y deja intactas las demás",
          "full_title": "applyGrupoTotalAdjustment > adapta las recetas de los tiempos afectados y deja intactas las demás",
          "line": 115,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fd9dd2b6382483dcf454",
          "title": "reporta las porciones que los tiempos elegidos no pudieron absorber",
          "full_title": "applyGrupoTotalAdjustment > reporta las porciones que los tiempos elegidos no pudieron absorber",
          "line": 129,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "933ef879af8e7b13d8ff",
          "title": "devuelve el diff por tiempo para poder mostrar el antes y después",
          "full_title": "applyGrupoTotalAdjustment > devuelve el diff por tiempo para poder mostrar el antes y después",
          "line": 141,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b185ef42c7023f8e575f",
          "title": "sin tiempos destino no cambia nada",
          "full_title": "applyGrupoTotalAdjustment > sin tiempos destino no cambia nada",
          "line": 156,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bc7a79de4d06482798b4",
          "title": "acepta una asignación manual en un tiempo que estaba en cero",
          "full_title": "applyGrupoTotalAdjustment > acepta una asignación manual en un tiempo que estaba en cero",
          "line": 169,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "095e0d199bafe13b41a8",
          "title": "reporta porciones libres cuando la receta no contiene el grupo asignado",
          "full_title": "applyGrupoTotalAdjustment > reporta porciones libres cuando la receta no contiene el grupo asignado",
          "line": 189,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "738050c59539ab1566e0",
          "title": "reporta como restante lo que el paso de 0.5 no pudo repartir",
          "full_title": "applyGrupoTotalAdjustment · remanente fraccionario > reporta como restante lo que el paso de 0.5 no pudo repartir",
          "line": 223,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e8ee6abbe7bf7eb6bf35",
          "title": "no inventa remanente cuando el reparto sí es exacto",
          "full_title": "applyGrupoTotalAdjustment · remanente fraccionario > no inventa remanente cuando el reparto sí es exacto",
          "line": 245,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/input-sanitizers.test.ts",
      "type": "unit",
      "sha256": "f57e5eb1b3d1b5f0794e99ae80e254cb9f20b8b221337bea5a1b795475d7e131",
      "tests": [
        {
          "id": "34445fceb076671773b4",
          "title": "remueve todo lo que no sea dígito y limita a 12",
          "full_title": "sanitizePhoneMxInput > remueve todo lo que no sea dígito y limita a 12",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3135a910e358acc6b4c2",
          "title": "conserva el + inicial",
          "full_title": "sanitizePhoneMxInput > conserva el + inicial",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "beb9371d2bb98e68eedc",
          "title": "string sin dígitos queda vacío",
          "full_title": "sanitizePhoneMxInput > string sin dígitos queda vacío",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "21d4c53775389ab16124",
          "title": "formatea 10 dígitos nacionales en bloques 3-3-4",
          "full_title": "formatPhoneMx > formatea 10 dígitos nacionales en bloques 3-3-4",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f4ad820e207836ec5ed7",
          "title": "normaliza el prefijo 52 a formato internacional",
          "full_title": "formatPhoneMx > normaliza el prefijo 52 a formato internacional",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e90561a368b020f91d29",
          "title": "formatea entradas parciales mientras se teclea",
          "full_title": "formatPhoneMx > formatea entradas parciales mientras se teclea",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "17eebf1aef2189bddf82",
          "title": "vacío retorna vacío",
          "full_title": "formatPhoneMx > vacío retorna vacío",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5ae150770b14c222aab0",
          "title": "agrupa 3-3-4 y deja el resto en un cuarto bloque",
          "full_title": "formatPhoneInput > agrupa 3-3-4 y deja el resto en un cuarto bloque",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f70ce9d6c999886a7679",
          "title": "limita a 15 dígitos",
          "full_title": "formatPhoneInput > limita a 15 dígitos",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4a9a47efa0a1b5c0baeb",
          "title": "remueve símbolos y letras",
          "full_title": "sanitizeCurrencyInput > remueve símbolos y letras",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9077eaf84eae969e3e15",
          "title": "colapsa múltiples puntos y limita a 2 decimales",
          "full_title": "sanitizeCurrencyInput > colapsa múltiples puntos y limita a 2 decimales",
          "line": 68,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e3ecdf1c6ae921d24217",
          "title": "mantiene el punto decimal aunque no haya decimales aún",
          "full_title": "sanitizeCurrencyInput > mantiene el punto decimal aunque no haya decimales aún",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f83c34f293a8173047e4",
          "title": "convierte a número y retorna 0 para entradas inválidas",
          "full_title": "parseCurrencyInput > convierte a número y retorna 0 para entradas inválidas",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a7e2414f0deda9abd7b8",
          "title": "convierte a mayúsculas, quita símbolos y recorta al máximo",
          "full_title": "sanitizeUpperAlnum > convierte a mayúsculas, quita símbolos y recorta al máximo",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dd5de002af524019deb5",
          "title": "acepta emails simples y hace trim",
          "full_title": "isValidEmail > acepta emails simples y hace trim",
          "line": 94,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "558b6d82be1b0362b3a8",
          "title": "rechaza email inválido: %s",
          "full_title": "isValidEmail > rechaza email inválido: %s",
          "line": 98,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "793550d6b87214b3a07e",
          "title": "acota el valor al rango [min, max]",
          "full_title": "clampNumber > acota el valor al rango [min, max]",
          "line": 107,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/meal-slot-consumers.test.ts",
      "type": "unit",
      "sha256": "e163e8636e904421f7d95a66e1b0704ba77b2c7384d3757e348388ab953bf987",
      "tests": [
        {
          "id": "84de8a61545b8070567d",
          "title": "/portal/plan conserva la palomita de un caché legacy sin snapshots",
          "full_title": "consumidores de slots resueltos > /portal/plan conserva la palomita de un caché legacy sin snapshots",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5ce57b53a50c4ab31b99",
          "title": "/portal/plan rescata una comida histórica después de vaciar el slot",
          "full_title": "consumidores de slots resueltos > /portal/plan rescata una comida histórica después de vaciar el slot",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "15bbd7b5cd9fc6879a87",
          "title": "el detalle al que enlazan plan y agenda resuelve el snapshot histórico",
          "full_title": "consumidores de slots resueltos > el detalle al que enlazan plan y agenda resuelve el snapshot histórico",
          "line": 67,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f9622108f6dda3ba628c",
          "title": "/portal/plan muestra el mismo slot libre que la agenda",
          "full_title": "consumidores de slots resueltos > /portal/plan muestra el mismo slot libre que la agenda",
          "line": 84,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/meal-slot-view.test.ts",
      "type": "unit",
      "sha256": "70ddebabb2ca175ced08cb4eeaed7b98fb65852d8ab3b075491951243d71c375",
      "tests": [
        {
          "id": "cac1559357de952004fe",
          "title": "mantiene la receta vigente normal",
          "full_title": "resolveMealSlotsForDay > mantiene la receta vigente normal",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4577993acd15f6dee588",
          "title": "crea una comida libre cuando quedan porciones",
          "full_title": "resolveMealSlotsForDay > crea una comida libre cuando quedan porciones",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c01a4e634b04b432574d",
          "title": "conserva una comida declarada libre aunque su distribución esté en cero",
          "full_title": "resolveMealSlotsForDay > conserva una comida declarada libre aunque su distribución esté en cero",
          "line": 63,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "56df2bf55dba72d6d625",
          "title": "descarta un slot vacío sin registro",
          "full_title": "resolveMealSlotsForDay > descarta un slot vacío sin registro",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1c64e70df9d1c9515e8c",
          "title": "rescata un slot vacío desde un registro moderno",
          "full_title": "resolveMealSlotsForDay > rescata un slot vacío desde un registro moderno",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7700edeab00280017a55",
          "title": "rescata un slot vacío desde un registro legacy",
          "full_title": "resolveMealSlotsForDay > rescata un slot vacío desde un registro legacy",
          "line": 112,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bb13cd237f4c3f1084ef",
          "title": "ordena tiempos canónicos y agrega los registrados ausentes",
          "full_title": "resolveMealSlotsForDay > ordena tiempos canónicos y agrega los registrados ausentes",
          "line": 125,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7986edae53c95b1f3d5f",
          "title": "no convierte un patient-* en receta principal fantasma",
          "full_title": "resolveMealSlotsForDay > no convierte un patient-* en receta principal fantasma",
          "line": 142,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/metric-direction.test.ts",
      "type": "unit",
      "sha256": "5b03c59ae25ab76dfdf411e94fb0db49ce9919c683c9a33681863d412929a19d",
      "tests": [
        {
          "id": "07ad15ab58f91fa6aa4d",
          "title": "resuelve las metricas de composicion corporal",
          "full_title": "getMetricDirection > resuelve las metricas de composicion corporal",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "771baf7391c8eb88fd5d",
          "title": "acepta la nomenclatura del detalle de consulta",
          "full_title": "getMetricDirection > acepta la nomenclatura del detalle de consulta",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4ba993454d489a400cbf",
          "title": "trata el rpe como neutral porque depende de la fase del bloque",
          "full_title": "getMetricDirection > trata el rpe como neutral porque depende de la fase del bloque",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "58d9d6579a1f09d8ae48",
          "title": "no supone nada ante una metrica desconocida",
          "full_title": "getMetricDirection > no supone nada ante una metrica desconocida",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a52f2ad176993efb4760",
          "title": "deja que un override gane sobre el mapa por defecto",
          "full_title": "getMetricDirection > deja que un override gane sobre el mapa por defecto",
          "line": 33,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6fd6911a9f913644a43b",
          "title": "%s con delta %d es %s",
          "full_title": "getDeltaTone > %s con delta %d es %s",
          "line": 61,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "9e3ebfa585965a4ec61d",
          "title": "no colorea cuando no hay movimiento ni dato",
          "full_title": "getDeltaTone > no colorea cuando no hay movimiento ni dato",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0c8fdcdc2368d4d5a1e2",
          "title": "una metrica desconocida nunca se pinta",
          "full_title": "getDeltaTone > una metrica desconocida nunca se pinta",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "88c3a71ca1084e5b1e98",
          "title": "respeta el override, util para un atleta en volumen",
          "full_title": "getDeltaTone > respeta el override, util para un atleta en volumen",
          "line": 77,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a38f02a6f8e7aa451c01",
          "title": "%s con cambio %d es mejora=%s",
          "full_title": "isImprovement > %s con cambio %d es mejora=%s",
          "line": 99,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "5c73624189f37bb65709",
          "title": "mantenerse cuenta como mejora, igual que antes",
          "full_title": "isImprovement > mantenerse cuenta como mejora, igual que antes",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1b9cc420e50cf469ce99",
          "title": "una metrica neutral o desconocida no se marca como retroceso",
          "full_title": "isImprovement > una metrica neutral o desconocida no se marca como retroceso",
          "line": 108,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/nutrition-revision-contract.test.ts",
      "type": "unit",
      "sha256": "3b24842450595bcdba49ae2bf87153d5290ebb3a6cabae3767004ef18757b4fe",
      "tests": [
        {
          "id": "6c0117d74aeb38cfc3cb",
          "title": "limita los tiempos afectados y rechaza los que no pertenecen a la distribución",
          "full_title": "contrato puro de revisiones nutricionales > limita los tiempos afectados y rechaza los que no pertenecen a la distribución",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6f81d544badece9314aa",
          "title": "publica ahora todo el plan y conserva sólo el snapshot del check-in afectado",
          "full_title": "contrato puro de revisiones nutricionales > publica ahora todo el plan y conserva sólo el snapshot del check-in afectado",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4d19bfb88318bea213b9",
          "title": "respeta exactamente el arranque futuro y no anuncia cambios actuales",
          "full_title": "contrato puro de revisiones nutricionales > respeta exactamente el arranque futuro y no anuncia cambios actuales",
          "line": 38,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d0adc7c4357aed0f917c",
          "title": "la fecha elegida manda sobre el automatismo y deja de anunciar cambios de hoy",
          "full_title": "contrato puro de revisiones nutricionales > la fecha elegida manda sobre el automatismo y deja de anunciar cambios de hoy",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "09fd388c44d5b505182a",
          "title": "no deja programar fuera de la vigencia del plan",
          "full_title": "contrato puro de revisiones nutricionales > no deja programar fuera de la vigencia del plan",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "68a75b67f6853a006b22",
          "title": "un plan que aún no arranca no se puede adelantar con la fecha elegida",
          "full_title": "contrato puro de revisiones nutricionales > un plan que aún no arranca no se puede adelantar con la fecha elegida",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0360422dc7a4eaeb162b",
          "title": "normaliza check-ins booleanos legacy igual que el lector cliente",
          "full_title": "contrato puro de revisiones nutricionales > normaliza check-ins booleanos legacy igual que el lector cliente",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "31999823a4365ed321c4",
          "title": "anuncia la fecha real de un ajuste futuro y no etiqueta como programado uno vigente",
          "full_title": "contrato puro de revisiones nutricionales > anuncia la fecha real de un ajuste futuro y no etiqueta como programado uno vigente",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/nutrition-revision-diff.test.ts",
      "type": "unit",
      "sha256": "43d09e3cf0c9232fe34e2476c5721f059396bbefc81b8396071b17688cb62ea4",
      "tests": [
        {
          "id": "d8c131314b95ec204573",
          "title": "ignores server update timestamps in dirty tracking",
          "full_title": "nutrition revision diff > ignores server update timestamps in dirty tracking",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f6f8fda4f9eb839bb9b0",
          "title": "detects portions, recipes, menus and weekly assignments",
          "full_title": "nutrition revision diff > detects portions, recipes, menus and weekly assignments",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/onboarding-measurement.test.ts",
      "type": "unit",
      "sha256": "50e57935bcd58b762c8a14e9566bbd623b2a8f93664b89f6a46fb12cb2a49ae5",
      "tests": [
        {
          "id": "ec9bf2e22cce1fe031a4",
          "title": "preserva mediciones clínicas y agrega una medición inicial identificable",
          "full_title": "mergeOnboardingInitialMeasurement > preserva mediciones clínicas y agrega una medición inicial identificable",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a588c39b7119019aca4f",
          "title": "reemplaza la medición onboarding previa sin duplicarla",
          "full_title": "mergeOnboardingInitialMeasurement > reemplaza la medición onboarding previa sin duplicarla",
          "line": 26,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e58381a5cc506e81930f",
          "title": "migra el registro legacy id 1 sin borrar otras mediciones",
          "full_title": "mergeOnboardingInitialMeasurement > migra el registro legacy id 1 sin borrar otras mediciones",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/operational-routine-performance.test.ts",
      "type": "unit",
      "sha256": "81dc98b55370a0a5faba519dc8cfa29656251a60d90c7ebc00133bd85ea7eee8",
      "tests": [
        {
          "id": "ac8f81937fbd942a0310",
          "title": "expone peso y repeticiones reales, no los objetivos prescritos",
          "full_title": "performance del calendario operativo > expone peso y repeticiones reales, no los objetivos prescritos",
          "line": 26,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1fcaed129e4b43405477",
          "title": "omite sets no completados y ordena por ejercicio y número de serie",
          "full_title": "performance del calendario operativo > omite sets no completados y ordena por ejercicio y número de serie",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d316f380723000f6d304",
          "title": "muestra duración y distancia para modalidades sin peso ni reps",
          "full_title": "performance del calendario operativo > muestra duración y distancia para modalidades sin peso ni reps",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/patient-date-access.test.ts",
      "type": "unit",
      "sha256": "8a747198bbf1c5d1cac2a532bcc7fce4980220afb59b577621b3edf184f37a39",
      "tests": [
        {
          "id": "827f5fc4cad49c6b2dd4",
          "title": "(%j, demo=%j) → %j — %s",
          "full_title": "canAccessPatientDate > (%j, demo=%j) → %j — %s",
          "line": 19,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/patient-helpers.test.ts",
      "type": "unit",
      "sha256": "c8ca03f2376538e204f2a43004cabd9e875a200ad39c20245841adfed50b93a2",
      "tests": [
        {
          "id": "c52ee0e8df7bcf44619a",
          "title": "%s → %s",
          "full_title": "getInitials > %s → %s",
          "line": 22,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "2e6ad39c949c60be6a43",
          "title": "retorna null sin fecha",
          "full_title": "formatDate > retorna null sin fecha",
          "line": 33,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cdb49a35e3a51c3bf011",
          "title": "formatea día numérico + mes corto en es-MX",
          "full_title": "formatDate > formatea día numérico + mes corto en es-MX",
          "line": 38,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c69af6596b97251bf71c",
          "title": "retorna null sin fecha",
          "full_title": "formatLastActivity > retorna null sin fecha",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "150a8a9e1a96edfd4de1",
          "title": "hace %s ms → %s",
          "full_title": "formatLastActivity > hace %s ms → %s",
          "line": 60,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "a327862be78b3205ff08",
          "title": "más de 7 días cae al formato de fecha corta",
          "full_title": "formatLastActivity > más de 7 días cae al formato de fecha corta",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "737b5bf0941d9af0c484",
          "title": "sin fecha usa el estilo neutro",
          "full_title": "getLastActivityBadgeClass > sin fecha usa el estilo neutro",
          "line": 89,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2177ea4e0a3ff03118ea",
          "title": "actividad reciente (<= 1 día) es verde",
          "full_title": "getLastActivityBadgeClass > actividad reciente (<= 1 día) es verde",
          "line": 93,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "01179ec992e5d396ac82",
          "title": "actividad de la semana (<= 7 días) es ámbar",
          "full_title": "getLastActivityBadgeClass > actividad de la semana (<= 7 días) es ámbar",
          "line": 97,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aa779b8c43e543206d4e",
          "title": "actividad vieja (> 7 días) es roja",
          "full_title": "getLastActivityBadgeClass > actividad vieja (> 7 días) es roja",
          "line": 101,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/patient-pipeline-state.test.ts",
      "type": "unit",
      "sha256": "b0f8a1534807ac47c7c514591d69b758f655b8a50fca086497f118b54b809de9",
      "tests": [
        {
          "id": "d025dd06cf4a3157710f",
          "title": "marca dieta y rutina como pendientes al volver al seguimiento clínico",
          "full_title": "buildPatientPipelineUpdates > marca dieta y rutina como pendientes al volver al seguimiento clínico",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4f3435d7beb2b8287ba4",
          "title": "completa ambos planes al cerrar la etapa clínica",
          "full_title": "buildPatientPipelineUpdates > completa ambos planes al cerrar la etapa clínica",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4e305c0c56965730c44b",
          "title": "conserva los datos clínicos al iniciar una renovación",
          "full_title": "buildPatientPipelineUpdates > conserva los datos clínicos al iniciar una renovación",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "86528dd6b396ee778af6",
          "title": "conserva la etapa de captación %s al abrir el drawer",
          "full_title": "resolvePatientEditorPipelineStatus > conserva la etapa de captación %s al abrir el drawer",
          "line": 40,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "2140fb851f9a06787b1c",
          "title": "usa Nuevo paciente sólo cuando el registro no tiene estado",
          "full_title": "resolvePatientEditorPipelineStatus > usa Nuevo paciente sólo cuando el registro no tiene estado",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/perfil-alimenticio.test.ts",
      "type": "unit",
      "sha256": "adb06e187a218826c90e065907520f7129831de893c875ac1889d7f90f7ab3f7",
      "tests": [
        {
          "id": "380acebe0daebbcf3d01",
          "title": "una alergia que coincide con un ingrediente sensible es alerta dura (review)",
          "full_title": "getRecetaPerfilAviso > una alergia que coincide con un ingrediente sensible es alerta dura (review)",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5fae2253e5d030445cd0",
          "title": "un ingrediente marcado no_gusta permanece como preferencia, no como alerta dura",
          "full_title": "getRecetaPerfilAviso > un ingrediente marcado no_gusta permanece como preferencia, no como alerta dura",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "58da06699a48f5f68ccc",
          "title": "sin restricciones aplicables devuelve ok",
          "full_title": "getRecetaPerfilAviso > sin restricciones aplicables devuelve ok",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/peso-unidad.test.ts",
      "type": "unit",
      "sha256": "805a19570dbadb87c5ac4b798af6233d0a883d489e5c20e7acecb99f98cf6607",
      "tests": [
        {
          "id": "410930b585750c0014d4",
          "title": "kg es identidad; lb convierte con el factor exacto",
          "full_title": "peso-unidad > kg es identidad; lb convierte con el factor exacto",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fdc82ef1799f065aceae",
          "title": "captura en libras y vuelta: 194 lb es estable",
          "full_title": "peso-unidad > captura en libras y vuelta: 194 lb es estable",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8880cb9b81df05b28f13",
          "title": "captura en kg no se toca",
          "full_title": "peso-unidad > captura en kg no se toca",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "368babe867903e72e89c",
          "title": "guardar sin tocar no mueve el kg canónico en valores no redondos",
          "full_title": "peso-unidad > guardar sin tocar no mueve el kg canónico en valores no redondos",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d759433bcb27d23ea1e4",
          "title": "deltaDesdeKg conserva décimas: a 0.2 kg del máximo no se reporta 0",
          "full_title": "peso-unidad > deltaDesdeKg conserva décimas: a 0.2 kg del máximo no se reporta 0",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b5f3e9319f3ca820f3ae",
          "title": "formatPeso muestra en la unidad pedida y calla sin dato",
          "full_title": "peso-unidad > formatPeso muestra en la unidad pedida y calla sin dato",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/plan-helpers.test.ts",
      "type": "unit",
      "sha256": "57923846dba6347c8f76892050ad7703741d304003a879f75142fb5723027142",
      "tests": [
        {
          "id": "56383750f4c7ed6c0bb9",
          "title": "resolves menu overrides by meal time, not by cell or array order",
          "full_title": "BK-47 canonical plan portions > resolves menu overrides by meal time, not by cell or array order",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "19b16253854405b1c35d",
          "title": "migrates legacy cell portions deterministically without changing meal content",
          "full_title": "BK-47 canonical plan portions > migrates legacy cell portions deterministically without changing meal content",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fde24817709f9ffcdb1d",
          "title": "updates every recipe reference in the same menu + meal time scope",
          "full_title": "BK-47 canonical plan portions > updates every recipe reference in the same menu + meal time scope",
          "line": 83,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/plan-navigation.test.ts",
      "type": "unit",
      "sha256": "497ef616482467a0046403ccb9365a8fac899a922358dea9a615b4c27b33e884",
      "tests": [
        {
          "id": "92ae065ffbf742a2bf62",
          "title": "conecta AAR con el wizard en modo edicion para el plan activo",
          "full_title": "plan wizard navigation > conecta AAR con el wizard en modo edicion para el plan activo",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/portal-back-navigation.test.ts",
      "type": "unit",
      "sha256": "41307d88f1646718fa701375ac786958477cc4ce1df7531e508804bcc6ada4bc",
      "tests": [
        {
          "id": "95672db9259564ba1852",
          "title": "prefers history.back when Next.js history idx is greater than 0",
          "full_title": "resolvePortalBackNavigation > prefers history.back when Next.js history idx is greater than 0",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "61cd31da72aa9978b246",
          "title": "falls back to the parent route when history idx is 0",
          "full_title": "resolvePortalBackNavigation > falls back to the parent route when history idx is 0",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5c4fe7e98c92b33a6d77",
          "title": "uses history.length when idx is unavailable",
          "full_title": "resolvePortalBackNavigation > uses history.length when idx is unavailable",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "98a5de557fb34f2a7348",
          "title": "reads numeric idx from history state",
          "full_title": "readPortalHistoryIdx > reads numeric idx from history state",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/portal-nutrition-day.test.ts",
      "type": "unit",
      "sha256": "c1107aadcf301f1eaece5a189121207c8a0b812db9fd0b5eb5a48775360c2f88",
      "tests": [
        {
          "id": "489739d284d779b79f28",
          "title": "suma snapshots por receta sin duplicar el snapshot superior ni consultar el plan vivo",
          "full_title": "adherencia nutricional histórica > suma snapshots por receta sin duplicar el snapshot superior ni consultar el plan vivo",
          "line": 96,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2b56a91cdf3e4d7f61bd",
          "title": "no inventa nutrición para una receta anidada sin snapshot",
          "full_title": "adherencia nutricional histórica > no inventa nutrición para una receta anidada sin snapshot",
          "line": 114,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5b180aabd3066372dcaf",
          "title": "ancla consumo y objetivo al assignmentVersionId aunque el plan vivo cambie después",
          "full_title": "adherencia nutricional histórica > ancla consumo y objetivo al assignmentVersionId aunque el plan vivo cambie después",
          "line": 124,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1de3afe48e644a62c048",
          "title": "resuelve el legado al inicio del día y marca aproximado si hubo otra versión",
          "full_title": "adherencia nutricional histórica > resuelve el legado al inicio del día y marca aproximado si hubo otra versión",
          "line": 140,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9c01b9cb3606b43a17d2",
          "title": "marca aproximado un día legacy aunque sólo exista una versión y estima su fallback nutricional",
          "full_title": "adherencia nutricional histórica > marca aproximado un día legacy aunque sólo exista una versión y estima su fallback nutricional",
          "line": 157,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2edfcd944b4809e3c38b",
          "title": "marca aproximada la procedencia mixta o con varias versiones ancladas",
          "full_title": "adherencia nutricional histórica > marca aproximada la procedencia mixta o con varias versiones ancladas",
          "line": 172,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "25ea6aa6fc4d546510c1",
          "title": "no sustituye una versión referenciada ausente ni un histórico vacío con el plan vivo",
          "full_title": "adherencia nutricional histórica > no sustituye una versión referenciada ausente ni un histórico vacío con el plan vivo",
          "line": 196,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "80f537ef62bafd77d378",
          "title": "reserva el plan vigente para hoy y el futuro",
          "full_title": "adherencia nutricional histórica > reserva el plan vigente para hoy y el futuro",
          "line": 214,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/progress-analytics.test.ts",
      "type": "unit",
      "sha256": "af72b0843deff69a148617d14cf6193c06d1c567145482b93098d5a64b452898",
      "tests": [
        {
          "id": "6bcbd7fafbcc13585ff8",
          "title": "descarta días sin registro y usa la versión histórica de cada día",
          "full_title": "promedios calóricos de adherencia > descarta días sin registro y usa la versión histórica de cada día",
          "line": 86,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "74ecaeaf42371ac7edc9",
          "title": "ofrece 7, 30 y 60 días a las superficies controlada e interna",
          "full_title": "promedios calóricos de adherencia > ofrece 7, 30 y 60 días a las superficies controlada e interna",
          "line": 122,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "95814f060acb6a28822f",
          "title": "mantiene el mismo contrato en el rango de %i días",
          "full_title": "promedios calóricos de adherencia > mantiene el mismo contrato en el rango de %i días",
          "line": 126,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "b8a3e0c2923f7e2d1198",
          "title": "arrastra precisión aproximada y declara no disponible un rango sin ningún día comparable",
          "full_title": "promedios calóricos de adherencia > arrastra precisión aproximada y declara no disponible un rango sin ningún día comparable",
          "line": 146,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0cc37baa0a009fe7379b",
          "title": "excluye y declara días inválidos sin borrar los promedios de los días comparables",
          "full_title": "promedios calóricos de adherencia > excluye y declara días inválidos sin borrar los promedios de los días comparables",
          "line": 181,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3abc5c0ca48744dc622e",
          "title": "no presenta como consumo medido un rango compuesto sólo por un check-in sin snapshot nutricional",
          "full_title": "promedios calóricos de adherencia > no presenta como consumo medido un rango compuesto sólo por un check-in sin snapshot nutricional",
          "line": 210,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c62f2b2a0ee0d675f52f",
          "title": "representa como ausencia, no como cero, los días sin check-in en la serie de la gráfica",
          "full_title": "promedios calóricos de adherencia > representa como ausencia, no como cero, los días sin check-in en la serie de la gráfica",
          "line": 232,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/quick-plan-adjustments.test.ts",
      "type": "unit",
      "sha256": "6adaaeee181931cfb3097e6c1473dd8818efec5e975f81e1bf4d8310bea2b76d",
      "tests": [
        {
          "id": "7972f87aa1ca7068448e",
          "title": "inicia un ajuste inmediato en el instante real y cierra la versión previa un milisegundo antes",
          "full_title": "quick adjustment version windows > inicia un ajuste inmediato en el instante real y cierra la versión previa un milisegundo antes",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f340b5726fb924fa53d6",
          "title": "programa un ajuste diferido desde el inicio del día siguiente",
          "full_title": "quick adjustment version windows > programa un ajuste diferido desde el inicio del día siguiente",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3f38eff122dace45ba08",
          "title": "conserva el día civil de México al programar cerca de medianoche",
          "full_title": "quick adjustment version windows > conserva el día civil de México al programar cerca de medianoche",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f809d0bc3a1abfe21f1d",
          "title": "aplica desde ahora cuando el paciente no ha registrado ese tiempo de comida",
          "full_title": "resolveQuickAdjustmentWindow > aplica desde ahora cuando el paciente no ha registrado ese tiempo de comida",
          "line": 50,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "12fa8247318425ee4769",
          "title": "difiere al día siguiente cuando ese tiempo ya tiene registro",
          "full_title": "resolveQuickAdjustmentWindow > difiere al día siguiente cuando ese tiempo ya tiene registro",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5adf0a3de8a7ac84676e",
          "title": "respeta la vigencia de un plan programado a futuro por encima de todo",
          "full_title": "resolveQuickAdjustmentWindow > respeta la vigencia de un plan programado a futuro por encima de todo",
          "line": 62,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3762462b51ce67451108",
          "title": "usa el mismo formato de arranque que el servidor, para que las dos publicaciones no se pisen",
          "full_title": "resolveQuickAdjustmentWindow > usa el mismo formato de arranque que el servidor, para que las dos publicaciones no se pisen",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8df0fcdf1c1819f9c6bf",
          "title": "ignora una vigencia ya pasada y vuelve a la regla de check-ins",
          "full_title": "resolveQuickAdjustmentWindow > ignora una vigencia ya pasada y vuelve a la regla de check-ins",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f17852772cf5c1b265ba",
          "title": "ignora una fecha de inicio malformada en vez de dejar al paciente sin versión",
          "full_title": "resolveQuickAdjustmentWindow > ignora una fecha de inicio malformada en vez de dejar al paciente sin versión",
          "line": 90,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ad1cae0926e1b72800d1",
          "title": "el gate intersecta tiempos afectados y registrados, no el día completo",
          "full_title": "gate por tiempo de comida > el gate intersecta tiempos afectados y registrados, no el día completo",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/recetas-slot.test.ts",
      "type": "unit",
      "sha256": "385ca39dff7e430ec1ed46af9d82d004becd5deb72dca0c8a148a20cb602162c",
      "tests": [
        {
          "id": "245676503fc84a7deaf9",
          "title": "envuelve un slot legacy single en array",
          "full_title": "normalizarRecetasSlot > envuelve un slot legacy single en array",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "817b5e08944b35e793d7",
          "title": "devuelve array vacío para slot ausente",
          "full_title": "normalizarRecetasSlot > devuelve array vacío para slot ausente",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1c3e077642459dcf1f46",
          "title": "conserva todas las opciones de un slot multi-receta",
          "full_title": "normalizarRecetasSlot > conserva todas las opciones de un slot multi-receta",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "26661205f7f84c5bb351",
          "title": "descarta huecos falsy que Firestore pueda haber dejado en el array",
          "full_title": "normalizarRecetasSlot > descarta huecos falsy que Firestore pueda haber dejado en el array",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "988e0dd7bd97c233d0fe",
          "title": "devuelve la primera opción del slot multi-receta",
          "full_title": "getPrimaryReceta > devuelve la primera opción del slot multi-receta",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6757b4ebe51f3adc19a3",
          "title": "devuelve undefined para slot vacío",
          "full_title": "getPrimaryReceta > devuelve undefined para slot vacío",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "523829d2ac6be49c5446",
          "title": "salta huecos falsy en vez de reportar el slot como vacío",
          "full_title": "getPrimaryReceta > salta huecos falsy en vez de reportar el slot como vacío",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "88887ed65cd829fb077f",
          "title": "toggle agrega y quita conservando el resto de opciones",
          "full_title": "mutaciones de slot > toggle agrega y quita conservando el resto de opciones",
          "line": 60,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8b0ddd7dd4380f083fb6",
          "title": "set es idempotente al incluir una receta ya presente",
          "full_title": "mutaciones de slot > set es idempotente al incluir una receta ya presente",
          "line": 66,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b44d5a69437a1f4e2786",
          "title": "upsert reemplaza en su posición sin reordenar las opciones",
          "full_title": "mutaciones de slot > upsert reemplaza en su posición sin reordenar las opciones",
          "line": 72,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/registered-meal-snapshot.test.ts",
      "type": "unit",
      "sha256": "8c83e6235f229b92ca8f1c306abeebbe2c49ebbdd22cc9967954d087bd4314c6",
      "tests": [
        {
          "id": "91f25c674ebaf4a44792",
          "title": "devuelve todas las recetas modernas marcadas y su nutrición explícita",
          "full_title": "resolveRegisteredMealSnapshots > devuelve todas las recetas modernas marcadas y su nutrición explícita",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cdf1dac51d95514c6da3",
          "title": "conserva un registro legacy marcado $name",
          "full_title": "resolveRegisteredMealSnapshots > conserva un registro legacy marcado $name",
          "line": 43,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "0f9fd25d8f6ae8528c4d",
          "title": "prefiere moderno sobre legacy sin duplicar el consumo",
          "full_title": "resolveRegisteredMealSnapshots > prefiere moderno sobre legacy sin duplicar el consumo",
          "line": 67,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "29f5abf5e5522ce3df08",
          "title": "ignora registros desmarcados",
          "full_title": "resolveRegisteredMealSnapshots > ignora registros desmarcados",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/registered-meal-view.test.ts",
      "type": "unit",
      "sha256": "2f2de6114484019fc49c9704fcf27c450f4450ae07bed023d877a4717cbdd17c",
      "tests": [
        {
          "id": "4faeb3d7a4294e25d84d",
          "title": "muestra lo que el paciente registró cuando el plan ya cambió",
          "full_title": "resolveRegisteredMealView > muestra lo que el paciente registró cuando el plan ya cambió",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "19f35f4c9cbf1fdbc8f7",
          "title": "muestra el plan nuevo cuando esa comida no tiene registro",
          "full_title": "resolveRegisteredMealView > muestra el plan nuevo cuando esa comida no tiene registro",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d26df40a144aa299b30e",
          "title": "no marca como histórico cuando el registro corresponde a la receta vigente",
          "full_title": "resolveRegisteredMealView > no marca como histórico cuando el registro corresponde a la receta vigente",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "afd5ad24321f85166e7a",
          "title": "cae al plan vigente si el registro no conserva snapshot",
          "full_title": "resolveRegisteredMealView > cae al plan vigente si el registro no conserva snapshot",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4112d706085a0e857dbd",
          "title": "conserva el check legacy sin inventar una receta histórica cuando sólo hay nutrición",
          "full_title": "resolveRegisteredMealView > conserva el check legacy sin inventar una receta histórica cuando sólo hay nutrición",
          "line": 84,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e26c2329cfa633eb4203",
          "title": "conserva el check legacy aunque el caché antiguo no tenga snapshots",
          "full_title": "resolveRegisteredMealView > conserva el check legacy aunque el caché antiguo no tenga snapshots",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "76e5c55b3cf4f27ef573",
          "title": "ignora registros desmarcados: son comidas que el paciente deshizo",
          "full_title": "resolveRegisteredMealView > ignora registros desmarcados: son comidas que el paciente deshizo",
          "line": 111,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eaba467adf05aa0708a0",
          "title": "devuelve la clave del registro histórico, no la de la receta vigente",
          "full_title": "resolveRegisteredMealView > devuelve la clave del registro histórico, no la de la receta vigente",
          "line": 134,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0d6e7b134d1c387853d1",
          "title": "usa la clave vigente cuando no hay histórico que preservar",
          "full_title": "resolveRegisteredMealView > usa la clave vigente cuando no hay histórico que preservar",
          "line": 146,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/requested-effective-from.test.ts",
      "type": "unit",
      "sha256": "50c0bbaefb4cf0c3252f8aa1067af4bc722a02d6b209316953e9f7b045a8664e",
      "tests": [
        {
          "id": "900ae9b829936844ffe0",
          "title": "sin fecha devuelve exactamente lo que decide el automatismo",
          "full_title": "fecha de vigencia elegida por el especialista > sin fecha devuelve exactamente lo que decide el automatismo",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "11f16552021a5546e68a",
          "title": "pedir hoy no reabre las horas ya transcurridas: conserva la ventana del suelo",
          "full_title": "fecha de vigencia elegida por el especialista > pedir hoy no reabre las horas ya transcurridas: conserva la ventana del suelo",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8a59ced6a7bad5482547",
          "title": "una fecha posterior arranca en la medianoche de negocio de ese día",
          "full_title": "fecha de vigencia elegida por el especialista > una fecha posterior arranca en la medianoche de negocio de ese día",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c4682db77ddeb6007e02",
          "title": "rechaza los días ya transcurridos",
          "full_title": "fecha de vigencia elegida por el especialista > rechaza los días ya transcurridos",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b4fb076349bcb06ba7e4",
          "title": "no deja saltarse el suelo cuando el automatismo ya difirió a mañana",
          "full_title": "fecha de vigencia elegida por el especialista > no deja saltarse el suelo cuando el automatismo ya difirió a mañana",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "541538e7bfe2468ff30a",
          "title": "no deja programar más allá del fin de vigencia",
          "full_title": "fecha de vigencia elegida por el especialista > no deja programar más allá del fin de vigencia",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fab62cacb9588871a4d6",
          "title": "un plan sin fin de vigencia no impone techo",
          "full_title": "fecha de vigencia elegida por el especialista > un plan sin fin de vigencia no impone techo",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c556c8d62a0e960fe6fa",
          "title": "rechaza lo que no es una fecha civil",
          "full_title": "fecha de vigencia elegida por el especialista > rechaza lo que no es una fecha civil",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/routine-exercise-filters.test.ts",
      "type": "unit",
      "sha256": "264f27826acf70b1affcc0c3aa3a33a93459c0a861ebe8743c16b24a7380f100",
      "tests": [
        {
          "id": "3d1b646199e9e3890504",
          "title": "acepta varias opciones de un grupo y combina los grupos entre sí",
          "full_title": "filtros de ejercicios de rutina > acepta varias opciones de un grupo y combina los grupos entre sí",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4f6e35788073bc21ca1d",
          "title": "ofrece opciones presentes en ejercicios que cumplen los demás filtros y conserva las seleccionadas",
          "full_title": "filtros de ejercicios de rutina > ofrece opciones presentes en ejercicios que cumplen los demás filtros y conserva las seleccionadas",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e2612557c59b35d6d9c7",
          "title": "recupera una selección anterior de una opción y el nuevo formato múltiple",
          "full_title": "filtros de ejercicios de rutina > recupera una selección anterior de una opción y el nuevo formato múltiple",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/routine-template-preview.test.ts",
      "type": "unit",
      "sha256": "1ec6ebc0d800394c6b91b072444d1d4997d7c21ed41d393bf19840b596b8eb12",
      "tests": [
        {
          "id": "b4683e6805a6cf6eceda",
          "title": "preselecciona una plantilla por id aunque use el formato legacy",
          "full_title": "routine template preview > preselecciona una plantilla por id aunque use el formato legacy",
          "line": 27,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1d8bbd778073339f5480",
          "title": "construye fallback por días cuando una plantilla legacy no tiene media ni ejercicios",
          "full_title": "routine template preview > construye fallback por días cuando una plantilla legacy no tiene media ni ejercicios",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/routine-template-recommendation.test.ts",
      "type": "unit",
      "sha256": "bd9d34b67ead3283b8c3d81307384ffa621c2a454d6355f55a9f5b109d3acd77",
      "tests": [
        {
          "id": "2e5f3a193ecd20ccf6fb",
          "title": "explica una recomendación por objetivo, nivel y contexto del paciente",
          "full_title": "getRoutineTemplateRecommendation > explica una recomendación por objetivo, nivel y contexto del paciente",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a508d25a55df24006689",
          "title": "no inventa razones cuando la plantilla no coincide",
          "full_title": "getRoutineTemplateRecommendation > no inventa razones cuando la plantilla no coincide",
          "line": 38,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/rutina-block-template.test.ts",
      "type": "unit",
      "sha256": "bd630beefba68961355caf73307d5ea9a9591464407c67fede2066b595da43b5",
      "tests": [
        {
          "id": "2a778307253b057a8bb6",
          "title": "normalizes exercises to dia-1 without segmentoId",
          "full_title": "rutina block templates > normalizes exercises to dia-1 without segmentoId",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c2559a427aca673963c0",
          "title": "applies a block template into an empty day with new segment ids",
          "full_title": "rutina block templates > applies a block template into an empty day with new segment ids",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "84c0a873700d14dd168f",
          "title": "composes day templates into legacy dia-k slots for assignment",
          "full_title": "rutina block templates > composes day templates into legacy dia-k slots for assignment",
          "line": 55,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e96581f73556ab5a351c",
          "title": "composes block templates into a multi-day legacy routine",
          "full_title": "rutina block templates > composes block templates into a multi-day legacy routine",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/rutina-categorias-segmento.test.ts",
      "type": "unit",
      "sha256": "d3fbd55b8a2589f90608aae618e75c297810f6d98557c20ce252d87326962d5c",
      "tests": [
        {
          "id": "f94314ff3516f77562d9",
          "title": "representa los tipos y limpia al deseleccionar o elegir Libre",
          "full_title": "categorías visibles al seleccionar un bloque > representa los tipos y limpia al deseleccionar o elegir Libre",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "35fc0a67bb504a30de8c",
          "title": "no excluye ejercicios legacy sin categoría admitidos en Ejercicios",
          "full_title": "categorías visibles al seleccionar un bloque > no excluye ejercicios legacy sin categoría admitidos en Ejercicios",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6724843f441c9c52107c",
          "title": "refleja las categorías admitidas por etiquetas sin dejar ejercicios fuera",
          "full_title": "categorías visibles al seleccionar un bloque > refleja las categorías admitidas por etiquetas sin dejar ejercicios fuera",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "45016ef2769ee7b4223b",
          "title": "no añade categorías incompatibles del catálogo",
          "full_title": "categorías visibles al seleccionar un bloque > no añade categorías incompatibles del catálogo",
          "line": 19,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/rutina-day-model.test.ts",
      "type": "unit",
      "sha256": "6678977209647ac02881eeab48693d84f2377933eb59e0872ea8c12b8883528b",
      "tests": [
        {
          "id": "7ac0a4ed51f01e2df850",
          "title": "expande PPL 3 días × 2 weekdays a 6 slots legacy",
          "full_title": "rutina-day-model expand/compress > expande PPL 3 días × 2 weekdays a 6 slots legacy",
          "line": 90,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6643e86d1268a23d4ea7",
          "title": "round-trip: expand → compress recupera 3 rutinas-día y el calendario",
          "full_title": "rutina-day-model expand/compress > round-trip: expand → compress recupera 3 rutinas-día y el calendario",
          "line": 132,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "298566e26580c9f0d9af",
          "title": "hydrateWizardDayModel sincroniza diasRutina y slots legacy",
          "full_title": "rutina-day-model expand/compress > hydrateWizardDayModel sincroniza diasRutina y slots legacy",
          "line": 175,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e6492a7f799767a52621",
          "title": "conserva los comentarios al sincronizar y distingue días legacy con indicaciones diferentes",
          "full_title": "rutina-day-model expand/compress > conserva los comentarios al sincronizar y distingue días legacy con indicaciones diferentes",
          "line": 189,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "39156a43af3f9ad07ec7",
          "title": "syncWizardDayModel mantiene el mismo id de día en ambas weekdays",
          "full_title": "rutina-day-model expand/compress > syncWizardDayModel mantiene el mismo id de día en ambas weekdays",
          "line": 210,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/rutina-day-operations.test.ts",
      "type": "unit",
      "sha256": "5d6d42bf021385b4db568d723f53955130cf7b368e46efcc8917326d4f2abafe",
      "tests": [
        {
          "id": "32115fea401ff5a7e547",
          "title": "mueve bloques vacíos junto con el día y su calendario",
          "full_title": "mueve bloques vacíos junto con el día y su calendario",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dc37d488e2a82efe61be",
          "title": "conserva duración e indicaciones al guardar el día como plantilla",
          "full_title": "conserva duración e indicaciones al guardar el día como plantilla",
          "line": 11,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/rutina-duration-estimate.test.ts",
      "type": "unit",
      "sha256": "0b70d136a3f6208e39d33092106a8980eb74224c058a2cf60aac68aff42dad37",
      "tests": [
        {
          "id": "0d55ee41a30dcdeccbb9",
          "title": "suma trabajo y descansos entre series sólo del día activo",
          "full_title": "suma trabajo y descansos entre series sólo del día activo",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "24e1270d4547a0f35e29",
          "title": "usa la duración completa del bloque y conserva cardio vacío sin duplicar ejercicios",
          "full_title": "usa la duración completa del bloque y conserva cardio vacío sin duplicar ejercicios",
          "line": 9,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6e24e192a37b42f4cb20",
          "title": "indica estimación incompleta para un bloque sin ejercicios ni duración",
          "full_title": "indica estimación incompleta para un bloque sin ejercicios ni duración",
          "line": 13,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8c479df2f87477051e2e",
          "title": "dos series de 30 s con un descanso de 60 s suman 120 s",
          "full_title": "dos series de 30 s con un descanso de 60 s suman 120 s",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c7be1aa2ebbb2b8429b9",
          "title": "usa punto medio de rangos y tempo interpretable, sin presentar AMRAP como exacto",
          "full_title": "usa punto medio de rangos y tempo interpretable, sin presentar AMRAP como exacto",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "73ef56662addc31cc7c9",
          "title": "cuenta un descanso de grupo entre rondas y no uno por miembro",
          "full_title": "cuenta un descanso de grupo entre rondas y no uno por miembro",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b07d7a2d2d857386fef2",
          "title": "distancia sin duración permanece por estimar en lugar de inventar 600 segundos",
          "full_title": "distancia sin duración permanece por estimar en lugar de inventar 600 segundos",
          "line": 33,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/rutina-primary-distribution.test.ts",
      "type": "unit",
      "sha256": "b8a3e07d23844835cf93d2b6a9005b80a4afee30a6d4cfd3e0c506802db13506",
      "tests": [
        {
          "id": "1a896e89cd8fde45a7f2",
          "title": "atribuye las series al principal e ignora músculos secundarios y otros grupos",
          "full_title": "distribución por grupos principales > atribuye las series al principal e ignora músculos secundarios y otros grupos",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a0cb562c3a1a4cd4528e",
          "title": "recupera el principal del catálogo y acumula Brazos sin desglosar músculos finos",
          "full_title": "distribución por grupos principales > recupera el principal del catálogo y acumula Brazos sin desglosar músculos finos",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1ebf143628c281798004",
          "title": "conserva orden corporal y representa legacy sin grupo sin inferir secundarios",
          "full_title": "distribución por grupos principales > conserva orden corporal y representa legacy sin grupo sin inferir secundarios",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/rutina-reemplazo-ejercicio.test.ts",
      "type": "unit",
      "sha256": "24fbe05f09e0b094868979211454b1b952b75f3fc727c620f9b5f8684b4edaf2",
      "tests": [
        {
          "id": "52494a485c9f6fd48454",
          "title": "reemplaza sólo la ocurrencia elegida y conserva prescripción, bloque y agrupación",
          "full_title": "reemplaza sólo la ocurrencia elegida y conserva prescripción, bloque y agrupación",
          "line": 7,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fd5836e758c82ab43984",
          "title": "incompatibilidad reinicia valores y no arrastra peso, reps ni media anterior",
          "full_title": "incompatibilidad reinicia valores y no arrastra peso, reps ni media anterior",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1a865b893942976e7efd",
          "title": "rechaza candidatos archivados, bloques incompatibles y ocurrencias ausentes",
          "full_title": "rechaza candidatos archivados, bloques incompatibles y ocurrencias ausentes",
          "line": 24,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/rutina-return-context.test.ts",
      "type": "unit",
      "sha256": "5099d71399837d76f1b04ec7503543f600be078f0186e5796fe5caee4a9d4f95",
      "tests": [
        {
          "id": "288ea2c2ae03389995b8",
          "title": "rechaza retorno fuera de editores: %s",
          "full_title": "rechaza retorno fuera de editores: %s",
          "line": 6,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "49182ab4af93ca2bd14c",
          "title": "sin borrador del mismo usuario/pestaña no ofrece retorno ni usa ruta arbitraria",
          "full_title": "sin borrador del mismo usuario/pestaña no ofrece retorno ni usa ruta arbitraria",
          "line": 9,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/rutina-segmentos.test.ts",
      "type": "unit",
      "sha256": "d9695f13e0cbf372253ea85d158d61532f5782f4f91a857c110827792ede4e19",
      "tests": [
        {
          "id": "da8df4c12da658090a01",
          "title": "presenta una rutina antigua como libre sin escribir ni cambiar sus ejercicios",
          "full_title": "segmentos de un día de entrenamiento > presenta una rutina antigua como libre sin escribir ni cambiar sus ejercicios",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3994e0cb16a55f769ffe",
          "title": "muestra warm-up y cardio con instrucciones propias y ocurrencias independientes",
          "full_title": "muestra warm-up y cardio con instrucciones propias y ocurrencias independientes",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bcecd61d2aafe2e89c32",
          "title": "añade warm-up sin mover ni cambiar la prescripción previa",
          "full_title": "añade warm-up sin mover ni cambiar la prescripción previa",
          "line": 38,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bfdbac9c2f146193eb6d",
          "title": "mueve cardio antes del principal conservando cada ocurrencia y los otros días",
          "full_title": "mueve cardio antes del principal conservando cada ocurrencia y los otros días",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6a06b489ccd84b2e9c8c",
          "title": "reordena los bloques guardados y completa instrucciones para poder persistirlos",
          "full_title": "reordena los bloques guardados y completa instrucciones para poder persistirlos",
          "line": 65,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5a3c3d7c1a12ad566871",
          "title": "conserva duración al añadir otro bloque y admite bloques libres",
          "full_title": "conserva duración al añadir otro bloque y admite bloques libres",
          "line": 83,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e23ef419cfd3d2020dc6",
          "title": "restringe cardio y warm-up por metadatos, no por el nombre del ejercicio",
          "full_title": "restringe cardio y warm-up por metadatos, no por el nombre del ejercicio",
          "line": 90,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ab5a321ad04d78712083",
          "title": "valida bloques vacíos y permite categorías distintas sin relajar estructura",
          "full_title": "valida bloques vacíos y permite categorías distintas sin relajar estructura",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ccc61fcef32748528d32",
          "title": "retira sólo el contenido del bloque elegido",
          "full_title": "retira sólo el contenido del bloque elegido",
          "line": 106,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d628ce2fc10067fc23ff",
          "title": "copia un día con bloques sin compartir sus identificadores ni prescripción editable",
          "full_title": "copia un día con bloques sin compartir sus identificadores ni prescripción editable",
          "line": 114,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7d567cfb52e11ea9f32e",
          "title": "materializa ejercicios antiguos como libres y no inventa un bloque en un día vacío",
          "full_title": "materializa ejercicios antiguos como libres y no inventa un bloque en un día vacío",
          "line": 126,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "45eb4c396f961d657f09",
          "title": "inserta Enfriamiento al final y mantiene las reglas de los demás bloques",
          "full_title": "inserta Enfriamiento al final y mantiene las reglas de los demás bloques",
          "line": 133,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e19d72a600e301160c3b",
          "title": "añade Ejercicios antes del primer Enfriamiento sin mover los bloques existentes",
          "full_title": "añade Ejercicios antes del primer Enfriamiento sin mover los bloques existentes",
          "line": 142,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a52844d05babcd027ecf",
          "title": "un bloque legacy libre se puede editar y retirar conservando los otros bloques",
          "full_title": "un bloque legacy libre se puede editar y retirar conservando los otros bloques",
          "line": 152,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c5ecf5471d2d449e41c1",
          "title": "reconoce Principal generado en la edición anterior sin renombrar bloques creados por el coach",
          "full_title": "reconoce Principal generado en la edición anterior sin renombrar bloques creados por el coach",
          "line": 170,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3cd840dd7a2e3b5e306b",
          "title": "mueve sólo la ocurrencia elegida desde Libre legacy a un bloque vacío y conserva prescripción y otros días",
          "full_title": "mueve sólo la ocurrencia elegida desde Libre legacy a un bloque vacío y conserva prescripción y otros días",
          "line": 180,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/rutina-template-application.test.ts",
      "type": "unit",
      "sha256": "d7ffae303ad8b725ef97015048850272e1793acd7ccc3b9ea76278022ad210cf",
      "tests": [
        {
          "id": "e2280055d187341e55b1",
          "title": "detecta conflicto con un borrador que sólo contiene bloques libres",
          "full_title": "detecta conflicto con un borrador que sólo contiene bloques libres",
          "line": 7,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "19e876b1b4eea172236a",
          "title": "añade una plantilla sin colisiones y conserva bloques vacíos",
          "full_title": "añade una plantilla sin colisiones y conserva bloques vacíos",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6717369c06e231b47cba",
          "title": "reemplazar con una plantilla antigua no deja bloques del borrador anterior",
          "full_title": "reemplazar con una plantilla antigua no deja bloques del borrador anterior",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/rutina-vigencia.test.ts",
      "type": "unit",
      "sha256": "454eaaa98973b2e9c331004da65b7901e147341dc6496f8f66eb5f847dabffbf",
      "tests": [
        {
          "id": "820f113b2a620b2c6188",
          "title": "una rutina asignada hace cinco días prescribe cinco días, no la ventana entera",
          "full_title": "calcularSetsPrescritosEnVentana — el denominador respeta la asignación > una rutina asignada hace cinco días prescribe cinco días, no la ventana entera",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "54bbc1bd8b0adace4fe6",
          "title": "control positivo: una rutina vigente toda la ventana conserva el cálculo previo",
          "full_title": "calcularSetsPrescritosEnVentana — el denominador respeta la asignación > control positivo: una rutina vigente toda la ventana conserva el cálculo previo",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "041ac558832b77fdb53f",
          "title": "sin fechas conocidas se asume vigente toda la ventana (datos legacy)",
          "full_title": "calcularSetsPrescritosEnVentana — el denominador respeta la asignación > sin fechas conocidas se asume vigente toda la ventana (datos legacy)",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "201576bd765cfeb3c92c",
          "title": "sin fechaInicio cae a createdAt",
          "full_title": "calcularSetsPrescritosEnVentana — el denominador respeta la asignación > sin fechaInicio cae a createdAt",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c8efd9144cc9e2a1418d",
          "title": "sustitución a mitad de ventana: cada rutina prescribe su tramo",
          "full_title": "calcularSetsPrescritosEnVentana — el denominador respeta la asignación > sustitución a mitad de ventana: cada rutina prescribe su tramo",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aefcda83bfd70b1c3b24",
          "title": "tramos sin rutina no prescriben: hueco entre desasignación y nueva asignación",
          "full_title": "calcularSetsPrescritosEnVentana — el denominador respeta la asignación > tramos sin rutina no prescriben: hueco entre desasignación y nueva asignación",
          "line": 69,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "9e7085106fa3f764e755",
          "title": "una rutina asignada a futuro o desasignada antes de la ventana prescribe cero",
          "full_title": "calcularSetsPrescritosEnVentana — el denominador respeta la asignación > una rutina asignada a futuro o desasignada antes de la ventana prescribe cero",
          "line": 77,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/rutina-wizard-validation.test.ts",
      "type": "unit",
      "sha256": "3b9d35510671d2fe6f1faccf83dc27f1b1447ac2a1d8c47b7f9602fdbcf13f24",
      "tests": [
        {
          "id": "b5e3035d234e3900b30b",
          "title": "acepta el paso 1 solo con metadatos (calendario se define al construir)",
          "full_title": "validateRutinaStep > acepta el paso 1 solo con metadatos (calendario se define al construir)",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "678badc8e3c159ea0180",
          "title": "rechaza nombre corto en el paso 1",
          "full_title": "validateRutinaStep > rechaza nombre corto en el paso 1",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1e5e5668350933bfd52e",
          "title": "bloquea cierre si cualquier día entrenable está vacío",
          "full_title": "validateRutinaStep > bloquea cierre si cualquier día entrenable está vacío",
          "line": 40,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c4efcb48d3676c9d40b5",
          "title": "acepta cierre sólo con días explícitos y ejercicios válidos por día",
          "full_title": "validateRutinaStep > acepta cierre sólo con días explícitos y ejercicios válidos por día",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f4d2c3c4d0d483e4752f",
          "title": "valida rutinas-día asignadas en el modelo comprimido",
          "full_title": "validateRutinaStep > valida rutinas-día asignadas en el modelo comprimido",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "bb780d60bb68836a7cfd",
          "title": "permite un día con un bloque de cardio libre sin ejercicios ficticios",
          "full_title": "permite un día con un bloque de cardio libre sin ejercicios ficticios",
          "line": 74,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/staff-plan-adjustment-presentation.test.ts",
      "type": "unit",
      "sha256": "9d4eb449c852ac2e496a3d44d59d8363defec726acb6fd082cac07be6cad028d",
      "tests": [
        {
          "id": "7b3a301515bd19a3b946",
          "title": "traduce recetas y porciones sin revelar el JSON ni los ids internos",
          "full_title": "presentación visual de ajustes del equipo > traduce recetas y porciones sin revelar el JSON ni los ids internos",
          "line": 5,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1361d583c2013cccf484",
          "title": "resume cambios desconocidos sin serializar su estructura interna",
          "full_title": "presentación visual de ajustes del equipo > resume cambios desconocidos sin serializar su estructura interna",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/suplemento-image.test.ts",
      "type": "unit",
      "sha256": "2a23c96fa488edc8d5d9678763ac270e019252d156465a864f9b19ce1cbb8f43",
      "tests": [
        {
          "id": "fa621f156c22ddf5d9d7",
          "title": "rechaza el placeholder público de Odoo",
          "full_title": "isUsableSuplementoImage > rechaza el placeholder público de Odoo",
          "line": 6,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6ab68eff15eaa422fec2",
          "title": "acepta una foto hospedada",
          "full_title": "isUsableSuplementoImage > acepta una foto hospedada",
          "line": 10,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a15af5350397fca68e1e",
          "title": "rechaza vacío",
          "full_title": "isUsableSuplementoImage > rechaza vacío",
          "line": 14,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b377c0be5d36f3aac833",
          "title": "colapsa si la única URL es el placeholder de Odoo",
          "full_title": "getSuplementoImage > colapsa si la única URL es el placeholder de Odoo",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/supplementation-assignment.test.ts",
      "type": "unit",
      "sha256": "014b93c7785b9cc080359be71633a2e0b7babf11906dcfcf98cdeb8436efaac6",
      "tests": [
        {
          "id": "516165ba25e468184dd1",
          "title": "mapea etiquetas legacy al id del catálogo",
          "full_title": "normalizeSuplementoCategoria > mapea etiquetas legacy al id del catálogo",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "34b5385c674771e33bf0",
          "title": "acepta id y etiqueta para el mismo filtro",
          "full_title": "suplementoCategoriaMatches > acepta id y etiqueta para el mismo filtro",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "888fc641edf9e014a911",
          "title": "usa la versión vigente del plan activo",
          "full_title": "resolveAssignedSuplementos > usa la versión vigente del plan activo",
          "line": 49,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "53c34a86ca8ffe4b1295",
          "title": "cae a plan.suplementos si no hay versión",
          "full_title": "resolveAssignedSuplementos > cae a plan.suplementos si no hay versión",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/supplementation-kit.test.ts",
      "type": "unit",
      "sha256": "38852228743693612afa9fdec129deed1c48275a3c76bff1af1e86ded50f95c5",
      "tests": [
        {
          "id": "ef4e0f39f378ba5247da",
          "title": "copia dosis del kit sobre el producto del catálogo",
          "full_title": "kitToAssignedSuplementos > copia dosis del kit sobre el producto del catálogo",
          "line": 17,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3072a8fcec545bd68049",
          "title": "arma un suplemento mínimo si el producto ya no está en el catálogo",
          "full_title": "kitToAssignedSuplementos > arma un suplemento mínimo si el producto ya no está en el catálogo",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/supplementation-stack.test.ts",
      "type": "unit",
      "sha256": "e60f2465291deb6bfa28f7e463621b14c9da4eeea2191232627bbbb0a1e9a307",
      "tests": [
        {
          "id": "f462313061d264abe4f8",
          "title": "agrega varios productos distintos al stack",
          "full_title": "addSupplementToStack > agrega varios productos distintos al stack",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3be8d6a9f1092cf43ec7",
          "title": "no duplica un producto que ya está en la dieta",
          "full_title": "addSupplementToStack > no duplica un producto que ya está en la dieta",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "27cd032614b18e95a0ed",
          "title": "actualiza dosis del producto ya asignado sin quitar los demás",
          "full_title": "patchAssignedSupplement > actualiza dosis del producto ya asignado sin quitar los demás",
          "line": 42,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/tiempo-comida-labels.test.ts",
      "type": "unit",
      "sha256": "b91c14fe677c278e2532b720536b18fef2e598d0f02e5a0f3c2d6cc859b33ba5",
      "tests": [
        {
          "id": "608c6b13b7d3572a32c8",
          "title": "sanitiza %j → %j",
          "full_title": "sanitizeTiempoComidaLabel > sanitiza %j → %j",
          "line": 18,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "519867e7d0d400c9e8a2",
          "title": "usa el fallback cuando el valor está vacío o ausente",
          "full_title": "sanitizeTiempoComidaLabel > usa el fallback cuando el valor está vacío o ausente",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/utils/tiempo-comida-selector.test.ts",
      "type": "unit",
      "sha256": "ada11d9e9ec563316879c61cca3f2cb5b7db8d8bae393756153f3e21201314b1",
      "tests": [
        {
          "id": "da5c04460aac8346c7da",
          "title": "una receta sin tiempo asignado se conserva en cualquier slot",
          "full_title": "pasaFiltroTiempoDelSelector — GJ-02 > una receta sin tiempo asignado se conserva en cualquier slot",
          "line": 16,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "18c87539a700a7e3037a",
          "title": "control positivo: el filtro sigue excluyendo tiempos que no coinciden",
          "full_title": "pasaFiltroTiempoDelSelector — GJ-02 > control positivo: el filtro sigue excluyendo tiempos que no coinciden",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3c8b070ed5b16bd1b544",
          "title": "las equivalencias existentes no se rompen",
          "full_title": "pasaFiltroTiempoDelSelector — GJ-02 > las equivalencias existentes no se rompen",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f4cce86af826adf61238",
          "title": "el filtro de tiempo del modal usa el predicado seguro, no hasTiempo directo",
          "full_title": "contrato del selector de recetas (fuente) > el filtro de tiempo del modal usa el predicado seguro, no hasTiempo directo",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7de9494f8015a6141d3a",
          "title": "el bloqueo clínico corre antes que el filtro de tiempo: el rescate de recetas sin tiempo jamás relaja alergias",
          "full_title": "contrato del selector de recetas (fuente) > el bloqueo clínico corre antes que el filtro de tiempo: el rescate de recetas sin tiempo jamás relaja alergias",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "af03cf0aa3e5c00ab62e",
          "title": "el vacío por tiempo ofrece el escape 'Ver todos los tiempos' en vez de un callejón",
          "full_title": "contrato del selector de recetas (fuente) > el vacío por tiempo ofrece el escape 'Ver todos los tiempos' en vez de un callejón",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/validations/cita/cita.validator.test.ts",
      "type": "unit",
      "sha256": "93a64c77aaca05534859212ae0b1ec0b6216f5c30913096975991120301c77b0",
      "tests": [
        {
          "id": "b48999e0584d4728441f",
          "title": "una cita completa y futura es válida",
          "full_title": "validarFormularioCita > una cita completa y futura es válida",
          "line": 37,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4adcab685585e683e723",
          "title": "acumula errores de requeridos sin short-circuit",
          "full_title": "validarFormularioCita > acumula errores de requeridos sin short-circuit",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ccb29a061a9116d8f111",
          "title": "el especialista solo es requerido cuando requiresNutriologo es true",
          "full_title": "validarFormularioCita > el especialista solo es requerido cuando requiresNutriologo es true",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5d45e2c727e352832dba",
          "title": "rechaza fechas pasadas al crear pero las permite al editar",
          "full_title": "validarFormularioCita > rechaza fechas pasadas al crear pero las permite al editar",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7e5eca75955b6f1a4a12",
          "title": "marca conflicto de horario en el campo hora",
          "full_title": "validarFormularioCita > marca conflicto de horario en el campo hora",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6755f31ee77f5cc85849",
          "title": "rechaza duración fuera del rango 15-120 (Zod)",
          "full_title": "validarFormularioCita > rechaza duración fuera del rango 15-120 (Zod)",
          "line": 75,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "659677302c557c38f1e0",
          "title": "rechaza notas con caracteres de inyección SQL o texto repetido",
          "full_title": "validarFormularioCita > rechaza notas con caracteres de inyección SQL o texto repetido",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e4fe9b15c3c8e3e8668c",
          "title": "zoom/teams/meet manual requieren URL",
          "full_title": "validarFormularioCita > modalidad online con provider > zoom/teams/meet manual requieren URL",
          "line": 93,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b6610fa0058ce4c19570",
          "title": "acepta una URL parseable (normaliza sin protocolo)",
          "full_title": "validarFormularioCita > modalidad online con provider > acepta una URL parseable (normaliza sin protocolo)",
          "line": 100,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "647e47bc4d669dae1d41",
          "title": "whatsapp no requiere URL pero si viene debe ser válida",
          "full_title": "validarFormularioCita > modalidad online con provider > whatsapp no requiere URL pero si viene debe ser válida",
          "line": 107,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8f6400169ef7ceb209bb",
          "title": "google_meet_auto exige especialista asignado",
          "full_title": "validarFormularioCita > modalidad online con provider > google_meet_auto exige especialista asignado",
          "line": 119,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "1dea40a3bc51e7387b7e",
          "title": "en modalidad presencial el provider se ignora",
          "full_title": "validarFormularioCita > modalidad online con provider > en modalidad presencial el provider se ignora",
          "line": 131,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "044e58e4ae461049a0ac",
          "title": "rechaza meetingNotes con caracteres de inyección",
          "full_title": "validarFormularioCita > rechaza meetingNotes con caracteres de inyección",
          "line": 139,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/validations/cuestionario-personalizado/cuestionario-personalizado.validator.test.ts",
      "type": "unit",
      "sha256": "c8982536f016eee61aedcd9b04129b3aadebb6b1ba80697b28d6ac0752585a5e",
      "tests": [
        {
          "id": "3341bda20897e992d25a",
          "title": "exige seleccionar plantilla antes de validar respuestas",
          "full_title": "validarRespuestasCuestionario > exige seleccionar plantilla antes de validar respuestas",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "79f99f91f04794269061",
          "title": "plantilla no cargada (null) invalida sin marcar errores por pregunta",
          "full_title": "validarRespuestasCuestionario > plantilla no cargada (null) invalida sin marcar errores por pregunta",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7abf500c5bf0b13c6b98",
          "title": "valida un cuestionario completo de todos los tipos de pregunta",
          "full_title": "validarRespuestasCuestionario > valida un cuestionario completo de todos los tipos de pregunta",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "37bdfb0d440b83a8c02b",
          "title": "marca las preguntas requeridas vacías",
          "full_title": "validarRespuestasCuestionario > marca las preguntas requeridas vacías",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8d7a24c0361504aee5a7",
          "title": "valida el rango de la escala con los límites de la pregunta",
          "full_title": "validarRespuestasCuestionario > valida el rango de la escala con los límites de la pregunta",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a25aab4536574ddb41ce",
          "title": "usa el rango default 1-10 cuando la pregunta no define límites",
          "full_title": "validarRespuestasCuestionario > usa el rango default 1-10 cuando la pregunta no define límites",
          "line": 92,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8d220482fd5a1ec1b4b1",
          "title": "opción múltiple solo acepta valores del listado",
          "full_title": "validarRespuestasCuestionario > opción múltiple solo acepta valores del listado",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c4bf6bd0c51b7945a896",
          "title": "si_no solo acepta los literales Si/No",
          "full_title": "validarRespuestasCuestionario > si_no solo acepta los literales Si/No",
          "line": 106,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "162f439c7dafd3dd862f",
          "title": "numero rechaza valores no numéricos",
          "full_title": "validarRespuestasCuestionario > numero rechaza valores no numéricos",
          "line": 114,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6714a8133ab681fce15b",
          "title": "texto libre rechaza inyección y texto basura",
          "full_title": "validarRespuestasCuestionario > texto libre rechaza inyección y texto basura",
          "line": 120,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/validations/cuestionario/cuestionario.validator.test.ts",
      "type": "unit",
      "sha256": "ce34d9d53d5141cbdf19f6b41cb667a7a487fb2b286266ee02bd047d808962c5",
      "tests": [
        {
          "id": "9db2d3c9efea09a82994",
          "title": "un check-in completo y en rango es válido",
          "full_title": "validarFormularioCuestionario > un check-in completo y en rango es válido",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5e7830b8122755e2675f",
          "title": "rechaza energía fuera de 1-10 (%s)",
          "full_title": "validarFormularioCuestionario > rechaza energía fuera de 1-10 (%s)",
          "line": 28,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "e77322626d5c59794fd5",
          "title": "rechaza adherencia fuera de 0-100 (%s)",
          "full_title": "validarFormularioCuestionario > rechaza adherencia fuera de 0-100 (%s)",
          "line": 33,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "b0ae1f06b5721b942a7c",
          "title": "rechaza horas de sueño fuera de 0-24 (%s)",
          "full_title": "validarFormularioCuestionario > rechaza horas de sueño fuera de 0-24 (%s)",
          "line": 38,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "338f2cb0dbb2537a7df6",
          "title": "acepta los valores frontera de los rangos",
          "full_title": "validarFormularioCuestionario > acepta los valores frontera de los rangos",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "548fa099ce01f1be3e52",
          "title": "rechaza digestión y mood fuera del enum",
          "full_title": "validarFormularioCuestionario > rechaza digestión y mood fuera del enum",
          "line": 56,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b5031eefa73c1b01601e",
          "title": "digestión y mood vacíos son requeridos",
          "full_title": "validarFormularioCuestionario > digestión y mood vacíos son requeridos",
          "line": 67,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c18eed1ce629f64c34da",
          "title": "las notas vacías no se validan, las notas normales pasan",
          "full_title": "validarFormularioCuestionario > las notas vacías no se validan, las notas normales pasan",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "61f3f4ea24af6b1d1822",
          "title": "rechaza notas con inyección SQL y notas basura",
          "full_title": "validarFormularioCuestionario > rechaza notas con inyección SQL y notas basura",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2f2895cb5aabcb08a5fe",
          "title": "rechaza notas de más de 500 caracteres",
          "full_title": "validarFormularioCuestionario > rechaza notas de más de 500 caracteres",
          "line": 93,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "4509526bdcb78149ecce",
          "title": "acumula todos los errores en una sola pasada",
          "full_title": "validarFormularioCuestionario > acumula todos los errores en una sola pasada",
          "line": 98,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/validations/ingrediente/ingrediente.validator.test.ts",
      "type": "unit",
      "sha256": "b18982304838b8441b03c0c70ec5686681bfae68ba6bbd9d472d9b99237bd0a0",
      "tests": [
        {
          "id": "7e3d7341f271be8fec04",
          "title": "un ingrediente completo con micros vacíos es válido",
          "full_title": "validarFormularioIngrediente > un ingrediente completo con micros vacíos es válido",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eefb2f76bdd51957c88a",
          "title": "acumula todos los campos requeridos vacíos",
          "full_title": "validarFormularioIngrediente > acumula todos los campos requeridos vacíos",
          "line": 54,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7428b48976cc2babc44c",
          "title": "rechaza cantidad no positiva o fuera de rango",
          "full_title": "validarFormularioIngrediente > rechaza cantidad no positiva o fuera de rango",
          "line": 78,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "851d12828d6b61b33037",
          "title": "rechaza macros negativos o desorbitados",
          "full_title": "validarFormularioIngrediente > rechaza macros negativos o desorbitados",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cea0ed8864a22ae9daac",
          "title": "rechaza grupo y unidad fuera de catálogo",
          "full_title": "validarFormularioIngrediente > rechaza grupo y unidad fuera de catálogo",
          "line": 99,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d76dc58da5fbea2d5ca0",
          "title": "acepta el grupo 'AOA muy bajo' (bigramas VV válidos, según comentario del fuente)",
          "full_title": "validarFormularioIngrediente > acepta el grupo 'AOA muy bajo' (bigramas VV válidos, según comentario del fuente)",
          "line": 108,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d3fc4f622c329a8c411d",
          "title": "valida rangos de micronutrientes opcionales cuando vienen",
          "full_title": "validarFormularioIngrediente > valida rangos de micronutrientes opcionales cuando vienen",
          "line": 115,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "94efa4a69102de69fa11",
          "title": "un micro opcional no numérico se ignora en vez de fallar",
          "full_title": "validarFormularioIngrediente > un micro opcional no numérico se ignora en vez de fallar",
          "line": 122,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5b6774bf37e23145a1e6",
          "title": "rechaza nombre demasiado corto (Zod min 2)",
          "full_title": "validarFormularioIngrediente > rechaza nombre demasiado corto (Zod min 2)",
          "line": 127,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e1968070acfe537954ae",
          "title": "rechaza nombre basura de teclado",
          "full_title": "validarFormularioIngrediente > rechaza nombre basura de teclado",
          "line": 132,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a4ba0b7a018ab5eb2e63",
          "title": "rechaza nombre con caracteres de inyección",
          "full_title": "validarFormularioIngrediente > rechaza nombre con caracteres de inyección",
          "line": 137,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/validations/paciente/paciente.business.test.ts",
      "type": "unit",
      "sha256": "284f90e28a1c90bb377e0c099e6a811d7009b88ccaa7603be41e8d0182f81b58",
      "tests": [
        {
          "id": "42896cb42c676f01c4e0",
          "title": "detecta duplicado exacto y retorna los datos del paciente",
          "full_title": "checkEmailDuplicadoCliente > detecta duplicado exacto y retorna los datos del paciente",
          "line": 15,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "600c58d2688d974f4fe0",
          "title": "compara sin distinguir mayúsculas ni espacios extremos",
          "full_title": "checkEmailDuplicadoCliente > compara sin distinguir mayúsculas ni espacios extremos",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cdb342e6a508839376c5",
          "title": "excludeId permite editar al propio paciente sin marcar duplicado",
          "full_title": "checkEmailDuplicadoCliente > excludeId permite editar al propio paciente sin marcar duplicado",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "44ed65eeee6081a0453a",
          "title": "email no registrado no es duplicado",
          "full_title": "checkEmailDuplicadoCliente > email no registrado no es duplicado",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0ae1d4caea6d4be3e58d",
          "title": "email vacío o solo espacios nunca es duplicado",
          "full_title": "checkEmailDuplicadoCliente > email vacío o solo espacios nunca es duplicado",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cb17dbd833b2ab362916",
          "title": "ignora pacientes sin email registrado",
          "full_title": "checkEmailDuplicadoCliente > ignora pacientes sin email registrado",
          "line": 46,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/validations/paciente/paciente.validator.test.ts",
      "type": "unit",
      "sha256": "b2851056109520d7b5bd72a75f2b2e071b0249685940b3096a1050a9ff7a3321",
      "tests": [
        {
          "id": "1df68a175f7d0b1fcf8b",
          "title": "el drawer comercial interpreta peso y estatura en 0 como datos todavía no capturados",
          "full_title": "validarFormularioPaciente > el drawer comercial interpreta peso y estatura en 0 como datos todavía no capturados",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b570175aa8640f1612df",
          "title": "un draft completo y correcto es válido sin errores",
          "full_title": "validarFormularioPaciente > un draft completo y correcto es válido sin errores",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8811c4f1a6148df0c8d9",
          "title": "los campos opcionales vacíos no generan errores",
          "full_title": "validarFormularioPaciente > los campos opcionales vacíos no generan errores",
          "line": 41,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f93e0ec4d75e176cefb8",
          "title": "acumula TODOS los errores a la vez (sin short-circuit)",
          "full_title": "validarFormularioPaciente > acumula TODOS los errores a la vez (sin short-circuit)",
          "line": 57,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "537445c17dcc4be65847",
          "title": "rechaza nombre con inyección SQL",
          "full_title": "validarFormularioPaciente > rechaza nombre con inyección SQL",
          "line": 73,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f985c4f05010c19443d2",
          "title": "rechaza género fuera del enum",
          "full_title": "validarFormularioPaciente > rechaza género fuera del enum",
          "line": 79,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d4d9ecc746db46b7ee6c",
          "title": "rechaza objetivoId fuera del catálogo (error en la key `objetivo`)",
          "full_title": "validarFormularioPaciente > rechaza objetivoId fuera del catálogo (error en la key `objetivo`)",
          "line": 87,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e8f8e554ded57ee9ec46",
          "title": "rechaza país fuera de la lista pero acepta países reales",
          "full_title": "validarFormularioPaciente > rechaza país fuera de la lista pero acepta países reales",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d145892b2d695c72b7bc",
          "title": "rechaza fecha de nacimiento futura",
          "full_title": "validarFormularioPaciente > rechaza fecha de nacimiento futura",
          "line": 103,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b0b0bca7465096543345",
          "title": "rechaza tipoPaciente de más de 80 caracteres",
          "full_title": "validarFormularioPaciente > rechaza tipoPaciente de más de 80 caracteres",
          "line": 108,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/validations/plan-wizard-steps.test.ts",
      "type": "unit",
      "sha256": "38c784816fe35db4e881ceec32e23fc8306b9509416fd1d2db04311f933e397f",
      "tests": [
        {
          "id": "b49aa6ce21e9967f6dbe",
          "title": "acepta una estructura completa sin depender de flags constantes",
          "full_title": "validatePlanWizardSteps > acepta una estructura completa sin depender de flags constantes",
          "line": 53,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "902ad1e75bfc885a8e57",
          "title": "bloquea porciones vacías o negativas",
          "full_title": "validatePlanWizardSteps > bloquea porciones vacías o negativas",
          "line": 62,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "48b7eb240a305a25de0a",
          "title": "bloquea tiempos duplicados pero deja las kcal pendientes como advertencia",
          "full_title": "validatePlanWizardSteps > bloquea tiempos duplicados pero deja las kcal pendientes como advertencia",
          "line": 70,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ef7205262315ad90c5d0",
          "title": "mantiene recetas incompletas como advertencia confirmable",
          "full_title": "validatePlanWizardSteps > mantiene recetas incompletas como advertencia confirmable",
          "line": 85,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "dd575956e386137eff60",
          "title": "bloquea un calendario incompleto o con un menuIndex inexistente",
          "full_title": "validatePlanWizardSteps > bloquea un calendario incompleto o con un menuIndex inexistente",
          "line": 91,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/validations/plantilla-cuestionario/plantilla-cuestionario.validator.test.ts",
      "type": "unit",
      "sha256": "aef5e619cc2184da6d6b9bb92f3e757d80d4d7cf26ade3d143431e967ad39fa5",
      "tests": [
        {
          "id": "ba2622f11e4a42f8f40a",
          "title": "una plantilla completa es válida",
          "full_title": "validarFormularioPlantilla > una plantilla completa es válida",
          "line": 33,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "52f11bae1e90673e4192",
          "title": "el nombre es obligatorio",
          "full_title": "validarFormularioPlantilla > el nombre es obligatorio",
          "line": 39,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "d07e7936d1b78142db06",
          "title": "rechaza nombre con inyección o basura",
          "full_title": "validarFormularioPlantilla > rechaza nombre con inyección o basura",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8eee2ff3422eb9d04188",
          "title": "rechaza nombre de más de 80 caracteres",
          "full_title": "validarFormularioPlantilla > rechaza nombre de más de 80 caracteres",
          "line": 53,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5778f2942a16d8a1d25a",
          "title": "la descripción es opcional pero se valida si tiene texto",
          "full_title": "validarFormularioPlantilla > la descripción es opcional pero se valida si tiene texto",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "eb7dbebe0ef09ef2d5d5",
          "title": "rechaza frecuencia fuera del enum",
          "full_title": "validarFormularioPlantilla > rechaza frecuencia fuera del enum",
          "line": 68,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e3298d1ef9340f7425bc",
          "title": "exige al menos una pregunta",
          "full_title": "validarFormularioPlantilla > exige al menos una pregunta",
          "line": 75,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2360e45d0cba20411519",
          "title": "cada pregunta necesita texto y lo valida contra basura",
          "full_title": "validarFormularioPlantilla > cada pregunta necesita texto y lo valida contra basura",
          "line": 80,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0408271b980eb22ecc1f",
          "title": "opción múltiple exige al menos 2 opciones",
          "full_title": "validarFormularioPlantilla > opción múltiple exige al menos 2 opciones",
          "line": 95,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/validations/portal-plan-mutation.test.ts",
      "type": "unit",
      "sha256": "629da3edc580d371deb8be0ea023455e9f87004c21c598ad5a9ddd4b7cde5248",
      "tests": [
        {
          "id": "d6cce9adc4867d2ddcf8",
          "title": "acepta únicamente acciones completas",
          "full_title": "payload de mutaciones autoritativas del portal > acepta únicamente acciones completas",
          "line": 12,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "c76ef9cfa94edae2394e",
          "title": "rechaza swaps sin identidad de la receta prescrita o candidata",
          "full_title": "payload de mutaciones autoritativas del portal > rechaza swaps sin identidad de la receta prescrita o candidata",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a73a64b7d129174f94ac",
          "title": "rechaza porciones negativas, infinitas o fuera del límite estructural",
          "full_title": "payload de mutaciones autoritativas del portal > rechaza porciones negativas, infinitas o fuera del límite estructural",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e2444c82b5fbf159041d",
          "title": "rechaza fechas arbitrarias y acciones desconocidas",
          "full_title": "payload de mutaciones autoritativas del portal > rechaza fechas arbitrarias y acciones desconocidas",
          "line": 47,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "6293544517d4b2ef9c2c",
          "title": "conserva la versión y la operación de un ajuste pendiente; rechaza IDs inválidos",
          "full_title": "payload de mutaciones autoritativas del portal > conserva la versión y la operación de un ajuste pendiente; rechaza IDs inválidos",
          "line": 52,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/validations/receta/receta.validator.test.ts",
      "type": "unit",
      "sha256": "4b0e2ac1aec37071cd2461f418960df4a9a53fbbd20fd017fb98672106fc70ef",
      "tests": [
        {
          "id": "6b2a085ca654a43df78b",
          "title": "receta completa y correcta es válida",
          "full_title": "validarFormularioReceta > receta completa y correcta es válida",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "04dcc132d883100fd07b",
          "title": "nombre vacío es requerido",
          "full_title": "validarFormularioReceta > nombre vacío es requerido",
          "line": 22,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f7de5e616ca66e2cd147",
          "title": "exige al menos un ingrediente",
          "full_title": "validarFormularioReceta > exige al menos un ingrediente",
          "line": 28,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fce6f17b3bd31f3247d5",
          "title": "acumula errores de varios campos a la vez",
          "full_title": "validarFormularioReceta > acumula errores de varios campos a la vez",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "efba7b14786f0e6d5da4",
          "title": "nombre demasiado corto falla por Zod",
          "full_title": "validarFormularioReceta > nombre demasiado corto falla por Zod",
          "line": 43,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "170d147043daed532f2a",
          "title": "nombre demasiado largo falla por Zod",
          "full_title": "validarFormularioReceta > nombre demasiado largo falla por Zod",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "633cb674dcd2bb767d2b",
          "title": "rechaza nombre con basura/inyección: %s",
          "full_title": "validarFormularioReceta > rechaza nombre con basura/inyección: %s",
          "line": 53,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "078aff8e877c24e336f5",
          "title": "la preparación es texto libre: acepta paréntesis, comillas y signos",
          "full_title": "validarFormularioReceta > la preparación es texto libre: acepta paréntesis, comillas y signos",
          "line": 61,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "09a79cc4c1af48873700",
          "title": "tiempoPreparacion inválido %s → %s",
          "full_title": "validarFormularioReceta > tiempoPreparacion inválido %s → %s",
          "line": 68,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "c258a7f53e6a1fb3ecd0",
          "title": "tiempoPreparacion undefined es opcional",
          "full_title": "validarFormularioReceta > tiempoPreparacion undefined es opcional",
          "line": 77,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "893344b23a95c355fb9c",
          "title": "acepta preparación como arreglo de pasos (formato nuevo)",
          "full_title": "validarFormularioReceta > acepta preparación como arreglo de pasos (formato nuevo)",
          "line": 81,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "883600247566bb85fa1e",
          "title": "rechaza más de 50 pasos de preparación",
          "full_title": "validarFormularioReceta > rechaza más de 50 pasos de preparación",
          "line": 88,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/validations/shared/field-validators.test.ts",
      "type": "unit",
      "sha256": "afda34b5cb15a17317ae2dc988c383d1fbfc516ba16672bbc8768aff17be563d",
      "tests": [
        {
          "id": "f060d34196309717553a",
          "title": "acepta nombre real en español: %s",
          "full_title": "validarNombre > acepta nombre real en español: %s",
          "line": 14,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "7526fbfd08196e9ffe6a",
          "title": "vacío no requerido es válido, vacío requerido falla",
          "full_title": "validarNombre > vacío no requerido es válido, vacío requerido falla",
          "line": 23,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "fcb2ec57026bddd66a50",
          "title": "rechaza nombre demasiado corto",
          "full_title": "validarNombre > rechaza nombre demasiado corto",
          "line": 31,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0ad75046afeefd06a3b6",
          "title": "rechaza nombre demasiado largo (> 100)",
          "full_title": "validarNombre > rechaza nombre demasiado largo (> 100)",
          "line": 35,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7b11127584069aab8902",
          "title": "rechaza basura/inyección: %s → %s",
          "full_title": "validarNombre > rechaza basura/inyección: %s → %s",
          "line": 42,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "8a2b83a951802196e4bd",
          "title": "rechaza dígitos sin permitirNumeros pero los acepta con permitirNumeros",
          "full_title": "validarNombre > rechaza dígitos sin permitirNumeros pero los acepta con permitirNumeros",
          "line": 51,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aa5af21b4cad3f8a15d4",
          "title": "hace trim antes de validar",
          "full_title": "validarNombre > hace trim antes de validar",
          "line": 59,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "72b81714807aa448f1f1",
          "title": "comportamiento actual: la abreviatura 'Ma.' es rechazada por la capa de bigramas",
          "full_title": "validarNombre > comportamiento actual: la abreviatura 'Ma.' es rechazada por la capa de bigramas",
          "line": 63,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "cf0250b8ea9840d4cd2d",
          "title": "acepta email válido: %s",
          "full_title": "validarEmail > acepta email válido: %s",
          "line": 77,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "ccd61305e3ebb0922578",
          "title": "rechaza email con formato inválido: %s",
          "full_title": "validarEmail > rechaza email con formato inválido: %s",
          "line": 84,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "9ebc5d7efe5470253182",
          "title": "rechaza emails de más de 254 caracteres",
          "full_title": "validarEmail > rechaza emails de más de 254 caracteres",
          "line": 91,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "67d14a6bb28d743fba04",
          "title": "vacío no requerido es válido, vacío requerido falla",
          "full_title": "validarEmail > vacío no requerido es válido, vacío requerido falla",
          "line": 96,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e8ed2bc081ec7912af6d",
          "title": "acepta teléfono MX en formato crudo: %s",
          "full_title": "validarTelefonoMX > acepta teléfono MX en formato crudo: %s",
          "line": 106,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "b75496b3f7af93cbb3b3",
          "title": "rechaza teléfono inválido: %s",
          "full_title": "validarTelefonoMX > rechaza teléfono inválido: %s",
          "line": 116,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "186f2c7ca33510b6cb00",
          "title": "vacío no requerido es válido, vacío requerido falla",
          "full_title": "validarTelefonoMX > vacío no requerido es válido, vacío requerido falla",
          "line": 126,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "b48ef39912898050333f",
          "title": "acepta texto libre legítimo en español",
          "full_title": "contieneTextoBasura > acepta texto libre legítimo en español",
          "line": 136,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "09524d00e865e0cdcc9e",
          "title": "no evalúa bigramas en tokens alfanuméricos (B12, Omega-3)",
          "full_title": "contieneTextoBasura > no evalúa bigramas en tokens alfanuméricos (B12, Omega-3)",
          "line": 140,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f8ed426ec750e6bdbbb2",
          "title": "reporta el primer fallo global en orden: SQL antes que repetido",
          "full_title": "contieneTextoBasura > reporta el primer fallo global en orden: SQL antes que repetido",
          "line": 144,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5e6472349e3b815ceaf6",
          "title": "rechaza palabras con fonotáctica ajena al español",
          "full_title": "contieneTextoBasura > rechaza palabras con fonotáctica ajena al español",
          "line": 152,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7d23b9097040b4ac1d78",
          "title": "remueve el primer caracter peligroso y hace trim",
          "full_title": "sanitizarTextoSeguro > remueve el primer caracter peligroso y hace trim",
          "line": 161,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a0dcb0811ad56732fa73",
          "title": "calcula la edad cuando el cumpleaños ya pasó este año",
          "full_title": "calcularEdad > calcula la edad cuando el cumpleaños ya pasó este año",
          "line": 179,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "2098364b48be9605378d",
          "title": "el día exacto del cumpleaños ya cuenta el año nuevo",
          "full_title": "calcularEdad > el día exacto del cumpleaños ya cuenta el año nuevo",
          "line": 183,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e2e018ce197c5f483023",
          "title": "resta un año si el cumpleaños aún no llega (mismo mes, día posterior)",
          "full_title": "calcularEdad > resta un año si el cumpleaños aún no llega (mismo mes, día posterior)",
          "line": 187,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7b44f8975a5c4377237a",
          "title": "resta un año si el cumpleaños es en un mes posterior",
          "full_title": "calcularEdad > resta un año si el cumpleaños es en un mes posterior",
          "line": 191,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "31b12316c1d073a09720",
          "title": "retorna null para %s (%s)",
          "full_title": "calcularEdad > retorna null para %s (%s)",
          "line": 195,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "f25896b985ad5f17c0fb",
          "title": "edad 0 para un recién nacido es válida",
          "full_title": "calcularEdad > edad 0 para un recién nacido es válida",
          "line": 206,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/validations/shared/regex-patterns.test.ts",
      "type": "unit",
      "sha256": "ffb3d2f2432df0960bf656c3843940b86164af49c4c0eff2d185a5ff75bfc2bb",
      "tests": [
        {
          "id": "c39a7e36c08defa82331",
          "title": "detecta payload malicioso o caracter bloqueado: %s",
          "full_title": "SQL_INJECTION_CHARS > detecta payload malicioso o caracter bloqueado: %s",
          "line": 16,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "7cca1928eb7b906218c1",
          "title": "NO marca texto legítimo en español: %s",
          "full_title": "SQL_INJECTION_CHARS > NO marca texto legítimo en español: %s",
          "line": 34,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "86a63927f46b639f5a1d",
          "title": "detecta 3+ repeticiones consecutivas: %s",
          "full_title": "TEXTO_REPETIDO > detecta 3+ repeticiones consecutivas: %s",
          "line": 47,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "0fff49b3182d04aabbab",
          "title": "NO marca repeticiones dobles legítimas: %s",
          "full_title": "TEXTO_REPETIDO > NO marca repeticiones dobles legítimas: %s",
          "line": 54,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "c19361d5bb8261a523a9",
          "title": "detecta mashing de fila de teclado: %s",
          "full_title": "KEYBOARD_MASHING > detecta mashing de fila de teclado: %s",
          "line": 63,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "90a89bd77c08c0aa4107",
          "title": "NO marca nombres con corridas legítimas: %s",
          "full_title": "KEYBOARD_MASHING > NO marca nombres con corridas legítimas: %s",
          "line": 70,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "e8801dec68125ce62125",
          "title": "acepta email válido: %s",
          "full_title": "EMAIL_RFC > acepta email válido: %s",
          "line": 79,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "f0687db8cee4fa915155",
          "title": "rechaza email inválido: %s",
          "full_title": "EMAIL_RFC > rechaza email inválido: %s",
          "line": 87,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "c8e75c29ff3a721b97ed",
          "title": "acepta nombre válido: %s",
          "full_title": "NOMBRE_VALIDO > acepta nombre válido: %s",
          "line": 101,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "ccc996a95d25f89eb312",
          "title": "rechaza formato inválido: %s",
          "full_title": "NOMBRE_VALIDO > rechaza formato inválido: %s",
          "line": 108,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "954cbd1d45e319f42d0c",
          "title": "acepta teléfono MX normalizado: %s",
          "full_title": "TELEFONO_MX > acepta teléfono MX normalizado: %s",
          "line": 117,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "969dca50e4e909838fa6",
          "title": "rechaza teléfono inválido: %s",
          "full_title": "TELEFONO_MX > rechaza teléfono inválido: %s",
          "line": 124,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "bcde782115f0918c75d1",
          "title": "SOLO_ENTERO_POSITIVO acepta enteros y rechaza decimales/negativos",
          "full_title": "patrones numéricos y de fecha > SOLO_ENTERO_POSITIVO acepta enteros y rechaza decimales/negativos",
          "line": 133,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3c7138b5af1b7e1a1930",
          "title": "DECIMAL_POSITIVO acepta hasta 2 decimales",
          "full_title": "patrones numéricos y de fecha > DECIMAL_POSITIVO acepta hasta 2 decimales",
          "line": 141,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "8f4d0023e4da45cec387",
          "title": "ISO_DATE valida el formato YYYY-MM-DD",
          "full_title": "patrones numéricos y de fecha > ISO_DATE valida el formato YYYY-MM-DD",
          "line": 150,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/lib/validations/shared/spanish-bigrams.test.ts",
      "type": "unit",
      "sha256": "8daae3482c91145e8e50695b1d8fbf5209a65b39a1374a72255add8e917c3ce9",
      "tests": [
        {
          "id": "0e1130fc42fdd2bcb33e",
          "title": "contiene bigrama válido en español: %s",
          "full_title": "COMMON_BIGRAMS_ES > contiene bigrama válido en español: %s",
          "line": 9,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "a10917b24ca802f09498",
          "title": "NO contiene bigrama ajeno al español: %s",
          "full_title": "COMMON_BIGRAMS_ES > NO contiene bigrama ajeno al español: %s",
          "line": 16,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "2e6c917f0d4276a50d17",
          "title": "texto español real puntúa alto (>= 0.8): %s",
          "full_title": "puntuarBigramasEspanol > texto español real puntúa alto (>= 0.8): %s",
          "line": 25,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "18465fe7d1c34ea29bda",
          "title": "garbage/keyboard-mash puntúa bajo (< 0.65): %s",
          "full_title": "puntuarBigramasEspanol > garbage/keyboard-mash puntúa bajo (< 0.65): %s",
          "line": 37,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "205b38db37f6bc9e56ad",
          "title": "es insensible a mayúsculas",
          "full_title": "puntuarBigramasEspanol > es insensible a mayúsculas",
          "line": 44,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "ee2882394c4777383f8e",
          "title": "una ventana local de garbage colapsa el score a 0 aunque el inicio sea válido",
          "full_title": "puntuarBigramasEspanol > una ventana local de garbage colapsa el score a 0 aunque el inicio sea válido",
          "line": 48,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0ed59487cdd3867f437a",
          "title": "strings de menos de 2 caracteres retornan 1 (no evaluables)",
          "full_title": "puntuarBigramasEspanol > strings de menos de 2 caracteres retornan 1 (no evaluables)",
          "line": 53,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "247df4cb209b1a5bd868",
          "title": "palabra corta sin ventana usa el ratio directo de bigramas",
          "full_title": "puntuarBigramasEspanol > palabra corta sin ventana usa el ratio directo de bigramas",
          "line": 58,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/middleware-public-capture.test.ts",
      "type": "unit",
      "sha256": "7e7afc2be8c874d6f25629fc115eefcb172121978ae57fb67556e10d0201e9c5",
      "tests": [
        {
          "id": "e388e443ebecd43d7df8",
          "title": "permite abrir y enviar la liga pública %s",
          "full_title": "middleware de captación pública > permite abrir y enviar la liga pública %s",
          "line": 7,
          "mode": "run",
          "parameterized": true,
          "dynamic": true,
          "definition": "static-definition-parameterized-not-expanded"
        },
        {
          "id": "ae9b7130b1fce9cec5c0",
          "title": "permite que un visitante sin sesión cargue la liga estable del PDF",
          "full_title": "middleware de captación pública > permite que un visitante sin sesión cargue la liga estable del PDF",
          "line": 21,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "7eed80736c4a15c86e1d",
          "title": "mantiene protegidas las rutas privadas para visitantes sin sesión",
          "full_title": "middleware de captación pública > mantiene protegidas las rutas privadas para visitantes sin sesión",
          "line": 32,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/scripts/showcase-commercial-flow.test.ts",
      "type": "unit",
      "sha256": "52fc112d88130ebf4fb0c269fcb375c0f85b59ec327a6404f8b9cfc276859739",
      "tests": [
        {
          "id": "f6dbbdfa9ccac8d67da8",
          "title": "permanece en dry-run por omisión y exige confirmación exacta al aplicar",
          "full_title": "showcase commercial flow HIXION > permanece en dry-run por omisión y exige confirmación exacta al aplicar",
          "line": 18,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "63ea364676c3b6dd6b69",
          "title": "declara cada rama operativa requerida y el flag exclusivo",
          "full_title": "showcase commercial flow HIXION > declara cada rama operativa requerida y el flag exclusivo",
          "line": 30,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "5fe7236ea3f85188fe23",
          "title": "deriva un saldo idempotente sin duplicar ids ni ofrecer sesiones reservadas",
          "full_title": "showcase commercial flow HIXION > deriva un saldo idempotente sin duplicar ids ni ofrecer sesiones reservadas",
          "line": 64,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "aec015dbf758dbc07d1b",
          "title": "limita reservas al saldo disponible y nunca produce números negativos",
          "full_title": "showcase commercial flow HIXION > limita reservas al saldo disponible y nunca produce números negativos",
          "line": 82,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/security/commercial-authority-rules.test.ts",
      "type": "unit",
      "sha256": "1baf309962f181db916cdcafefd39cde4abd18aae75de08c6baa292a246cd63a",
      "tests": [
        {
          "id": "1e8d9deaa4f0d8cdfbab",
          "title": "protege los cuatro ejes y campos financieros en pacientes",
          "full_title": "autoridad comercial en Firestore > protege los cuatro ejes y campos financieros en pacientes",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "0dfb52df1151e2fbd77d",
          "title": "mantiene tokens, intentos y comandos como colecciones server-only",
          "full_title": "autoridad comercial en Firestore > mantiene tokens, intentos y comandos como colecciones server-only",
          "line": 34,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/security/plan-overrides-authority.test.ts",
      "type": "unit",
      "sha256": "ea63004abe123f8f84a8cb3cd9d154bff4709f8684ea50bfd7c975fbdf94be50",
      "tests": [
        {
          "id": "afa4db9e7a1af936a2b1",
          "title": "el portal no importa el servicio de escrituras directas",
          "full_title": "autoridad de planOverrides > el portal no importa el servicio de escrituras directas",
          "line": 8,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "e5cebd74eec7ffb4f373",
          "title": "las reglas permiten lectura al paciente pero no create/update de overrides",
          "full_title": "autoridad de planOverrides > las reglas permiten lectura al paciente pero no create/update de overrides",
          "line": 20,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "a2b548c66d6ae52fc3d2",
          "title": "el endpoint vuelve a validar distribución, expediente y elegibilidad clínica",
          "full_title": "autoridad de planOverrides > el endpoint vuelve a validar distribución, expediente y elegibilidad clínica",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    },
    {
      "path": "tests/unit/services/portal-series-batches.test.ts",
      "type": "unit",
      "sha256": "de1e3f284d52a2baf45621a5aa18f44878d2356c57a61aeabe79690afac8af6c",
      "tests": [
        {
          "id": "cccce419301464ea8707",
          "title": "no publica totales parciales mientras falta un lote",
          "full_title": "series por lotes > no publica totales parciales mientras falta un lote",
          "line": 25,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "3fb06bb2bde19c848868",
          "title": "espera el resto de lotes aunque el primero falle",
          "full_title": "series por lotes > espera el resto de lotes aunque el primero falle",
          "line": 36,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        },
        {
          "id": "f8e45127cb0d91d363d0",
          "title": "comunica el error sin publicar ceros y permite recuperar el lote",
          "full_title": "series por lotes > comunica el error sin publicar ceros y permite recuperar el lote",
          "line": 45,
          "mode": "run",
          "parameterized": false,
          "dynamic": false,
          "definition": "static-definition"
        }
      ]
    }
  ]
}
