agentic-dev-boilerplate multi-harness release
Release · rama saiotest/harness-compatibility

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.

3harnesses
1cuerpo de instrucciones
16skills en .agents/skills
8 + 8wrappers generados
4 × 3MCP × formatos
9commits en el PR
01 · qué se implementó

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
02 · qué consume cada harness

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.

03 · generado contra versionado

Se edita la fuente, se regenera el resto

Fuente (commiteada)
  • 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
Salida de bun run agents:compat
  • 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.

04 · comandos

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.

05 · hook

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"
  • attribution en blanco: el harness no agrega su propio trailer

OpenCode

  • .opencode/plugins/personality-reinject.js
  • importa PERSONALITY_CONTRACT y lo agrega a output.system
  • el check falla si el plugin reasigna el system prompt

Codex

  • .codex/hooks.json
  • UserPromptSubmit con git rev-parse --show-toplevel y variante commandWindows
  • solo carga en un repo marcado como confiable
06 · mcp

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.

07 · instalación y actualización

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,codex para runs no interactivos
  • Termina reparando alias y wrappers
  • Wrappers bun run claude | opencode | codex cargan .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.md real → se promueve a AGENTS.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
08 · paridad con agentic-qa-boilerplate

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.

igual mismo mecanismo y mismo código o contrato adaptado mismo mecanismo, contenido propio del dev distinto decisión deliberada distinta
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.

09 · verificación

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
10 · límites conocidos

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/skills está 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.