ADR-0004 — Ubicación y schema del implements.yaml
- Estado: aceptado
- Fecha: 2026-07-03
- Decide: lead / arquitecto de la metodología
Contexto
El implements.yaml es el único registro autorado del link QUÉ↔CÓMO (Art. 9). Para que link-us lo scaffoldee y dai check/stamp lo parseen, hay que congelar dónde vive y qué campos tiene. Cierra la decisión abierta de METODOLOGIA §7 sobre el formato del link.
Dos requisitos duros (Art. 9): vive en el repo de código y está versionado por git — así el ac_hash estampado viaja con el commit y el link es revisable en el PR.
Decisión
Ubicación
- Co-localizado con la unidad de trabajo (una US = una capacidad entera = un
implements.yaml). Default usando OpenSpec:openspec/changes/<change-id>/implements.yaml.
El implements.yaml vive junto a los artefactos de OpenSpec del change, no aparte:
openspec/
└── changes/
└── finalizar-compra/ # el change = el CÓMO (una US = una capacidad entera)
├── proposal.md # opsx:propose · qué se propone y por qué
├── design.md # opsx:propose · el diseño técnico
├── tasks.md # opsx:propose · las tareas (se implementan con TDD)
├── specs/ # opsx:propose · las deltas de spec
│ └── carrito/
│ └── spec.md
└── implements.yaml # ★ dai link-us · el link QUÉ↔CÓMO (id + @version + ac_hash)Los tres primeros los genera OpenSpec (opsx:propose); el implements.yaml lo agrega dai (link-us) en la misma carpeta. Al archivar, el change entero (incluido el implements.yaml) se mueve a openspec/changes/archive/ y sigue contando para la cobertura.
- Tool-agnóstico por descubrimiento (Art. 2):
daino hardcodea la ruta de OpenSpec. Hace un glob de**/implements.yaml(excluyendonode_modules,dist, etc.). Un equipo con Swagger u otra herramienta lo pone donde quiera ydailo encuentra. - Incluye archivados: el glob abarca
openspec/changes/archive/**— un change archivado sigue vivo en el código, así que cuenta para la cobertura. - La lista de "qué implementa el repo" es derivada, no un manifiesto a mano: se arma escaneando los
implements.yamlco-localizados (Art. 10).
Schema
change: finalizar-compra # identidad del CÓMO (nombre local del change/spec).
# Explícito, NO derivado del path → sobrevive al archivado.
repo: frontend # repo donde vive.
implements: # FORWARD: qué QUÉ cumple este change (capacidad entera).
- id: ABC-482 # ticket de Jira/ClickUp (identidad del QUÉ). No se tipea: lo pone link-us.
version: v1 # spec_version de la US al implementar (nº legible).
ac_hash: 7f3a9c2e # snapshot del hash de criterios (lo calcula `dai ac-hash`).
introduces: # specs técnicas nuevas que crea este change (opcional).
- guard-carrito-vacio
autor: D. Force (dev) # quién implementa.changeyreposon obligatorios y hacen el registro auto-contenido:dai stamppuede reportar "ABC-482 ← frontend/finalizar-compra @v1" sin parsear rutas.implementses una lista (un change podría cumplir más de un QUÉ, aunque el default es uno — capacidad entera, §2.6).- El link es dirigido CÓMO→QUÉ; la inversa (QUÉ→CÓMO en Jira) la deriva
dai stamp.
Consecuencias
- ✅
link-ustiene molde exacto para generar;check/stamptienen contrato para parsear. - ✅ Registro auto-contenido → robusto al archivado y a mover carpetas.
- ✅ Independiente de OpenSpec: la ubicación es convención, el descubrimiento es glob.
- ⚠️ El glob debe excluir directorios ruidosos (
node_modules,dist,vendor) para no escanear de más. - ⚠️ Si un repo tuviera dos
implements.yamlcon el mismochange, es un error a detectar (identidad duplicada).
Alternativas consideradas
- Un manifiesto único en la raíz (
.dai/implements.yaml) — descartado: archivo compartido que todos editan (conflictos de merge), y pierde la co-localización que hace el diff del PR revisable. - Derivar la identidad del CÓMO del path de la carpeta — descartado: se rompe al archivar/mover; mejor un campo
changeexplícito. - Registrar el mapping también a mano en Jira — descartado: sería un tercer registro que se desincroniza; la inversa se deriva (Art. 10).