Conformidad contra los CLIs reales — corrida manual y opt-in
Todo lo que corre en CI es un fixture. Un fixture es una observación congelada, y una observación congelada no dice nada sobre si el contrato que congeló sigue vigente: lo único que puede responder eso es el binario oficial que tiene instalado la persona. Esta página es ese procedimiento.
La corrida vive fuera de CI por dos razones que no se negocian: la constitución §5 prohíbe que los tests dependan de la cuenta de un proveedor, y §2 exige que la autenticación viva en el CLI oficial — de modo que correrla gasta una sesión real en la cuenta de quien la corre. Nadie queda inscrito por omisión.
Cuándo se corre
Sección titulada «Cuándo se corre»- Antes de subir el piso o el techo de versión soportada de un adaptador.
- Cuando se re-ancla el volcado de esquema vendorizado
(
core/mock-provider/schemas/), que es lo que dice qué versión se soporta. - Cuando un proveedor publica una versión mayor de su CLI.
- Cuando alguien sospecha que un fixture dejó de parecerse a la realidad. Esa sospecha se resuelve corriendo esto, nunca leyendo el fixture otra vez.
Requisitos
Sección titulada «Requisitos»-
El toolchain del repo (
rust-toolchain.toml) y el workspace construido:cargo build --workspace -
El CLI oficial del proveedor instalado y con sesión iniciada, con su propia autenticación. Meltemi no lee, no guarda y no inyecta credencial alguna: si el CLI no está autenticado, la corrida falla y eso es correcto. Verificar a mano, una vez, antes de empezar:
Plataforma Comprobación Windows (PowerShell) Get-Command claude, codex | Select-Object Name, SourcemacOS / Linux command -v claude codexY que la flota los vea, que es la misma detección que usará la corrida:
meltemi fleet --json -
Nada más. La corrida no necesita una instalación de Meltemi: usa los adaptadores que este checkout acaba de construir. Esa es su única diferencia con lo que hace una persona instalada, donde los adaptadores viajan junto al daemon.
Cómo se corre
Sección titulada «Cómo se corre»El opt-in es explícito y doble: la variable de entorno y el flag --ignored.
Sin las dos, el test no hace absolutamente nada.
Windows (PowerShell)
$env:MELTEMI_CONFORMANCE_REAL = "1"$env:MELTEMI_CONFORMANCE_AGENT = "claude-code" # opcional: una sola entradacargo test -p meltemid --test conformance_real -- --ignored --nocapturemacOS / Linux
MELTEMI_CONFORMANCE_REAL=1 \MELTEMI_CONFORMANCE_AGENT=claude-code \cargo test -p meltemid --test conformance_real -- --ignored --nocaptureMELTEMI_CONFORMANCE_AGENT acota la corrida a una entrada del catálogo
(claude-code, codex-cli); sin ella corren todas las que tengan su CLI
presente. Existe porque cada dialecto gasta un turno en la cuenta de un
proveedor distinto, y quien re-ancla uno no tiene por qué pagar el otro.
Qué cuesta: dos sesiones reales por entrada, no una. La corrida tiene dos piernas, porque los cuatro criterios que el nivel 2 declara no caben en una sola sesión:
- Un turno que llega hasta el final —
streaming,permissions,session. - Un turno que se para en cuanto el CLI habla dentro de él —
cancellation.
La segunda se diseñó como la barata («cortada en sus primeras palabras») y medida contra el CLI real no lo es: cuando la sesión ve la primera palabra el proveedor ya produjo casi todo el turno, y su costo quedó a un décimo del de la primera. Presupueste dos turnos completos por dialecto. Si la sesión no llega a abrirse, no se gasta nada: el turno nunca se envía.
Qué queda registrado, y dónde
Sección titulada «Qué queda registrado, y dónde»El resultado se persiste como JSONL apend-only en el directorio de datos del
usuario, en conformance/results.jsonl:
| Plataforma | Ruta |
|---|---|
| Windows | %APPDATA%\meltemi\data\conformance\results.jsonl |
| macOS | ~/Library/Application Support/meltemi/conformance/results.jsonl |
| Linux | ~/.local/share/meltemi/conformance/results.jsonl |
Una línea por corrida, con su fecha y la versión del CLI que respondió:
{ "agentId": "claude-code", "verifiedLevel": 2, "agentVersion": "2.1.167", "runAt": "2026-07-31T13:41:21Z", "criteria": [ { "level": 2, "name": "streaming", "passed": true }, { "level": 2, "name": "session", "passed": true }, { "level": 2, "name": "permissions", "passed": true }, { "level": 2, "name": "cancellation", "passed": true } ]}Dos reglas de lectura, ambas deliberadas:
- Un criterio que la corrida no pudo ejercer no aparece. No se reporta como aprobado ni como fallido: no se reporta. El nivel solo se otorga cuando todos los criterios que ese nivel declara están presentes y aprobados, así que una corrida incompleta produce un resultado incompleto en vez de uno halagador.
verifiedLevel: 0es un resultado, no un error de la corrida. Significa que contra ese binario, ese día, el nivel no quedó verificado.
Las dos reglas juntas tienen una consecuencia que conviene decir en voz alta,
porque el número solo no la cuenta: un 0 por «nada funcionó» y un 0 por «falta
un criterio por ejercer» se leen igual en el número y no significan lo mismo.
La lista de criteria es la que lo distingue, y por eso se persiste entera.
La segunda pierna existe precisamente por eso. Hasta el 2026-07-31 la corrida
reportaba tres criterios y el nivel 2 declara cuatro: cancellation no se
ejercía nunca, de modo que el nivel era inalcanzable por construcción y no
por lo que la corrida encontrase. Un procedimiento manual que no puede otorgar
el nivel que documenta no es un procedimiento. Regla que la pierna nueva trae:
si el turno nunca llegó a estar en vuelo, no se envió paro alguno y el criterio
no se reporta — un fallo ahí sería un hallazgo sobre la corrida, no sobre
el puente.
meltemi fleet lee la última corrida por entrada y muestra el nivel
verificado junto al declarado. Declarado ≠ verificado es información, no
vergüenza.
Apagado entero contra un CLI instalado por npm (Windows, opt-in)
Sección titulada «Apagado entero contra un CLI instalado por npm (Windows, opt-in)»Esta comprobación no es un test: es un procedimiento de cinco minutos que
alguien corre a mano, en Windows, contra un CLI real. Existe porque lo que la
suite puede probar y lo que hay que creer no coinciden aquí. Los tests lanzan
un .cmd que ellos mismos escriben; lo que un instalador de npm genera es un
script que Meltemi no controla y que puede cambiar con la versión del CLI, del
gestor de paquetes o de Node. Un ensayo contra el shim real es el único que
responde por el shim real.
Se corre: cuando se toca el lanzamiento o el apagado de agentes, y cuando se sube la versión de un CLI de nivel 1 que instale por npm.
Qué hace falta: Windows, un CLI de nivel 1 instalado con npm i -g, y una
sesión abierta en él.
-
Confirme que la ruta que Meltemi va a lanzar es un shim y no un ejecutable. Un
.cmdo un.bates un shim; un.exeno, y entonces esta comprobación no tiene nada que medir en ese agente.Ventana de terminal (Get-Command claude).Source -
Abra una sesión con ese agente, desde cualquier superficie, y deje que empiece a trabajar.
-
Con la sesión en vuelo, mire el árbol. Anote los identificadores: el del shim y el del proceso que cuelga de él.
Ventana de terminal Get-CimInstance Win32_Process | Where-Object { $_.Name -in 'cmd.exe','node.exe' } | Select-Object ProcessId, ParentProcessId, CommandLine | Format-List -
Cancele la sesión.
-
Repita el paso 3. Ninguno de los identificadores anotados debe seguir ahí. Que desaparezca el shim y sobreviva el proceso de debajo es exactamente el defecto que el ámbito de proceso previene, y verlo aquí significa que el ámbito no se aplicó a esa ruta de lanzamiento.
-
La otra mitad, la que un apagado ordenado no ejercita: repita del 2 al 3, y en lugar de cancelar, mate el daemon sin darle ocasión de limpiar.
Ventana de terminal Stop-Process -Name meltemid -ForceVuelva al paso 3. Tampoco debe quedar nada: el sistema cierra los handles del proceso muerto, y el ámbito se lleva consigo lo que tenía dentro. Si sobrevive algo, el ámbito no se abrió o el agente no llegó a entrar en él.
Qué se registra: el resultado va abajo, en la corrida del día, con el CLI, su versión, la fecha y qué se observó — nunca «verificado» a secas. Un resultado negativo es un hallazgo, no un fallo del procedimiento.
Qué campo pobló cada agente para sus modos
Sección titulada «Qué campo pobló cada agente para sus modos»El protocolo deja anunciar los modos de sesión de dos maneras: por el campo de modos, que llegó primero, y como una opción de configuración de categoría modo, que llegó después. Meltemi lee las dos y da precedencia a la segunda, pero cuál usa cada CLI real es una medición, no una suposición — y de eso depende que la síntesis del campo viejo siga haciendo falta o sea código muerto esperando a que alguien lo note.
Para cada agente de nivel 1 instalado: abra una sesión ACP y anote cuál de los
dos campos vino poblado en la respuesta de apertura — modos, opciones de
configuración, las dos, o ninguno — con la versión del CLI y la fecha. El log
de la sesión lo dice sin herramientas extra: el evento config_options_announced
lleva lo anunciado ya traducido, y una opción de id mode y categoría mode
que el agente no anunció como tal es la señal de que vino del campo viejo.
El resultado se anota abajo, en la corrida del día.
Pierna de proveedor local — OpenCode contra un modelo de la propia máquina
Sección titulada «Pierna de proveedor local — OpenCode contra un modelo de la propia máquina»Estado: pendiente. Verificada en forma el 2026-09-25 con OpenCode 1.18.32:
con la receta 1 de docs/local-models.md, opencode models ollama lista ollama/qwen2.5-coder:7b, y a través de Meltemi, con su perfil,
OpenCode anuncia ese modelo como vigente, --model ollama/qwen2.5-coder:7b se
fija y se confirma antes del primer prompt y un modelo no anunciado se rehúsa
sin prompt. Sin servidor de modelos el turno falla a los ~70 s con el «Cannot
connect to API» de OpenCode. Nunca corrida en vivo: la máquina que la escribió
no tiene servidor de modelos. Quien la corra reemplaza este párrafo por su
resultado; hasta entonces la pierna no está aprobada.
Es una pierna de nivel 1, a mano y por opt-in como todo lo de esta página:
el arnés automatizado (conformance_real) cubre los dos adaptadores propios, y
automatizar un agente nativo es de corrida-integrada. No gasta la cuenta de
nadie: el modelo corre en la máquina.
Requisitos: el servidor de modelos instalado y sirviendo, con el modelo
bajado (winget install Ollama.Ollama; ollama pull qwen2.5-coder:7b);
OpenCode 1.18.32 o posterior; y la receta 1 como el perfil opencode-ollama de
un repositorio fixture — nunca este.
| Criterio | Cómo se ejerce | Pasa si |
|---|---|---|
| sesión | meltemi session "resume el README" --agent opencode-ollama en el fixture |
la sesión abre, el log trae agent_resolved y config_options_announced, y cierra con session_ended |
| streaming | el mismo turno | llegan fragmentos del agente (agent_update con agent_message_chunk) antes del cierre, y el turno no se declara vacío |
| permisos | meltemi session "crea NOTAS.md con una línea" --agent opencode-ollama, en un terminal |
el aprobador pregunta con las opciones del agente y la decisión queda en el log (permission_decided) |
| cancelación | otra sesión que pida escribir; Ctrl+C en la pregunta del aprobador | la sesión se cancela antes que nada, el turno cierra cancelled y el comando sale con 13 |
Qué se registra, en lugar del estado de arriba: la fecha, opencode --version, la versión del servidor de modelos, el modelo, el sistema, el
resultado por criterio y los tiempos medidos —hasta el primer fragmento y el
turno completo—. Un criterio que no se pudo ejercer no se escribe, y no se
escribe como aprobado.
Pierna de permisos — OpenCode lanzado de modo que pregunte
Sección titulada «Pierna de permisos — OpenCode lanzado de modo que pregunte»Estado: pendiente. Desde opencode-que-pregunta (2026-10-28), Meltemi lanza
OpenCode con OPENCODE_CONFIG_CONTENT en el entorno, con una configuración en
línea que pone en ask edit, bash, webfetch, websearch, task y
external_directory. Los tests de CI prueban que la resolución lleva la
variable, con su precedencia y sin tocar un comando literal; no pueden probar
que OpenCode, lanzado así, mande sus preguntas por ACP, porque CI no corre
agentes reales. Eso lo confirma esta pierna. Quien la corra reemplaza este
párrafo por su resultado; hasta entonces la pierna no está aprobada.
Es una pierna de nivel 1, a mano y por opt-in como todo lo de esta página,
en un repositorio fixture, nunca este. Necesita OpenCode 1.18.32 o posterior,
con un modelo que ya funcione (la receta local de
docs/local-models.md sirve y no gasta la cuenta de nadie),
y sin OPENCODE_CONFIG_CONTENT en el entorno de quien la corre: si la
variable ya está, gana la del usuario y la pierna mide otra cosa.
| Criterio | Cómo se ejerce | Pasa si |
|---|---|---|
| editar pregunta | meltemi session "crea NOTAS.md con una línea" --agent opencode, en un terminal |
el aprobador pregunta antes de que exista NOTAS.md, y la decisión queda en el log (permission_decided) |
| denegar detiene | la misma sesión, respondiendo que no | NOTAS.md no existe al cerrar, y el turno cierra sin error de protocolo |
| un comando pregunta | meltemi session "ejecuta git status y dime qué ves" --agent opencode |
el aprobador pregunta por el comando, con el comando en la petición |
| leer no pregunta | meltemi session "resume el README" --agent opencode |
la sesión cierra con session_ended sin ninguna petición de permiso |
| el usuario manda | la sesión de editar con OPENCODE_CONFIG_CONTENT='{"permission":{"edit":"allow"}}' en el entorno del daemon |
OpenCode edita sin preguntar: el valor del usuario ganó, que es lo que el diseño promete |
Qué se registra, en lugar del estado de arriba: la fecha, opencode --version, el sistema, el modelo, el resultado por criterio y, si un criterio
falla, lo que se vio en el log de la sesión. Si OpenCode no relevara ask por
ACP, la sesión de editar esperaría una respuesta que no llega: eso es una falla
de la pierna, cerrada (no edita), y se escribe como tal. Un criterio que no se
pudo ejercer no se escribe, y no se escribe como aprobado.
Cómo se registra en el método
Sección titulada «Cómo se registra en el método»Los escenarios que solo un CLI real ejerce se marcan con nota, fuente y fecha. El verbo vive en las paletas interactivas de la TUI y la GUI; desde un script se llama al método directamente:
cargo run -q -p meltemi --example rpc -- sdd/verify-mark '{ "projectRoot": "<ruta del repo>", "change": "<change>", "scenario": "<nombre exacto del escenario>", "note": "Verificado a mano contra <CLI> <versión> el <fecha>: <qué se observó>."}'La nota es el registro. Debe decir contra qué binario, en qué versión, en qué fecha y qué se observó — nunca «verificado» a secas, y nunca de memoria.
Formas de anuncio de modo — corrida del 2026-09-18
Sección titulada «Formas de anuncio de modo — corrida del 2026-09-18»Windows 11 (26200), x86_64. Corrida de apertura de sesión únicamente: se envía
initialize y session/new, se lee la respuesta y se cierra la entrada.
No se envía prompt, así que no gasta turno de proveedor.
Inventario del equipo: de las seis entradas de nivel 1 del catálogo, solo
una está instalada — opencode. gemini, copilot, agent (Cursor),
kiro-cli y kilo no se encuentran en este equipo, así que no se midieron y
esta página no dice nada sobre ellas. claude 2.1.261 y codex-cli 0.77.0
están instalados pero son de nivel 2: no responden ACP por sí mismos, sino
a través de nuestros adaptadores, y quedan fuera del alcance de esta
comprobación (design D8 acota a nivel 1).
opencode 1.14.33: las dos formas, pobladas
Sección titulada «opencode 1.14.33: las dos formas, pobladas»| Campo | Poblado | Contenido |
|---|---|---|
modes |
sí | build (actual) y plan, con descripción |
configOptions |
sí | model (categoría model, 317 valores) y mode (categoría mode, 2 valores, actual build) |
Tres cosas que esta medición decide, y que no se podían decidir suponiendo:
- La precedencia de D6 no es teórica: es el caso real. El único agente de nivel 1 instalado puebla las dos formas a la vez, con los mismos dos modos en ambas. Fusionarlas habría producido dos selectores de modo, o uno con valores duplicados. La regla «gana la opción de configuración, entera» deja exactamente un selector, que es lo que el agente ofrece.
- La síntesis del campo viejo no se ejercita contra este agente, y sigue
haciendo falta igual: es la rama para el agente que puebla
modesy noconfigOptions, que es el agente nacido antes de que las opciones de configuración existieran. Ninguno de los instalados aquí lo es. Queda cubierta por los tests unitarios y por el agente simulado bajo--modes, y esta página dice explícitamente que ningún binario real la ha ejercido todavía. modelo-y-esfuerzo-por-sesionD9 quedó desactualizada. Concluyó que «ningún proveedor pineado anuncia opciones de sesión»;opencode 1.14.33anuncia dos, una de ellas con 317 modelos. No es un error de aquella conclusión sino su fecha: lo que no anunciaba entonces, anuncia ahora.
Última corrida — 2026-09-26 (UTC)
Sección titulada «Última corrida — 2026-09-26 (UTC)»Windows 11 Pro (26200), x86_64, desde el taller de corrida-integrada. Dos
fuentes, y se dice cuál es cuál: el arnés conformance_real para el dialecto de
Claude Code, y la corrida integrada —la
matriz de siete verbos de scripts/smoke-agents.ps1— para OpenCode, que es
nativo y el arnés no cubre.
Dialecto de sesión headless — claude 2.1.280: nivel verificado 2
Sección titulada «Dialecto de sesión headless — claude 2.1.280: nivel verificado 2»MELTEMI_CONFORMANCE_AGENT=claude-code, a las 00:46Z. Las cuatro comprobaciones
se ejercieron contra el binario real y las cuatro aprobaron: streaming,
session, permissions y cancellation. El paro se respondió en 10,07 s
como cancelled, sin nada corriendo después: la ruta de abandono, la misma que
la corrida del 2026-07-31 midió en 10,1 s. Reemplaza, para esta versión, el
nivel verificado contra 2.1.167.
Lo que la conformidad no ve y la corrida integrada sí: las peticiones de este
adaptador llegan sin rawInput ni locations, de modo que una regla de
permisos acotada por ruta no puede concederlas (hallazgo 2 del informe;
adaptadores-con-rawinput). El criterio permissions mide que la petición
llegue y se decida, no que una regla la reconozca.
Nativo — opencode 1.18.32: observado, sin nivel verificado
Sección titulada «Nativo — opencode 1.18.32: observado, sin nivel verificado»Desde la corrida integrada, no desde el arnés, y por eso no se persiste como resultado de conformidad:
| Criterio | Lo observado |
|---|---|
| sesión | abre, anuncia sus opciones y cierra con session_ended; se reanuda con direct |
| streaming | fragmentos del agente antes del cierre en todas las sesiones, y uso reportado por el protocolo (contexto y costo 0 USD) |
| permisos | no pide ninguno: edita sin preguntar, con y sin regla (hallazgo 1 del informe). El proxy no lo gobierna en su configuración por omisión |
| cancelación | cancel sobre una sesión viva la terminó en 9,3 s |
Con un criterio que falla, el nivel 1 no queda verificado, y la página lo dice en vez de callar la fila.
No medidos
Sección titulada «No medidos»Los nivel 1 del catálogo que no están instalados en esta máquina: Gemini CLI, GitHub Copilot CLI, Cursor CLI, Kiro CLI y Kilo Code. Tampoco Aider (nivel 3, que el daemon no lanza hoy) ni Antigravity (nivel 4). Ninguno se da por aprobado ni por fallido: no se midieron.
Corrida del 2026-09-25 (UTC)
Sección titulada «Corrida del 2026-09-25 (UTC)»Windows 11 (26200), x86_64. Solo el dialecto de servidor
(MELTEMI_CONFORMANCE_AGENT=codex-cli): es la corrida que exige el re-anclaje
de codex-al-dia al volcado de 0.156.1 (escenario «El re-anclaje exige la
corrida real»). CLI con sesión iniciada en una cuenta ChatGPT, y la
configuración del mantenedor, que arranca tres servidores MCP propios.
Dialecto de servidor JSON-RPC — codex-cli 0.156.1: nivel verificado 2
Sección titulada «Dialecto de servidor JSON-RPC — codex-cli 0.156.1: nivel verificado 2»Las cuatro comprobaciones del nivel se ejercieron contra el binario real, y las cuatro aprobaron.
| Comprobación | Resultado | Lo observado |
|---|---|---|
session |
aprueba | El handshake leyó 0.156.1 del primer token del user agent, meltemi-codex-acp/0.156.1 (Windows 10.0.26200; x86_64) xterm-256color (meltemi-codex-acp; 0.1.1). La procedencia registra providerVersion y providerVerifiedVersion iguales, así que no hubo fila de distancia. |
streaming |
aprueba | El turno completo corrió hasta su fin y el mensaje del agente llegó en fragmentos. |
permissions |
aprueba | Seis aprobaciones llegaron al proxy: cinco comandos y una escritura. El decisor de la corrida concedió cada una «una vez», como lo haría un humano en la bandeja. |
cancellation |
aprueba | El paro se respondió en 56 ms como cancelled, y no quedó sesión activa. |
-
Compuerta efectiva observada, tal como la registró la procedencia de las dos sesiones:
approvalPolicyuntrusted,approvalsRevieweruserysandbox{"type":"readOnly","networkAccess":false}. Es exactamente la compuerta pedida. -
La escritura aprobada. La propuesta se escribió bajo el sandbox de solo lectura, en
.meltemi/changes/<change>/proposal.mddel proyecto de prueba, tras una aprobación de cambio de archivo que el proxy concedió. Confirma, en una sesión pilotada por Meltemi, lo que el riesgo R5 midió el 2026-09-24 con un cliente mínimo. -
Todos los comandos pidieron permiso, lecturas incluidas. En Windows el CLI envuelve cada comando en
pwsh.exe -Command …, y ninguno cayó en la lista de lecturas de confianza de la políticauntrusted. La fila «Codex runs some things without asking the permission proxy» sigue siendo cierta como cota superior; en esta plataforma y en esta corrida, ningún comando corrió sin decisión del proxy. -
Un comando leyó fuera del proyecto, en el directorio de skills del usuario (
~/.agents/skills/). Lo hizo con aprobación: el sandbox de solo lectura no impide leer, y el proxy lo vio y decidió. -
Consecuencia de la configuración MCP. Las dos sesiones declararon, antes del prompt, la fila de lo que la compuerta no cubre y una fila por cada servidor MCP que la configuración del mantenedor arranca:
codex_apps,cua_replynode_repl. Ninguna llamada a esos servidores apareció en el turno. De haber ocurrido sin aprobación, habría corrido fuera del proxy, que es justo lo que esas filas declaran. -
Tiempos, con el reloj del log de sesión (resolución de un segundo):
Tramo Duración Apertura: lanzamiento, handshake, hilo y compuerta 3 s Turno completo, del prompt al cierre 54 s Pierna del paro, del prompt a cancelled7 s Del paro a su respuesta 56 ms -
El nivel se otorga con esta corrida.
fleet/listreportacodex-clicon nivel verificado 2 yverifiedVersion0.156.1.
La corrida fallida de codex-cli 0.77.0, que esta reemplaza
Sección titulada «La corrida fallida de codex-cli 0.77.0, que esta reemplaza»La corrida del 2026-07-31, abajo, queda como la evidencia de por qué se
re-ancló. Con 0.77.0 el proveedor rechazaba cada turno con sus propias
palabras: «The ‘gpt-5.6-sol’ model requires a newer version of Codex. Please
upgrade to the latest app or CLI and try again.» streaming falló citando
ese motivo, y el nivel quedó en 0 (escenario «Una comprobación fallida se
registra con las palabras del proveedor»). Desde codex-al-dia el piso del
adaptador es 0.156.1: un CLI 0.77.0 ya no llega al turno, porque el handshake
lo rehúsa con el remedio de actualizarlo.
Corrida del 2026-07-31
Sección titulada «Corrida del 2026-07-31»Windows 11 (26200), x86_64. Ambos CLIs presentes y con sesión iniciada. Primera corrida con las dos piernas.
Dialecto de sesión headless — claude 2.1.167: nivel verificado 2
Sección titulada «Dialecto de sesión headless — claude 2.1.167: nivel verificado 2»Los cuatro criterios ejercidos y aprobados, por primera vez.
-
streaming,sessionypermissionsaprueban como el 2026-07-28, y por las mismas razones; la nota de aquella corrida sobre cuál de los dos canales de permiso quedó ejercido —el hook, no el prompt-tool— sigue siendo exacta y sigue vigente. -
cancellationaprueba: la segunda sesión se paró en cuanto el CLI habló dentro de su turno,proposerespondiócancelledy no quedó sesión alguna activa. Es el criterio que ninguna corrida anterior había ejercido. -
Dos observaciones distintas del mismo binario, y la diferencia importa. El paro se respondió en 4,1 s en la corrida de las 13:09Z y en 10,1 s en la de las 13:41Z. La gracia del turno cancelado son 5 s y la del apagado otros 5: por debajo de la primera, el CLI cerró su salida por su cuenta —vio el fin de entrada y terminó su turno—; alrededor de la suma de las dos, no lo hizo y el adaptador lo terminó. El criterio mide lo mismo en los dos casos (el turno para y no queda nada corriendo) y las dos rutas son las que el escenario «Turno cancelado terminado aunque el proveedor no lo atienda» describe: la de abandono no es teórica, ocurre.
-
Costo real, medido en los archivos de sesión del propio CLI (cuatro sesiones, las dos corridas del día):
Pierna Entrada Escritura de caché Lectura de caché Salida 13:09Z turno 3 648 20 263 70 324 1 670 13:09Z paro 4 436 9 024 84 774 1 913 13:41Z turno 4 397 8 658 64 503 1 842 13:41Z paro 4 464 8 237 64 079 1 667 La pierna del paro cuesta prácticamente lo mismo que la del turno completo. Se esperaba que fuera la barata; no lo es, y la página lo dice donde se presupuesta.
Dialecto de servidor JSON-RPC — codex-cli 0.77.0: nivel verificado 0
Sección titulada «Dialecto de servidor JSON-RPC — codex-cli 0.77.0: nivel verificado 0»Nivel no otorgado, y el motivo no está en el puente. Lo que la corrida sí estableció y lo que no, en orden:
- Un defecto real, que solo esta corrida podía encontrar: el CLI no se podía
lanzar. El catálogo lo daba por presente —y tenía razón— y el adaptador
rehusaba con «
codexcould not be launched (program not found)». La causa es de Windows:npm i -gdejacodex.cmdy ningúncodex.exe, yCreateProcesssólo añade.exea un nombre pelado. Corregido en el adaptador (design D14): el nombre declarado se resuelve al archivo que la plataforma ejecuta, con el mismo conjunto de extensiones que usa el catálogo. - Tras la corrección,
sessionaprueba: el CLI se lanza, el handshake responde y el adaptador lee0.77.0, dentro del rango declarado[0.77.0, 1.0.0). Lanzamiento, handshake y lectura de versión quedan verificados contra el binario real. streamingfalla, y el que dice que no es el proveedor: todo turno vuelve con «The ‘gpt-5.6-sol’ model requires a newer version of Codex. Please upgrade to the latest app or CLI and try again.» El modelo por defecto de la cuenta exige un Codex más nuevo que el 0.77.0 que esta change ancló. El adaptador propaga ese rechazo con las palabras del proveedor y su remedio, que es exactamente lo que debe hacer; no hay turno que streamear.permissionsycancellationno se reportan: sin turno en vuelo no hubo a qué pedir permiso ni qué parar.- Qué haría falta para otorgarle el nivel: subir el CLI a una versión que sirva el modelo por defecto de la cuenta y re-anclar el volcado de esquema vendorizado a esa versión — que es uno de los disparadores que esta misma página lista. Ni el uno sin el otro: el esquema congelado es lo que dice qué versión se soporta.
- El re-anclaje del esquema y el handshake del 2026-07-28 siguen vigentes: se
verificaron contra este mismo
0.77.0.
Corrida anterior — 2026-07-28
Sección titulada «Corrida anterior — 2026-07-28»Windows 11 (26200), x86_64. Ambos CLIs presentes y con sesión iniciada.
Dialecto de servidor JSON-RPC — codex-cli 0.77.0
Sección titulada «Dialecto de servidor JSON-RPC — codex-cli 0.77.0»codex app-server generate-json-schema --out <dir>produce 129 archivos; los 21 vendorizados encore/mock-provider/schemas/codex-app-server/son idénticos byte a byte al volcado fresco. El contrato congelado sigue siendo el contrato.- El handshake real responde
codex_cli_rs/0.77.0 (Windows 10.0.26200; x86_64) xterm-256color (meltemi-codex-acp; 0.1.0)al mismoinitializeque envía el adaptador — el formato de user agent del que el adaptador lee la versión, confirmado contra el binario.0.77.0cae dentro del rango declarado[0.77.0, 1.0.0). - El turno completo (que sí gasta cuota de la cuenta) no se corrió en esta fecha; el handshake y el esquema sí, y son lo que ancla la conformidad por versión. (El 2026-07-31 sí se intentó, y fue así como se descubrió que el lanzamiento del CLI estaba roto en Windows y que el modelo por defecto de la cuenta exige un Codex más nuevo: dos cosas que un handshake no puede ver.)
Dialecto de sesión headless — claude 2.1.167
Sección titulada «Dialecto de sesión headless — claude 2.1.167»Dos corridas el mismo día: la primera encontró un defecto que ninguna prueba podía encontrar, y la segunda —tras la tarea 5.3— es la que vale.
Lo que la primera corrida (17:22Z) encontró, y por qué era invisible: el CLI
no emite el evento inicial hasta recibir su primera entrada, y el adaptador lo
esperaba antes de enviar nada. Resultado: 60 segundos de espera y
provider_handshake_failed en toda sesión. Nivel verificado: 0, con los dos
criterios ejercidos fallando. Los guiones del cable simulado emitían el evento
inicial antes del primer await-input, de modo que ninguna prueba de CI podía
verlo: un fixture solo prueba aquello a lo que se le pidió parecerse. La tarea
5.3 corrigió las dos cosas —el adaptador lee el evento en el primer turno y
dicta la identidad de sesión con --session-id; los guiones anuncian la sesión
donde el CLI la anuncia— y el orden quedó clavado con una prueba que corre el
cable simulado sin entrada y exige silencio.
La segunda corrida (18:07Z), después de la corrección, con el mismo binario:
- La sesión abre y el turno completa.
proposecontra un repositorio fixture temporal devolviócompleted. streamingaprueba: los deltas del turno llegan al log de sesión como actualizaciones, no como un bloque tardío.sessionaprueba: el log registra el binario efectivo y2.1.167, la versión que el propio CLI respondió.permissionsaprueba: el CLI real preguntó por sus herramientas, la pregunta llegó a la bandeja comopermission/requesty volvió decidida. Lo que quedó ejercido es el hook, y conviene decir cuál porque el passthrough tiene dos canales y esta corrida no los ejerció los dos. El orden documentado del proveedor pone elPreToolUseprimero y su decisión es final; el adaptador lo instala con matcher*y su código no tiene camino por el que se abstenga —decideallowodeny, nunca «pregunta a otro»—, de modo que el prompt-tool solo se alcanza cuando el hook no puede correr o agota su plazo, y ninguna de esas dos cosas ocurrió aquí. El prompt-tool sigue configurado en el mismo lanzamiento y CI lo ejerce contra el cable simulado (directivaask-prompt-toolencore/meltemid/tests/e2e_adaptadores_claude.rs); contra un binario que no es un fixture no se ha ejercido todavía, y es el tirante del cinturón precisamente porque el cinturón no falló.cancellationno se ejerce, y por eso el nivel verificado sigue siendo 0: la corrida manual nunca ha ejercido ese criterio, y un nivel se otorga solo cuando todos los suyos están presentes y aprobados. Es un 0 distinto del de la mañana —tres de cuatro criterios aprobando contra el binario real frente a ninguno— y la lista decriteriapersistida es lo que los distingue. (Superado el 2026-07-31: la corrida ganó una segunda pierna que sí ejerce la cancelación, y esta entrada quedó en nivel verificado 2. La frase «gastaría otro turno y sería otra tarea» que aquí figuraba resultó ser el techo del procedimiento, no una decisión: mientras estuvo en pie, el nivel 2 era inalcanzable por construcción.)- Coste: un turno de opus — 8,8k tokens de entrada, 22,2k de escritura de caché, 139,7k de lectura de caché y 3,8k de salida.
Lo que el binario dijo de sí mismo, en las dos corridas:
apiKeySourcees real y vale"none"bajo la sesión iniciada. El nombre era provisional desde la tarea 1.3 y queda anclado: la guarda contra el modo de clave de API lee un campo que existe.- No hay arreglo
capabilitiesen el evento inicial de esta versión. El design D4 lo daba por existente para detección de features; no está. El adaptador lo trataba como información y no como requisito, así que nunca rehusó por su ausencia — y desde 5.3 ya no lo lee en absoluto: un campo que ningún CLI emite no describe nada. - El evento inicial trae
claude_code_version,permissionMode,model,tools,slash_commands,agents,skills,plugins,output_style,memory_pathsymcp_serverscomo objetos{name, status}. --session-id <uuid>existe y el CLI lo respeta al pie de la letra: el archivo de sesión que dejó el CLI lleva por nombre el UUID que el adaptador acuñó. Es lo que permite nombrar la sesión antes de que el CLI hable, y lo que sostiene la reanudación de la tarea 3.5.--permission-prompt-toolexiste y el parser lo acepta (un flag inventado falla conunknown option; este no), pero no aparece enclaude --help. La infradocumentación que el design D5 fijó como riesgo, confirmada en el binario y no de memoria.--baresigue siendo opt-in y su propia ayuda dice que la autenticación pasa a ser estrictamenteANTHROPIC_API_KEY: el flip que D4 teme no ha ocurrido en esta versión.