Nivel 5 · El arco, por traspasos
El arco, por traspasos
Sección titulada «El arco, por traspasos»Cuatro hitos. Como en los niveles anteriores, los tres roles están presentes desde el primero: 🧭 Nerea arriba (portal · producto), 🛡️ Diego en medio (portal · calidad), 🧑💻 Marta a la derecha (CLI + git). La lección de este arco es distinguir un defecto que se corrige de un límite que se declara, ambos sobre el mismo tratamiento: la fuga de característica es el ÚNICO control bloqueante y CIERRA (ROJO→VERDE por retirar la característica); la adecuación de datos y la equidad de selección se miden de verdad pero quedan advisory — no bloquean, y su residual se acepta conscientemente en la aprobación. El sistema cierra en VERDE, aprobado.
Los datos duros de cada hito —los comandos, el mensaje de commit, el diff y el color de la barrera— no están escritos a mano: cada bloque GuionStep los lee del guion steps/ del repositorio publicado. Si un paso no existiera, el build fallaría; lo que lees aquí es exactamente lo que el repositorio hace.
Nerea es la responsable de producto en Example Talent Corp, un empleador que despliega un modelo de cribado. Abre el portal y ejecuta las misiones de autoría encadenadas que generan el manifiesto de gobernanza froga.yaml. Tres cosas importan:
- El nivel es
high, declarado por un humano, por la ruta de empleo. La misiónclassify-systemrellena el bloqueclassificationcon la base: «Ley de IA de la UE Anexo III §4 (empleo / gestión de trabajadores / acceso al empleo)». El activador de alto riesgo aquí es el Anexo III §4 — no el Art. 6(1), no el §5(b). El motor no lo infiere; Nerea lo declara y lo asume. - Se declara un único control bloqueante y dos hallazgos medidos advisory. La misión
identify-risksañaderisk.feature-leakage— el modelo puede aprender una característica que filtra la etiqueta de colocación — con el control bloqueantemodel-feature-leakage(enforcement: gate);risk.inadequate-data— la muestra es demasiado pequeña para certificar la equidad de la selección — con el control advisorydata-sample-adequacy(enforcement: audit: se mide, no bloquea); yrisk.selection-fairness— el cribador puede seleccionar a distinto ritmo por sexo — con el control advisoryselection-rate-disparity-gender(enforcement: audit, cita Ley 15/2022 Art. 23). El atributo protegido para la equidad es el sexo; con n=215 ese control queda subpoderado (misma raíz quedata-sample-adequacy) — por eso es advisory y no gate: certificarlo bloquearía sobre una muestra que no puede sostenerlo. - También se declaran riesgos solo de auditoría y se fijan las normas. Supervisión humana insuficiente (Art. 14 / RGPD Art. 22) y transparencia insuficiente (Art. 13 / RGPD) — declarados, rastreados, no sujetos a barrera. La información al comité de empresa (ET Art. 64.4.d) también es declarada, pero su cláusula
et.64-4-d-transparencia-algoritmicaqueda Covered por el dossier firmado (rango 0 → sin presunción). La misióndeclare-standardsescribeapplicable_standardscon siete normas:eu/pren-18228@2026,iso/23894@2023,eu/ai-act@2024,eu/pren-18283@2026,eu/pren-18282@2026, y la legislación nacionales/et-rdleg-2-2015@2015+es/ley-15-2022@2022(rango 0, sin presunción) — sin DORA, sin MDR.
Las tres misiones abren un PR en el repositorio del sistema. El turno pasa a Diego, que lo revisa e integra. En el guion, la identidad, el encuadre y el programa de riesgos quedan como tres commits del manifiesto froga.yaml —el diff del último muestra el único bloqueante y los dos hallazgos advisory con sus umbrales numéricos fijados antes de que exista modelo:
Diego recibe el PR que abrieron las misiones de Nerea, revisa que el manifiesto sea correcto — nivel high por Anexo III §4 (empleo), el único bloqueante (risk.feature-leakage con model-feature-leakage, enforcement: gate), los dos hallazgos advisory (risk.inadequate-data con data-sample-adequacy, y risk.selection-fairness con selection-rate-disparity-gender, atributo protegido sex, ambos enforcement: audit), los riesgos solo de auditoría y las siete normas (incluida la legislación nacional ET + Ley 15/2022, rango 0) sin DORA ni MDR — e integra el PR. El froga.yaml declarado queda en el repositorio.
Todavía no hay nada que medir, pero Marta deja el reproductor listo en git: el proyecto uv + DVC y el dataset real Campus Placement con su manifiesto Croissant ya son commits del guion:
15
git init -q -b maingit config user.email marta@demo.seigarrena.devgit config user.name "Marta Demo"uv init --bare --name vldemo-talent-screening --python 3.12patch: pyproject-index.patchuv add "dvc>=3" pyyaml froga "venturalitica==0.6.11" "mlcroissant>=1.0" pyarrow "scikit-learn==1.8.0" pandas joblibpatch: gitignore.patchpatch: gitattributes.patchpatch: readme.patchuv syncuv run dvc init --no-scm -quv run dvc config core.analytics falsefroga pubkey --out .froga/PUBKEY.txtgit add -Agit commit -m "init: repo, proyecto uv (deps reales) y README del reproductor"
Con el manifiesto declarado y el andamiaje en git, el turno pasa a medir (hito 2).
Esperando. Nerea ha declarado el sistema y Diego lo ha integrado; ahora Diego espera la primera medición de Marta. No toca la CLI: cuando Marta informe del color de la barrera, verá qué controles fallan y por qué.
Marta mide el sistema tal como está — sin tratar. Primero compila el contrato de la barrera desde froga.yaml —qué controles se van a medir y con qué umbral—:
froga compilegit add -Agit commit -m "compile: assessment plan OSCAL (contrato del gate, antes del modelo)"
Ahora añade el modelo base —un clasificador GradientBoosting entrenado sobre las características crudas de Campus Placement, incluida salary (mitigate: false)— y corre la evidencia. froga run reproduce el pipeline, mide los controles y aplica la barrera:
patch: params.patchpatch: dvc.patchpatch: train.patchpatch: evaluate.patchpatch: compliance-eval.patchfroga runpuede fallargit add -Agit commit -m "modelo: gradient-boosting V1 (train/evaluate + dvc.yaml) — run: V1 evidencia base (gate RED esperado; seed=42)"
El único control bloqueante vuelve ROJO, y por qué es el punto:
model-feature-leakage=leaky_feature_flag1.0, barrera< 1.0→ FALLA (bloqueante). La característicasalarysolo está presente para los candidatos colocados (NaN para no colocados, imputado a 0), así que es colineal con la etiqueta de colocación — fuga total. Un modelo que «lee la respuesta» no está midiendo el mérito: es un defecto de seguridad (Art.15).
Y junto a él, dos controles ADVISORY se miden de verdad pero no bloquean el gate:
data-sample-adequacy=sample_adequacy_ratio0.43 (215 / 500). Con n=215 no hay suficientes registros para certificar que la selección es justa entre los valores desex. Se mide, se documenta — no bloquea.selection-rate-disparity-gender(paridad de selección por sexo, umbral< 0.10, que cita Ley 15/2022 Art. 23) se mide de verdad sobre la cohort test — pero con n=215 (≈24 mujeres) queda subpoderada: el motor no certifica equidad ni injusticia, la misma raíz quedata-sample-adequacy. Advisory, no bloqueante.
Así que la barrera global es ROJA — falla el único bloqueante. El comando sale con código de error, pero el paquete se firma y se ancla de todos modos: un resultado ROJO es evidencia, registrada honestamente. Un defecto de seguridad real, y dos hallazgos ya medidos que viajarán advisory hasta la aprobación.
Esperando. Diego ha visto el ROJO de V1 en el bloqueante. No toca git; espera la evidencia re-medida, donde leerá si el defecto de seguridad cierra.
Este es el tratamiento (ISO 23894 §6.5). Flipar mitigate a true en params.yaml hace que el pipeline retire la característica salary antes de entrenar — un cambio versionado en git que el motor atribuye al tratamiento del riesgo. El diff es el tratamiento, y en el mismo paso Marta vuelve a correr la barrera sobre el modelo ya sin la columna con fuga:
git checkout -b tratamiento/retira-salarypatch: params-mitigate.patchfroga rungit add -Agit commit -m "treatment: retira la feature fugada salary (mitigate false→true; cierra model-feature-leakage; data-sample-adequacy sigue abierto)"
model-feature-leakage=leaky_feature_flag0.0, barrera< 1.0→ PASA. El único bloqueante CIERRA — un ROJO→VERDE limpio por retirar la característica. Como es el único control bloqueante del programa, la barrera global pasa a VERDE.data-sample-adequacy=sample_adequacy_ratio0.43 — sin cambios. Retirar una columna no añade filas; sigue midiéndose como advisory, sin bloquear.selection-rate-disparity-gender— sigue medida y subpoderada en V2. Retirarsalaryno fabrica mujeres en la muestra: la paridad por sexo se vuelve a medir, pero n=215 sigue sin poder certificar la equidad. Es la cara de equidad del mismo límite de datos — advisory, no bloqueante.
Así que la barrera global pasa a VERDE. Léelo con precisión: el tratamiento arregla la contaminación (una característica con fuga) — eso es lo único que un cambio de código puede arreglar. No puede fabricar datos: el remedio para risk.inadequate-data sigue siendo «adquirir ≥500 registros representativos», una brecha de datos, no de código. Por eso ese hallazgo queda advisory en vez de gate: certificarlo bloquearía sobre una muestra que nunca podrá sostenerlo con este dataset.
Fuga verde, datos y equidad medidos-advisory — ese es el estado que se lleva a la decisión.
Este es el cierre. La evidencia VERDE tratada y firmada de Marta está en el repositorio, con la solicitud ya emitida (columna de Marta). Diego revisa el paquete, verifica la firma de Marta y ve el defecto de seguridad resuelto: el tratamiento demostró el cierre del único bloqueante — ya no hay nada que fallar en el gate. Pero quedan los dos hallazgos advisory que la barrera nunca certifica porque la muestra no lo permite: data-sample-adequacy (0,43) y selection-rate-disparity-gender (subpoderada).
Diego aprueba — la aprobación acepta esos dos hallazgos advisory a la vez: la adecuación de datos y la equidad de selección, ambos genuinamente medidos pero no certificables con n=215. La aprobación es un acto git-nativo y firmado: un acto DSSE .froga/acts/NNNN-approve.json —firmado ECDSA-P256 y verificado contra la clave de Diego declarada en signers:— registra quién aprobó; el commit que lo añade es texto plano, sin firma GPG (no esperes la insignia «Verified» al pincharlo en GitHub), pero el acto sí va criptográficamente firmado; el motivo que acepta el residual advisory queda en el acta del portal (la CLI no admite --reason en la mayoría de casos y lo omite con aviso; aquí froga approve --reason sí lo acepta y lo ancla en el acto); y junto al acto va firmada la evidencia (bundle.json.sig y las conformidades). Con la aprobación en la historia, la conformidad se re-emite y la cláusula 11 de prEN 18228 (revisión de gestión de riesgo) pasa de Gap a Cubierta al leer el acto de aprobación:
Motivo: «Aprobación en VERDE: el único control bloqueante (model-feature-leakage) está confirmado tras el tratamiento (retirada de la feature fugada salary) — el defecto de seguridad certificable está corregido. Los hallazgos de data-sample-adequacy (n=215/500=0.43) y selection-rate-disparity-gender (subpoderado con n=215) NO se certifican por ser Campus Recruitment una muestra topada; se aceptan conscientemente como residual, con vigilancia post-mercado trimestral de la disparidad de selección y revisión periódica P6M. Remedio real: adquirir datos representativos adicionales (Art.10(3), línea futura).»
El motor no borra el residual advisory: lo ancla en la aprobación, atribuible y verificable, con vigilancia post-mercado trimestral (Art.72) sobre la disparidad de selección. Esa es la diferencia entre aceptar conscientemente un límite y ocultarlo o forzarlo a verde.
Marta cierra su parte del ciclo desde la CLI. Con la V2 en VERDE firmada, emite la conformidad y el reconstruct sobre las normas aplicables y solicita la aprobación:
patch: entitlement.patchfroga conformance --standard eu/pren-18228@2026 --outpuede fallarfroga conformance --standard iso/23894@2023 --outpuede fallarfroga conformance --standard eu/ai-act@2024 --outpuede fallarfroga conformance --standard eu/pren-18283@2026 --outpuede fallarfroga conformance --standard eu/pren-18282@2026 --outpuede fallarfroga conformance --standard es/et-rdleg-2-2015@2015 --outpuede fallarfroga conformance --standard es/ley-15-2022@2022 --outpuede fallarfroga reconstruct --outpuede fallargit add -Agit commit -m ".froga: conformance + reconstruct firmados"
solicitud de aprobación (el acto lo commitea froga request)La aprobación es de Diego (arriba): Marta la solicitó con el acto anterior, no puede concederla ella misma. Cuando el acto firmado .froga/acts/NNNN-approve.json de Diego aterriza —con el residual advisory de datos/equidad aceptado conscientemente—, Marta re-emite la conformidad para que froga derive el ciclo de la historia git, ahora con la aprobación real:
froga conformance --standard eu/pren-18228@2026 --outpuede fallarfroga conformance --standard iso/23894@2023 --outpuede fallarfroga conformance --standard eu/ai-act@2024 --outpuede fallarfroga conformance --standard eu/pren-18283@2026 --outpuede fallarfroga conformance --standard eu/pren-18282@2026 --outpuede fallarfroga conformance --standard es/et-rdleg-2-2015@2015 --outpuede fallarfroga conformance --standard es/ley-15-2022@2022 --outpuede fallarfroga reconstruct --outpuede fallargit add -Agit commit -m ".froga: re-emisión de conformance + reconstruct tras aprobar (cl.11 = Approved)"
El sistema queda declarado, tratado, VERDE, solicitado y aprobado con el residual advisory de datos/equidad (adecuación de muestra + equidad de selección) en acta: el final del arco, con la aprobación de Diego anclada en la etiqueta talent-v2.0.0-approved de arriba.