Una fuente de verdad, tres harnesses que la consumen
Hasta este release el boilerplate de desarrollo solo sabía hablar con Claude Code: las
instrucciones vivían en CLAUDE.md, las skills en
.claude/skills/ y seis slash commands cargaban su workflow entero adentro.
Ahora hay exactamente una copia de cada instrucción y de cada skill, y Claude Code,
OpenCode y Codex (CLI y Desktop) la leen desde adaptadores delgados que se generan y se
verifican. Es el mismo modelo que agentic-qa-boilerplate ya
tiene en producción, adaptado a un flujo de desarrollo.
Nueve fases, un solo invariante: lo generado nunca se edita a mano
El plan se ejecutó en el orden en que el repo de QA lo hizo: primero el motor de verificación, después la fuente canónica, luego los adaptadores, y al final el updater que migra proyectos viejos y la documentación. Cada fase es un commit convencional en la rama, sin atribución de IA en el mensaje.
| Fase | Entregable | Archivos clave |
|---|---|---|
| 1 + 2 · Fuente canónica |
AGENTS.md es el único cuerpo de instrucciones;
CLAUDE.md
queda en 11 bytes (@AGENTS.md). Las 14 skills se mueven a
.agents/skills/; .claude/skills pasa a ser un alias
generado e ignorado por git. Regla crítica #15 nueva.
|
AGENTS.md, CLAUDE.md, .agents/skills/,
.gitignore, scripts/lint-skills.ts,
scripts/build-skill-registry.ts, .agents/project.yaml
|
| 0 · Motor de compatibilidad |
bun run agents:compat repara el alias y regenera wrappers;
--check valida alias, wrappers byte a byte, tres adaptadores de
hook y paridad MCP. Importación cerrada dentro de cli/lib, guardada
por ESLint.
|
cli/lib/agent-compatibility.ts,
cli/lib/agent-compatibility-contracts.ts,
scripts/agent-compatibility.ts
|
| 4 · Hook |
Un emisor del OUTPUT CONTRACT en .agents/hooks/; Claude y Codex lo
corren como command hook, OpenCode importa la constante desde un plugin.
|
.agents/hooks/personality-reinject.mjs,
.claude/settings.json, .codex/hooks.json,
.opencode/plugins/personality-reinject.js
|
| 5 · MCP |
.codex/config.toml declara los mismos cuatro servidores que
.mcp.json y opencode.jsonc. La paridad se compara por
las variables de .env de las que depende cada servidor, no por el
texto.
|
.codex/config.toml, contratos en cli/lib |
| 3 · Comandos |
Seis cuerpos inline (1.900 líneas) pasan a dos skills nuevas:
project-context (cinco modos) y sync-ai-memory. Los
ocho comandos quedan como aliases de 7 líneas generados desde un manifest.
|
.agents/compatibility/command-aliases.json,
.claude/commands/, .opencode/commands/
|
| 6 · Installer, doctor, gates |
bun run setup detecta los tres harnesses (INSTALL_AGENTS
para forzar); setup:doctor reporta cada superficie y la confianza
del repo en Codex como WARN; agents:compat:check entra en
repo:check, pre-push y pre-commit condicional. Nuevo wrapper
bun run codex.
|
cli/install.ts, cli/doctor.ts,
package.json,
.husky/
|
| 7 · Updater y scaffolder |
bun run up corre un preflight que promueve CLAUDE.md,
mueve skills legacy, archiva colisiones en
.template/pre-agents-migration/ y nunca borra. Componentes
renombrados para el layout nuevo.
|
cli/lib/updater-harness-migration.ts,
cli/update-boilerplate.ts,
packages/create-agentic-dev/
|
| 8 · Documentación |
README, CONTEXT, INSTALLER, CHANGELOG, docs/**, onboarding y decks
reescritos para el modelo; ADR-0002 registra la decisión; esta página cierra el
release.
|
.context/ADR/ADR-0002-multi-harness-single-source.md |
Cinco superficies, un adaptador por diferencia real
Donde los tres harnesses coinciden no hay adaptador: OpenCode y Codex leen
AGENTS.md y .agents/skills/ nativamente. Donde difieren de
verdad (formato de MCP, API de hooks, si existen slash commands) cada uno mantiene un
archivo fino, versionado o generado.
| Superficie | Claude Code | OpenCode | Codex CLI + Desktop |
|---|---|---|---|
| Instrucciones |
CLAUDE.md → @AGENTS.md
generado
|
AGENTS.md nativo |
AGENTS.md nativo |
| Skills | .claude/skills alias generado |
.agents/skills/ nativo |
.agents/skills/ nativo |
| Comandos | .claude/commands/*.md generado |
.opencode/commands/*.md generado
|
ninguno: se invoca skill + modo |
| Hook | .claude/settings.json → UserPromptSubmit |
.opencode/plugins/personality-reinject.js |
.codex/hooks.json → UserPromptSubmit |
| MCP | .mcp.json |
opencode.jsonc |
.codex/config.toml |
Por qué CLAUDE.md es un include y no un symlink. Un
symlink no sobrevive un checkout en Windows sin permisos de developer. Un archivo de
11 bytes sí. El alias de skills, en cambio, es un symlink POSIX o una junction de
Windows porque Claude Code no ofrece otra forma de apuntar su descubrimiento de skills
a otra carpeta.
Se edita la fuente, se regenera el resto
- AGENTS.md
- .agents/skills/ 16 skills + REGISTRY.md
- .agents/compatibility/command-aliases.json
- .agents/hooks/personality-reinject.mjs
- .mcp.json · opencode.jsonc · .codex/config.toml
- CLAUDE.md shim, commiteado
- .claude/skills alias, gitignored
- .claude/commands/*.md 8, commiteados
- .opencode/commands/*.md 8, commiteados
bun run agents:compat:check compara los wrappers byte a byte contra el
manifest, exige que CLAUDE.md sea exactamente el shim, verifica que el
alias resuelva a la tienda canónica, que los tres adaptadores de hook tengan el comando
exacto y que los cuatro MCP existan en los tres formatos. Un wrapper al que alguien le
agregue un párrafo falla como contains workflow prose.
Los slash commands son transporte, no workflow
Antes, cinco comandos de contexto y sync-ai-memory tenían el procedimiento
completo adentro del archivo de comando, invisible para cualquier harness que no fuera
Claude Code. Ahora el workflow vive en la skill y el comando solo nombra skill, modo y
reenvía $ARGUMENTS.
| Alias | Skill / modo | Escribe | Mutabilidad |
|---|---|---|---|
| business-data-map | project-context / data |
.context/business/business-data-map.md |
local, con aprobación |
| business-feature-map | project-context / features |
.context/business/business-feature-map.md |
local, con aprobación |
| business-api-map | project-context / api |
.context/business/business-api-map.md |
local, con aprobación |
| master-implementation-plan | project-context / master-plan |
.context/master-implementation-plan.md |
local, con aprobación |
| dev-roadmap | project-context / dev-roadmap |
.context/dev-roadmap.md |
local, con aprobación |
| sync-ai-memory | sync-ai-memory / sync |
AGENTS.md, README, CONTEXT, INSTALLER, docs/** | local, con aprobación |
| jira-components | jira-administration / components |
Jira | externa, con aprobación |
| jira-instance-migration | jira-administration / instance-migration |
Jira + .agents/ |
externa y local, con aprobación |
---
description: Generate or refresh the business data and flow map.
argument-hint: [project-path]
---
Invoke skill `project-context` in mode `data`.
Forward `$ARGUMENTS` unchanged.
Ese es un wrapper completo. En Codex no existe la capa: el usuario pide "load skill project-context, mode data" y llega al mismo reference.
Un emisor, tres adaptadores
El OUTPUT CONTRACT (PM Voice, markdown, bullets butler, sin em dash) se reinyecta en
cada prompt como contrapeso al plugin caveman. El texto existe una sola vez en
.agents/hooks/personality-reinject.mjs, que exporta la constante y la
imprime cuando se ejecuta directo.
Claude Code
.claude/settings.json-
UserPromptSubmit →
node "$CLAUDE_PROJECT_DIR/.agents/hooks/personality-reinject.mjs" attributionen blanco: el harness no agrega su propio trailer
OpenCode
.opencode/plugins/personality-reinject.js-
importa
PERSONALITY_CONTRACTy lo agrega aoutput.system - el check falla si el plugin reasigna el system prompt
Codex
.codex/hooks.json-
UserPromptSubmit con
git rev-parse --show-toplevely variantecommandWindows - solo carga en un repo marcado como confiable
Cuatro servidores, tres formatos, paridad semántica
context7, tavily, supabase y
n8n existen en los tres archivos. La comparación normaliza JSON, JSONC y
TOML a una forma común y verifica que cada servidor dependa de las mismas variables de
.env en cada host. Agregar un servidor a un solo host es un fallo del
check.
| Servidor | .mcp.json | opencode.jsonc | .codex/config.toml |
|---|---|---|---|
| context7 | bunx @upstash/context7-mcp |
igual, command: [] |
igual |
| tavily | mcp-remote con ${TAVILY_API_KEY} en la URL |
igual con {env:TAVILY_API_KEY} |
HTTP directo + bearer_token_env_var |
| supabase | --access-token ${SUPABASE_ACCESS_TOKEN} + 3 env |
igual |
sin flag: env_vars reenvía SUPABASE_ACCESS_TOKEN y las
3 claves por nombre
|
| n8n | npx n8n-mcp + env |
igual | env_vars + tabla env literal |
Codex no expande ${VAR} dentro de args. Por
eso tavily cambia de transporte y supabase pasa a autenticación por entorno, que es la
variable requerida que el paquete registra oficialmente. El contrato codifica esa
regla: se compara la dependencia, no el texto del comando, y un placeholder dentro de
la tabla env de Codex es un error explícito.
Setup, doctor y un updater que no destruye lo anterior
bun run setup
-
Detecta Claude Code (
~/.claude), OpenCode (~/.config/opencode) y Codex (binario o.codex/config.toml) -
INSTALL_AGENTS=claude-code,opencode,codexpara runs no interactivos - Termina reparando alias y wrappers
-
Wrappers
bun run claude | opencode | codexcargan.env
bun run setup:doctor
- Filas por superficie: instrucciones, alias, wrappers por host, tres hooks, paridad MCP × 3
- Codex: CLI en PATH, config presente
- Confianza del repositorio en Codex: WARN, es estado de runtime que ningún archivo puede probar
bun run up
- Preflight antes de sincronizar cualquier componente
-
CLAUDE.mdreal → se promueve aAGENTS.md; shim huérfano → bloqueo con comando de recuperación -
Skills legacy se mueven; colisiones se archivan en
.template/pre-agents-migration/ - Idempotente; nunca borra
bun run agents:compat # regenera alias + wrappers, luego verifica
bun run agents:compat:check # solo verifica (repo:check, pre-push, pre-commit condicional)
bun run setup:doctor # estado por superficie, incluida la confianza en Codex
bun run up # migra un proyecto creado antes de este release
Qué es igual, qué se adaptó y qué es distinto a propósito
Los dos boilerplates comparten el modelo y gran parte del código del motor. Difieren donde el flujo de trabajo difiere: QA testea, dev construye. La tabla marca cada preocupación con su estado real después de este release.
| Preocupación | agentic-qa-boilerplate | agentic-dev-boilerplate | Estado |
|---|---|---|---|
| Cuerpo de instrucciones | AGENTS.md + shim @AGENTS.md |
idéntico; el cuerpo tiene reglas propias (UI fidelity, orquestación dev) | igual |
| Tienda de skills | .agents/skills/, 19 skills, alias generado |
.agents/skills/, 16 skills, alias generado |
igual |
Motor agents:compat |
cli/lib/agent-compatibility.ts |
mismo archivo, byte a byte | igual |
| Contratos de hook | tres comandos exactos, sin rutas personales, sin duplicados | idénticos | igual |
| Manifest de comandos | 10 aliases, hosts claude + opencode | 8 aliases, mismos hosts y mismo vocabulario de mutabilidad | adaptado |
| Skill contenedora de contexto | project-context: data, features, api, test-plan |
project-context: data, features, api, master-plan, dev-roadmap
|
adaptado |
| Skill de sincronización de docs | sync-ai-context |
sync-ai-memory (nombre histórico del comando) con el mismo shim
guard
|
adaptado |
| Servidores MCP | 6: context7, tavily, playwright, dbhub, openapi, postman | 4: context7, tavily, supabase, n8n | adaptado |
| Contrato de paridad MCP | una forma esperada por servidor, igual en los tres hosts |
forma esperada por host; compara variables de .env dependientes;
rechaza placeholders en la tabla env de Codex
|
distinto |
| Parser JSONC | quita comentarios | quita comentarios y comas finales (Prettier las escribe aquí) | distinto |
| Adaptadores de hook | .mjs + settings + hooks.json + plugin |
copiados verbatim; el contrato cita AGENTS.md §2 |
igual |
| Instalador | detecta 3, INSTALL_AGENTS, repara compat al final |
mismo flujo; los avisos de caveman y engram siguen siendo Claude-only y lo dicen | igual |
| Doctor | diagnoseAgentCompatibility + trust de Codex en WARN |
mismo diagnóstico | igual |
| Migración del updater | preflight: promover, mover, archivar, nunca borrar |
mismo preflight; además archiva el hook legacy de .claude/hooks/
|
igual |
| Gates | repo:check, pre-push, pre-commit condicional; kata manifest |
mismos tres puntos; sin kata (aquí hay git:policy verify y
vars:env:check)
|
adaptado |
| Trailer de sesión en commits | ninguno: cero atribución, punto |
Claude-Session: solo cuando el harness expone un transcript;
OpenCode y Codex lo omiten
|
distinto |
| Tests del motor | contratos en cli/lib, ciclo de vida en cli/ |
motor + contratos en cli/lib/agent-compatibility.test.ts con
fixtures temporales; ciclo de vida y migración en cli/
|
adaptado |
| Scaffolder | create-agentic-qa neutral; el alias lo genera setup |
create-agentic-dev igual; manifest menciona AGENTS.md,
.codex/, .opencode/
|
igual |
| Gemini y Cursor |
solo templates en docs/mcp/ y compatibility: declarada
|
lo mismo | igual |
| Página publicada | harnesses.es.html en gh-pages |
esta página, misma ruta, con la sección de paridad | adaptado |
Resumen honesto: el mecanismo es el mismo en todas las filas. Las diferencias son de contenido (qué skills, qué MCPs, qué comandos) y dos decisiones deliberadas: el contrato MCP por host, que el dev necesita porque supabase y tavily no se expresan igual en Codex, y el trailer de sesión, que el dev conserva como puntero forense.
Lo que se corrió antes de abrir el PR
| Check | Comando | Resultado |
|---|---|---|
| Suite completa | bun run test |
137 + 37 tests, 0 fallos (motor, contratos, ciclo de vida, migración, scripts, acli) |
| Gates del repo | bun run repo:check |
exit 0 format, lint, types, vars, vars:env, skills, registry, agents:compat |
| Contrato multi-harness | bun run agents:compat:check |
OK alias, 8 + 8 wrappers, 3 hooks, 4 × 3 MCP |
| Doctor | bun run setup:doctor |
7 filas OK + 2 WARN (Codex CLI no está en PATH en esta máquina; confianza del repo no verificable) |
| Migración de un proyecto Claude-era | clon en 0295587 + preflight del updater |
14 skills movidas, AGENTS.md promovido, nada borrado, segunda corrida no-op |
| Lint de skills y registro | bun run skills:check · skills:registry:check |
16 skills, 0 errores, registro al día |
| Sesión real en OpenCode y Codex | bun run opencode · bun run codex |
manual pendiente de correr en una máquina con los binarios instalados |
Lo que todavía no está cerrado
-
Confianza del repo en Codex. Codex solo carga
.codex/en un repositorio marcado como confiable. Es estado de runtime: el doctor lo reporta como WARN y no puede verificarlo. -
Windows. La junction para
.claude/skillsestá implementada (misma rama de código que QA) pero no hay runner de Windows en CI; queda como verificación manual. - Supabase en Codex. La autenticación solo por entorno se basa en la variable requerida que registra el paquete oficial; no se ejecutó una sesión real de Codex contra Supabase en este release.
-
Skills de usuario (T4). Siguen siendo por harness (
~/.claude/skills/y el equivalente de cada host); solo las de proyecto comparten tienda. -
Gemini CLI y Cursor. Templates de MCP y
compatibility:declarada, sin adaptador de runtime. Igual que en QA. -
Rama
feat/codex-cli-support. Un commit de mayo, solo docs, queda superseded por este release; borrarla es una acción que se confirma aparte.