Codex vs Claude: en este código no se trata tanto de encontrar un ganador universal como de elegir el estilo de trabajo en el que puedas confiar cada día. Ambos productos permiten inspeccionar un repositorio, editar varios archivos, ejecutar comandos, realizar pruebas y explicar un parche. Las diferencias significativas residen en cómo se delega el trabajo, con qué frecuencia se supervisa al agente, cuánta información se puede examinar y cómo se adapta cada herramienta al entorno de desarrollo existente.
En nuestra prueba controlada con el repositorio de cuatro tareas, Codex obtuvo una puntuación de 98/100 y Claude Code, de 99/100. Ambos completaron el trabajo principal. Claude Code mostró una revisión más amplia y una mayor cobertura de casos extremos, mientras que Codex a menudo alcanzó el resultado requerido con un alcance menor y generó pruebas de ejecución más sólidas en nuestra configuración. Una diferencia de un punto en un pequeño programa de prueba en Python no es motivo para declarar un ganador absoluto.
Los desarrolladores que no quieran que el resto de su flujo de trabajo quede limitado a un único agente de programación pueden añadir GlobalGPT como una capa independiente multimodelo y multimodal. GlobalGPT integra más de 100 modelos destacados, como GPT 5.6, Claude Opus 5 y GPT Image 2, en un único panel de control. Además, el GlobalGPT CLI, Las rutas MCP y Skill pueden utilizarse desde Codex o Claude Code para obtener segundas opiniones, realizar la planificación, elaborar la documentación o utilizar otros modelos compatibles, mientras que el host de programación nativo mantiene el control sobre las modificaciones y las pruebas del repositorio.

La comparación combina información oficial sobre los planes, trabajo práctico con el repositorio, resultados completos y ampliables, y experiencias de la comunidad claramente descritas. El objetivo es ayudar a un desarrollador independiente a elegir un lenguaje de programación principal sin que le resulten confusos los accesos por suscripción, los créditos adicionales y la facturación de la API.
Codex frente a Claude: resumen del código
| Factor decisivo | Códice | Código Claude |
|---|---|---|
| Trabajo relacionado con el repositorio principal | Lee, edita, ejecuta comandos y verifica los cambios en todas las superficies Codex compatibles | Agente de terminal conversacional para leer, editar, ejecutar comandos y verificar cambios |
| Estilo observado en nuestra prueba | Más compacto en algunos aspectos de la implementación y la corrección de errores | Más exhaustivo en cuanto al análisis de los repositorios, las pruebas y el alcance de la revisión |
| Conocimiento del repositorio | Vínculo funcional en la tarea congelada | |
| Revisión del código | Se han detectado y corregido dos defectos válidos de mayor gravedad | Se notificaron más problemas reproducibles y se pasó de 19/20 a 18/20. |
| Velocidad observada | Resultados dispares; los entornos no eran lo suficientemente equivalentes como para que hubiera un ganador claro. | |
| Gama básica para el consumidor | $20 al mes a través del plan ChatGPT correspondiente | Claude Pro: $20 al mes en EE. UU. |
| Niveles de mayor consumo | Niveles $100 y $200 | Máximo 5x en $100 y máximo 20x en $200 |
La tabla describe una decisión de compra, no una clasificación de modelos. Un cambio en la configuración del modelo, el repositorio, los permisos o la especificación de la tarea puede alterar el resultado. Si ya utilizas un producto, la mejor comparación es una tarea delimitada de tu propio código fuente con el mismo comando de éxito y sin repeticiones por motivos de calidad.
Perfil de prueba controlado
Primer resultado válido, con la misma rúbrica fija. Las barras se han normalizado en función de la puntuación máxima de cada tarea.
La imagen muestra de dónde proviene esa diferencia de un punto; no convierte un partido concreto en una clasificación general.
Qué son realmente el Codex y el código Claude
«Codex» y «Claude Code» son productos de la marca, no simples nombres de modelos. El concepto subyacente Modelo de IA para la programación es importante, pero el entorno que lo rodea también determina qué archivos puede ver el agente, qué comandos puede ejecutar, cómo funcionan las autorizaciones, cómo se conserva el contexto y qué pruebas quedan tras la tarea. Si solo se compara la reputación del modelo, se pasa por alto gran parte de la experiencia que el desarrollador está adquiriendo.
Codex abarca flujos de trabajo en línea de comandos, IDE, de escritorio y orientados a la nube. Esa versatilidad lo hace adecuado tanto para la colaboración local directa como para una delegación más delimitada. Claude Code se centra en un flujo de trabajo conversacional en terminal con integraciones compatibles y amplias posibilidades de personalización; esto Guía para el uso de Claude en la codificación ofrece una introducción más amplia a ese flujo de trabajo. Para algunos desarrolladores, ver cómo trabaja un agente en la terminal resulta tranquilizador. Para otros, lo importante es el diff final, las pruebas y el registro de auditoría, más que una conversación continua.
Esta distinción también explica por qué dos pruebas que utilizan modelos nominalmente sólidos pueden comportarse de forma diferente. El anfitrión decide cómo se presentan las instrucciones, cómo se invocan las herramientas y cuándo debe un ser humano aprobar una acción. Las comparaciones entre comunidades que ignoran el entorno de pruebas pueden confundir un comportamiento a nivel de producto con una verdad a nivel de modelo.
- El modelo proporciona capacidad de razonamiento y de generación.
- El arnés del agente gestiona archivos, comandos, contexto, autorizaciones y recuperación.
- El contrato de tareas del usuario determina el alcance, las comprobaciones de éxito y cuándo debe detenerse el agente.

Flujo de trabajo y control: ¿delegación o dirección continua?
La pregunta más útil sobre el Código Codex frente al Claude no suele ser “¿Cuál permite escribir mejor código?”, sino “¿Cómo quiero trabajar con él?”. Una tarea clara y delimitada puede delegarse con un objetivo exacto, un alcance permitido y una orden de verificación. Una refactorización ambigua se beneficia del debate, la inspección intermedia y la oportunidad de reorientar al agente antes de que introduzca demasiados cambios.
La orientación continua resulta útil cuando los requisitos están en constante evolución o cuando aún se está negociando el diseño arquitectónico. Se convierte en un coste cuando el agente solicita repetidamente decisiones que podrían haberse resuelto a partir de las instrucciones del repositorio. La ejecución autónoma resulta útil cuando el contrato de la tarea es estable. Se vuelve arriesgada cuando el agente hace suposiciones sin contrastar o amplía el alcance sin una vía clara de reversión.
Un informe detallado publicado en Reddit con fecha del 13 de abril de 2026 describía unas 100 horas de trabajo con Claude Code y 20 horas con Codex en un proyecto de Python y TypeScript de aproximadamente 80 000 líneas con unas 2 800 pruebas. El autor describió Claude como más rápido y más interactivo, aunque requiere más supervisión, y Codex como más lento y más metódico. Ese informe resulta especialmente útil porque incluye el contexto del proyecto y la experiencia, pero sigue representando el flujo de trabajo de un solo desarrollador.

Nuestros propios tiempos de ejecución no permitieron determinar un ganador claro. Claude completó las dos primeras tareas más rápido, mientras que Codex completó las dos últimas más rápido. Además, Claude recurrió a un subagente de reserva cuando su ruta de autenticación CLI no estaba disponible, por lo que las rutas de ejecución no eran equivalentes a las del laboratorio. La conclusión más acertada es que la velocidad depende de la tarea, el modelo seleccionado, la configuración del esfuerzo, el contexto y el host, y no que uno de los dos productos sea siempre más rápido.
Comprensión del repositorio y gestión del contexto
Comprender el repositorio es algo más que nombrar carpetas. Un agente útil debe rastrear cómo se mueven los datos entre los archivos, identificar los contratos que limitan un cambio, localizar las pruebas, distinguir un síntoma de su posible origen y explicar el riesgo que supone modificar la capa equivocada. Un mapa exhaustivo puede revelar incoherencias ocultas; un mapa conciso puede llevar a un desarrollador a tomar una decisión de implementación segura con mayor rapidez.
En nuestra tarea T1 de solo lectura, ambos agentes obtuvieron una puntuación de 20/20 y basaron sus respuestas en el repositorio. El código Claude fue más exhaustivo y puso de manifiesto inconsistencias adicionales en el contrato. Codex fue más conciso, aunque identificó los archivos relevantes, los riesgos y el plan de implementación. Ninguno de los dos estilos es automáticamente superior. La exhaustividad resulta útil en migraciones y auditorías con las que no se está familiarizado; la concisión ayuda cuando el desarrollador ya conoce la arquitectura y desea un plan de cambios específico.
T1 · Prueba del repositorio controlado
Familiarízate con un repositorio que no conozcas antes de modificarlo
Analizar la arquitectura, rastrear el flujo de datos, identificar los riesgos relacionados con las funciones prioritarias y no realizar cambios en los archivos.
Arquitectura: modelo, repositorio, servicio y una utilidad de tiempo sin cablear. Principales riesgos: semántica de prioridades indefinida, compatibilidad, ordenación, validación y discrepancia de ámbito.
Opinión del crítico: Compacto, basado en los expedientes y suficiente para la resolución suspendida.
Tres capas más un módulo independiente. Sin ciclos; inyección de dependencias mínima. También puso de manifiesto la asignación de identificadores, los contratos de error asimétricos y una cobertura de pruebas escasa.
Opinión del crítico: Más exhaustivo, sin dejar de cumplir el mismo contrato de solo lectura.
Ver el resultado completo de Codex
Salida completa, desinfectada y válida por primera vez
Arquitectura ##
Se trata de un pequeño paquete de Python que utiliza únicamente la biblioteca estándar y que cuenta con cuatro capas funcionales:
- **Modelo de datos:** [`Job`]() es una clase de datos inmutable que contiene `identifier`, `name` y `owner`. `Job.to_dict()` la serializa mediante `dataclasses.asdict()`.
- **Repositorio:** [`JobRepository`]() almacena los trabajos en un diccionario en memoria cuyas claves son los identificadores. No hay base de datos ni persistencia duradera.
- **Servicio:** [`SchedulerService`]() se encarga de la asignación de identificadores, la normalización de entradas, la creación, la visualización en la lista y la eliminación.
- **Utilidad de tiempo:** [`next_daily_run()`]() calcula la hora de la siguiente ejecución, pero no hay nada que la importe ni la conecte con `Job`, `SchedulerService` o `JobRepository`.
- **API pública:** [`scheduler.__init__`]() solo exporta `SchedulerService`.
A pesar del nombre del repositorio, los trabajos no contienen actualmente información de programación, y no existe una cola de ejecución. Por lo tanto, la prioridad puede afectar a los metadatos almacenados y al orden de aparición en la lista, pero no puede influir en la ejecución real de los trabajos sin ampliar considerablemente el sistema.
## Flujo de datos de creación y listado
Creación mediante `SchedulerService.create_job(name, owner)`:
1. [`create_job()`]() rechaza un nombre compuesto únicamente por espacios en blanco.
2. Recorta `name` y `owner` y, a continuación, construye un objeto `Job` inmutable utilizando el `_next_identifier` del servicio.
3. [`JobRepository.save()`]() guarda el objeto en `_jobs[job.identifier]`.
4. El servicio incrementa `_next_identifier` y devuelve el mismo `Job`.
Listado mediante `SchedulerService.list_jobs()`:
1. [`list_jobs()`]() delega directamente en `repository.all()`.
2. [`JobRepository.all()`]() ordena numéricamente las claves del diccionario.
3. Por lo tanto, los trabajos se devuelven en orden ascendente por identificador o fecha de creación, no por orden de programación o prioridad.
## Archivos que probablemente se vean afectados por las prioridades de los trabajos
Requerido:
- [`scheduler/models.py`](): añadir el campo de prioridad y cualquier valor por defecto. `to_dict()` lo incluirá automáticamente, ya que utiliza `asdict()`.
- [`scheduler/service.py`](): aceptar, validar y pasar la prioridad a `Job`.
- [`tests/test_scheduler.py`](): cubrir el comportamiento por defecto, explícito, no válido, serializado y de ordenación.
Condicional:
- [`scheduler/repository.py`](): modificar `all()` solo si se pretende que las prioridades controlen el orden de la lista. Una clave secundaria determinista, como `identifier`, debería gestionar los empates.
- [`scheduler/__init__.py`](): actualizar solo si la prioridad se convierte en una enumeración pública o en un objeto de valor.
- [`README.md`](): documentar los valores aceptados, el valor por defecto y la semántica de ordenación.
Probablemente no se vea afectado:
- `scheduler/time_rules.py`, ya que su único símbolo opera sobre una fecha y hora proporcionadas y no tiene relación alguna con los trabajos ni con la lista.
## Riesgos principales
- **Semántica indefinida:** El código no ofrece ningún precedente sobre si los valores más grandes o más pequeños significan una mayor prioridad, qué valores son válidos o si la prioridad afecta en absoluto al orden de la lista.
- **Compatibilidad:** Añadir un argumento obligatorio a `Job` o a `create_job()` rompería las llamadas existentes, como `create_job("Daily report", "Alex")` en la prueba actual. Un valor por defecto preservaría esa llamada.
- **Rotura del constructor posicional:** Actualmente, `Job` se construye de forma posicional en `SchedulerService.create_job()`. La ubicación de los campos y futuras llamadas posicionales podrían asignar valores erróneos de forma silenciosa; la construcción mediante palabras clave sería más segura.
- **Cambio en el orden:** Cambiar el orden de los identificadores en `JobRepository.all()` altera el comportamiento establecido. Las prioridades iguales necesitan un criterio de desempate estable.
- **Ambiguidad en la validación:** Un valor de Python sin restricciones podría admitir cadenas, valores booleanos o enteros arbitrarios que son difíciles de comparar de forma coherente.
- **Discrepancia en el ámbito de ejecución:** No hay ningún distribuidor ni cola de ejecución. Afirmar que la prioridad determina qué trabajo se ejecuta primero no estaría respaldado por ningún símbolo actual.
- **Limitaciones en memoria:** La prioridad, al igual que cualquier otro campo del trabajo, desaparece cuando finaliza el proceso, ya que `_jobs` es solo un diccionario de instancia.
## Plan de implementación y validación
1. Definir primero el contrato: representación, valores permitidos, valor por defecto, dirección de la prioridad, desempate y si cambia el orden de la lista.
2. Añadir `priority` a `Job`, preferiblemente con un valor por defecto compatible con versiones anteriores.
3. Ampliar `SchedulerService.create_job()` con un parámetro de prioridad por defecto, validarlo y construir `Job` con argumentos clave.
4. Si la lista tiene en cuenta la prioridad, cambiar `JobRepository.all()` para ordenar primero por prioridad y luego por identificador. De lo contrario, dejar el orden del repositorio sin cambios.
5. Añadir pruebas para:
- La creación existente con dos argumentos.
- La conservación explícita de la prioridad.
- La prioridad por defecto.
- Tipos no válidos y valores fuera de rango.
- Salida de `Job.to_dict()`.
- Orden de la lista con prioridades múltiples.
- Orden estable para prioridades iguales.
- Comportamiento actual ante nombres en blanco.
6. Actualiza el README con el contrato público exacto.
7. Ejecutar el conjunto completo de pruebas sin conexión con `python3 -m unittest discover -s tests -v`.
## Verificación realizada
- Conjunto de pruebas de referencia: **2 pruebas superadas**.
- La búsqueda de símbolos en todo el repositorio ha confirmado que `next_daily_run()` está desconectado y que no hay símbolos de prioridad existentes.
- Archivos modificados: **ninguno**.
- Implementación de la funcionalidad: **no realizada**, tal y como se requería.
> **Conclusión + decisión**
>
> El cambio es mínimo si la prioridad es solo metadatos o el orden de la lista: principalmente `models.py`, `service.py`, `repository.py` y las pruebas. Antes de la implementación, hay que decidir si la prioridad simplemente describe un trabajo o si cambia el orden de `list_jobs()`, además de la escala de valores, el valor por defecto, la dirección y la regla de desempate.Ver el código Claude completo
Registro completo de la transferencia, incluida la salida válida inicial tal y como se ha registrado.
Registro de ejecución de # T1 — Comprensión de un repositorio desconocido
Parámetros de ejecución de ##
| Campo | Valor |
| --- | --- |
| Tarea | `T1` |
| Prompt congelado | `01-prompts/T1.md` |
| SHA-256 del prompt congelado | `a571ff15c2502ec29b172ae2e77dd9ab0caca2869b91f9468d47571434d16c17` |
| Unidad de ejecución | subagente aislado Claude, "uso general", contexto nuevo |
| Método de aislamiento | **recurso de reserva del subagente** (rebaja documentada — véase `results/isolation-decision.md`) |
| Copia limpia asignada | `runtime/workspaces/T1` |
| Versión del código Claude | `2.1.220` |
| Configuración del modelo | `claude-opus-5` |
| start_utc | `2026-07-28T10:27:31Z` |
| end_utc | `2026-07-28T10:28:32Z` |
| Duración | 61 s (simultáneo con T2–T4; no es una medición de latencia precisa) |
| Número de intento | 1 |
| Estado | `válido` |
| Intervención humana | ninguna |
## Resumen de la herramienta segura
`date`, `find`, `ls`, `wc`, `stat`, `grep`, `Read` en 7 archivos del espacio de trabajo, `python3 -V`,
`python3 -m unittest discover -s tests -v`. Todas las lecturas se limitan al espacio de trabajo asignado.
Sin red, sin instalaciones, sin escrituras.
## Resultado de la comprobación sin conexión
`python3 -m unittest discover -s tests -v` → 2 pruebas, OK (salida 0). Revisado de nuevo por el
coordinador desde fuera del espacio de trabajo: véase `results/checks/T1-public.txt`.
## Parche
Ninguno. `results/T1/patch.diff` está vacío, y `diff -ru 02-fixture runtime/workspaces/T1`
no devuelve diferencias. La tarea requería una inspección sin modificar archivos; esto se
ha confirmado, no se ha afirmado.
## Errores corregidos
Un error recuperable del shell: un `grep` inicial utilizaba patrones `--include=*.py` sin comillas que
el shell intentó expandir; al volver a ejecutarlo con los patrones entre comillas, se completó con éxito. No afectó a los resultados.
## Salida visible (textual)
### 1. Qué es este repositorio
Un fixture autónomo de la biblioteca estándar de Python denominado `local-scheduler-1.0.0` (`fixture-manifest.json`), con licencia MIT, sin dependencias de red ni de producción (`README.md`, `"network_required": false`). Es muy pequeño: 8 archivos de código fuente y documentación, más el manifiesto, con un total de 103 líneas. El intérprete utilizado es **Python 3.9.6**. No hay ningún `pyproject.toml`, `setup.py`, configuración de CI, linter ni configuración de verificador de tipos; el único flujo de trabajo declarado es `python3 -m unittest discover -s tests -v` (`README.md`).
### 2. Arquitectura
Tres capas más un módulo independiente, todo bajo `scheduler/`:
| Capa | Archivo | Símbolo | Responsabilidad |
|---|---|---|---|
| Fachada del paquete | `scheduler/__init__.py` | `__all__ = ['SchedulerService"]` | Solo reexporta `SchedulerService`; `Job`, `JobRepository` y `time_rules` no forman parte de la superficie pública |
| Modelo de dominio | `scheduler/models.py` | `Job` (`@dataclass(frozen=True)`) | Objeto de valor inmutable con los campos `identifier: int`, `name: str`, `owner: str`, además de `to_dict()` implementado como `asdict(self)` |
| Persistencia | `scheduler/repository.py` | `JobRepository` | `dict` en memoria con el identificador como clave; `save`, `get`, `all`, `delete` |
| Servicio de aplicación | `scheduler/service.py` | `SchedulerService` | Validación, asignación de identificadores, orquestación: `create_job`, `list_jobs`, `delete_job` |
| Huérfano | `scheduler/time_rules.py` | `next_daily_run` | Cálculos de recurrencia — **no es importado por ningún otro archivo** |
La dirección de las dependencias es clara y unidireccional: `service.py` importa desde `.models` y `.repository`; `repository.py` importa desde `.models`; `models.py` solo importa `dataclasses`. No hay ciclos. La inyección de dependencias está presente, pero es mínima: `SchedulerService.__init__(self, repository=None)` ejecuta `self.repository = repository or JobRepository()`, por lo que se puede sustituir por un repositorio ficticio en las pruebas.
Ten en cuenta que `scheduler/time_rules.py` es código obsoleto a día de hoy. He buscado con grep en todo el árbol las cadenas `time_rules` y `next_daily_run`; los únicos resultados son su propia definición y su aparición en `expected_files` de `fixture-manifest.json`. Se incluye en la distribución, pero no está conectado.
### 3. Flujo de datos
**Creación de un trabajo** — `SchedulerService.create_job(name, owner)`:
1. Condición de seguridad: `if not name.strip(): raise ValueError("name is required')`. Esta es la *única* validación del sistema: nunca se comprueba `owner`, por lo que se acepta un propietario en blanco.
2. Construcción: `Job(self._next_identifier, name.strip(), owner.strip())` — construcción posicional, y ambas cadenas se normalizan mediante la eliminación de espacios.
3. Persistencia: `self.repository.save(job)`, que realiza `self._jobs[job.identifier] = job` y devuelve el trabajo.
4. Avanzar el contador: `self._next_identifier += 1` — *después* de guardar, de modo que si se produce una excepción, el contador queda sin consumir.
5. Devolver la instancia de `Job` (el servicio devuelve su propio `job` local, no el valor de retorno del repositorio).
La asignación de identificadores se realiza en el **servicio**, no en el repositorio (`self._next_identifier = 1` en `SchedulerService.__init__`). Este es un punto de unión importante: inyectar un repositorio que ya contenga trabajos los sobrescribirá de forma silenciosa, ya que el nuevo servicio reinicia la numeración desde 1.
**Listado de trabajos** — `SchedulerService.list_jobs()` es un simple reenvío a `JobRepository.all()`, que devuelve `[self._jobs[key] for key in sorted(self._jobs)]`. Por lo tanto, el orden es **ascendente por identificador entero**, lo que actualmente coincide con el orden de inserción. Se crea una nueva lista en cada llamada, pero los objetos `Job` son referencias compartidas —lo cual es seguro únicamente porque `Job` está congelado—.
**Eliminación** — `SchedulerService.delete_job(identifier)` delega en `JobRepository.delete`, que es `self._jobs.pop(identifier)` sin valor por defecto, por lo que lanza un `KeyError` si falta el identificador. Esto es inconsistente con `JobRepository.get`, que devuelve `None` en la misma condición.
**Cobertura de pruebas de estas rutas** — `tests/test_scheduler.py` tiene exactamente dos casos: `test_create_and_list_jobs` (verifica que `job.identifier == 1` y `[job] == service.list_jobs()`) y `test_blank_name_is_rejected`. Ambos se superan en el árbol actual.
### 4. Archivos que probablemente se vean afectados por la incorporación de prioridades de trabajos
Por orden descendente de certeza:
1. **`scheduler/models.py`** — seguro. `Job` necesita un campo `priority`. Dado que la clase de datos tiene `frozen=True` y los campos son posicionales, un nuevo campo debe llevar un valor por defecto (p. ej., `priority: int = 0`) o debe colocarse después de los tres existentes; de lo contrario, se romperá toda la construcción posicional existente. `to_dict()` no necesita modificación: `asdict(self)` recoge el campo automáticamente, lo que supone un cambio silencioso en el contrato de serialización más que un cambio en el código.
2. **`scheduler/service.py`** — seguro. `create_job` necesita un parámetro `priority` y debe pasarlo al constructor posicional `Job(...)` en la línea 13. Si se valida la prioridad (rango, pertenencia a una enumeración), la condición debe ir junto a la comprobación existente `name.strip()`. Si la lista debe ordenarse por prioridad, `list_jobs` es donde se aplicaría una política de ordenación si se desea que el repositorio siga siendo un almacén "tonto".
3. **`scheduler/repository.py`** — probable. El método `sorted(self._jobs)` de `JobRepository.all()` ordena las *claves* del diccionario, es decir, los identificadores. Para obtener una lista ordenada por prioridad, es necesario cambiar esto para ordenar los valores mediante una clave como `(-job.priority, job.identifier)`, o añadir un accesor ordenado independiente. No dejes el desempate implícito: el orden de inserción en el diccionario no es un contrato estable en el que se pueda confiar.
4. **`tests/test_scheduler.py`** — seguro. Nuevos casos para la prioridad por defecto, la prioridad explícita, el orden y el rechazo por prioridad no válida. Ten en cuenta que el `assertEqual([job], service.list_jobs())` existente compara el *mismo* objeto, por lo que se mantiene tras la adición de un campo; no se mantendría si comparara un literal `Job` recién creado.
5. **`README.md`** — Probable, si es necesario indicar en algún lugar la semántica de la prioridad (qué extremo del rango prevalece, valor por defecto).
6. **`scheduler/__init__.py`** — solo si es necesario exportar una enumeración de prioridades o constantes junto con `SchedulerService`.
7. **`fixture-manifest.json`** — solo si añades un nuevo archivo de módulo; su matriz `expected_files` es un inventario explícito y se desajustaría.
8. **`scheduler/time_rules.py`** — *no* debe modificarse. La prioridad es ortogonal a la recurrencia, y este módulo no está conectado.
### 5. Riesgos principales
- **La semántica de ordenación supone un cambio de comportamiento silencioso.** `JobRepository.all()` garantiza actualmente un orden ascendente por identificador y `list_jobs` lo hereda. Cualquier consumidor que confíe en esa garantía se verá afectado por la reordenación. Decídase explícitamente: ¿cambia de significado `list_jobs()`, o aparece junto a ella una nueva función `list_jobs_by_priority()`? La primera opción es la de mayor riesgo y la prueba existente `test_create_and_list_jobs` es demasiado débil para detectar una regresión (lista de un solo elemento).
- **La dirección de ordenación es realmente ambigua.** 'Prioridad 1" significa convencionalmente *la más alta* en algunos sistemas y *la más baja* en otros. No hay nada en este repositorio —ni comentarios, ni cadenas de documentación, ni líneas en el README— que lo aclare. Elige una convención, documéntala en `models.py` e incorpórala en una prueba con al menos tres prioridades distintas para que la dirección quede fijada.
- **Empates inestables en la ordenación.** Si dos trabajos comparten una prioridad, el resultado debe seguir siendo determinista. La función `sorted` de Python es estable, pero la estabilidad solo es útil si el orden de entrada está definido. Incluye siempre `identifier` como clave secundaria explícita.
- **Orden de los campos de una dataclass congelada.** `Job` es `@dataclass(frozen=True)` con tres campos posicionales, y `service.py:13` lo construye de forma posicional. Insertar `priority` en cualquier lugar que no sea el último, o sin un valor por defecto, supone un cambio que rompe la compatibilidad y que las dos pruebas existentes podrían no detectar de forma evidente.
- **Ampliación del contrato de `to_dict()`.** `asdict(self)` refleja todos los campos. Añadir `priority` modifica todas las cargas útiles serializadas sin que se aprecie ningún cambio en el cuerpo del método de `models.py`. Cualquier afirmación sobre la forma exacta del diccionario dejará de funcionar.
- **Restricciones de Python 3.9.6.** No se admite `@dataclass(slots=True)` (3.10+), ni `kw_only=True` (3.10+), y las anotaciones PEP 604 `int | None` fallan en tiempo de ejecución sin `from __future__ import annotations`. Si "priority' se modela como una enumeración, `enum.StrEnum` es a partir de la versión 3.11 y no está disponible. Cualquiera de estos casos provocaría un error en el fixture con este intérprete.
- **La asignación de identificadores se encuentra en la capa equivocada.** `SchedulerService._next_identifier` implica que el servicio se encarga de la generación de identificadores, mientras que el repositorio se encarga del almacenamiento. Si el trabajo sobre prioridades da lugar a una segunda implementación del repositorio o a fixtures precargados, esta separación provocará colisiones de identificadores y trabajos sobrescritos.
- **Contratos de error asimétricos.** `JobRepository.get` devuelve `None` en caso de no encontrar el elemento, mientras que `JobRepository.delete` lanza un `KeyError`. Cualquier nueva consulta relacionada con la prioridad debería adoptar una convención de forma deliberada, en lugar de heredar la inconsistencia por accidente.
- **Precedente de laguna en la validación.** `create_job` valida `name` pero no `owner`. No se debe copiar esa laxitud para `priority`: una prioridad no validada se propaga a la clave de ordenación y puede lanzar un `TypeError` en lo más profundo de `sorted` (por ejemplo, al comparar un `int` con `None`) en lugar de en el punto de llamada.
- **Trampa del código muerto.** `scheduler/time_rules.py` parece contener lógica de programación y atraerá modificaciones. No lo importa ningún otro archivo, y su propia cadena de documentación admite el error: añade 24 horas transcurridas en UTC y 'desplaza la hora local del reloj de pared durante los cambios de horario de verano». Modificarlo no produce ningún efecto observable en el sistema probado, por lo que el esfuerzo dedicado a ello es en vano y cualquier prueba escrita al respecto no valida nada de lo que se lanza.
- **Red de seguridad escasa.** Dos pruebas, sin CI, sin linter, sin comprobación de tipos. No hay nada que detecte una regresión, salvo lo que uno mismo escriba. Considera el conjunto de pruebas existente como una cobertura básica insuficiente, no como un filtro de acceso.
### 6. Plan de implementación (no ejecutado)
1. **Establece primero la semántica, por escrito.** Tipo de prioridad (`int` frente a enum), rango válido, valor por defecto para trabajos existentes o no especificados, y dirección de ordenación. Regístralo como una cadena de documentación en `Job`, en `scheduler/models.py`; ese es el único lugar del que ya dependen todas las capas.
2. **Amplía el modelo.** Añade `priority: int = 0` como el *último* campo de `Job` en `scheduler/models.py`, conservando la compatibilidad posicional con `service.py:13` y sin modificar `to_dict()`.
3. **Incorpóralo en el servicio.** Añade un parámetro `priority` con valor por defecto a `SchedulerService.create_job`, valídalo junto al filtro existente `name.strip()` (genera un `ValueError`, coincidiendo con el tipo de error establecido) y pásalo al constructor de `Job`.
4. **Establece explícitamente el contrato de listado.** Opción preferida de bajo riesgo: deja `JobRepository.all()` tal cual como la primitiva ordenada por identificador y añade un accesor independiente ordenado por prioridad. Si, por el contrario, `list_jobs()` debe cambiar de significado, modifica la comprensión de `JobRepository.all()` para ordenar los valores mediante una clave compuesta explícita que incluya `identifier` como criterio de desempate, y anota el cambio en `README.md`.
5. **Mantén `scheduler/__init__.py` sincronizado** solo si se ha introducido una enumeración o constante pública.
6. **No modifiques `scheduler/time_rules.py`**, y no añadas archivos a menos que se actualice `expected_files` de `fixture-manifest.json` en el mismo cambio.
### 7. Plan de validación
- **Primero, la línea de base.** Ejecuta `python3 -m unittest discover -s tests -v` antes de cualquier modificación y confirma que las dos pruebas se superan. Ya lo he hecho: se superan sin problemas.
- **Control de regresión.** Ambas pruebas existentes deben seguir pasando sin modificaciones. Si es necesario editar `test_create_and_list_jobs`, eso es una señal de que el cambio está causando un fallo, no una razón para editar la prueba.
- **Nuevos casos en `tests/test_scheduler.py`:**
- Prioridad por defecto cuando se llama a `create_job` con la antigua firma de dos argumentos (demuestra la compatibilidad con versiones anteriores).
- Recorridos de ida y vuelta de la prioridad explícita a través de `create_job` → `list_jobs`.
- Ordenación con **tres o más** prioridades distintas, verificando la lista completa; esto es lo que determina la dirección de la ordenación.
- Desempate: dos trabajos con la misma prioridad se ordenan por identificador ascendente.
- Una prioridad no válida genera un `ValueError`, al igual que en el caso de un nombre en blanco.
- `to_dict()` incluye `priority` con el valor esperado.
- Ruta de repositorio inyectada: `SchedulerService(repository=fake)` sigue asignando la prioridad correctamente, poniendo a prueba la integración entre `repository` y `JobRepository()`.
- **Comprobación de compatibilidad.** Confirma que todo funciona específicamente en Python 3.9.6: sin `slots=`, sin `kw_only=`, sin uniones PEP 604 sin calificaciones, sin `StrEnum`.
- **Comprobación de inventario.** Vuelve a leer `fixture-manifest.json` y confirma que el conjunto de archivos en disco sigue coincidiendo con `expected_files`.
- **Determinismo.** Ejecuta la suite dos o tres veces; los errores de ordenación relacionados con el orden de iteración de los diccionarios pueden aparecer de forma intermitente.
- **Limpieza.** Ejecuta con `PYTHONDONTWRITEBYTECODE=1` para que los directorios `__pycache__` no aparezcan como desviaciones no rastreadas respecto al manifiesto.
Limitación del entorno de pruebas: se trataba de un pequeño repositorio de Python fuera de línea. Claude utilizaba un subagente de reserva tras la caducidad del OAuth de la CLI, por lo que el aislamiento y la visibilidad de los tokens no eran idénticos.
Este resultado no refleja el rendimiento en un monorepo de gran tamaño ni en una sesión autónoma de una semana de duración. El entorno de prueba se diseñó deliberadamente para ser pequeño y controlado. Para realizar una evaluación real, empieza con una solicitud de solo lectura: pide a cada herramienta que trace la ruta de llamadas, indique los archivos que modificaría, identifique las pruebas y enumere las suposiciones sin resolver antes de permitir la edición.
Estilo de implementación, alcance de los parches y corrección de errores
Trabajo con múltiples archivos
Un mismo comportamiento de superación de pruebas puede deberse a diferentes decisiones de ingeniería. En T2, ambos productos obtuvieron una puntuación de 30/30 y superaron el validador oculto congelado. El código Claude situó la validación en más de un límite e incorporó pruebas más amplias de modelo directo y de umbral. Codex utilizó una implementación más reducida, centrada en los servicios, que también cumplió con el contrato.
Para un desarrollador independiente, se trata de una auténtica disyuntiva. Una validación más amplia puede proteger las invariantes cuando los objetos se crean a través de múltiples rutas. Un parche más pequeño puede reducir la superficie de regresión y el tiempo de revisión cuando un servicio es el punto de entrada previsto. La respuesta correcta depende de las normas de propiedad del repositorio, no de contar las líneas modificadas o las pruebas de forma aislada.
T2 · Prueba del repositorio controlado
Implementar una función prioritaria en varios archivos
Añadir el comportamiento congelado, mantener la compatibilidad y superar las comprobaciones tanto públicas como ocultas.
Se ha añadido prioridad al modelo inmutable, se ha validado en el límite del servicio y se ha mantenido el orden en el repositorio. Las siete pruebas públicas y el validador oculto congelado han superado las pruebas.
Opinión del crítico: Una implementación más reducida y centrada en los servicios que cubría todos los comportamientos congelados.
Se ha validado la prioridad tanto en el límite del servicio como en el modelo inmutable, se ha conservado el filtrado del repositorio y se ha añadido la cobertura de umbrales. Se han superado catorce pruebas públicas y el validador oculto.
Opinión del crítico: Una cobertura más amplia del modelo directo y del umbral, sin que ello suponga una ventaja en cuanto a la puntuación funcional.
Ver el resultado completo de Codex
Salida completa, desinfectada y válida por primera vez
Se ha implementado la función de prioridad de los trabajos programados. Archivos modificados: - [scheduler/models.py](./scheduler/models.py:5) — se ha añadido el parámetro serializado `priority`, cuyo valor por defecto es `0`. - [scheduler/service.py](./scheduler/service.py:11): se ha añadido la validación, el almacenamiento y el filtrado `list_jobs(min_priority=0)`, conservando el orden de los identificadores. - [tests/test_scheduler.py](./tests/test_scheduler.py:16) — Se ha añadido cobertura para valores por defecto, serialización, almacenamiento, límites, valores no válidos y filtrado. Pruebas ejecutadas: ```text python3 -m unittest discover -s tests -v Se han ejecutado 7 pruebas — OK ``` **Conclusión:** La tarea congelada está completada y verificada. **Decisión necesaria:** Ninguna.
Ver el código Claude completo
Registro completo de la transferencia, incluida la salida válida inicial tal y como se ha registrado.
Registro de ejecución de # T2 — Función de prioridad de varios archivos Parámetros de ejecución de ## | Campo | Valor | | --- | --- | | Tarea | `T2` | | Mensaje congelado | `01-prompts/T2.md` | | SHA-256 del prompt congelado | `8476a56034c4e719b97ee4081b32b4c73efd45feafe1bda9d477c83cb8fdb698` | | Unidad de ejecución | subagente aislado Claude, "uso general", contexto nuevo | | Método de aislamiento | **recurso de reserva del subagente** (rebaja documentada — véase `results/isolation-decision.md`) | | Copia limpia asignada | `runtime/workspaces/T2` | | Versión del código Claude | `2.1.220` | | Configuración del modelo | `claude-opus-5` | | start_utc | `2026-07-28T10:27:52Z` | | end_utc | `2026-07-28T10:29:33Z` | | Duración | 101 s (simultáneo con T1, T3 y T4; no es una medición de latencia precisa) | | Número de intento | 1 | | Estado | `válido` | | Intervención humana | ninguna | ## Resumen de la herramienta segura `date`, `Read`/`Edit`/`Write` únicamente dentro del espacio de trabajo asignado, `grep`, `python3 -m unittest discover`, `python3 -m compileall`, comprobaciones de funcionamiento `python3 -c` en línea. Sin red, sin instalaciones, solo biblioteca estándar. ## Archivos modificados `scheduler/models.py`, `scheduler/repository.py`, `scheduler/service.py`, `tests/test_scheduler.py`. No se han añadido ni eliminado archivos, por lo que `fixture-manifest.json` sigue siendo correcto. Parche completo: `results/T2/patch.diff`. ## Resultados de la comprobación sin conexión Informado por el ejecutor de tareas: | Comprobación | Resultado | | --- | --- | | `python3 -m unittest discover -s tests -v` (referencia, antes del cambio) | 2 superadas, 0 fallidas | | `python3 -m compileall -q scheduler tests` | sin errores | | comprobaciones rápidas en línea (construcción heredada de 3 argumentos, umbral de palabra clave/posicional, `to_dict`) | superada | | `python3 -m unittest discover -s tests -v` (final) | **14 superadas, 0 fallidas** | Revisado de forma independiente por el coordinador desde fuera del espacio de trabajo: | Comprobación | Salida | Resultado | | --- | --- | --- | | conjunto de pruebas público (`results/checks/T2-public.txt`) | 0 | 14 pruebas, OK | | validador oculto `03-checks/test_t2_hidden.py` (`results/checks/T2-hidden.txt`) | 0 | 2 pruebas, OK | El validador oculto falla (salida 1) con una copia prístina del fixture, lo que confirma que la superación es significativa. Véase `results/checks/validator-controls.md`. ## Cobertura del comportamiento del mapa de referencia Se cumplen los cinco comportamientos requeridos en `03-checks/gold-map.json`: | Comportamiento requerido | Evidencia | | --- | --- | | prioridad por defecto 0 | `Job.priority: int = MIN_PRIORITY`; `create_job(..., priority=MIN_PRIORITY)`; `test_priority_defaults_to_zero` | | rango de enteros del 0 al 9 | `validate_priority` rechaza valores que no sean enteros y que estén fuera de rango; `test_out_of_range_priority_is_rejected`, `test_non_integer_priority_is_rejected` | | prioridad serializada | `to_dict()` mediante `asdict`; `test_priority_appears_in_serialized_output` verifica que el diccionario sea exactamente el mismo | | filtrado por prioridad mínima | `list_jobs(min_priority=0)` → `JobRepository.all(min_priority)`; `test_filter_is_inclusive_of_the_threshold` | | orden de identificadores | filtro aplicado dentro de la comprensión existente `sorted(self._jobs)`; `test_filter_preserves_ascending_identifier_order` | ## Errores corregidos Ninguno. El ejecutor de tareas eliminó los artefactos `__pycache__` de las ejecuciones de pruebas; una revisión del coordinador confirmó que el espacio de trabajo está limpio. ## Salida visible (textual) Se ha añadido un campo entero `priority` a los trabajos programados en las capas de modelo, repositorio y servicio del fixture del programador local, además de la lista filtrada. **`scheduler/models.py`** — Se han añadido las constantes del módulo `MIN_PRIORITY = 0` / `MAX_PRIORITY = 9` y una función reutilizable `validate_priority(priority)` que lanza un `ValueError` para valores que no sean enteros y para valores fuera del rango 0–9, ambos inclusive. Se rechaza explícitamente `bool` a pesar de ser una subclase de `int`, por lo que `True` no puede pasar como prioridad 1. La clase de datos congelada `Job` incorpora `priority: int = MIN_PRIORITY` como campo final con un valor por defecto, lo que mantiene el funcionamiento de la construcción existente de tres argumentos, y un `__post_init__` que llama al validador para que no se puedan construir prioridades no válidas por ninguna vía. `to_dict()` se ha dejado como `asdict(self)`, por lo que la prioridad aparece automáticamente en la salida serializada y en el orden de los campos. **`scheduler/repository.py`** — `JobRepository.all()` admite un `min_priority` opcional y filtra dentro de la comprensión existente `sorted(self._jobs)`, de modo que el orden ascendente de los identificadores queda garantizado por la misma ruta de código única que ya lo proporcionaba, en lugar de tener que restablecerse tras el filtrado. El argumento por defecto conserva el comportamiento anterior sin argumentos para las llamadas existentes. El almacenamiento en sí no requirió ningún cambio: el repositorio almacena instancias completas de `Job`, por lo que la prioridad se mantiene y se conserva en las operaciones `save`/`get`/`all`. **`scheduler/service.py`** — `create_job` acepta un argumento opcional `priority` al final (por defecto 0) y lo valida antes de construir el `Job`, de modo que el error se genera en el límite del servicio con el mismo contrato `ValueError` que la comprobación existente de nombre en blanco. `list_jobs(min_priority=0)` delega en el repositorio. La ordenación y el filtrado se realizan en un único lugar, en lugar de duplicarse. **Nota de diseño sobre la validación de `min_priority`:** se comprueba el tipo como un entero, pero deliberadamente *no* se restringe el rango a 0–9. La regla de 0–9 limita lo que un trabajo puede almacenar; un umbral es un parámetro de consulta, y rechazar, por ejemplo, `min_priority=10` convertiría una consulta legítima de "sin coincidencias" en un error. El hecho de que `list_jobs(min_priority=9)` devuelva `[]` cuando ningún trabajo tiene prioridad 9 está cubierto por una prueba. Se leyó `scheduler/time_rules.py`, pero no se modificó: su cambio de horario de verano documentado es un comportamiento predefinido ya existente, fuera del alcance de esta tarea, y modificarlo habría supuesto un cambio de comportamiento no relacionado. No se ha añadido ni eliminado ningún archivo, por lo que `fixture-manifest.json` sigue siendo correcto (verificado mediante programación). ### Cobertura de pruebas añadida Valor predeterminado 0; recorrido de ida y vuelta por el almacenamiento a través de `JobRepository`; prioridad en la salida de `to_dict()`; se aceptan los valores límite 0 y 9; se rechazan los valores fuera de rango −1/10/100; se rechazan los valores no enteros `"3"`/`3.5`/`None`/`True`; validación directa a nivel de modelo; orden de listado sin filtrar; listado filtrado que conserva el orden ascendente de los identificadores; umbral inclusivo; resultado vacío; se rechaza un `min_priority` no entero.
Limitación del entorno de pruebas: se trataba de un pequeño repositorio de Python fuera de línea. Claude utilizaba un subagente de reserva tras la caducidad del OAuth de la CLI, por lo que el aislamiento y la visibilidad de los tokens no eran idénticos.
Corrección de errores en casos extremos
T3 probó un fallo en la programación del horario de verano. Ambos agentes obtuvieron una puntuación de 30/30 y superaron las mismas comprobaciones ocultas de primavera y otoño. El código Claude añadió una cobertura explícita para horas locales ambiguas y omitidas, entradas simplistas y cambios de varios días. Codex realizó un cambio más limitado en los días naturales y superó el contrato congelado.
Las pruebas más amplias de casos extremos generan confianza cuando la biblioteca se reutiliza ampliamente o cuando el coste de un error de programación es elevado. Un cambio limitado resulta atractivo cuando el defecto está bien aislado y el equipo desea minimizar los cambios de comportamiento. La pregunta práctica que hay que plantearse no es “¿Quién ha escrito más?”, sino si el parche corrige la causa raíz indicada, preserva los contratos circundantes y comprueba los límites de riesgo.
T3 · Prueba de repositorio controlado
Corregir la programación diaria durante los cambios de horario de verano
Mantener la hora de reloj de pared de Nueva York solicitada durante los cambios de estación de primavera y otoño.
Causa principal: la implementación añadió 24 horas UTC transcurridas. Solución: avanzar un día natural según el calendario local. Se superaron ambas comprobaciones de «spring» y «fall» congeladas.
Opinión del crítico: El grupo más reducido aprobó el contrato en su totalidad.
Se han tenido en cuenta la primavera y el otoño, los horarios locales omitidos y ambiguos, las entradas simplistas y los rollos de varios días. Ambas comprobaciones ocultas «congeladas» se han superado tras ampliar la cobertura de la regresión.
Opinión del crítico: Mayor cobertura de los casos extremos; la misma puntuación funcional según la rúbrica «congelada».
Ver el resultado completo de Codex
Salida completa, desinfectada y válida por primera vez
Se ha implementado la corrección del horario de verano. Archivos modificados: - [scheduler/time_rules.py](./scheduler/time_rules.py) - [tests/test_time_rules.py](./tests/test_time_rules.py) Cambios: - Avanza un día natural local en lugar de 24 horas UTC. - Se han añadido pruebas de regresión para el cambio horario de primavera y otoño en Nueva York. - Se ha comprobado que los intervalos transcurridos en UTC son de 23 y 25 horas, respectivamente. Comprobaciones: - Pruebas unitarias: 4 superadas. - Comprobación sintáctica en memoria: han pasado 7 archivos de Python. - Se bloqueó la compilación del código de bytes porque Python redirige su caché fuera del espacio de trabajo asignado; no se escribieron archivos externos. **Conclusión:** `next_daily_run` ahora conserva la hora real solicitada durante las transiciones del horario de verano. No es necesario tomar ninguna decisión.
Ver el código Claude completo
Registro completo de la transferencia, incluida la salida válida inicial tal y como se ha registrado.
Registro de ejecución de # T3 — Corrección de un error de DST
Parámetros de ejecución de ##
| Campo | Valor |
| --- | --- |
| Tarea | `T3` |
| Mensaje congelado | `01-prompts/T3.md` |
| SHA-256 del prompt congelado | `3fee50d0230f27f8d6dd7f12e050fa3f0b7f5fb77d9559361fee96e5f1a47307` |
| Unidad de ejecución | subagente aislado Claude, "de uso general", contexto nuevo |
| Método de aislamiento | **recurso de reserva del subagente** (rebaja documentada — véase `results/isolation-decision.md`) |
| Copia limpia asignada | `runtime/workspaces/T3` |
| Versión del código Claude | `2.1.220` |
| Configuración del modelo | `claude-opus-5` |
| start_utc | `2026-07-28T10:28:05Z` |
| end_utc | `2026-07-28T10:30:03Z` |
| Duración | 118 s (simultáneo con T1, T2 y T4; no es una medición de latencia precisa) |
| Número de intento | 1 |
| Estado | `válido` |
| Intervención humana | ninguna |
Resumen de la herramienta segura ##
`date`, `Read`/`Edit` únicamente dentro del espacio de trabajo asignado, `python3 -m unittest discover`,
`python3 -m compileall`, una ejecución de control negativo en memoria, una comprobación de integridad del manifiesto.
Solo biblioteca estándar (`datetime`, `zoneinfo`, `unittest`). Sin red, sin instalaciones.
## Archivos modificados
`scheduler/time_rules.py` (la corrección: solo el cuerpo y la cadena de documentación de `next_daily_run`),
`tests/test_scheduler.py` (se ha añadido `NextDailyRunTests`, 8 pruebas, además de una función auxiliar `_elapsed`;
las pruebas existentes no se han modificado). No se ha modificado ningún otro módulo, ni se han añadido archivos. Parche completo:
`results/T3/patch.diff`.
## Resultados de la comprobación sin conexión
Informado por el ejecutor de tareas:
| Comprobación | Resultado |
| --- | --- |
| `python3 -m unittest discover -s tests -v` | **10 superadas, 0 fallidas** (2 preexistentes + 8 nuevas) |
| `python3 -m compileall -q scheduler tests` | sin errores |
| Integridad del manifiesto frente a `fixture-manifest.json` | 0 faltantes, 0 inesperados |
| Control negativo: implementación inicial parcheada en memoria, solo `NextDailyRunTests` | Se ejecutaron 8, **5 fallaron según lo previsto**, incluidas ambas pruebas de horario de verano |
Revisado de forma independiente por el coordinador desde fuera del espacio de trabajo:
| Comprobación | Salida | Resultado |
| --- | --- | --- |
| conjunto de pruebas público (`results/checks/T3-public.txt`) | 0 | 10 pruebas, OK |
| validador oculto `03-checks/test_t3_hidden.py` (`results/checks/T3-hidden.txt`) | 0 | 2 pruebas, OK |
El validador oculto falla (salida 1) con una copia prístina del fixture. Véase
`results/checks/validator-controls.md`.
## Cobertura del comportamiento del mapa de referencia
Se cumplen los cuatro comportamientos requeridos en `03-checks/gold-map.json`:
| Comportamiento requerido | Evidencia |
| --- | --- |
| hora del reloj de pared conservada | incremento aplicado al reloj de pared local sin ajustes, zona reasignada; verificado en todas las pruebas de horario de verano |
| Transición de primavera cubierta | `test_spring_forward_keeps_requested_hour`: se mantiene la hora 09:30, desfase `-04:00`, tiempo transcurrido 23 h |
| Transición de otoño cubierta | `test_fall_back_keeps_requested_hour`: se mantiene la hora 09:30, desfase `-05:00`, tiempo transcurrido: 25 h |
| Zona horaria de la IANA utilizada | `ZoneInfo("America/New_York")` de la biblioteca estándar |
Más allá del conjunto requerido, el ejecutor también cubrió los dos casos límite de la PEP 495 —una hora real
omitida por el cambio horario de primavera y una hora real ambigua durante el cambio horario de otoño—, además de los ciclos de siete días
que abarcan cada transición y el comportamiento de entrada ingenua.
## Errores corregidos
Dos fallos autoinfligidos en las aserciones de prueba: `siguiente - anterior` en dos fechas y horas que comparten un
`tzinfo` resta los relojes de pared «naïve», por lo que las aserciones de tiempo transcurrido indicaban 1 día y fallaron.
La implementación era correcta y las aserciones eran erróneas; se corrigió con un auxiliar `_elapsed`
que convierte primero ambos operandos a UTC. No hubo errores de herramientas, importaciones ni de entorno.
Los artefactos de `__pycache__` se eliminaron tras la ejecución.
## Salida visible (textual)
**Reproducción**
Se llamó a `scheduler/time_rules.py::next_daily_run` con una fecha y hora `America/New_York` a cada lado de una transición al horario de verano de 2024:
```
2024-03-09T09:30:00-05:00 -> 2024-03-10T10:30:00-04:00 #: la hora se desvió +1
2024-11-02T09:30:00-04:00 -> 2024-11-03T08:30:00-05:00 #: la hora se desvió -1
```
Un trabajo programado para las 09:30 hora local se ejecutó a las 10:30 tras el cambio horario de verano y a las 08:30 tras el cambio horario de invierno.
**Causa principal**
La implementación inicial trataba «daily» como una duración absoluta fija:
```python
elapsed = previous_run.astimezone(timezone.utc) + timedelta(hours=24)
return elapsed.astimezone(previous_run.tzinfo)
```
Al convertir primero a UTC, `timedelta(hours=24)` equivale exactamente a 24 horas transcurridas. Sin embargo, un día natural local en una zona IANA dura 23 h el día del cambio horario de primavera y 25 h el día del cambio horario de otoño, por lo que un desfase constante de 24 h se aleja necesariamente una hora de la hora real solicitada.
**La solución** (`scheduler/time_rules.py`)
La recurrencia diaria es una regla de calendario, por lo que el incremento se aplica ahora al reloj local «ingenuo», y la zona se vuelve a asociar posteriormente:
```python
tzinfo = previous_run.tzinfo
naive_next = previous_run.replace(tzinfo=None) + timedelta(days=1)
if tzinfo is None:
return naive_next
candidate = naive_next.replace(tzinfo=tzinfo, fold=0)
return candidate.astimezone(timezone.utc).astimezone(tzinfo)
```
Por qué es correcto:
- La aritmética del reloj de pared sobre el valor «naive» es independiente del desfase, por lo que la hora y los minutos solicitados se conservan por definición; A continuación, `zoneinfo` proporciona el desfase UTC vigente en la nueva fecha (verificado: `-04:00` tras el cambio horario de primavera, `-05:00` tras el cambio horario de otoño; tiempo real transcurrido de 23 h y 25 h, respectivamente).
- `fold=0`, junto con el ciclo de ida y vuelta UTC, gestiona los dos casos extremos que la aritmética pura del reloj de pared deja mal definidos. Según la PEP 495, una hora de pared *omitida* (02:30 del 10 de marzo de 2024) tiene el desfase anterior a la transición en `fold=0`, por lo que el «round-trip» lo normaliza al instante real 03:30 EDT en lugar de devolver una fecha y hora que no designa ningún instante. Una hora real *ambigua* (01:30 del 3 de noviembre de 2024) se resuelve de forma determinista como su primera aparición (EDT), en lugar de ser arbitraria.
- La entrada «naive» conserva su significado anterior (simplemente +1 día natural); no se impone ningún nuevo requisito de `tzinfo` a los usuarios que la invocan.
El alcance se ha mantenido limitado: solo ha cambiado el cuerpo y la cadena de documentación de `next_daily_run`. `service.py`, `models.py`, `repository.py` y `__init__.py` no se han modificado, no se han añadido archivos nuevos y el manifiesto de fixtures sigue coincidiendo exactamente (sin archivos que falten o inesperados). Solo se ha utilizado la biblioteca estándar (`datetime`, `zoneinfo`, `unittest`).
**Pruebas de regresión** (`tests/test_scheduler.py`, nuevo `NextDailyRunTests`, 8 pruebas) cubren: estabilidad en días normales, cambio horario de primavera (hora + desfase + 23 h transcurridas), cambio horario de otoño (hora + desfase + 25 h transcurridas), ciclos de siete días que abarcan cada transición, tiempo real omitido, tiempo real ambiguo y entradas simplistas.
Hay dos observaciones que vale la pena señalar. En primer lugar, mis aserciones iniciales sobre el tiempo transcurrido utilizaban «siguiente - anterior»; Python ignora un `tzinfo` compartido y resta los relojes de pared ingenuos, por lo que ambas devolvían 1 día y fallaban. La implementación era correcta y las aserciones eran erróneas; se corrigió con un auxiliar `_elapsed` que convierte ambos operandos a UTC. En segundo lugar, verifiqué que las pruebas detectaran realmente el defecto original volviendo a ejecutar `NextDailyRunTests` sobre una copia en memoria de la implementación inicializada: fallaron 5 de 8, incluidas ambas pruebas de horario de verano. Las tres que pasan en la versión con errores (día normal, omitido, ambiguo) documentan el comportamiento más que detectar regresiones; los casos omitidos/ambiguos coinciden por casualidad porque un desplazamiento absoluto de +24 h resulta caer en el mismo instante normalizado.
Limitación del entorno de pruebas: se trataba de un pequeño repositorio de Python fuera de línea. Claude utilizaba un subagente de reserva tras la caducidad del OAuth de la CLI, por lo que el aislamiento y la visibilidad de los tokens no eran idénticos.
Calidad de la revisión de código y verificación humana
Las tareas de revisión de código requieren una habilidad distinta a la de la implementación de funcionalidades. Un revisor competente debe detectar errores reproducibles, evaluar su impacto, indicar la ruta exacta del código y distinguir entre un defecto real y una advertencia especulativa. La corrección de un error supone una prueba adicional: el parche no debe introducir una nueva regresión.
El código Claude fue el que obtuvo la ventaja más clara en T4. Informó de cinco hallazgos reproducibles de forma independiente, incluidos los dos objetivos de revisión preestablecidos, y obtuvo una puntuación de 19/20. Codex informó y corrigió dos defectos válidos de mayor gravedad, pero no mencionó el par preestablecido, por lo que obtuvo una puntuación de 18/20. Los hallazgos de Codex no fueron falsos positivos; la diferencia observada radicaba en la amplitud, más que en la corrección básica.
T4 · Prueba de repositorio controlado
Identifica los defectos reproducibles y soluciona los problemas que presenten mayor fiabilidad
Revisa el caso de prueba, reproduce los defectos reales, aplica las correcciones permitidas y comprueba que las pruebas de regresión verifican el comportamiento.
1. Alto: los trabajos existentes podrían sobrescribirse sin previo aviso. 2. Medio: las ejecuciones diarias se desfasaron durante los cambios de horario de verano. Verificación: cuatro pruebas superadas, cero fallos.
Opinión del crítico: Se han detectado fallos útiles de mayor gravedad y se han aplicado correcciones específicas, pero no se han alcanzado los dos objetivos de revisión fijados.
D1 Desviación del horario de verano (DST) · Alta D2 Adquisición ingenua de la zona horaria · Media D3 Colisión de identificadores · Alta D4 La eliminación de un elemento que no existe provoca un KeyError · Media D5 Propietario no validado · Baja
Opinión del crítico: Mayor cobertura de la revisión sin falsos positivos reproducidos; no se corrigieron dos hallazgos dependientes del contrato.
Ver el resultado completo de Codex
Salida completa, desinfectada y válida por primera vez
## Hallazgos que requieren acción 1. **Alto — Los trabajos existentes podrían sobrescribirse sin previo aviso** - Evidencia: inicialización del identificador en [service.py](./scheduler/service.py:8), combinada con la sustitución del diccionario en [repository.py](./scheduler/repository.py:9). - Impacto: al crear un servicio con un repositorio ya existente, se podría reutilizar el identificador `1`, lo que sustituiría el trabajo almacenado y provocaría una pérdida de datos. - Reproducción mínima: guarda `Job(1, ...)`, crea `SchedulerService(repository)` y, a continuación, crea otro trabajo. Antes de la corrección, solo quedaba el nuevo trabajo. - Solución: inicializar el siguiente identificador a partir del identificador más alto existente en el repositorio. - Prueba de regresión: [test_scheduler.py](./tests/test_scheduler.py:22). 2. **Medio — Las ejecuciones diarias se desfasaban con los cambios de horario de verano** - Evidencia: cálculo corregido en [time_rules.py](./scheduler/time_rules.py:4). - Impacto: una tarea diaria programada para las 09:00 se ejecutó a las 10:00 tras el cambio al horario de verano en primavera. - Reproducción mínima: calcular la siguiente ejecución después de `2026-03-07 09:00 America/New_York`. Antes de la corrección, el resultado era `2026-03-08 10:00`. - Solución: añadir un día natural local en lugar de 24 horas UTC transcurridas. - Prueba de regresión: [test_scheduler.py](./tests/test_scheduler.py:33). ## Archivos modificados - [scheduler/service.py](./scheduler/service.py) - [scheduler/time_rules.py](./scheduler/time_rules.py) - [tests/test_scheduler.py](./tests/test_scheduler.py) ## Verificación - Se observó que ambas pruebas de regresión fallaban antes de sus correcciones. - Cada una de ellas superó la prueba tras su correspondiente corrección. - `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s tests -v` - Resultado: **4 pruebas superadas, 0 fallos**. - No quedan artefactos de caché de Python generados. ## Conclusión y decisión - **Conclusión:** Ambos defectos confirmados están corregidos y cubiertos por pruebas de regresión específicas. - **Decisión necesaria:** Ninguna.
Ver el código Claude completo
Registro completo de la transferencia, incluida la salida válida inicial tal y como se ha registrado.
Registro de ejecución de # T4 — Prueba y revisión del código
Parámetros de ejecución de ##
| Campo | Valor |
| --- | --- |
| Tarea | `T4` |
| Prompt congelado | `01-prompts/T4.md` |
| SHA-256 del prompt congelado | `5e69546007ea776f40b3d334ddb75671ff684c151c77551bad27de3b0fdce640` |
| Unidad de ejecución | subagente aislado Claude, "de uso general", contexto nuevo |
| Método de aislamiento | **recurso de reserva del subagente** (regresión documentada — véase `results/isolation-decision.md`) |
| Copia limpia asignada | `runtime/workspaces/T4` |
| Versión del código Claude | `2.1.220` |
| Configuración del modelo | `claude-opus-5` |
| start_utc | `2026-07-28T10:28:22Z` |
| end_utc | `2026-07-28T10:32:17Z` |
| Duración | 235 s (simultáneo con T1–T3; no es una medición de latencia limpia) |
| Número de intento | 1 |
| Estado | `válido` |
| Intervención humana | ninguna |
La condición de la indicación congelada —"corrígelas si la configuración de ejecución congelada permite
correcciones"— fue resuelta por el coordinador a partir de `00-control/run-config.json` y
`00-control/safety-policy.json`, que permiten escribir copias aisladas de los fixtures. Se indicó al ejecutor
que se permiten las correcciones. No se le mostró ningún archivo de control.
## Resumen de herramientas seguras
`date`, `Read`/`Edit`/`Write` únicamente dentro del espacio de trabajo asignado, `grep`,
`python3 -m unittest discover`, `python3 -m compileall`, una ejecución de vacuidad con copia temporal, una
comprobación de presencia del manifiesto. Sin red, sin instalaciones, solo biblioteca estándar.
## Archivos modificados
`scheduler/time_rules.py` (modificado), `scheduler/service.py` (modificado),
`tests/test_scheduler.py` (modificado, +2 pruebas), `tests/test_time_rules.py` (añadido, 5 pruebas).
Parche completo: `results/T4/patch.diff`.
## Resultados de la comprobación sin conexión
Informado por el ejecutor de tareas:
| Comprobación | Resultado |
| --- | --- |
| `python3 -m unittest discover -s tests -v` | **9 superadas, 0 fallidas, 0 omitidas** (línea de base 2) |
| `python3 -m compileall -q scheduler tests` | superada |
| Comprobación de presencia del manifiesto frente a `expected_files` | los 8 presentes |
| Ejecución de vacuity con las implementaciones originales restauradas | 5 fallos, 4 superadas, según lo previsto |
Revisado de forma independiente por el coordinador desde fuera del espacio de trabajo:
| Comprobación | Salida | Resultado |
| --- | --- | --- |
| conjunto de pruebas público (`results/checks/T4-public.txt`) | 0 | 9 pruebas, OK |
Comprobación cruzada informativa, sin puntuación: el validador oculto de T3 también supera la prueba en este
espacio de trabajo (salida 0), ya que T4 corrigió de forma independiente el mismo defecto `next_daily_run`. Véase
`results/checks/validator-controls.md`.
## Cobertura de defectos del mapa de referencia
`03-checks/gold-map.json` introduce dos defectos para la puntuación de T4. Ambos se han detectado:
| Defecto introducido | Detectado | Notificado como |
| --- | --- | --- |
| propietario en blanco aceptado | sí | **D5**, `scheduler/service.py:11,13`, gravedad Baja, notificado y no corregido deliberadamente |
| La eliminación de un identificador que falta genera un `KeyError` | sí | **D4**, `scheduler/repository.py:19` → `service.py:22`, gravedad media, notificado y no corregido deliberadamente |
Se notificaron tres hallazgos adicionales más allá del conjunto inicial. Ninguno es un falso positivo:
- **D1** — Desviación del horario de verano en `next_daily_run`. Este es el tercer defecto que figura en
`03-checks/seeded-defects.md` (está clasificado como T3, no como T4, pero se trata de un error simulado auténtico).
- **D2** — Una entrada ingenua en `next_daily_run` adquiere silenciosamente la zona horaria del host y devuelve una
fecha y hora que la tiene en cuenta. Real y dependiente del host; verificado por el coordinador comparándolo con el código fuente del fixture.
- **D3** — Colisión de identificadores entre un servicio y un repositorio inyectado o compartido, lo que provoca
una sobrescritura silenciosa. Real: `SchedulerService._next_identifier` siempre comienza en 1, mientras que
`JobRepository.save` es una asignación de diccionario sin protección. T1 identificó de forma independiente la misma
falla a partir del fixture original.
Recuento de falsos positivos: **0**. El ejecutor también informó correctamente de que no había hallazgos de seguridad, señalando
que el fixture no presenta vulnerabilidades de red, E/S, deserialización, subprocesos ni credenciales.
## Desviación digna de mención
La indicación fija solicitaba pruebas y correcciones sobre "los dos defectos con mayor nivel de confianza". El
ejecutor seleccionó D1/D2 (una corrección, misma función) y D3, en lugar de los dos defectos introducidos en el mapa de referencia
D4 y D5. Su razón expuesta es defendible: tanto D4 como D5 dependen de un contrato de API
no especificado, por lo que corregirlos sería una conjetura. El crédito por detección no se ve afectado: se detectaron ambos defectos preestablecidos,
con indicación de archivo, línea, gravedad, impacto y reproducción. Las correcciones aplicadas correspondían a defectos reales
y están respaldadas por una comprobación de vacuidad.
## Errores saneados
Dos fallos de prueba autoinducidos debidos a la sustracción del tiempo real frente al tiempo absoluto en fechas y horas
que comparten un `tzinfo`; corregidos y verificados de nuevo. No se produjeron errores de herramientas ni de entorno. Los artefactos `__pycache__`
se eliminaron tras la ejecución.
## Salida visible (textual)
### Informe de defectos
**D1 — `scheduler/time_rules.py:10-11` — La programación diaria se desvía una hora durante el horario de verano — Gravedad: Alta**
`next_daily_run` suma 24 horas *transcurridas* mediante un ciclo de ida y vuelta en UTC en lugar de avanzar el día natural. Una tarea diaria se define según la hora real, por lo que todas las tareas en una zona que aplica el horario de verano se ejecutan silenciosamente una hora antes o después a partir del día de cambio de hora, y el error persiste (cada ejecución se reinicia a partir de la hora desplazada). La propia cadena de documentación admitía este comportamiento.
```
prev = datetime(2024, 3, 9, 9, 0, tzinfo=ZoneInfo("America/New_York"))
next_daily_run(prev) # -> 2024-03-10 10:00:00-04:00, esperado 09:00-04:00
prev = datetime(2024, 11, 2, 9, 0, tzinfo=ZoneInfo('America/New_York'))
next_daily_run(prev) # -> 3 de noviembre de 2024 a las 08:00:00-05:00, previsto a las 09:00-05:00
```
**D2 — `scheduler/time_rules.py:10-11` — Una entrada "naiva" adquiere silenciosamente la zona horaria local del equipo — Gravedad: Media**
Las mismas dos líneas. `.astimezone(timezone.utc)` sobre un objeto `datetime` "naive" asume la hora local del sistema, y `.astimezone(None)` devuelve un objeto `datetime` "aware". Una llamada con entrada "naive" devuelve una salida 'aware', por lo que el resultado no es comparable con los demás valores 'naive' del llamante (`TypeError` al compararlos) y la respuesta depende de la `TZ` del host.
```
next_daily_run(datetime(2024, 1, 1, 9, 0))
# -> datetime(2024, 1, 2, 9, 0, tzinfo=timezone(timedelta(hours=8), "CST")) # dependiente del host
```
**D3 — `scheduler/service.py:8,13,15` (prefijo) — La colisión de identificadores destruye silenciosamente los trabajos almacenados — Gravedad: Alta**
El contador de identificadores era específico de cada `SchedulerService` y siempre comenzaba en 1, ignorando el contenido del repositorio inyectado. Cualquier segundo servicio que utilizara un repositorio compartido, o cualquier servicio que utilizara un repositorio prellenado, reasignaba el identificador 1; `JobRepository.save` es una asignación a un diccionario, por lo que el trabajo existente se sobrescribía sin generar ningún error. Pérdida de datos.
```
repo = JobRepository()
SchedulerService(repo).create_job("Copia de seguridad nocturna", 'Alex') # id 1
SchedulerService(repo).create_job('Exportación de nóminas', 'Blake') # id 1 de nuevo
repo.all() # -> [Job(1, 'Exportación de nóminas', 'Blake')] — El trabajo de Alex ha desaparecido
```
**D4 — `scheduler/repository.py:19` → `scheduler/service.py:22` — `delete_job` genera un `KeyError` por un id desconocido — Gravedad: Media**
`delete` utiliza `dict.pop` sin un valor por defecto, mientras que su homólogo `get` (línea 13) devuelve `None` si no encuentra el elemento. Esta inconsistencia provoca un `KeyError` sin tratar a través de `SchedulerService.delete_job`, que es poco probable que los usuarios que lo llaman detecten. Reproducción: `SchedulerService().delete_job(999)` → `KeyError: 999`. La solución sería `pop(identifier, None)` o una excepción de dominio explícita; se ha dejado deliberadamente sin corregir, ya que el contrato correcto (operación nula silenciosa frente a error tipificado) es una decisión de la API que corresponde al propietario.
**D5 — `scheduler/service.py:11,13` — El campo «owner» no se valida — Gravedad: Baja**
Se comprueba que `name` no esté vacío, pero no se comprueba `owner`: `create_job("ok", " ")` almacena `owner=''`. Ambos parámetros también llaman a `.strip()` sin protección, por lo que `None` se muestra como `AttributeError: el objeto 'NoneType' no tiene el atributo 'strip'` en lugar del `ValueError` que la API utiliza normalmente para entradas incorrectas. Se ha dejado sin corregir: el contrato previsto para `owner` no se especifica en ninguna parte del fixture.
**Seguridad:** sin hallazgos. El fixture no presenta vulnerabilidades de red, E/S, deserialización, subprocesos ni credenciales; el almacenamiento es un diccionario dentro del proceso.
**Laguna de cobertura:** `scheduler/time_rules.py` y `scheduler/repository.py` tenían una cobertura de pruebas nula antes de esta ejecución; las dos pruebas predefinidas solo ejecutaban la ruta normal del servicio.
### Pruebas y correcciones aplicadas
Los dos defectos con mayor nivel de confianza fueron **D1/D2** (misma función, una corrección) y **D3**.
**Corrección 1 — `scheduler/time_rules.py`:** se sustituyó el cálculo de ida y vuelta en UTC por `previous_run + timedelta(days=1)`. Python realiza operaciones aritméticas con tiempo real sobre fechas y horas conscientes, y `zoneinfo` vuelve a resolver el desfase UTC de forma diferida a partir del nuevo tiempo real; así, las 09:00 siguen siendo las 09:00, mientras que el desfase se desplaza correctamente de `-05:00 → -04:00`. Esto corrige D1 y D2 a la vez: las entradas «naïve» ahora se mantienen como tales y nunca se deduce la zona horaria local. El caso de la hora de transición entre «inaccesible» y «ambigua» se documenta como un caso en el que se recurre a las reglas de redondeo de `tzinfo`.
**Corrección 2 — `scheduler/service.py`:** se ha eliminado el contador `_next_identifier` por instancia; ahora los identificadores se asignan a partir del estado del repositorio como `max(ids existentes, por defecto=0) + 1` en el momento de la creación. Esto es correcto tanto para repositorios compartidos como para los prellenados. Compensación: O(n) por creación en lugar de O(1), lo cual es aceptable para un almacén en memoria respaldado por un diccionario, y garantiza la corrección frente a un contador que no puede ver el almacén en el que escribe.
**Pruebas — `tests/test_time_rules.py` (nuevo, 5 pruebas):** Conservación del reloj real en el cambio al horario de verano y al horario de invierno, tiempo transcurrido absoluto de 23 h/25 h durante esas transiciones, resolución de la diferencia horaria con respecto al UTC, «naive-stays-naive» y un simple avance de un día en el UTC. Se omite correctamente si no hay ninguna base de datos de la IANA disponible.
**Pruebas — `tests/test_scheduler.py` (ampliado, +2 pruebas):** dos servicios que comparten un repositorio deben emitir identificadores distintos y conservar ambos trabajos; un servicio sobre un repositorio prellenado con los identificadores 1 y 7 debe emitir el 8 y dejar intactos los trabajos existentes.
**Comprobación de vacuidad:** he restaurado ambas implementaciones originales en una copia temporal y he vuelto a ejecutar el conjunto de pruebas: las 5 pruebas nuevas fallaron (`FAILED (failures=5)`), y todas pasan con el código corregido. Las pruebas identifican realmente los defectos.
Una corrección durante la ejecución: en mi primer borrador, la aserción del tiempo transcurrido en horario de verano se realizaba como `resultado - anterior`, lo que Python evalúa como aritmética de reloj real cuando ambos operandos comparten un `tzinfo`, por lo que se interpretaba como `24 h` y las pruebas fallaban. Ahora las aserciones se convierten a UTC antes de la resta. El fallo estaba en mi prueba, no en la corrección.
### Notas para el responsable
El archivo `fixture-manifest.json` no se modificó, por lo que `tests/test_time_rules.py` es un archivo nuevo válido que no aparece en `expected_files` — actualiza el manifiesto si una comprobación exige un conjunto exacto de archivos. Y los errores D4 y D5 se notificaron, pero no se corrigieron deliberadamente: ambos dependen de una decisión sobre el contrato de la API (semántica de «miss-on-delete», reglas de validación del propietario) que no se especifica en ninguna parte del fixture, por lo que elegir una opción habría sido una suposición más que una corrección.
Limitación del entorno de pruebas: se trataba de un pequeño repositorio de Python fuera de línea. Claude utilizaba un subagente de reserva tras la caducidad del OAuth de la CLI, por lo que el aislamiento y la visibilidad de los tokens no eran idénticos.
Ninguna de estas puntuaciones elimina la necesidad de una revisión humana. Una vez que cualquiera de los agentes haya completado una reparación, comprueba las interfaces modificadas, el ámbito de los archivos inesperados, la falta de pruebas negativas, el historial de permisos o comandos y la ruta de reversión. Una explicación convincente no sustituye a una prueba reproducible.
Permisos, entornos aislados y seguridad operativa
Un agente de programación puede leer código privado, ejecutar comandos de shell, modificar numerosos archivos y llamar a herramientas externas. Por ello, el diseño de los permisos forma parte de la calidad del producto. Las cuestiones relevantes son: qué operaciones requieren autorización, a qué puede acceder el agente por defecto, con qué claridad advierte de las acciones de riesgo y si un desarrollador puede revisar los cambios resultantes.
No hay que dar por sentado que un menor número de solicitudes de aprobación sea automáticamente mejor. Una baja fricción resulta útil en un entorno aislado sin credenciales ni conexiones de producción. Sin embargo, ese mismo comportamiento puede resultar peligroso en un repositorio conectado a scripts de implementación, datos reales de clientes o permisos amplios en la nube. Por el contrario, un número excesivo de aprobaciones puede hacer que la automatización segura resulte poco práctica y animar a los usuarios a aprobar solicitudes sin leerlas.
Nuestra prueba se realizó con copias locales recientes, sin sistemas de producción, sin implementación y sin intervención humana en la ejecución efectiva. Por lo tanto, evalúa la realización de las tareas en condiciones seguras, no la seguridad relativa de los dos productos. Antes de utilizar cualquiera de estas herramientas en trabajos importantes, empieza con acceso de solo lectura, define los directorios y comandos permitidos, revisa las diferencias y realiza comprobaciones objetivas en un entorno recuperable.
- Empieza con una arquitectura de solo lectura o asume el riesgo.
- Acepta únicamente aquellos comandos cuyo alcance y consecuencias comprendas.
- Mantén las credenciales, los datos de producción y el acceso a la implementación fuera del entorno de prueba.
- Exige un archivo «diff», pruebas y una vía clara para revertir los cambios antes de aceptar el resultado.
MCP, habilidades, instrucciones del proyecto y personalización
Ambos ecosistemas pueden ampliarse, pero los términos de «ampliación» no deben considerarse sinónimos. Las instrucciones de proyecto indican a un agente cómo comportarse en un repositorio. Las «skills» reutilizables agrupan un flujo de trabajo repetible o conocimientos especializados. MCP conecta un host con herramientas o datos externos. Los «hooks» y los comandos pueden automatizar momentos concretos de un ciclo de desarrollo. Un producto puede destacar en una capa sin que ello implique que tenga que igualar otra característica punto por punto.
La cuestión a la hora de comprar no es si existe un logotipo de integración, sino si la conexión se puede instalar, autenticar, aprobar y poner en práctica en el servidor que realmente utilizas. Además, debe contar con un modo de fallo comprensible. Un servidor MCP que funcione de forma interactiva pero que espere a recibir la aprobación en una sesión desatendida sigue siendo útil, pero no debería describirse como una automatización sin fricciones.
Las instrucciones reutilizables tienen una salvedad similar. Los archivos de instrucciones extensos no garantizan el cumplimiento, y un conjunto de reglas demasiado complicado puede restar contexto o generar contradicciones. Las directrices del repositorio deben ser breves, comprobables y estar estrechamente relacionadas con el código. Indica los comandos de verificación obligatorios, las áreas protegidas, las normas de estilo y cuándo el agente debe detenerse para solicitar autorización.
- Instrucciones del proyecto: normas específicas del repositorio y comandos de verificación.
- Competencias: procedimientos reutilizables o paquetes de conocimientos especializados.
- MCP: conexiones con herramientas y datos externos.
- Ganchos y comandos: automatización en torno a eventos definidos del flujo de trabajo.
Codex frente a Claude: precios y límites de uso
La comparación de tarifas básicas para consumidores comienza en $20 al mes. El acceso a Codex está incluido en el plan correspondiente $20 ChatGPT, mientras que Claude Pro cuesta $20 al mes en Estados Unidos e incluye Claude Code. Ambos ofrecen niveles de consumo más elevados, de $100 y $200. Que los precios de catálogo sean similares no significa que la capacidad sea idéntica.
El plan “Claude Max 5x” cuesta $100 al mes y el plan “Max 20x” cuesta $200 al mes. Las denominaciones «5x» y «20x» describen el uso por sesión en comparación con la versión Pro, y no un número garantizado de tareas de codificación. Claude y Claude Code comparten los límites por sesión y semanales. La longitud del contexto, los archivos adjuntos, la elección del modelo y el uso de funciones pueden influir en la rapidez con la que se agota la asignación.

Los créditos de uso opcionales de Claude permiten continuar con el trabajo elegible una vez agotado el uso incluido, siempre que estén activados, y dichos créditos se facturan por separado según las tarifas estándar de la API. La autenticación mediante clave API constituye otra vía de facturación independiente. Calificar cualquiera de estas vías como “ilimitada” ocultaría el coste marginal real.

Codex distingue asimismo entre el uso incluido en el plan de consumidor, los créditos adquiridos válidos y la facturación de la API. Los mensajes locales y los chats en la nube comparten un intervalo de cinco horas, y también pueden aplicarse límites semanales. Un campo de token en un registro de la CLI no se puede convertir directamente en un porcentaje de cinco horas, en un importe de crédito de suscripción ni en una factura de la API.
Para tareas ocasionales de programación en solitario, el nivel $20 en cualquiera de los dos lados es el punto de partida más sensato. El trabajo diario con repositorios que impliquen contextos largos puede justificar un nivel de uso superior, pero solo tras observar los reinicios reales y el consumo de tareas. El trabajo paralelo intensivo requiere aún más cuidado: pagar por un nivel superior no elimina la necesidad de controlar el contexto, dividir las tareas y reservar el razonamiento más costoso para el trabajo que requiera un alto nivel de juicio.
Resultados de nuestra prueba comparativa controlada entre el código Codex y el código Claude
Antes de que se ejecutara ninguno de los agentes, fijamos cuatro instrucciones en inglés, un programa estándar público en Python, comprobaciones objetivas, una rúbrica de puntuación y una regla de «primer resultado válido». Las tareas abarcaban la comprensión de un repositorio desconocido, una característica de varios archivos, la corrección de un error en el horario de verano y la revisión de código con correcciones. Los resultados válidos no fueron objeto de intervención humana, y los resultados válidos pero deficientes no pudieron volver a ejecutarse para obtener una puntuación mejor.
Codex ejecutó procesos CLI aislados con JSONL sin procesar, conservando los campos de tokens emitidos por la CLI. La sesión OAuth de la CLI de Claude de nuestro compañero había caducado, por lo que Claude Code utilizó un coordinador con contextos de subagentes nuevos. Reconstruimos los parches de Claude en copias de auditoría limpias y volvimos a ejecutar las comprobaciones públicas y ocultas. Esto generó pruebas prácticas creíbles, pero no se obtuvieron superficies de aislamiento ni de control de modelos idénticas.
| Tarea | Códice | Código Claude | Interpretación acotada |
|---|---|---|---|
| T1: comprensión del repositorio | 20/20 | 20/20 | Empate: «compacto» frente a «exhaustivo» |
| T2: función de múltiples archivos | 30/30 | 30/30 | Conexión funcional; alcance diferente de la validación y las pruebas |
| T3: Reparación del DST | 30/30 | 30/30 | Ambas opciones fueron aprobadas: una cobertura más reducida frente a una cobertura más amplia de los bordes |
| T4: revisión y reparación | 18/20 | 19/20 | El código Claude mostró una mayor amplitud de revisión |
Prueba supervisada · Rúbrica de «Frozen»
Codex 98/100 frente a Claude Código 99/100
La diferencia de un punto no es una señal inequívoca de victoria. Las diferencias que resultan útiles dependen de cada tarea concreta.
| Ámbito de decisión | Resultado observado | Lectura práctica |
|---|---|---|
| Conocimiento del repositorio | Empate · 20/20 cada uno | Claude era más exhaustivo; Codex era más conciso. |
| Función de múltiples archivos | Empate · 30/30 cada uno | Ambos superaron las comprobaciones ocultas; Claude añadió pruebas más exhaustivas. |
| Corrección de un error relacionado con el horario de verano | Empate · 30/30 cada uno | Claude cubría más bordes; Codex utilizó el parche más estrecho. |
| Revisión y reparación | Claude 19/20; Codex 18/20 | Claude detectó más defectos reproducibles. |
| Velocidad observada | Mixto | Claude lideró T1/T2; Codex lideró T3/T4. |
Aviso: Las mismas instrucciones y el mismo entorno, pero con rutas de aislamiento diferentes. No generalices esta pequeña prueba a todos los repositorios, niveles de modelo o sesiones autónomas.
La prueba es demasiado limitada para evaluar el rendimiento en repositorios de gran tamaño, en todos los lenguajes, en todos los niveles de modelos o en sesiones autónomas prolongadas. La diferencia total de un punto debe interpretarse como “ambos tuvieron éxito, con una diferencia en el alcance de la revisión”, y no como una clasificación universal.
Lo que cuentan los usuarios reales… y hasta qué punto hay que fiarse de ello
Los comentarios de la comunidad son valiosos cuando incluyen un proyecto, una tarea, un modelo, el contexto de la iniciativa y el plazo previsto. Son poco útiles cuando una publicación solo señala que un producto “parece más inteligente” o “es mucho más rápido”. Los distintos usuarios comparan diferentes repositorios, niveles de suscripción, indicaciones, extensiones y grados de supervisión.
El ejemplo contextual de Reddit anterior sugiere una disyuntiva en cuanto a la interacción: un estilo rápido y coloquial puede exigir más atención, mientras que una ejecución deliberada puede parecer más lenta, pero requiere menos correcciones. Nuestra prueba controlada solo coincide parcialmente con ese relato. En las ejecuciones válidas se observó una velocidad variable y ninguna intervención humana, por lo que no puede validar la afirmación sobre la «supervisión». Precisamente por eso, las pruebas de la comunidad y las pruebas controladas deben presentarse conjuntamente, sin fusionarlas.
El comentario sobre el arnés X plantea una segunda cuestión útil: el resultado de la codificación pertenece al sistema en su conjunto, no solo a la etiqueta del modelo. Ninguna de las dos publicaciones debe considerarse como datos de una encuesta. Úsalas para identificar cuestiones para tu propio ensayo: número de intervenciones, magnitud de la diferencia, calidad de la prueba y recuperación tras una suposición errónea.
Ampliación de Codex y del código Claude con GlobalGPT
GlobalGPT es un espacio de trabajo multimodelo y multimodal, no sustituye a los agentes nativos del repositorio. Su función práctica consiste en ampliar las capacidades de un host ya existente: utilizar otro modelo de agente disponible para obtener una segunda opinión, revisar un plan, realizar una comprobación de la documentación o generar resultados especializados, mientras que Codex o Claude Code siguen siendo responsables del acceso al repositorio, los comandos de shell, las pruebas y las aprobaciones.
GlobalGPT publica guías oficiales de la interfaz de línea de comandos (CLI) para Códice y Código Claude, así como Cursor. En nuestra prueba de integración segura de T5, la CLI completó la misma tarea «congelada» en ambos entornos comparados. En Codex, también verificamos una llamada de solo lectura a la lista de modelos del MCP e instalamos, cargamos y utilizamos la skill GlobalGPT.
Integración GlobalGPT · Excluida de la puntuación de codificación
CLI verificado en ambos flujos de trabajo; MCP y Skill verificados en Codex
Esto mide las vías de integración, no si GlobalGPT sustituye a alguno de los agentes de codificación nativos.
| Ruta | Entorno de Codex | Carrera de los compañeros de Claude |
|---|---|---|
| GlobalGPT CLI | Verificado con gpt-5.6-sol y gpt-5.6-luna | Verificado con las mismas tareas congeladas |
| MCP | Lista interactiva de modelos de solo lectura verificada | No conectado durante la carrera |
| Habilidad | Instalado, cargado y probado | Instalado, pero no se utiliza para llamadas en espera |
| MCP sin supervisión | Se ha observado una limitación en la autorización | No se ha probado |
Ver toda la documentación sobre la integración
Resumen de la prueba de integración # GlobalGPT Ámbito de aplicación de # # La medida T5 evalúa la integración de GlobalGPT y queda excluida de la puntuación de codificación nativa de Codex. No se utilizó Yukie. ## Bloqueo y disponibilidad de los modelos - Modelo A: `gpt-5.6-sol` - Modelo B: `gpt-5.6-luna` - Ambos modelos aparecieron en la lista de modelos activos. - Ambas pruebas de disponibilidad ligeras devolvieron texto válido y analizable. - Los modelos se bloquearon antes de las llamadas formales de salida de T5A y T5B. ## Resultados formales de la CLI | Tarea | Modelo | Estado | Duración | Tokens de la indicación | Tokens de finalización | Encabezados requeridos | |---|---|---|---:|---:|---:|---:| | T5A | gpt-5.6-sol | Válido | 27 s | 2.116 | 1.207 | 6/6 | | T5B | gpt-5.6-luna | Válido | 14 s | 2.116 | 1.441 | 6/6 | Ambas llamadas formales utilizaron la misma tarea congelada de planificación de productos en inglés a través de `glbgpt exec`, devolvieron resultados visibles únicamente en inglés y no expusieron ninguna credencial. No se produjo ninguna repetición de la prueba de calidad. ## Resultado de MCP - `codex mcp list` mostró que `globalgpt` estaba habilitado. - Una sesión no interactiva de Codex detectó e intentó ejecutar `mcp__globalgpt__glbgpt_list_models`, pero la llamada se canceló porque no se disponía de la aprobación automática de MCP. No se relajó ningún ajuste de seguridad. - La sesión interactiva activa de Codex invocó con éxito la misma herramienta MCP GlobalGPT de solo lectura y recibió nueve modelos de chat. - Resultado: se ha verificado el acceso interactivo a MCP; la ejecución no interactiva y desatendida de MCP sigue sujeta a una limitación de aprobación. Resultado de la habilidad ## - Las habilidades GlobalGPT y GlobalGPT Coding están instaladas en el directorio de habilidades de Codex a través de enlaces al paquete de habilidades CLI incluido. - Se cargó la habilidad GlobalGPT y se siguió para la comprobación de la sesión configurada, la selección de modelos en tiempo real, la disponibilidad, las opciones de seguridad de crédito y la gestión de fallos. - Resultado: instalada y puesta en práctica en el flujo de trabajo activo de Codex. ## Conclusión limitada Esta ejecución verifica el funcionamiento de las llamadas CLI de GlobalGPT, una llamada interactiva de solo lectura a MCP de GlobalGPT desde Codex y una ruta de la habilidad GlobalGPT instalada y utilizada. No demuestra que todos los modelos, herramientas multimedia, hosts, cuentas o configuraciones MCP desatendidas funcionen. No prueba Yukie ni respalda la afirmación de que GlobalGPT sustituya a Codex o a Claude Code.
Ámbito verificado: llamadas específicas a la CLI, una lectura interactiva de MCP y una ruta de Skill instalada/utilizada. Esto no garantiza todos los modelos, herramientas multimedia, hosts o configuraciones automáticas.
El límite es importante. La ejecución de Claude realizada por el compañero verificó la ruta CLI, no el MCP del lado de Claude. La Skill estaba instalada allí, pero no se utilizó para las llamadas congeladas. Un intento de Codex MCP sin supervisión seguía requiriendo aprobación manual. La disponibilidad de los modelos y los créditos también dependen del plan GlobalGPT actual, por lo que los desarrolladores deberían consultar la lista activa en lugar de dar por sentado que todos los modelos están incluidos.
GlobalGPT también abarca imágenes, vídeos, audio y flujos de trabajo guiados para agentes. Las entradas de Yukie para diapositivas, documentos e imágenes constituyen una categoría diferente: ofrecen plantillas explícitas y una guía paso a paso en el navegador para tareas que no requieren código. Esto puede resultar más sencillo que abrir una CLI de programación para crear una presentación o un documento estructurado, pero no sustituye a la lectura del repositorio, la ejecución de pruebas ni la revisión del código.
Categoría de flujo de trabajo diferente
Pruebas de agente del navegador guiadas por Yukie
Útil para flujos de trabajo web específicos; no se ha probado como sustituto de un agente de codificación de repositorios.
| Flujo de trabajo | Criterios cumplidos | Primer resultado válido |
|---|---|---|
| Diapositivas | 5/6 | Se ha generado una presentación de cinco diapositivas; no se han podido verificar las notas del ponente. |
| Documento | 6/6 | Resumen de investigación estructurado con historial de exportaciones y versiones. |
| Imagen + pie de foto | 5/6 | Imagen llamativa de formato cuadrado; faltaba el pie de foto obligatorio. |
Conclusión fundamentada: puntos de entrada claros y flujos de trabajo multimodales guiados. Conclusiones sin fundamento: uso ilimitado, precio exacto o sustitución total del código Codex/Claude.
¿Qué lenguaje de programación debería elegir un desarrollador independiente?
- Elige Codex cuando la ejecución compacta y acotada, la combinación de superficies disponibles o los datos de ejecución disponibles en tu configuración se adapten a tu forma de trabajar.
- Selecciona el código Claude cuando lo que más importa es un bucle de terminal conversacional, las funciones de personalización que has comprobado o un comportamiento de revisión más amplio, como el observado en nuestro dispositivo de prueba.
- Utiliza cualquiera de las dos opciones para un repositorio que no conozcas solo tras superar una prueba de arquitectura de solo lectura y de riesgos. Ambas herramientas llevaron a cabo esa tarea correctamente.
- Utilízalas ambas de forma selectiva cuando la revisión por parte de un segundo agente justifica el coste adicional de la suscripción y la coordinación. Pide a la segunda herramienta que cuestione una diferencia o un plan, en lugar de repetir la tarea a ciegas.
- Añadir GlobalGPT cuando el enrutamiento multimodelo o el trabajo multimodal es el eslabón que falta. Mantén las operaciones nativas del repositorio en el servidor de programación.
Una prueba de siete días ofrece más información que una tabla de clasificación. Elige una función real, pero que no sea de producción, un error y revísalos. Congela la indicación y el comando de éxito. Registra la primera diferencia válida, las pruebas superadas, el tiempo transcurrido, las intervenciones humanas, el alcance inesperado y cómo el trabajo ha afectado a tu uso disponible. El mejor producto es aquel cuyos errores puedes detectar y cuyo flujo de trabajo puedes mantener.
Preguntas frecuentes sobre el código Codex frente al Claude
¿Es Codex mejor que el código Claude?
No en todos los casos. Ambos completaron las cuatro tareas de nuestro programa de pruebas. Claude Code destacó por la amplitud de su revisión, mientras que Codex resultó más conciso en algunos aspectos de la implementación y la corrección de errores.
¿Cuál es la mejor opción para repositorios de gran tamaño?
Nuestra pequeña herramienta no puede responder a eso. Evalúa ambas opciones en una tarea de arquitectura de solo lectura desde tu propio repositorio antes de permitir cambios.
¿Qué agente de codificación requiere menos supervisión?
Depende de la claridad de la tarea, la configuración del modelo, las instrucciones del repositorio y el estilo de interacción deseado. Un usuario de Reddit mencionó diferentes patrones de supervisión, pero esa experiencia individual no constituye una regla válida para todo el producto.
¿Cuál es más barato?
Los principales niveles de consumo se sitúan en $20, $100 y $200 al mes, pero la capacidad incluida y las condiciones de límite varían. Los créditos opcionales y la facturación por API se gestionan por separado.
¿Comparten los planes Claude y Claude los mismos límites de uso?
Sí. Claude y Claude Code utilizan una sesión compartida y límites semanales en los planes Pro o Max asociados.
¿Comparten los límites las tareas locales y en la nube de Codex?
Los mensajes locales y los chats en la nube comparten un margen de cinco horas, y también pueden aplicarse límites semanales.
¿Puede el GlobalGPT sustituir al Codex o al Claude Code?
No. Puede ampliar cualquiera de los dos flujos de trabajo con modelos adicionales, la interfaz de línea de comandos (CLI), MCP, Skills y herramientas multimodales, pero el acceso al repositorio y el comportamiento del agente de programación siguen estando en el servidor anfitrión.
¿Puedo utilizar el código GlobalGPT de ambos productos?
GlobalGPT ofrece tutoriales oficiales sobre la interfaz de línea de comandos (CLI) para ambos. Hemos comprobado el uso de la CLI en ambos entornos y el uso de MCP junto con Skill en Codex, con una limitación de autorización para el MCP sin supervisión.
Los precios, las condiciones de los planes, las integraciones y las fuentes de información se verificaron en julio de 2026. Los detalles del producto pueden sufrir cambios.
Abierto GlobalGPT si quieres incorporar una capa multimodelo y multimodal a tu flujo de trabajo de codificación actual.


