Kit de conformidad de DCP-AI¶
Traducción informativa de la versión en inglés, actualizada el 02/11/2026. Si difieren, prevalece la versión en inglés. Los identificadores, comandos y bloques de código se conservan sin traducir.
tools/conformance.js ejecuta las suites declaradas en catalog.json contra los fixtures y
vectores del repositorio. El catálogo está versionado (kit_version) y fija, para cada suite, los IDs exactos
de los casos que debe producir. El kit solo lee fixtures: no genera claves ni fixtures en el checkout, y no
cambia la canonicalización, las firmas, los perfiles, los esquemas ni los vectores.
Comandos¶
npm run conformance # every suite (same as node tools/conformance.js)
node tools/conformance.js --list # suites, pinned case counts and inventory
node tools/conformance.js --suite v1.integrity --suite v2.hashes
node tools/conformance.js --report /tmp/conformance.json
node --test tools/conformance/conformance.test.mjs # runner self-tests (temporary copies only)
Las rutas de fixtures, esquemas y catálogo se resuelven desde el script, no desde el directorio actual.
Ejecuta los comandos anteriores desde la raíz del repositorio; desde otro directorio, invoca el script por su
ruta absoluta (node /path/to/dcp-ai/tools/conformance.js). --root <dir> apunta el kit a otra copia de los
fixtures (las autopruebas lo usan con copias temporales).
Contrato¶
| Salida | Significado |
|---|---|
| 0 | Todos los casos seleccionados pasaron |
| 1 | Al menos una discrepancia (un valor o veredicto difiere de su expectativa) |
| 2 | Error de uso: argumento desconocido, ID de suite desconocido, duplicado o vacío, --list con --report |
| 3 | Error de infraestructura: fixture ausente o mal formado, expectativa mal formada, kind de configuración desconocido, catálogo ausente o mal formado, excepción, un caso fijado que no se produjo o uno no fijado que se produjo, o ninguna comprobación ejecutada |
- Un caso que falta o sobra es un error, así que quitar un vector de una colección que aún tiene otros no puede reducir la cobertura en silencio. Una suite que lanza una excepción falla aunque ya haya producido todos los casos fijados.
- Con
--report, se escribe un marcadorincompleteantes de la ejecución y se sustituye por el informe final; un error de uso o de catálogo también sustituye el archivo. Esto vale para los errores que el kit gestiona y una ruta de informe escribible: si Node no arranca o el archivo no se puede escribir, puede quedar un archivo anterior, así que quien lo consuma debe comprobar el código de salida y los camposrun.idystatusdel informe en lugar de fiarse de un archivo existente. - El informe incluye
kit(nombre, versión, SHA-256 del catálogo),run(uuid, marcas de tiempo, raíz, versión de Node),selection,status,totals(cases,executed,pass,fail,error), cada suite con su afirmación, límites y casos (status, ykind:discrepancy,fixture,expectation,exceptionocatalog), el inventario y los límites globales.
Suites de prueba¶
| Suite | Casos | Especificación | Esquemas / vectores | Qué prueba un resultado correcto |
|---|---|---|---|---|
v1.schema |
7 | DCP-01, DCP-02, DCP-03, BUNDLE.md |
schemas/v1, tests/conformance/examples/*.json |
Solo validez de esquema |
v1.integrity |
9 | VERIFICATION.md (V1), DCP-03 |
citizenship_bundle.signed.json, keys/public_key.txt, golden_vectors.json#v1_bundle_verification |
Firma Ed25519, hash del bundle, raíz de Merkle, hash de la intención, hashes de las entradas y cadena prev_hash de un fixture, comparados con los valores golden; metadatos de clave coherentes |
v2.canonicalization |
11 | CANONICALIZATION_PROFILE.md |
golden_vectors.json#canonicalization, #v2_canonicalization, interop_vectors.json#canonicalization |
Bytes dcp-jcs-v1 del canonicalizador de referencia |
v2.hashes |
9 | DCP-AI-v2.0 §2.3, §10.3 | golden_vectors.json#hash_vectors, #dual_hash_vectors |
Valores SHA-256 / SHA3-256, hashes duales y raíces de Merkle duales |
v2.signed_bundle |
10 | VERIFICATION.md (V2) |
lib/verify.js, bundle construido en memoria |
Veredictos del verificador de referencia para bundles válidos y alterados; verificado no es admisión |
v2.secondary_sha3 |
51 | VERIFICATION.md V2-11 y su errata |
secondary_sha3_vectors.json, lib/verify.js |
Veredictos del verificador de referencia sobre los campos secundarios SHA3-256 presentes, con un control por caso rechazado; no una cadena secundaria completa |
v2.schema |
4 | DCP-AI-v2.0 | schemas/v2, ejemplos *_v2.json |
Solo validez de esquema |
ext.dcp05_09.schema |
18 | DCP-05 a DCP-09 | schemas/v2, ejemplos <record>_v2.json |
Solo validez de esquema |
ext.dcp05_09.wire |
28 | DCP-05, DCP-08, DCP-09, V2-WIRE-COMPATIBILITY.md |
schemas/v2, registros firmados en memoria, interop_vectors.json#domain_separation_all_contexts |
Representación, validez de esquema y firma de estos registros como capas separadas; no conformidad funcional |
admission.strict_v1 |
117 | spec/profiles/admission |
admission_vectors.json |
Resultado de admisión y códigos de error esperados; action_authorized siempre es false |
Límites¶
- Pasar un subconjunto certifica esas comprobaciones sobre estos fixtures y nada más: no una implementación completa de DCP, otro SDK ni un despliegue. Las suites de esquema solo prueban validez de esquema; la suite de esquemas V2 no hace una implementación V2 sin las suites de canonicalización, hashes y firmas.
verifiedyadmittednunca autorizan una acción.- Los adaptadores (Express, FastAPI, OpenClaw, el servidor de verificación) no forman parte del kit.
- Puntos del protocolo, según
spec/V2-WIRE-COMPATIBILITY.md: el campo de caducidad de DCP-09 (valid_until) y los contextos de firma de DCP-05, DCP-08 y DCP-09 están corregidos por erratas y los pruebaext.dcp05_09.wire. Siguen pendientes de un diseño explícito y versionado, y el kit no los decide: las métricas fraccionarias devitality_reportde DCP-05 (condcp-jcs-v1solo 0 y 1 son representables), los derechos con notación de dominio inverso de DCP-08 §4.3 (rechazados por el enumright_type) y los segundos firmantes o acuses (DCP-05 §4.4, DCP-09 §3.3 y §6.3). Los ejemplos de DCP-04, DCP-06 y DCP-07 quedan fuera de estas erratas.
Inventario¶
Aparece en --list y en el informe, fuera de los totales: el kit no lo ejecuta, y su presencia no es una
prueba superada. Los vectores de ataque, los casos límite de canonicalización y las demás categorías de
interop_vectors.json y los vectores de bundles y mandatos se ejecutan en las suites de prueba de los SDK.
tests/nist-kat/ contiene vectores de respuesta conocida y pruebas de propiedades, cada uno con su propio
alcance (véase su README); también se ejecutan en las suites de los SDK. El candidato dcp-jcs-v2 es experimental, queda fuera de dcp-jcs-v1 y tiene su propia
suite (tests/interop/candidate/).
Implementaciones externas¶
tools/conformance-external.js ejecuta un catálogo aparte, external-catalog.json
(contrato dcp-ai-conformance-external/1), contra una implementación que indicas como argv después de --. La
implementación puede estar escrita en cualquier lenguaje: lee una petición JSON por stdin y escribe una respuesta
JSON por stdout. El runner necesita Node 20 o posterior y solo usa módulos integrados. El comando, el catálogo y el
informe anteriores no cambian.
node tools/conformance-external.js --list
node tools/conformance-external.js --report /tmp/external.json -- node tools/conformance/wrappers/ts-sdk.mjs
node tools/conformance-external.js -- /path/to/python -I tools/conformance/wrappers/python_sdk.py
node tools/conformance-external.js --suite external.digest -- ./my-implementation --its-own-flag
node --test tools/conformance/external.test.mjs
DCP_EXTERNAL_PYTHON=/path/to/python DCP_EXTERNAL_REQUIRE_PYTHON=1 node --test tools/conformance/external.test.mjs
Sin DCP_EXTERNAL_PYTHON, node --test omite el wrapper de Python y lo indica; con
DCP_EXTERNAL_REQUIRE_PYTHON=1, la falta de intérprete es un fallo. El último comando ejecuta todas las pruebas.
| Suite | Casos | Vectores |
|---|---|---|
external.dcp-jcs-v1.canonicalize |
30 | golden_vectors.json#canonicalization, #v2_canonicalization; interop_vectors.json#canonicalization y sus 19 edge_cases, 3 de los cuales deben rechazarse |
external.digest |
8 | golden_vectors.json#hash_vectors; #dual_hash_vectors raw e intent, SHA-256 y SHA3-256 |
Un intercambio mínimo, el SHA-256 de cero bytes, que puedes ejecutar contra el wrapper de TypeScript:
printf '%s' '{"contract":"dcp-ai-conformance-external/1","run_id":"00000000-0000-4000-8000-000000000000","nonce":"00000000000000000000000000000000","cases":[{"id":"c1","op":"digest","alg":"sha256","input_b64":""}]}' | node tools/conformance/wrappers/ts-sdk.mjs
Una respuesta válida a esa petición (el wrapper añade además resolved_from y runtime):
{"contract":"dcp-ai-conformance-external/1","run_id":"00000000-0000-4000-8000-000000000000","nonce":"00000000000000000000000000000000","implementation":{"name":"example","version":"0.0.1"},"results":[{"id":"c1","status":"ok","hex":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}]}
- Sobres: la petición y la respuesta son JSON en UTF-8. La respuesta es exactamente un documento JSON en stdout, y el proceso termina con código 0.
- Petición:
contract,run_id,nonceycases, cada uno conid,op(canonicalizeconprofiledcp-jcs-v1, odigestconalgsha256osha3-256) einput_b64. Los IDs son aleatorios en cada corrida y no se envía ninguna expectativa. La entrada de canonicalize es texto JSON: un vectorinput_jsonexactamente como está en el repositorio, y un valorinputtal como lo escribeJSON.stringify. - Respuesta: los mismos
contract,run_idynonce;implementation(name,version, y opcionalmenteresolved_fromyruntime, todos declarados por la implementación); yresults, un array con exactamente un objeto por caso, cada uno con elidde ese caso y unstatus:okconoutput_b64(los bytes canónicos) ohex,rejectedconcodeparseoprofile,errorconmessage, ounsupported. No se acepta ningún otro campo. - Codificaciones:
input_b64youtput_b64son base64 RFC 4648 con el alfabeto estándar, padding y sin saltos de línea, en forma canónica;hexson exactamente 64 caracteres hexadecimales en minúsculas. - Evaluación: los bytes se comparan exactamente, y un vector que debe rechazarse solo pasa si se rechaza. Un
resultado
errorounsupported, un resultado mal formado, ausente, duplicado o extra, unrun_idononceobsoleto, una salida inválida, un código de salida distinto de cero o una señal, un timeout y un stdout de más de 4 MiB son errores, nunca un pase. Los códigos de salida son los del contrato. - Proceso: se arranca sin shell, en su propio grupo de procesos en POSIX. Un único plazo (
--timeout-ms, 30 000 por defecto) se mide con un reloj monotónico; después, SIGTERM, SIGKILL 2 s más tarde y una espera acotada de 2 s a que se cierren los pipes (si no,cleanup_incomplete). stderr se conserva hasta 64 KiB y nunca se interpreta. - El catálogo y los fixtures se validan antes: IDs únicos, punteros existentes, expectativas coherentes con los vectores, y cada miembro de cada colección fijado una vez por operación y algoritmo. Un defecto da exit 3. El informe registra los hashes del catálogo, de los fixtures y de la petición, y la identidad que declara la implementación separada de lo que observa el runner.
- Informe: un valor de
--reportausente, que parezca una opción o repetido es un error de uso (exit 2) y no se escribe ningún informe, porque la ruta es ambigua. Como con el comando anterior, también puede quedar un informe antiguo si Node no arranca, no se puede escribir el informe o se mata el runner. Comprueba siempre el código de salida y, después, elrun.idy elstatusdel informe. - Wrappers:
wrappers/ts-sdk.mjs(@dcp-ai/sdk;--sdk <path>es opcional) ywrappers/python_sdk.py(eldcp_aiinstalado; ejecútalo con-I). Cada uno llama una vez al canonicalizador real y solo informarejected/profilepara el rechazo de números no enteros documentado del SDK. Los SDK no tienen códigos de error estables, así que se reconoce por su tipo y su mensaje: si cambian, un rechazo correcto aparece como error, nunca como pase. La CI ejecuta los dos wrappers. - Límites: un pase muestra que el argv ejecutado pasa estos 38 casos, a través del parser que usa; no es una
implementación completa de
dcp-jcs-v1ni conformidad DCP, y la identidad es declarada por la implementación. No es un sandbox: la implementación se ejecuta con tus privilegios y tu entorno, los descendientes que salen del grupo de procesos (setsid) no se ven, y en Windows solo se mata al hijo directo.
Wrappers de Go, Rust y WASM¶
Tres wrappers más ejecutan los mismos 38 casos y el mismo contrato contra el SDK de Go, el SDK de Rust y la
compilación WASM del SDK de Rust, todos de este checkout. build.mjs los compila en
tools/conformance/wrappers/.build/ (ignorado por Git); necesita Go 1.25 o posterior, Cargo y wasm-pack con el
target wasm32-unknown-unknown. No instala ni publica ningún paquete de DCP ni cambia manifests ni lockfiles
del SDK. Sin --offline, Cargo y wasm-pack pueden descargar herramientas de compilación y las dependencias
fijadas a sus cachés; --offline usa GOPROXY=off, Cargo --offline y wasm-pack --mode no-install, y
necesita esas cachés ya llenas.
node tools/conformance/wrappers/build.mjs all
node tools/conformance/wrappers/build.mjs --offline go rust wasm
node tools/conformance-external.js -- tools/conformance/wrappers/.build/go-wrapper
node tools/conformance-external.js -- tools/conformance/wrappers/.build/rust-target/debug/dcp-ai-conformance-wrapper
node tools/conformance-external.js -- node tools/conformance/wrappers/wasm.mjs
DCP_EXTERNAL_REQUIRE_NATIVE=1 node --test tools/conformance/external-native.test.mjs
- Compilación: Go compila con
-mod=readonlycontra elgo.mody elgo.sumdel wrapper, cuyas versiones son un subconjunto de las del propio SDK; Cargo compila con--locked, el wrapper nativo con suCargo.lock(una copia del lock del SDK, resuelta sin red, con las mismas versiones fijadas) y el artefacto WASM con elCargo.lockdel SDK.--offlineusa solo las cachés locales. No cambia ningún manifest ni lockfile del SDK. - Go (
wrappers/go): la entrada debe ser UTF-8 (se comprueba primero, porqueencoding/jsonreemplazaría los bytes inválidos), despuésjson.Unmarshaleninterface{}, como hace el propio test de interop del SDK, y una sola llamada av2.CanonicalizeV2; solo su rechazo documentado (non-integer float at,non-finite float at) esrejected/profile. Digests:v2.SHA256Hex,v2.SHA3256Hex. La versión se informa como el checkout local. - Rust (
wrappers/rust): decodificación UTF-8 fatal,serde_json::from_stry una sola llamada adcp_ai::v2::canonicalize::canonicalize_v2; solo su rechazo documentado (Non-integer number in V2 canonical form:,Non-finite number in V2 canonical form:, tras prefijos de ruta) esrejected/profile. Digests:sha256_hex,sha3_256_hex. La versión esDCP_SDK_VERSION. - WASM (
wrappers/wasm.mjs): carga el.wasmreal quewasm-packcompiló desdesdks/rustcon la featurewasm(--target web) e informa su SHA-256. El texto JSON crudo va a una sola llamada dewasm_canonicalize_v2, sinJSON.parseniJSON.stringifyen JavaScript antes de la llamada; después, la entrada y la salida solo se inspeccionan para marcar la ambigüedad, sin recalcular bytes. Esa exportación informa los errores dentro del propio resultado como{"error": "…"}, que una entrada válida también puede producir, así que un resultado{"error": string}de una sola clave es unerrorambiguo cuando la entrada es a su vez un objeto raíz cuya única clave eserror(sea cual sea su valor), y en otro caso solo es un rechazo para los mensajes documentados. Las exportaciones de digest reciben texto: las 8 entradas de digest son texto, y los bytes que no son UTF-8 son unerror, nunca se sustituyen. - Errores: en los tres, una petición con un tipo erróneo (por ejemplo
input_b64numérico onull, ocasesque no es un array) es inválida y el wrapper termina con 2; cualquier error distinto del rechazo documentado eserror. En Rust y WASM, el rechazo documentado de números se reconoce en una sola pasada lineal, sin expresiones regulares con backtracking; un mensaje de más de 1 MiB en UTF-8 es unerror, nunca un rechazo (el límite se aplica a ese clasificador, no a los errores de parseo). SinDCP_EXTERNAL_REQUIRE_NATIVE=1,node --testomite lo que no está compilado y lo indica; con ella, la falta de una herramienta o de un artefacto es un fallo. La CI compila los tres wrappers desde el checkout y ejecuta los 38 casos con cada uno, así que allí los cinco wrappers son obligatorios. - Límites: el wrapper WASM llama a las exportaciones crudas de wasm-bindgen alojadas en Node: no cubre la API
JavaScript del paquete
@dcp-ai/wasm(comoDcpClient) ni un navegador. La clasificación de los rechazos depende del texto de los mensajes, porque los SDK no tienen códigos de error estables, y, a través de WASM, una entrada raíz{"error": …}de una sola clave solo es un error ambiguo cuando la exportación devuelve el sobre{"error": string}({"error":5}sigue siendook). Un pase muestra que estos 38 casos pasan con cada wrapper, nada más.
Catálogo de evidencia: Merkle y verificación de firmas¶
--catalog evidence selecciona un catálogo opcional, external-evidence-catalog.json
(contrato dcp-ai-conformance-external/2, 241 casos), que se ejecuta contra el mismo tipo de argv. El catálogo
por defecto, el contrato /1 y sus 38 casos no cambian; el contract del catálogo decide cómo se carga y se
juzga. Los cinco wrappers aceptan ambos contratos y responden con el contrato de la petición. Solo se usan
fixtures públicos versionados: no se genera ninguna clave ni se firma nada.
node tools/conformance-external.js --catalog evidence --list
node tools/conformance-external.js --catalog evidence --assessment verdict --report /tmp/evidence-ts.json -- node tools/conformance/wrappers/ts-sdk.mjs --sdk sdks/typescript/dist/index.js
node tools/conformance-external.js --catalog evidence --assessment verdict -- /path/to/python -I tools/conformance/wrappers/python_sdk.py
node tools/conformance-external.js --catalog evidence --assessment verdict -- tools/conformance/wrappers/.build/go-wrapper
node tools/conformance-external.js --catalog evidence --assessment verdict -- tools/conformance/wrappers/.build/rust-target/debug/dcp-ai-conformance-wrapper
node tools/conformance-external.js --catalog evidence --assessment verdict -- node tools/conformance/wrappers/wasm.mjs
DCP_EXTERNAL_PYTHON=/path/to/python DCP_EXTERNAL_REQUIRE_PYTHON=1 DCP_EXTERNAL_REQUIRE_NATIVE=1 node --test tools/conformance/external-evidence.test.mjs
| Suite | Casos | Vectores |
|---|---|---|
external.merkle.dual_root |
6 | tests/conformance/v2/merkle_vectors.json: 1, 2, 3 y 5 hojas y hojas reordenadas (spec/VERIFICATION.md V2-11), generadas sin conexión con la biblioteca estándar de Python |
external.signature.composite |
24 | interop_vectors.json: firmas compuestas y solo clásicas, sus ataques versionados y alteraciones deterministas de cada positivo |
external.v2.bundle_vectors |
131 | bundle_vectors.json: el bundle firmado, sus 38 mutaciones versionadas, 9 bundles aceptados y 79 rechazados construidos aparte, el bundle de rutina solo clásico en 4 modos |
external.v2.secondary_sha3 |
80 | secondary_sha3_vectors.json: casos aceptados y rechazados y el control versionado de cada caso rechazado |
- Operaciones:
dual_merkle_rootrecibeleaves, un array no vacío de{sha256, sha3_256}con cadenas hexadecimales de 64 caracteres en minúsculas, y respondeokconsha256ysha3_256(64 hex en minúsculas).composite_verifyrecibecontext,payload_b64(los bytes canónicos del payload),composite_sigtal como está versionada,classical_public_key_b64ypq_public_key_b64(onull), y respondeokconvalidyclassical_valid(booleanos) ypq_valid(booleano onull).verify_signed_bundle_v2recibebundle_b64(el JSON del bundle firmado),policy(un objeto de política del verificador) yverification_time(RFC 3339 conZo un desfase), y respondeokconverified(booleano) yerrors(un array de cadenas). Una petición con una forma incorrecta (una hoja vacía o en mayúsculas, una hoja que no es una cadena, una hora sin zona, un campo desconocido) hace que el wrapper salga con 2. - Juicio: los casos compuestos comparan solo los campos que el fixture fija; el veredicto de un bundle debe
coincidir, y un rechazo debe contener el
error_containsversionado (sin distinguir mayúsculas, sobre los errores unidos).verified: truecon errores es un errorcontradictory_result.rejectedconcodeparsefalla, porque toda entrada versionada se puede analizar;erroryunsupportednunca son un pase. - Controles: un caso negativo solo cuenta si su control pasó; si no, es
control_failed, un error. El bundle firmado base controla sus 38 mutaciones; el positivo del mismo payload controla sus alteraciones y los ataques versionados; un modo válido del bundle solo clásico controla sus modos que rechazan; cada caso secundario rechazado tiene su control versionado. Los 79must_reject_bundlesno tienen control por caso: solo cuentan si la base y los 9 bundles aceptados pasan (un control a nivel de suite, una garantía más débil) y se juzgan por suerror_containsversionado. El informe da para cada caso susource, suinput_sha256y suscontrols. - Firma PQ ausente: ante una firma
pq_over_classicalsin parte PQ,composite_verifyde Rust devuelve un error y la exportación WASM el error en bandaSignature error: pq_over_classical binding requires PQ signature. Esos dos wrappers lo informan comorejectedconcodemissing_pq_signature, solo para ese error exacto y solo cuando la entrada tiene ese binding ypq: null. El runner solo lo acepta donde el fixture fija únicamentevalid: false(stripping_pq_removal) y su control positivo pasó; el mismo código ante una firma válida o ante cualquier otro negativo falla. TypeScript, Python y Go responden a ese caso con booleanos. - Las alteraciones del payload cambian el último carácter del primer valor de cadena del payload canónico, de modo que el payload sigue siendo JSON canónico válido (la exportación WASM recibe el JSON del payload) y un error del analizador nunca se cuenta como un fallo criptográfico.
- Hora: cada caso de bundle envía
verification_time: 2026-10-22T00:00:00Z, dentro de la ventana de validez del bundle base y de cada bundle que debe verificarse (algunos negativos están caducados o aún no son válidos en ese instante a propósito), o la hora versionada de los vectores secundarios. TypeScript pasa{ now }, Pythonnow_ms, GoVerifySignedBundleV2Aty Rustverify_signed_bundle_v2_at. La exportación WASM leeDate.now, así que el wrapper WASM sustituyeDate.nowpor ese instante solo durante la llamada síncrona y lo restaura enfinally, también cuando la llamada lanza una excepción; suruntimelo indica. Los self-tests muestran que cada wrapper rechaza el bundle base en 2020-01-01, lo acepta en 2026-10-22 y lo rechaza en 2100-01-01, y la restauración tras una excepción. - Merkle a través de WASM: el wrapper usa el
wasm_dual_merkle_rootcompilado desde este checkout, que delega en el núcleo de Rust; el helper publicado en@dcp-ai/wasm2.1.1 y anteriores falla los casos multihoja (véase el CHANGELOG). - V2-2 y la redacción comprometida: los verificadores de los SDK en
mainejecutan primero V2-2, así que 16 de los 146 rechazos que llevan unerror_contains(7 filasmust_rejectde codificación de firmas y 9must_reject_bundles) reciben ahora un diagnóstico de esquema en lugar de la redacción de la comprobación más profunda. Conlegacy, cada wrapper de SDK supera por tanto 225 casos y falla esos 16 como discrepancias de redacción con el veredicto correcto, lo que no es un éxito; converdictsuperan los 241, 130 con redacciónmatchedy esos 16 condiffers. La CI evalúa los cinco wrappers de SDK converdict. Los fixtures, sus cadenaserror_containsy la evaluación por defecto no cambian. - Self-tests:
external-evidence.test.mjsejecuta los 241 casos con los cinco wrappers, la prueba del reloj, peticiones malformadas y una implementación falsa construida a partir de las respuestas reales del wrapper de TypeScript: verificarlo todo, rechazarlo todo, resultados contradictorios, un texto de error incorrecto, una reducción Merkle que ignora el orden o que hashea el texto hexadecimal, una base que falla,missing_pq_signatureen su sitio y fuera de él, y errores ounsupporteden los negativos. Los fixtures y catálogos manipulados son errores de infraestructura (salida 3). El tiempo límite por defecto de este catálogo es de 300 000 ms (--timeout-ms). Con las dos variablesREQUIRE, un wrapper ausente falla en lugar de omitirse; CI ejecuta los cinco. En cada ejecución con los wrappers se comprueban las dos evaluaciones:legacydebe fallar exactamente las 16 filas anteriores como discrepancias de redacción, yverdictdebe superar las 241. - Límites: un pase muestra que el argv ejecutado pasa estos 241 casos, nada más. La política es
default_verifier_policycon losverifier_policy_overridesversionados de cada caso, más los 4 modos del bundle solo clásico; no se prueba ninguna otra política.composite_verifyes una primitiva con el binding y las claves dados, y el veredicto de un bundle no es admisión, confianza en el principal ni autorización de una acción.stripping_pq_with_downgradese juzga porclassical_validypq_valid; rechazarlo corresponde a una políticahybrid_required. Los campos secundarios se comprueban donde están presentes, así que un pase no muestra una cadena secundaria completa. A través de WASM, la hora es el reloj del host sustituido. Las cadenaserror_containsson la redacción de diagnóstico de los SDK, no son normativas:spec/VERIFICATION.mdno define códigos de error. Un pase de esos 146 casos muestra acuerdo con esa redacción; un verificador con los mismos veredictos y otros mensajes los falla, y ese fallo por sí solo no muestra un veredicto erróneo. La evaluación por veredicto juzga esos casos solo por el veredicto.
Evaluación por veredicto¶
spec/VERIFICATION.md define cuándo un bundle no se verifica, no la redacción de sus errores. --assessment verdict
juzga este catálogo solo por los veredictos, para implementaciones con mensajes propios. Sin la opción, o con
--assessment legacy, el juicio, el informe y los códigos de salida son exactamente los descritos arriba. La opción
solo se aplica a un catálogo /2, el versionado o un archivo propio; con el catálogo /1 o /3 es un error de uso,
también legacy, porque sus códigos forman parte del contrato o son normativos.
node tools/conformance-external.js --catalog evidence --assessment verdict --list
node tools/conformance-external.js --catalog evidence --assessment verdict --report /tmp/evidence-verdict.json -- ./my-implementation
DCP_EXTERNAL_PYTHON=/path/to/python DCP_EXTERNAL_REQUIRE_PYTHON=1 DCP_EXTERNAL_REQUIRE_NATIVE=1 node --test tools/conformance/external-evidence-assessment.test.mjs
- Qué cambia: solo los 146 rechazos que llevan un
error_contains(38 mutaciones, 79 bundles construidos por separado y 29 casos secundarios). Cada uno pasa cuando su resultado esok, está bien formado y no es contradictorio, diceverified: falsey sus controles pasaron en la misma ejecución; los mensajes no se juzgan. Todas las demás reglas son las del juicio anterior, controles incluidos. Con las mismas respuestas, un caso que pasa conlegacypasa converdict, y todo caso fuera de esos 146 obtiene el mismo estado y el mismo tipo. - Anclajes y premisas: cada uno de esos casos indica su paso de
spec/VERIFICATION.md(anchor): elspec_stepde los vectores de bundle (V2-1 a V2-14), o V2-11 y el campo de la errata del grupo secundario (audit_merkle_root_secondary,prev_hash_secondaryointent_hash_secondary). Todo control debe ser un positivo simple: se espera que verifique (o que sea válido), sinerror_containsy sin controles propios, de modo que la evaluación nunca cambia el resultado de un control. Ambas cosas se comprueban antes de la ejecución; un plan que no las cumple da salida 3 converdict(conlegacyaún puede pasar, por ejemplo con un--rootpropio), y la propiedad anterior solo vale para planes válidos en las dos evaluaciones. - Diagnóstico: la redacción de un rechazo que pasa se informa como
matched(contiene elerror_containsversionado),differsono_message(errors: []). Cualquier otro resultado esnot_evaluated, con sureason:run_error,missing,duplicate,malformed_result,implementation,unsupported,rejected,contradictory_result,wrong_verdictocontrol_failed. Un veredicto equivocado nunca pasa, sea cual sea su mensaje, y la redacción nunca cambia un estado, un total ni el código de salida. - Informe:
kit.contractsigue siendo/2; el informe añadeassessment(suschemaesdcp-ai-conformance-assessment/1, aparte del contrato del wire) ydiagnostic_totals. Susclaimylimitsindican lo que comprueba esta evaluación, fijados por el runner para cada suite sea cual sea la prosa del catálogo; esa prosa se conserva literal encatalog_claimycatalog_limitsy no es un resultado comprobado. Cada uno de los 146 casos muestraexpect: {"verified": false}, suanchory sudiagnostic. Un informe legacy no tiene ninguno de los campos nuevos (assessment,diagnostic_totals,catalog_claim,catalog_limits,anchor,diagnostic), y susclaim,limitsyexpect(conerror_contains) quedan como estaban.--listcon--assessmentindica el modo; sin ella, su salida no cambia. - Códigos de salida y frescura: los del contrato. Solo el estado
pass(salida 0) significa que la ejecución pasó: los diagnósticos de una ejecución con estadofailoerror, por ejemplo con un resultado de más, son detalle parcial, nunca conformidad. Todo error de--assessment(un valor desconocido, repetido o ausente, un catálogo/1o/3, un anclaje que falta) llega después del marcador, así que reemplaza un informe anterior por un error nuevo con unrun.idnuevo; solo las excepciones descritas para el comando no dejan informe. - Self-tests:
external-evidence-assessment.test.mjsejecuta en las dos evaluaciones un fake construido con las respuestas reales del wrapper de TypeScript: veredictos correctos con el mensaje esperado, uno neutral o ninguno; veredictos equivocados con el mensaje esperado; verificarlo o rechazarlo todo; resultados con error,unsupported, ausentes, duplicados, malformados, rechazados y de más; controles fallidos, una ejecución fallida y un timeout; cada suite seleccionada sola; la propiedad anterior en todos los modos, incluidos un Merkle que ignora el orden, la antigua promoción del nodo impar ymissing_pq_signatureen su sitio y fuera de él; anclajes y premisas; informes frescos sobre un pase anterior; formas del informe y--list. También ejecuta los 241 casos con los cinco wrappers converdict; la CI lo ejecuta con todos los wrappers obligatorios. - Límites: un pase con
verdictmuestra veredictos correctos en estos casos, con la argv ejecutada y con las mismas políticas; no evalúa si los mensajes son exactos o útiles. El contrato del wire/2, el catálogo, los fixtures y los wrappers no cambian.
Diagnóstico del límite de esquemas (opcional)¶
El catálogo separado external-schema-catalog.json usa el contrato /2 para 15 casos del corpus nuevo de cierre de esquemas. Conserva la expectativa normativa V2-2: un miembro desconocido de un objeto cerrado debe rechazarse aunque sus hashes y firmas sigan siendo válidos. La base y las extensiones permitidas son controles positivos; una extensión firmada desactualizada es un negativo distinto de integridad. Este diagnóstico no cambia los 241 casos del catálogo de evidencia predeterminado.
node tools/conformance-external.js --catalog tools/conformance/external-schema-catalog.json --assessment verdict --list
node tools/conformance-external.js --catalog tools/conformance/external-schema-catalog.json --assessment verdict --report /tmp/schema-diagnostic.json -- ./my-implementation
node --test tools/conformance/external-schema.test.mjs
Los verificadores de los SDK en main ejecutan V2-2, y los cinco wrappers de SDK superan estos 15 casos; una implementación que falla sigue siendo un fallo, nunca una excepción aceptada. schema-boundary.js informa por separado la validación de esquemas de referencia, una comprobación de integridad limitada a este corpus finito y la composición de esquema con SDK TypeScript en el instante del corpus. Que pase la composición no demuestra que el SDK solo aplique V2-2; tampoco es la API de referencia ejecutada con un reloj inyectado. El helper de integridad no es un verificador normativo general. Las autopruebas ejercitan el diagnóstico, sus controles y expectativas; que pasen no certifica la conformidad de los SDK. Véanse el mapa de cobertura y la guía del implementador.
Catálogo de validación de esquemas (V2-2, opt-in)¶
external-schema-validation-catalog.json usa el contrato /2 para los 57 casos firmados de wiring_cases.json, cada uno fijado por ID: 13 positivos y 44 negativos. Cada negativo cuenta solo cuando el caso base verifica en la misma ejecución, y cada caso indica su paso de spec/VERIFICATION.md (de V2-1 a V2-14). Un caso guardado como texto se envía exactamente con esos bytes. Solo se juzgan los veredictos: ningún caso lleva un error_contains, así que legacy y verdict los juzgan igual. Los catálogos por defecto no cambian.
node tools/conformance-external.js --catalog tools/conformance/external-schema-validation-catalog.json --assessment verdict --list
node tools/conformance-external.js --catalog tools/conformance/external-schema-validation-catalog.json --assessment verdict --report /tmp/schema-validation.json -- ./my-implementation
node --test tools/conformance/external-schema-validation.test.mjs
Las autopruebas comprueban que el kit construye el mismo plan que el lector del corpus de tools/schema-ir/, la integridad del catálogo y del corpus (un ID ausente, sobrante o duplicado, un caso dado en las dos formas, una base que no es positiva) y los controles del ejecutor: aceptarlo todo falla exactamente los 44 negativos, rechazarlo todo no cuenta ninguno, un verificador que ignora una restricción falla exactamente ese caso, y los resultados ausentes, duplicados o malformados son errores. La CI ejecuta las autopruebas y el catálogo con los cinco wrappers de SDK, todos obligatorios. Superarlo cubre estos 57 casos con su política e instante de verificación; no es la conformidad V2 completa.
Catálogo de políticas del verificador (opt-in)¶
external-policy-catalog.json usa el contrato /2 para 74 casos con tiempo fijo: 10 controles positivos, 24 casos de PQ presente inválida en cuatro modos solicitados, 14 casos de listas de algoritmos y advisories y 26 casos de tier. El catálogo de evidencia predeterminado no cambia. Cada negativo exige que pase su control positivo; no se evalúa la redacción del diagnóstico.
node tools/conformance-external.js --catalog tools/conformance/external-policy-catalog.json --assessment verdict --list
node tools/conformance-external.js --catalog tools/conformance/external-policy-catalog.json --assessment verdict --report /tmp/policy-conformance.json -- ./my-implementation
node --test tools/conformance/external-policy.test.mjs
El positivo híbrido routine alcanza classical_only efectivo; el positivo standard eleva esa solicitud a hybrid_preferred. La derivación registrada de tier y modo es explicativa, no una evaluación de campos del resultado. Los casos de tier usan bundles versionados firmados por separado, no gemelos de un solo cambio. Las exclusiones de algoritmos clásicos bajo pq_only siguen omitidas sin veredicto esperado hasta aclarar la interpretación. Consulta las preguntas del corpus y el mapa de cobertura para los límites restantes. CI ejecuta las autopruebas del corpus y runner y el catálogo mediante los cinco wrappers SDK; aprobar solo acredita estos casos.
Catálogo estructural (opt-in)¶
external-structural-catalog.json ejecuta 16 casos con tiempo fijo mediante /2 y hybrid_preferred efectivo: dos controles positivos y 14 negativos estructurales. Cada negativo depende del control base, y el caso de sesión invertida también depende de su gemelo positivo. Los catálogos predeterminados no cambian.
node tools/conformance-external.js --catalog tools/conformance/external-structural-catalog.json --assessment verdict --list
node tools/conformance-external.js --catalog tools/conformance/external-structural-catalog.json --assessment verdict --report /tmp/structural-conformance.json -- ./my-implementation
node --test tools/conformance/external-structural.test.mjs
Solo se evalúan los veredictos. El corpus registra la procedencia de las firmas y los límites de aislamiento, especialmente la evidencia obsoleta del payload con fracción y la inexistencia de una raíz válida para un rango invertido. CI ejecuta las autopruebas del corpus y runner y los cinco wrappers SDK. Aprobar se limita a estos casos y esta política; no acredita conformidad V2 completa.
Catálogo de admisión: admisión strict-v1¶
--catalog admission selecciona un segundo catálogo opt-in, external-admission-catalog.json
(contrato dcp-ai-conformance-external/3, 140 casos), que se ejecuta contra el mismo tipo de argv. Juzga la decisión de
admisión de spec/profiles/admission sobre el
admission_vectors.json versionado, que el catálogo fija por SHA-256 junto
con bundle_vectors.json. Los contratos /1 y /2 y sus catálogos no cambian. Los wrappers de TypeScript, Python, Go y
Rust responden a /3 mediante su SDK; la compilación WASM no tiene API de admisión, así que su wrapper rechaza /3
(exit 2) y nada la emula.
node tools/conformance-external.js --catalog admission --list
node tools/conformance-external.js --catalog admission --report /tmp/admission-ts.json -- node tools/conformance/wrappers/ts-sdk.mjs --sdk sdks/typescript/dist/index.js
node tools/conformance-external.js --catalog admission -- /path/to/python -I tools/conformance/wrappers/python_sdk.py
node tools/conformance-external.js --catalog admission -- tools/conformance/wrappers/.build/go-wrapper
node tools/conformance-external.js --catalog admission -- tools/conformance/wrappers/.build/rust-target/debug/dcp-ai-conformance-wrapper
node --test tools/conformance/external-admission.test.mjs
DCP_EXTERNAL_PYTHON=/path/to/python DCP_EXTERNAL_REQUIRE_PYTHON=1 DCP_EXTERNAL_REQUIRE_NATIVE=1 node --test tools/conformance/external-admission-wrappers.test.mjs
| Suite | Casos | Origen |
|---|---|---|
external.admission.outcomes |
65 | Los vectores de resultado de admission_vectors.json |
external.admission.config |
71 | Sus 49 configuraciones rechazadas, sus 2 perfiles aceptados con sus máximos, y la carga de los 14 conjuntos de confianza y 6 perfiles que usan los vectores |
external.admission.composed |
4 | Escritos en el catálogo a partir de las entradas de los vectores: un perfil sin conjunto de confianza, un perfil con una política de verificador de quien llama, y dos composiciones cuyos códigos descolocaría un orden alfabético |
- Operaciones:
admit_signed_bundle_v2recibebundle_b64,now,profile,trust_set,verifier_policyyrevocation, siempre los seis;load_admission_configrecibekind(trust_setoprofile) yconfig. nowes exactamenteYYYY-MM-DDTHH:mm:ss.sssZ, una fecha real de los años 0001 a 9999. Es solo el rango de este transporte: no cambia ninguna marca de tiempo del protocolo.revocationesnullsin perfil,{"state": "unavailable"}(sin instantánea, R1) o{"state": "snapshot", "snapshot": {…}}con un objeto, completo o no, que juzga el SDK. Se da exactamente cuando hay perfil.profile,trust_setyverifier_policyson objetos onull. El conjunto de confianza y el perfil son la configuración del operador, dada fuera de banda en la petición, nunca tomada del bundle. Sin perfil, la petición lleva una política de verificador completa: los 14 campos dedefault_verifier_policy, másrequire_session_expiry: false,require_audience_binding: falseymax_session_duration_seconds: 0. Con perfil no lleva ninguna, salvo en el caso que comprueba su rechazo.- Una petición con otra forma hace que el wrapper salga con 2 antes de llamar al SDK.
- Respuestas:
okcon exactamenteverified,principal_trust,profile,profile_compliance,admitted,action_authorized,recorded_decision,trust_set_version,codes(los códigos deerrors, en el orden emitido, con sus repeticiones) yverification_verified; para una configuración,accepted: true, y para un perfil sus dos máximos.rejectedlleva un códigoCONFIG_*, solo cuando el SDK lanzó su error de configuración tipado.erroryunsupportednunca son un pase. - Juicio, en este orden:
- los tipos de los campos (
malformed_result); - las reglas que se desprenden de spec/profiles/admission (
contradictory_result):action_authorizedesfalse;admittedse cumple exactamente cuando no se añadió ningún código; la confianza en el principal se evalúa solo con un conjunto de confianza y un bundle verificado; - los campos exactos y el conjunto de códigos;
- el orden de la secuencia cruda según las fases de §5.1 y §5.2, con las paradas de P1, T1, R1 y R2 (
order); - las repeticiones (
cardinality): solo P4, T2, R4 y R5, que se aplican a varios componentes o sujetos, pueden repetir su código.
Una configuración rechazada pasa con uno de sus allowed_codes: su propio código, o cualquiera de los dos aplicables en
los tres conjuntos de confianza con dos problemas (§2.2). Los perfiles se juzgan por sus máximos efectivos (§4.1).
- Controles: un negativo cuenta solo si sus controles pasaron, de forma transitiva; si no, es control_failed, un
error.
- 29 negativos de resultado se controlan con el positivo de su propio bundle.
- 19 no tienen positivo en su bundle y se controlan a nivel de suite, una garantía más débil.
- Los rechazos de configuración se controlan cargando un conjunto de confianza o un perfil válido.
- Los casos compuestos se controlan con los vectores que componen.
--suite ejecuta también los controles que necesita un caso seleccionado y los informa (controls_included,
included_for_controls).
- Proceso y errores: como con /1 y /2 (run id, nonce, ids de caso aleatorios, grupo de procesos y límites); el
timeout por defecto de este catálogo es de 300 000 ms. Un fallo al arrancar, un exit distinto de cero, una señal, un
timeout, ids obsoletos, o un resultado malformado, ausente, duplicado o extra es un error, nunca un rechazo ni un «no
admitido». El catálogo y sus fixtures se validan al cargarse (hashes fijados, claves y tipos exactos, cada expectativa
coherente con el texto); un defecto es exit 3.
- Wrappers: una llamada por caso a la función combinada o a los cargadores del SDK, con el now de la petición,
nunca el reloj del sistema: admitSignedBundleV2, createPrincipalTrustStore y resolveStrictV1Profile
(TypeScript); admit_signed_bundle_v2, create_principal_trust_store y resolve_strict_v1_profile (Python);
AdmitSignedBundleV2, NewPrincipalTrustStore y ResolveStrictV1Profile (Go); admit_signed_bundle_v2,
PrincipalTrustStore::from_json y resolve_strict_v1_profile (Rust), con los proveedores Ed25519 y ML-DSA-65. Solo
el error tipado es un rechazo: AdmissionConfigError (TypeScript, Python, Rust) o *AdmissionConfigError mediante
errors.As (Go). DCP_CONFORMANCE_WRAPPER_FAULT es solo para las autopruebas y únicamente puede convertir una
respuesta en un error.
- Autopruebas:
- external-admission.test.mjs juzga respuestas sintéticas construidas a partir de las expectativas del propio
catálogo, sin SDK: decisiones erróneas, orden, cardinalidad, códigos de configuración, fallos de infraestructura, y
catálogos y fixtures alterados.
- external-admission-wrappers.test.mjs ejecuta los cuatro wrappers con sus SDK: los 140 casos de cada uno, el reloj
de la petición (un caso en tres instantes), peticiones malformadas, fallos inyectados, una implementación que no
puede cargarse, el rechazo de WASM, y un SDK que toma la confianza del bundle, mediante TypeScript.
La CI ejecuta ambas, con los cuatro wrappers obligatorios.
- Límites:
- Qué muestra un pase. El argv ejecutado pasa estos 140 casos, nada más. La admisión nunca es una autorización:
action_authorized es siempre false.
- Una proyección. La respuesta es una proyección del resultado de admisión. La verificación incluida (sus errores,
avisos, detalles, signer_keys, modo y nivel) no se juzga; verification_verified lo informa la propia
implementación.
- Errores tipados. Un rechazo se juzga por su código. Que proceda del error tipado del SDK lo ve el wrapper, no el
runner.
- Garantías más débiles. Los 19 controles a nivel de suite, y las 4 expectativas compuestas, que están escritas en
el catálogo y no en los vectores.
- Fuera de este catálogo. Los adaptadores, las fuentes de confianza y de revocación, TLS, los inquilinos, la caché y
el uso único. El corpus son los vectores versionados y estas cuatro composiciones, no cualquier entrada.
Línea base de rendimiento¶
tools/conformance-perf.js mide el verificador V2 normativo de los cinco SDK de este checkout con los mismos bytes de
entrada, la misma política y el mismo instante de verificación (contrato dcp-ai-perf/1). Es una línea base local,
pequeña y reproducible; no es un benchmark de este harness, ni un ranking de seguridad, ni un requisito de velocidad:
la CI nunca comprueba una velocidad.
node tools/conformance-perf.js --list
node tools/conformance-perf.js --offline --warmup 5 --samples 20 --rounds 1 --python /path/to/python --report /tmp/perf-sanity.json
node tools/conformance-perf.js --no-build --warmup 20 --samples 100 --rounds 3 --python /path/to/python --report /tmp/perf.json
DCP_PERF_PYTHON=/path/to/python DCP_EXTERNAL_REQUIRE_PYTHON=1 DCP_EXTERNAL_REQUIRE_NATIVE=1 node --test tools/conformance/perf.test.mjs
- Cargas: seis bundles versionados, que se leen y nunca se vuelven a firmar, con
default_verifier_policyen 2026-10-22T00:00:00Z:hybrid_ok,classical_sig_invalid(la firma Ed25519 del RPR),pq_sig_invalid(la firma ML-DSA-65 del bundle),secondary_root_ok,secondary_root_wrongysecondary_all_ok. El par secundario comparte los artefactos base y las entradas, y difiere enaudit_merkle_root_secondarydel manifest y en la firma del bundle que lo cubre (manifest_hash, los dos valores de firma,signature.hash_alg): no es un experimento que aísle el coste de un solo campo. - Qué se mide, por llamada, en un proceso por SDK, sin arrancar un proceso por muestra: TypeScript
JSON.parseyawait verifySignedBundleV2(eldistlocal, no un paquete publicado); Pythonjson.loadsyawait verify_signed_bundle_v2en un único loopasynciopersistente (eldcp_aidel checkout); GoVerifySignedBundleV2At, cuya API de bytes incluye el parseo; Rustserde_json::from_sliceyverify_signed_bundle_v2_at(un example en release); WASM la decodificación,wasm_verify_signed_bundle_v2yJSON.parsede su resultado, conDate.nowsustituido por el instante de verificación durante la llamada, con los costes de la frontera pública incluidos y nombrados. Los registries y las políticas se preparan una vez; ningún resultado se cachea. - Comprobaciones: las llamadas de calentamiento y las medidas se ejecutan en round-robin sobre las cargas, y cada
llamada se comprueba: el veredicto esperado, ningún error en un positivo, errores en un negativo. Una comprobación
fallida detiene ese hijo: responde
check_failedsin tiempos y termina con salida 0, y el runner informa la ejecución como fallida (su salida 1). El informe conserva los resultados de los demás hijos y entonces no es una línea base (baseline: false); nunca se presenta ningún tiempo del hijo fallido. Un hijo responde o bien un éxito con todas las muestras o bien ese fallo; el runner comprueba el contrato, la identidad de la ejecución, el SDK y la entrada, los recuentos, y que cada muestra sea un entero seguro positivo de nanosegundos. - Estadísticas: se conservan todas las muestras, en orden; por SDK y carga,
n,min, la mediana (la media de los dos valores centrales para unnpar), p90 y p99 por rango más cercano,maxy el IQR. Sin ranking y sin porcentajes atribuidos; un negativo puede detenerse en su primera comprobación fallida, y suerrors_firstes solo diagnóstico. - Procedencia: por defecto la herramienta construye lo que mide (el
distde TypeScript, el hijo Go con-obajo.build, el example de Rust y WASM en release, WASM en su propia carpeta) bajo un único plazo de build acotado. Después registra, para cada SDK seleccionado, los árboles de fuente, manifests y locks, hijos, hashes de artefactos (todo eldist, el.wasmy su glue JS, los binarios), versiones de herramientas y opciones de build, y no mide nada si falta alguno.--no-buildrecalcula todo eso para los SDK pedidos y rechaza cualquier diferencia o hueco; la hora, el commit y el indicador dirty son informativos. Cada hijo informa de lo que cargó (TypeScript: cada import resuelto desdedist, con su manifest de paquete y su hash; Python: eldcp_aidel checkout y los módulos realmente importados). El record liga los artefactos a este checkout; no certifica la cadena de suministro. - Procesos e informe: cada hijo se ejecuta bajo el runner del kit externo (plazo, grupo de procesos, límites de
salida). El informe recibe primero un marcador
incompletey se reemplaza en todos los casos; un--reportambiguo (ausente, con forma de opción o repetido) no escribe nada. Códigos de salida del runner: 0 pasa, 1 una comprobación fallida, 2 uso, 3 infraestructura, build o procedencia.--child-argvsustituye un hijo solo para probar el harness; ese informe nunca es una línea base. - Self-tests:
perf.test.mjsejecuta un humo obligatorio de los cinco hijos (W=1, N=2, R=1); hijos falsos (resultados ausentes, duplicados o desconocidos, recuentos erróneos, muestras malformadas o no seguras, una identidad equivocada, contradicciones, accept-all, fallos en el calentamiento y en las muestras, un fallo malformado, una salida válida seguida de un exit distinto de cero, un cuelgue); un hijo real con un positivo esperado erróneo; informes frescos sobre un pase anterior; procedencia falsificada, rancia e incompleta; una selección en un árbol temporal; y un paso de build que ignora SIGTERM. La CI los ejecuta en el job nativo con todos los hijos obligatorios. - Límites: una sola máquina y una muestra pequeña: la frecuencia, el estado térmico y de energía y la carga de fondo no se controlan (se registran la CPU y el SO). Cada SDK se mide tal como está, con sus propias bibliotecas criptográficas; el perfil de Rust es el release por defecto. Los resultados se quedan en local: esta sección documenta el método, no cifras.