Skip to content

v3.11.0 — verificar-sesion, y corrige una declaración de compatibilidad falsa - #34

Merged
novanoticia merged 2 commits into
mainfrom
claude/plugins-compatibility-0pxfco
Aug 7, 2026
Merged

v3.11.0 — verificar-sesion, y corrige una declaración de compatibilidad falsa#34
novanoticia merged 2 commits into
mainfrom
claude/plugins-compatibility-0pxfco

Conversation

@novanoticia

Copy link
Copy Markdown
Owner

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 con osascript -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 compatibility pasan 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.jsonl no recibió ni una línea, ni scores.jsonl. Sus últimas entradas siguen siendo del 23 de julio. El mtime de ~/.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:

  • 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. 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.

  • 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. Dos decisiones de diseño que importan: un log ausente no es un error del comando, es el hallazgo; y un registro sin to_folder no cuenta, porque es telemetría y no prueba que se moviera nada.
  • Nuevo PASO 5.V en el SKILL.md, obligatorio antes del resumen. El veredicto del script manda sobre la impresión del modelo de haber terminado. Con incompleto o sin_registro el triaje no es válido y hay que decirlo, 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.
  • CLAUDE.md recoge la invariante para quien venga después.

Comprobado

  • Los siete gates en verde en local, incluido el fuzz de 20 000 iteraciones, que pasa de 4 a 5 entradas con cmd_verificar_sesion dentro
  • 296 tests, nueve de ellos nuevos, incluido el caso que lo motivó: log ausente → sin_registro, y registro sin to_folder → no prueba movimiento
  • Totalidad verificada: entradas None, [], "x", 3, {}, esperados booleano, negativo o no entero, y ruta no-cadena, todas devuelven un dict serializable sin lanzar
  • Los cinco comportamientos del subcomando probados también por CLI, no solo por función
  • Revalidado después del rebase sobre main, sin dar por hecho que siguiera verde
  • Versión a v3.11.0 con bump-version.sh en los nueve sitios, changelog escrito

Notas

Un gate que ya existía me pilló una omisión, y merece decirse porque es la prueba de que sirven: test_contrato_skill exige 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 main tras el merge del PR #33, conservando los dos commits nuevos.


Generated by Claude Code

claude added 2 commits August 7, 2026 14:20
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
@novanoticia
novanoticia merged commit 6ea8eeb into main Aug 7, 2026
2 checks passed
@novanoticia
novanoticia deleted the claude/plugins-compatibility-0pxfco branch August 7, 2026 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants