ADR-0015 — dai publish en un Jira corporativo (campos propios, épicas, TLS)
- Estado: aceptado
- Fecha: 2026-07-15
- Decide: lead / arquitecto de la metodología
Contexto
dai publish funcionaba contra un Jira limpio y fallaba contra uno real. Lo vimos en vivo: un analista funcional intentó publicar su primera US en el Jira de su empresa y createUS mandaba solo project, issuetype, summary y description. El proyecto exigía un campo propio (Tipo de trabajo, customfield_10042) → 400 en todos los intentos. Además el proxy corporativo intercepta TLS con su propia CA, y Node no usa el trust store del sistema → fetch reventaba antes de llegar.
Lo que pasó después es el verdadero motivo de esta ADR. El asistente, bloqueado por el CLI, improvisó: leyó el schema del campo por su cuenta, armó la llamada a la API de Jira con node -e, y para pasar el proxy corrió NODE_TLS_REJECT_UNAUTHORIZED=0 — apagando la verificación de certificado mientras mandaba el token de Jira. Lo reportó como "nota técnica resuelta en el camino". Su propio razonamiento fue: "bypassing dai's limitation entirely".
Publicó bien, de casualidad. Pero eso es exactamente lo que la capa 2 de la ADR-0002 existe para evitar: "el output es idéntico con Claude, con Copilot, o sin ningún asistente". Cuando el CLI no llega, el agente inventa — y el improviso siguiente puede no respetar el contrato del ac_hash, y ahí la trazabilidad se rompe en silencio.
La lección: un CLI que no cubre el caso real no es neutral, es una invitación a puentearlo. Cada hueco de la capa 2 se llena con inteligencia no determinista.
Decisión
1. Los campos propios se declaran, con nombre humano y opciones válidas..dai/jira-fields.json (o DAI_JIRA_FIELDS_FILE), por issuetype:
{
"Story": {
"clasificacion": {
"field": "customfield_10042",
"shape": "select",
"default": "Mejora",
"options": ["Mejora", "Corrección"]
}
}
}- Por issuetype, porque en Jira corporativo los campos obligatorios de una Epic y de una Story casi nunca son los mismos.
default+--field alias=valor, porque el valor no es config fija: la clasificación es Mejora o Corrección según la US. Un default fijo publicaría todas iguales — un dato incorrecto en Jira, en silencio, que es peor que fallar.optionsvalida ANTES de la red. Un typo da'Mejraa' no es opción de 'clasificacion' — válidas: Mejora | Corrección, no un 400 críptico.- Un campo declarado tiene que resolver (default u override): se declara porque Jira lo exige. Si es opcional, no se declara.
shapecubre las formas de Jira (select{value:X}·textX ·multi[{value:X}]), conrawcomo escape hatch para cualquier cosa rara: dai no necesita entender todo Jira para no estorbar.- Sin archivo →
{}. Un Jira sin campos obligatorios publica igual que siempre.
2. --parent y --issuetype. --parent cuelga la US de su épica (createUS nunca mandaba parent); --issuetype Epic permite que grill-epic publique épicas por CLI, que hasta hoy no tenían fallback y quedaban como un .md para pegar a mano.
3. Un fallo de TLS enseña el camino correcto, y desaconseja el peligroso. daiFetch traduce los códigos de certificado a un error que manda a NODE_EXTRA_CA_CERTS y dice explícitamente por qué NODE_TLS_REJECT_UNAUTHORIZED=0 no es una alternativa. dai nunca baja la verificación por su cuenta.
4. DAI_JIRA_PROJECT se valida. PROJ-42 es un ticket, no un proyecto — el error de config más común. El mensaje da los dos caminos: DAI_JIRA_PROJECT=PROJ, o --parent PROJ-42 si lo que querías era colgarla de esa épica.
5. La constitución prohíbe las dos maniobras, para todos los asistentes:
- No bajes la seguridad para avanzar: si una llamada falla por el certificado, declara la CA. Nunca
NODE_TLS_REJECT_UNAUTHORIZED=0,verify=False,-k.- Si el CLI no llega, para y dilo: no improvises una llamada a la API por fuera.
Consecuencias
dai publishsirve en un Jira corporativo, que es donde vive el usuario real.grill-epicgana un fallback CLI: MCP →dai publish --issuetype Epic→.md.- El caso que motivó el bypass ahora está cubierto, que es la única forma honesta de pedirle a un agente que no improvise: no dejarle el hueco.
dai doctorvalida la clave de proyecto y que el archivo de campos parsee. Sigue sin verificar que el token sirva — eso es red, y lo dicedai publish. El chequeo ahora lo admite en voz alta en vez de dar un ✓ engañoso.- ClickUp queda con el mismo hueco de TLS.
daiFetchestá listo para adoptarse allí con un import; no lo hicimos ahora por alcance.
Alternativas descartadas
- Una sola variable
DAI_JIRA_FIELDS={"customfield_10042":{"value":"Mejora"}}. Una línea ilegible, sin distinguir Story de Epic, sin validación, y con el valor fijo — el problema original. - Resolver los nombres de campo contra
createmetade Jira. Menos config, pero pide red para armar el payload, y falla distinto según permisos. Un archivo explícito se lee y se versiona. - Que dai reintente sin verificar TLS si el certificado falla. Es exactamente lo que hizo el agente. Automatizarlo sería institucionalizar el bug.