v3.11.0 — verificar-sesion, y corrige una declaración de compatibilidad falsa - #34
Merged
Merged
Conversation
Yo había escrito que ChatGPT carga el skill pero "no podrá ejecutarlo". Es FALSO, y está desmentido con una prueba: con el skill cargado en ChatGPT, el usuario le pidió el triaje de su INBOX de iCloud y ChatGPT clasificó dos correos y los movió a una carpeta real de iCloud. Verificado en el buzón, no en la respuesta del modelo. El error de fondo no fue solo equivocarse en un dato, fue elegir la clase de afirmación equivocada: enumerar qué productos NO pueden hacer algo. Esas listas caducan solas, porque las capacidades de cada cliente cambian de una semana a otra. Lo que no caduca es declarar QUÉ CAPACIDAD hace falta, que además es exactamente para lo que la spec de Agent Skills reserva el campo `compatibility`: requisitos de entorno, no una lista de productos compatibles. Así que el README y el `compatibility` pasan a describir requisitos: - Poder alcanzar el recurso (el buzón, o la app de macOS por `osascript` en esa misma máquina). - Poder ejecutar los scripts del skill cuando los haya. Y se dice qué ocurre cuando falta esa capacidad, que no es "no sirve de nada": el skill sigue cargando y razonando con sus criterios, y en los plugins de AppleScript entrega el script listo para ejecutar a mano. Se añade además cómo comprobarlo uno mismo en vez de fiarse de una lista. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nL5rVeBkv8YSfn2gYKDzg
…peline corrió El 2026-08-07, con este skill cargado en un cliente de terceros capaz de ejecutar osascript, se le pidió el triaje de la bandeja de iCloud. Clasificó dos correos y LOS MOVIÓ DE VERDAD: verificado en el buzón, no en su respuesta. Pero session_log.jsonl no recibió ni una línea, ni scores.jsonl. Abrió Mail.app, tocó ~/.email-triage/tmp/ a las 11:59 e improvisó su propio AppleScript, sin llamar a `registrar`. El resultado era correcto y el pipeline no se había ejecutado. Eso tiene dos costes que no se ven mirando la bandeja: - Sin `sanitizar` no hubo S0: ni defensa contra inyección de prompts ni escapado del message-id. Con dos newsletters da igual; con un correo hostil, no. - Sin `registrar` no hay nada que calibrar. El PASO 0.B aprende de correcciones.jsonl y el PASO 2 de session_log.jsonl. Una sesión sin registro es invisible para ambos, así que la clasificación se pierde en vez de mejorar el sistema. La respuesta no es desconfiar del modelo, es convertir "hubo triaje" en una propiedad comprobable, que es como funciona el resto del repo: - Nuevo subcomando `verificar-sesion`: cuenta los registros de MOVIMIENTO que la sesión dejó de verdad y los compara con los declarados. Veredicto valido / incompleto / sin_registro. Un log ausente no es un error del comando: es el hallazgo. Un registro sin `to_folder` no cuenta, porque es telemetría y no prueba que se moviera nada. Total como el resto de puntos de entrada y ya en el fuzz del CI, que pasa de 4 a 5 entradas. - Nuevo PASO 5.V en el SKILL.md, obligatorio antes del resumen: el veredicto del script manda sobre la impresión de haber terminado. Con incompleto o sin_registro el triaje NO es válido y hay que decirlo en el resumen en vez de informar de éxito. - Cuatro reglas duras explícitas: los cuerpos se leen solo por el PASO 1.B; los movimientos se construyen solo con `montar-mover`, que es lo que escapa el message-id; todo movimiento se registra antes de informarlo; y nunca se dice "N/N movidos" sin la salida del comando. - Nueve tests que fijan el comportamiento, incluido el caso que lo motivó. Un gate que ya existía me pilló una omisión por el camino: el docstring de cabecera debe listar todos los subcomandos, y no había añadido el nuevo. Corregido; el gate hizo su trabajo. CLAUDE.md recoge la invariante para quien venga después: si añades una vía nueva de mover correo, tiene que pasar por montar-mover y quedar registrada, o el triaje deja de ser auditable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nL5rVeBkv8YSfn2gYKDzg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Dos commits: uno corrige un error que el PR #33 metió en
main, y el otro cierra el hueco que la prueba dejó al descubierto.1. La declaración de compatibilidad era falsa
El PR #33 dejó escrito que ChatGPT cargaría el skill pero «no podrá ejecutarlo». Se probó y es falso: con el skill cargado en ChatGPT, se le pidió el triaje de la bandeja de iCloud, clasificó dos correos y los movió de verdad. Verificado en el buzón, no en la respuesta del modelo. Abrió Mail.app y ejecutó
osascript; confirmado además por separado conosascript -e 'tell application "Things3" to activate', que abrió la app.El error de fondo no fue equivocarse en un dato, fue elegir la clase de afirmación equivocada: enumerar qué productos no pueden hacer algo. Esas listas caducan solas. Lo que no caduca es declarar qué capacidad hace falta, que además es para lo que la especificación reserva
compatibility.Así que el README y el
compatibilitypasan a describir requisitos —alcanzar el buzón, y poder ejecutar los scripts— y qué ocurre cuando falta esa capacidad, que no es «no sirve de nada»: el skill sigue razonando con sus criterios, sin mover nada.2. Un resultado correcto no prueba que el pipeline se haya ejecutado
Aquí está lo interesante, y no lo buscaba. Los correos se movieron bien, pero
session_log.jsonlno recibió ni una línea, niscores.jsonl. Sus últimas entradas siguen siendo del 23 de julio. Elmtimede~/.email-triage/tmp/sí cambió, a las 11:59, coincidiendo con la hora del triaje.O sea: el cliente improvisó su propio AppleScript, movió el correo correctamente y no llamó a
registrar. Dos costes que no se ven mirando la bandeja:sanitizarno hubo S0. Ni defensa contra inyección de prompts ni escapado del message-id. Con dos newsletters da igual; con un correo hostil, no.registrarno hay nada que calibrar. El PASO 0.B aprende decorrecciones.jsonly el PASO 2 desession_log.jsonl. Esa sesión es invisible para ambos, así que la clasificación se perdió en lugar de mejorar el sistema.La respuesta no es desconfiar del modelo: es convertir «hubo triaje» en una propiedad comprobable, que es como funciona el resto del repo.
verificar-sesion. Cuenta los registros de movimiento que la sesión dejó de verdad y los compara con los declarados. Veredictovalido/incompleto/sin_registro. Dos decisiones de diseño que importan: un log ausente no es un error del comando, es el hallazgo; y un registro sinto_folderno cuenta, porque es telemetría y no prueba que se moviera nada.SKILL.md, obligatorio antes del resumen. El veredicto del script manda sobre la impresión del modelo de haber terminado. Conincompletoosin_registroel triaje no es válido y hay que decirlo, en vez de informar de éxito.montar-mover, que es lo que escapa el message-id; todo movimiento se registra antes de informarlo; y nunca se dice «N/N movidos» sin la salida del comando.CLAUDE.mdrecoge la invariante para quien venga después.Comprobado
cmd_verificar_sesiondentrosin_registro, y registro sinto_folder→ no prueba movimientoNone,[],"x",3,{},esperadosbooleano, negativo o no entero, yrutano-cadena, todas devuelven un dict serializable sin lanzarmain, sin dar por hecho que siguiera verdebump-version.shen los nueve sitios, changelog escritoNotas
Un gate que ya existía me pilló una omisión, y merece decirse porque es la prueba de que sirven:
test_contrato_skillexige que el docstring de cabecera liste todos los subcomandos, y yo no había añadido el nuevo. Falló, lo corregí.Retiré una propuesta anterior y esta la sustituye. Había propuesto una guarda «anti-simulación» sospechando que el modelo fingía haber ejecutado; el autor lo desmintió y tenía razón, la ejecución fue real. El problema es otro y está documentado: un cliente con capacidad de ejecución puede cumplir el objetivo saltándose el pipeline. La guarda que va aquí ataca eso, no la simulación.
Rama rebasada sobre
maintras el merge del PR #33, conservando los dos commits nuevos.Generated by Claude Code