@dforce2055/dai 0.14.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.dai.example +16 -0
- package/CHANGELOG.md +127 -0
- package/CONTRIBUTING.md +22 -0
- package/README.md +3 -2
- package/VERSION +1 -1
- package/cli/dai.mjs +571 -17
- package/cli/lib/bootstrap.mjs +4 -4
- package/cli/lib/branch-flow.mjs +6 -6
- package/cli/lib/branch-scope.mjs +24 -9
- package/cli/lib/help.mjs +76 -0
- package/cli/lib/notify.mjs +205 -0
- package/cli/lib/pm-adapter.mjs +16 -0
- package/cli/lib/pm-clickup.mjs +17 -0
- package/cli/lib/pm-jira.mjs +92 -8
- package/cli/lib/pr-remote.mjs +14 -0
- package/cli/lib/release-files.mjs +119 -0
- package/cli/lib/release-plan.mjs +221 -0
- package/cli/lib/release-stamp.mjs +87 -0
- package/cli/lib/review-findings.mjs +2 -2
- package/cli/lib/us-format.mjs +2 -2
- package/cli/lib/us.mjs +6 -6
- package/docs/adr/0008-estrategia-de-i18n.md +58 -0
- package/docs/adr/0019-ciclo-de-version-y-aviso-de-release.md +190 -0
- package/docs/adr/README.md +1 -0
- package/docs/guias/index.md +3 -0
- package/docs/guias/releases.md +156 -0
- package/docs/tutoriales/ciclo-de-release.md +266 -0
- package/docs/tutoriales/index.md +6 -0
- package/package.json +1 -1
- package/skills/dai-release/SKILL.md +167 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# ADR-0019 — El ciclo de versión: manifiesto de US, dos mitades con una firma en el medio, y el aviso como evento
|
|
2
|
+
|
|
3
|
+
- **Estado:** aceptado
|
|
4
|
+
- **Fecha:** 2026-09-09
|
|
5
|
+
- **Decide:** lead / arquitecto de la metodología
|
|
6
|
+
|
|
7
|
+
## Contexto
|
|
8
|
+
|
|
9
|
+
Hay equipos —grandes y medianos, no solo los desprolijos— que **no arman ramas de release
|
|
10
|
+
ni etiquetan versiones**. Desplegar a producción se vuelve un ejercicio de memoria: hay que
|
|
11
|
+
acordarse de qué ramas componen la funcionalidad y mergearlas a la rama de producción una
|
|
12
|
+
por una, en el momento del despliegue.
|
|
13
|
+
|
|
14
|
+
Eso tiene tres consecuencias, y las tres se ven en repos reales:
|
|
15
|
+
|
|
16
|
+
1. **Se despliega una combinación que nunca se probó.** Cada orden de merge produce un
|
|
17
|
+
árbol distinto; el que llega a producción no existió en ningún ambiente antes.
|
|
18
|
+
2. **No se puede contestar qué hay en cada ambiente.** Ni qué funcionalidades comprende.
|
|
19
|
+
Cuando algo falla a las tres de la mañana, no hay a qué volver.
|
|
20
|
+
3. **Se cuelan funcionalidades que no estaban listas**, porque la selección de qué va se
|
|
21
|
+
hace rama por rama y a mano.
|
|
22
|
+
|
|
23
|
+
dai ya sabía atar una User Story a su código (`implements.yaml`) y a su commit
|
|
24
|
+
(`dai stamp`). Lo que faltaba era el último eslabón: atar la US a la **versión desplegada**.
|
|
25
|
+
Sin eso, la trazabilidad llega hasta la PR y se corta justo donde el negocio pregunta.
|
|
26
|
+
|
|
27
|
+
## Decisión
|
|
28
|
+
|
|
29
|
+
### 1. dai aporta el manifiesto, no la estrategia de branching
|
|
30
|
+
|
|
31
|
+
La estrategia de ramas la elige cada equipo, y dai no opina: es una herramienta, no un
|
|
32
|
+
mandato. Lo que dai aporta es el eje que ya es suyo, extendido un paso:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
implements.yaml → commit → PR → VERSIÓN → AMBIENTE
|
|
36
|
+
(ADR-0004) (ADR-0005) ←── esto es lo nuevo ──→
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`dai release plan` produce el **manifiesto**: qué User Stories entran entre el último tag y
|
|
40
|
+
la rama de integración, en qué estado está cada una, y qué entró **sin** declarar US. Los
|
|
41
|
+
otros comandos son formas distintas de publicar ese mismo dato — el CHANGELOG, el tag, el
|
|
42
|
+
comentario en cada ticket, el aviso al canal.
|
|
43
|
+
|
|
44
|
+
**Cómo se resuelve qué US entró:** leyendo los `implements.yaml` tal como estaban en cada
|
|
45
|
+
commit del rango, no por el nombre de la rama. El link viaja con el código, así que la
|
|
46
|
+
respuesta sobrevive a que la rama se borre y a que el change se archive — que es
|
|
47
|
+
exactamente el estado del repo cuando se llega a cortar la versión, días después del merge.
|
|
48
|
+
|
|
49
|
+
**Alternativa descartada:** deducirlo del nombre de la rama en el commit de merge. Es más
|
|
50
|
+
simple y es frágil: desaparece con squash, con rebase, y con cualquier equipo que renombre.
|
|
51
|
+
Se conserva solo como señal secundaria, para detectar lo que entró **sin** link.
|
|
52
|
+
|
|
53
|
+
### 2. El bump se propone; lo firma una persona
|
|
54
|
+
|
|
55
|
+
dai deriva un piso del tipo de los commits (`feat:` → minor, `!` → major) y **lo dice**: es
|
|
56
|
+
una propuesta, no un veredicto. La regla que manda mira el **comportamiento observable**.
|
|
57
|
+
|
|
58
|
+
La evidencia es de este mismo repo: la versión 0.14.0 salió con cuatro commits `fix:` y era
|
|
59
|
+
minor, porque cambió un default que ve quien no configura nada. Cualquier herramienta que
|
|
60
|
+
derive la versión de los tipos de commit —semantic-release, standard-version, Conventional
|
|
61
|
+
Commits puro— habría cortado un patch equivocado.
|
|
62
|
+
|
|
63
|
+
**Alternativa descartada:** versionado automático desde los commits. Es precisamente el
|
|
64
|
+
pedazo que no hay que automatizar: convierte una decisión de comunicación en un efecto
|
|
65
|
+
secundario de cómo alguien tituló un commit.
|
|
66
|
+
|
|
67
|
+
### 3. El corte son dos comandos, porque hay una firma humana en el medio
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
plan ──▶ [FIRMA: la versión] ──▶ cut ──▶ dai pr ──▶ [FIRMA: merge + publicar]
|
|
71
|
+
│
|
|
72
|
+
done ◀────────┘
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`cut` **prepara y no habla hacia afuera**: rama de release, número, entrada de CHANGELOG,
|
|
76
|
+
commit. Ni push, ni tag, ni PR.
|
|
77
|
+
|
|
78
|
+
`done` **cierra después del merge**: tag anotado, release note, back-merge y aviso. Existe
|
|
79
|
+
como comando separado porque los dos pasos que más se olvidan cuando la ceremonia se hace a
|
|
80
|
+
mano —el release note y el back-merge— viven en esta mitad, la que queda después de la firma.
|
|
81
|
+
|
|
82
|
+
**Una vez creado el tag, ningún paso posterior aborta.** El tag es la versión: si existe, la
|
|
83
|
+
versión existe. Fallar y salir dejaría el corte a medio camino sin decir en qué mitad quedó,
|
|
84
|
+
así que cada paso reporta y sigue.
|
|
85
|
+
|
|
86
|
+
### 4. El tag es la versión; los archivos son espejos
|
|
87
|
+
|
|
88
|
+
`dai release cut` sube el número en `VERSION` y `package.json` **si existen**, informa cuáles
|
|
89
|
+
tocó, y no se planta si no hay ninguno. Un repo .NET o un frontend corporativo versionan
|
|
90
|
+
igual de bien sin ninguno de los dos.
|
|
91
|
+
|
|
92
|
+
El bump de `package.json` es **quirúrgico** —solo la línea de la versión—: reserializar el
|
|
93
|
+
JSON reformatea los objetos compactos y llena el diff de la release de ruido que nadie pidió.
|
|
94
|
+
|
|
95
|
+
### 5. El CHANGELOG lo escribe una persona; dai deja el material
|
|
96
|
+
|
|
97
|
+
`cut` inserta la entrada con las US del manifiesto **en un comentario HTML** (se ve al
|
|
98
|
+
editar, desaparece al renderizar) y las secciones vacías. La prosa no la escribe dai: sabe
|
|
99
|
+
*qué* entró, no *por qué importa*.
|
|
100
|
+
|
|
101
|
+
**Alternativa descartada:** generar el changelog desde los subjects de los commits. Produce
|
|
102
|
+
una lista que nadie lee, y encima da la sensación de que el trabajo está hecho.
|
|
103
|
+
|
|
104
|
+
### 6. Estampar la versión en cada US es opcional, y avisa su alcance
|
|
105
|
+
|
|
106
|
+
`dai release stamp <v> --env <ambiente>` deja en **cada US del release** un comentario con
|
|
107
|
+
versión, app, ambiente y fecha. Con eso el funcional lee el ticket en vez de preguntar, y
|
|
108
|
+
una US federada en varios repos acumula sola su matriz (backend en producción, frontend en
|
|
109
|
+
pre).
|
|
110
|
+
|
|
111
|
+
Tres cuidados, porque escribe N veces hacia afuera en tickets de gente distinta:
|
|
112
|
+
|
|
113
|
+
- **Muestra el alcance real antes de escribir** — cuántos comentarios y en qué tickets,
|
|
114
|
+
descontando los que ya están. Un número inflado enseña a ignorar el aviso.
|
|
115
|
+
- **Es idempotente por `(app, versión, ambiente)`**, con una marca en el propio comentario.
|
|
116
|
+
Redesplegar no llena el ticket de repetidos; la misma versión en otro ambiente sí es un
|
|
117
|
+
evento nuevo.
|
|
118
|
+
- **Si no puede leer los comentarios, lo dice** en vez de suponer que no estampó. Es la
|
|
119
|
+
misma distinción que ADR-0003 hace entre "no hay US" y "no hubo respuesta"; acá afirmar de
|
|
120
|
+
más se paga en duplicados que nadie puede borrar.
|
|
121
|
+
|
|
122
|
+
**Decir que no sale con 0.** Para cuando el comando corre, el tag y el release note ya
|
|
123
|
+
existen: la versión está hecha. Que un equipo elija no hacer ruido en veinte tickets es una
|
|
124
|
+
decisión legítima, no un error a corregir.
|
|
125
|
+
|
|
126
|
+
### 7. El aviso al canal es un tercer adaptador, y su mensaje tiene estructura sin formato
|
|
127
|
+
|
|
128
|
+
`DAI_NOTIFY` elige el backend igual que `DAI_PM` elige el tracker: `discord`, `slack`,
|
|
129
|
+
`webex`, `telegram`, un `webhook` genérico, o `none` (el default: dai no habla hacia afuera
|
|
130
|
+
sin que se lo pidan).
|
|
131
|
+
|
|
132
|
+
El mensaje es **uno solo para todos los canales**, con estructura —qué salió, quién, cuándo,
|
|
133
|
+
qué trae, dónde mirar— y **sin formato**. La distinción es de costo, no estética: la
|
|
134
|
+
estructura son saltos de línea y viñetas, y se ve igual en los cinco; el formato son cuatro
|
|
135
|
+
dialectos incompatibles (Discord con `**negrita**` y sin links con nombre, Slack con
|
|
136
|
+
`*negrita*` y `<url|texto>`, Webex con markdown completo, Telegram con `parse_mode` y el
|
|
137
|
+
escapeo de MarkdownV2 que devuelve 400 por un punto suelto). Con esa decisión, el adaptador
|
|
138
|
+
es una tabla de cinco líneas en lugar de un módulo de render.
|
|
139
|
+
|
|
140
|
+
**Las viñetas son las User Stories, no los subjects de los commits.** Un aviso que dice *"se
|
|
141
|
+
implementa el conversor de propiedades no serializables"* lo entiende quien escribió el
|
|
142
|
+
código; uno que dice *"Checkout sin duplicado"* lo entiende el negocio. Es la misma
|
|
143
|
+
distinción entre el QUÉ y el CÓMO que sostiene el método, aplicada al canal.
|
|
144
|
+
|
|
145
|
+
**El endpoint es la credencial:** quien lo tiene, postea. Vive en `.env.dai` (ADR-0017) y
|
|
146
|
+
dai muestra el host, nunca la URL — tampoco en los mensajes de error.
|
|
147
|
+
|
|
148
|
+
**Qué versión hay en cada ambiente NO vive en el repo.** Un despliegue es un evento, no un
|
|
149
|
+
archivo: cambia sin que cambie el código. Guardarlo en un archivo obligaría al CI a
|
|
150
|
+
commitear en cada deploy. Su registro es el stamp en el tracker y el release del forge.
|
|
151
|
+
|
|
152
|
+
### 8. `done` borra la rama de release que acaba de cerrar
|
|
153
|
+
|
|
154
|
+
Es el único punto del ciclo donde dai puede **afirmar** que borrarla es seguro: ya está
|
|
155
|
+
mergeada en producción, etiquetada y con el back-merge hecho. Si no se hace ahí, se
|
|
156
|
+
acumulan — este mismo repo tenía cinco cuando se implementó el comando.
|
|
157
|
+
|
|
158
|
+
La red de seguridad la pone git, no una suposición: `git branch -d` (minúscula) se niega a
|
|
159
|
+
borrar una rama sin mergear. Hay precedente en el CLI: `dai done` ya hace exactamente esto
|
|
160
|
+
con la rama de una US.
|
|
161
|
+
|
|
162
|
+
**Alternativa descartada:** un `dai release cleanup` que borre ramas de release viejas en
|
|
163
|
+
masa. dai no las creó, no puede saber si alguien conserva una a propósito, y limpiar ramas
|
|
164
|
+
en general no es su dominio. `dai release status` las nombra y deja el comando escrito;
|
|
165
|
+
borrarlas es del equipo.
|
|
166
|
+
|
|
167
|
+
### 9. Lo que dai NO hace
|
|
168
|
+
|
|
169
|
+
- **No despliega.** Llega hasta el tag y vuelve a aparecer después, estampando. Quién
|
|
170
|
+
despliega es el pipeline.
|
|
171
|
+
- **No mergea ni publica.** Son firmas humanas ([Art. 5](../MANIFIESTO.md#art-5)).
|
|
172
|
+
- **No impone un modelo de branching.** La rama de release es opcional (`--no-branch`), y
|
|
173
|
+
las dos ramas de vida larga las declara el repo (`DAI_BRANCH_DEV` / `DAI_BRANCH_PROD`).
|
|
174
|
+
- **No es un framework de notificaciones.** Avisa eventos de release con su manifiesto.
|
|
175
|
+
|
|
176
|
+
## Consecuencias
|
|
177
|
+
|
|
178
|
+
- Un equipo puede contestar, sin reunirse: **qué versión hay en cada ambiente y qué US
|
|
179
|
+
comprende**. Desde el ticket, no desde un Excel.
|
|
180
|
+
- El manifiesto expone, antes de cortar, **las US atrasadas** y **lo que entró sin link** —
|
|
181
|
+
la última pantalla donde eso se puede ver.
|
|
182
|
+
- El ciclo funciona igual con rama de release (ventana de estabilización) y sin ella (tag
|
|
183
|
+
directo desde integración), así que dai no fuerza a nadie a cambiar de estrategia para
|
|
184
|
+
ganar trazabilidad.
|
|
185
|
+
- Aparecen dos variables nuevas de config (`DAI_NOTIFY`, `DAI_NOTIFY_WEBHOOK`, más
|
|
186
|
+
`DAI_NOTIFY_CHAT_ID` solo para Telegram) y dos métodos nuevos en el adaptador de PM
|
|
187
|
+
(`comment`, `listComments`), que cualquier backend nuevo tiene que implementar para
|
|
188
|
+
soportar el estampado de versión.
|
|
189
|
+
- La skill `/dai-release` conduce el ciclo pero **no recalcula nada**: narra lo que dicen
|
|
190
|
+
los comandos. Si la skill y el CLI se contradicen, gana el CLI.
|
package/docs/adr/README.md
CHANGED
|
@@ -24,6 +24,7 @@ decisión cambia, se escribe un ADR nuevo que supersede al viejo. Molde en
|
|
|
24
24
|
| [0016](0016-review-inline.md) | Review inline: `review.json` como contrato y puerta humana, el CLI valida las posiciones contra el diff, `--yes` explícito, nunca `APPROVE` | aceptado |
|
|
25
25
|
| [0017](0017-env-dai.md) | La config de dai vive en `.env.dai` (no versionado), no en el `.env` del equipo; el loader lee ambos con precedencia shell > `.env.dai` > `.env` | aceptado |
|
|
26
26
|
| [0018](0018-alcance-de-stamp-y-gate-de-ci.md) | El alcance de `dai stamp` lo decide la rama (ante la duda pregunta, no estampa de más), `dai check --ci` ejecuta el gate de governance con ramas exentas, y `dai edit-us`/`update-us` editan el QUÉ validando el formato y proponiendo el `spec_version` | aceptado |
|
|
27
|
+
| [0019](0019-ciclo-de-version-y-aviso-de-release.md) | El ciclo de versión: el manifiesto de US como dato central, el bump se propone y lo firma una persona, el corte son dos comandos con una firma en el medio, el tag es la versión y los archivos son espejos, estampar es opcional y avisa su alcance, y el canal es un tercer adaptador con estructura sin formato | aceptado |
|
|
27
28
|
|
|
28
29
|
> Estas son las decisiones que cierran las "Decisiones abiertas" de
|
|
29
30
|
> [`METODOLOGIA.md §7`](../METODOLOGIA.md) y las enmiendas al
|
package/docs/guias/index.md
CHANGED
|
@@ -7,6 +7,9 @@ y tu **día a día**.
|
|
|
7
7
|
porqué, nunca el CÓMO. Entrás por `grill-user-story`, `grill-epic` o `doc-to-backlog`.
|
|
8
8
|
- [**Guía del dev / ingeniero**](./dev) — dueño del **CÓMO** y del **link**: el agente
|
|
9
9
|
implementa con `/opsx:apply`, tú revisas y autoras el `implements.yaml`.
|
|
10
|
+
- [**Guía de releases**](./releases) — el *porqué* del versionado: qué problema resuelve un
|
|
11
|
+
tag, por qué "una rama por ambiente" falla, los dos modelos y la pregunta que elige entre
|
|
12
|
+
ellos. Transversal: la lee el lead para decidir y el dev para entender qué firma.
|
|
10
13
|
- [**Guía del lead / SM / arquitecto**](./lead) — custodio de las **invariantes** y del
|
|
11
14
|
**nivel de ceremonia** (N1 / N2 / N3): haces que el método se cumpla y se aligere donde
|
|
12
15
|
corresponde.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Guía de releases — por qué versionar, y cómo hacerlo sin ceremonia inútil
|
|
2
|
+
|
|
3
|
+
> En una frase: **desplegar tiene que ser desplegar algo que ya existía y que ya se probó,
|
|
4
|
+
> no armarlo en el momento.** Todo lo demás de esta guía sale de ahí.
|
|
5
|
+
|
|
6
|
+
Esta guía es el *porqué*. Si ya lo tienes claro y solo quieres los comandos, ve al
|
|
7
|
+
[tutorial del ciclo completo](../tutoriales/ciclo-de-release).
|
|
8
|
+
|
|
9
|
+
## El problema, tal como se ve por dentro
|
|
10
|
+
|
|
11
|
+
Hay equipos que no arman ramas de release ni etiquetan versiones. Cuando llega el momento
|
|
12
|
+
de subir a producción, alguien se sienta a recordar qué ramas componen la funcionalidad y
|
|
13
|
+
las va mergeando a la rama de producción, una por una.
|
|
14
|
+
|
|
15
|
+
Eso falla de tres formas distintas, y las tres duelen:
|
|
16
|
+
|
|
17
|
+
1. **Se despliega algo que nunca se probó.** Cada orden de merge produce un árbol distinto.
|
|
18
|
+
El que llega a producción no existió en ningún ambiente antes de ese momento.
|
|
19
|
+
2. **Nadie puede decir qué hay en producción.** Ni qué funcionalidades comprende. Y cuando
|
|
20
|
+
algo se rompe de noche, no hay un punto conocido al que volver.
|
|
21
|
+
3. **Se cuelan cosas que no estaban listas**, porque la selección se hace rama por rama, a
|
|
22
|
+
mano y con prisa.
|
|
23
|
+
|
|
24
|
+
Si te suena, el resto de la guía es para ti.
|
|
25
|
+
|
|
26
|
+
## Lo que hay que separar, porque no es lo mismo
|
|
27
|
+
|
|
28
|
+
La trampa habitual es intentar resolver los tres problemas con un solo mecanismo —
|
|
29
|
+
normalmente "armemos una rama de release". En realidad son tres:
|
|
30
|
+
|
|
31
|
+
| Problema | Se resuelve con |
|
|
32
|
+
|---|---|
|
|
33
|
+
| **Trazabilidad** — qué código hay en cada ambiente | identidad inmutable: un **tag** y un artefacto |
|
|
34
|
+
| **Selección** — qué funcionalidades van | lo que está integrado, más *feature flags* |
|
|
35
|
+
| **Estabilización** — arreglar sin frenar el desarrollo | **la rama de release**, y solo esto |
|
|
36
|
+
|
|
37
|
+
La rama de release resuelve el tercero. Si tu equipo no necesita una ventana de
|
|
38
|
+
estabilización, es ceremonia sin beneficio.
|
|
39
|
+
|
|
40
|
+
## Las dos reglas que valen más que cualquier estrategia de ramas
|
|
41
|
+
|
|
42
|
+
### 1. Construye una vez, promueve el artefacto
|
|
43
|
+
|
|
44
|
+
Promover a producción es **desplegar el mismo binario/imagen/paquete** que ya pasó por
|
|
45
|
+
test, con otra configuración. No es reconstruir desde otra rama.
|
|
46
|
+
|
|
47
|
+
Si en cada ambiente vuelves a construir, probaste una cosa y desplegaste otra. Es el fallo
|
|
48
|
+
más profundo del modelo "una rama por ambiente": el mismo commit produce artefactos
|
|
49
|
+
distintos, y las ramas divergen en silencio cuando alguien olvida un *back-merge*.
|
|
50
|
+
|
|
51
|
+
### 2. Desplegar no es lanzar
|
|
52
|
+
|
|
53
|
+
Que el código esté en producción no obliga a que la funcionalidad esté visible. Un *feature
|
|
54
|
+
flag* separa las dos cosas, y con eso desaparece el motivo real por el que se cuelan cosas a
|
|
55
|
+
medias: ya no hace falta retener el código para retener la funcionalidad.
|
|
56
|
+
|
|
57
|
+
Si algo llega a la rama de integración a medio hacer, ninguna rama de release lo va a
|
|
58
|
+
salvar: vas a terminar sacando cosas *de* la release, que es la misma enfermedad al revés y
|
|
59
|
+
más difícil de revertir. El control real es doble: **nada se integra si no es desplegable**
|
|
60
|
+
(el gate de `dai check --ci`) y lo desplegable-pero-no-lanzable va detrás de un flag.
|
|
61
|
+
|
|
62
|
+
## Los dos modelos, y la pregunta que elige
|
|
63
|
+
|
|
64
|
+
**Modelo A — tronco y tag.** Una sola rama de vida larga. Ramas cortas (uno o dos días) con
|
|
65
|
+
PR y gate de CI. Cada integración produce un artefacto; promover a producción es desplegar
|
|
66
|
+
ese artefacto y etiquetar el commit exacto. Los *hotfix* salen del tag que está en
|
|
67
|
+
producción, se etiquetan y se reintegran.
|
|
68
|
+
|
|
69
|
+
**Modelo B — tronco y rama de release.** Igual que A, pero al cortar se crea
|
|
70
|
+
`release/X.Y.Z` desde integración. Solo entran correcciones, y siempre **arregladas primero
|
|
71
|
+
en integración y llevadas a la release** — nunca al revés, que es como se pierde el
|
|
72
|
+
*back-merge*. Se etiqueta, se despliega, y **la rama se borra**: una rama de release que
|
|
73
|
+
sobrevive a su release es un fork. (`dai release done` la borra solo; `dai release status`
|
|
74
|
+
avisa de las que quedaron de antes.)
|
|
75
|
+
|
|
76
|
+
> **La pregunta que decide:** ¿cuántos días pasan entre "dejamos de agregar" y "está en
|
|
77
|
+
> producción"? Menos de un día → modelo A, la rama sería ceremonia. Varios días con
|
|
78
|
+
> desarrollo en paralelo (QA de regresión, comité de cambios, ventana fija de despliegue) →
|
|
79
|
+
> modelo B, y ahí la rama se gana su lugar.
|
|
80
|
+
|
|
81
|
+
Nota incómoda pero honesta: la investigación de entrega de software encuentra que los
|
|
82
|
+
equipos de alto desempeño tienen **ramas de vida corta y pocas ramas de larga vida**. Una
|
|
83
|
+
rama de release que vive semanas correlaciona con peor desempeño, no mejor. Si tu ventana se
|
|
84
|
+
alarga, conviene preguntarse si es porque hay menos que entregar o porque cada release
|
|
85
|
+
duele — la segunda es un síntoma a atacar, no una razón para espaciar más.
|
|
86
|
+
|
|
87
|
+
## Qué versión poner
|
|
88
|
+
|
|
89
|
+
Depende de quién consume tu software:
|
|
90
|
+
|
|
91
|
+
- **Una librería, un SDK, un paquete**: **semver**. El número comunica compatibilidad a
|
|
92
|
+
quien depende de ti, y eso es un contrato.
|
|
93
|
+
- **Una aplicación interna** que nadie consume como dependencia: semver puede volverse
|
|
94
|
+
teatro — vas a discutir en reunión si algo es *minor* o *patch* para un número que no le
|
|
95
|
+
comunica nada a nadie. Ahí lo que necesitas es identidad **inequívoca y ordenada**
|
|
96
|
+
(`2026.09.1`, o un secuencial) más un manifiesto de qué contiene. El valor está en el
|
|
97
|
+
manifiesto, no en el número.
|
|
98
|
+
|
|
99
|
+
Sea cual sea el esquema, **la decisión es de una persona**. dai propone un piso mirando los
|
|
100
|
+
tipos de commit y lo dice; la regla que manda mira el **comportamiento observable**. Un
|
|
101
|
+
cambio que mueve un valor por omisión es *minor* aunque todos los commits digan `fix:`.
|
|
102
|
+
|
|
103
|
+
## Qué aporta dai
|
|
104
|
+
|
|
105
|
+
dai no elige tu estrategia de ramas: eso es tuyo. Lo que aporta es el eslabón que falta en
|
|
106
|
+
la trazabilidad.
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
User Story → implements.yaml → commit → PR → VERSIÓN → AMBIENTE
|
|
110
|
+
└── esto es lo que agrega ──┘
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Con eso, preguntas que hoy requieren una reunión se contestan desde el ticket:
|
|
114
|
+
|
|
115
|
+
- **¿Qué entra en la próxima versión?** → `dai release plan`, que además muestra las US
|
|
116
|
+
cuyos criterios cambiaron después de implementarlas y lo que se coló sin declarar US.
|
|
117
|
+
- **¿Esto ya está en producción?** → el comentario en la propia User Story, con versión,
|
|
118
|
+
aplicación, ambiente y fecha.
|
|
119
|
+
- **¿Dónde estamos en el ciclo?** → `dai release status`, que también avisa si quedó un
|
|
120
|
+
*back-merge* pendiente.
|
|
121
|
+
|
|
122
|
+
Y dos límites deliberados: **dai no despliega** (llega hasta el tag y vuelve a aparecer
|
|
123
|
+
después, estampando) y **no mergea ni publica** — esas son firmas humanas.
|
|
124
|
+
|
|
125
|
+
## Cómo lo adopta un equipo sin que se abandone en dos sprints
|
|
126
|
+
|
|
127
|
+
1. **Declara tus dos ramas de vida larga** en el `.env.dai` y deja de discutirlo:
|
|
128
|
+
`DAI_BRANCH_DEV` (la que integra) y `DAI_BRANCH_PROD` (la que va a producción). Con eso,
|
|
129
|
+
`dai pr` deduce la base del tipo de rama y avisa cuando apuntas a producción.
|
|
130
|
+
2. **Etiqueta la próxima versión aunque el proceso todavía sea manual.** El tag es lo que te
|
|
131
|
+
da el punto de retorno; todo lo demás se puede agregar después.
|
|
132
|
+
3. **Corre `dai release plan` antes de cada corte, y léelo en voz alta en la reunión.** Es
|
|
133
|
+
donde aparecen las sorpresas mientras todavía se pueden arreglar.
|
|
134
|
+
4. **Empieza a estampar solo en producción.** Si estampar en todos los ambientes hace
|
|
135
|
+
demasiado ruido, con producción alcanza para contestar la pregunta que más se hace.
|
|
136
|
+
5. **Deja el aviso al canal para el final.** Es lo que hace visible el cambio para el resto
|
|
137
|
+
de la organización, y conviene encenderlo cuando el ciclo ya funciona.
|
|
138
|
+
|
|
139
|
+
## Preguntas frecuentes
|
|
140
|
+
|
|
141
|
+
**No usamos npm y no tenemos `package.json`. ¿Sirve igual?**
|
|
142
|
+
Sí. El tag es la versión; los archivos son espejos opcionales. dai actualiza los que
|
|
143
|
+
reconoce, dice cuáles tocó, y no se detiene si no hay ninguno.
|
|
144
|
+
|
|
145
|
+
**Nuestra rama principal despliega a producción.**
|
|
146
|
+
Decláralo en `DAI_BRANCH_PROD`. dai marcará esa base en el previo de la PR y pedirá una
|
|
147
|
+
confirmación explícita antes de proponer un merge hacia ahí.
|
|
148
|
+
|
|
149
|
+
**¿Y si no queremos llenar los tickets de comentarios?**
|
|
150
|
+
No estampes. La versión ya está hecha cuando llegas a ese paso: `dai release stamp` es
|
|
151
|
+
opcional y decir que no no rompe nada. Muchos equipos prefieren avisar solo al canal.
|
|
152
|
+
|
|
153
|
+
**¿Cada cuánto conviene sacar una versión?**
|
|
154
|
+
Tan seguido como el equipo tolere sin dolor. Si la respuesta a "¿por qué no más seguido?" es
|
|
155
|
+
*"no hay tanto que entregar"*, está bien. Si es *"porque cada release cuesta"*, eso es lo que
|
|
156
|
+
hay que arreglar — y espaciar los releases lo empeora, porque agranda el lote.
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# Ciclo de release, paso a paso
|
|
2
|
+
|
|
3
|
+
De "hay cosas para sacar" a "producción sabe qué versión tiene y cada User Story lo dice".
|
|
4
|
+
Unos 15 minutos la primera vez.
|
|
5
|
+
|
|
6
|
+
El *porqué* de todo esto está en la [guía de releases](../guias/releases). Acá van los
|
|
7
|
+
comandos.
|
|
8
|
+
|
|
9
|
+
> **Lo hace la persona, no el agente.** dai prepara, muestra y confirma; mergear, publicar y
|
|
10
|
+
> desplegar los firmas tú. Si prefieres que un asistente te acompañe paso a paso, invoca la
|
|
11
|
+
> skill `/dai-release` — usa exactamente estos comandos y frena en las mismas firmas.
|
|
12
|
+
|
|
13
|
+
## Antes de empezar
|
|
14
|
+
|
|
15
|
+
Declara las dos ramas de vida larga de tu repositorio en el `.env.dai`. Es lo único que
|
|
16
|
+
tienes que configurar, y se hace una sola vez:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# .env.dai (no se versiona)
|
|
20
|
+
DAI_BRANCH_DEV=develop # la rama que integra el desarrollo
|
|
21
|
+
DAI_BRANCH_PROD=main # la rama que despliega a producción
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Comprueba que dai las ve:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
dai release status
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
› versión declarada: 0.4.2 (VERSION)
|
|
32
|
+
› último tag: v0.4.2
|
|
33
|
+
⚠ 'develop' tiene 6 commit(s) sin promover · 3 US · bump propuesto: minor.
|
|
34
|
+
Ver el detalle: dai release plan
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Si dice que falta un *back-merge* de la versión anterior, resuélvelo antes de seguir: cortar
|
|
38
|
+
una versión sobre otra a medio cerrar arrastra el problema.
|
|
39
|
+
|
|
40
|
+
## Paso 1 · Qué entra — `dai release plan`
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
dai release plan
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
── Release a preparar ────────────────────────────────
|
|
48
|
+
desde: v0.4.2
|
|
49
|
+
hasta: develop
|
|
50
|
+
cambios: 6 commit(s) · 5 merge(s)
|
|
51
|
+
versión: 0.4.2 → 0.5.0 (minor)
|
|
52
|
+
─────────────────────────────────────────────────────
|
|
53
|
+
User Stories que entran (3):
|
|
54
|
+
✅ ACME-482 v2 Checkout sin duplicado
|
|
55
|
+
⚠️ ACME-491 v1 Alta de póliza sin duplicar cliente
|
|
56
|
+
ATRASADA
|
|
57
|
+
✅ ACME-503 v3 Recordar medio de pago
|
|
58
|
+
|
|
59
|
+
⚠ 1 US ATRASADA(S): el QUÉ cambió después de implementarlo.
|
|
60
|
+
Esta release las llevaría sin cubrir el criterio nuevo. Revisalas antes de cortar.
|
|
61
|
+
|
|
62
|
+
Sin US, por tipo de branch (1): chore/deps
|
|
63
|
+
|
|
64
|
+
⚠ 1 branch(es) sin US y sin prefijo exento:
|
|
65
|
+
arreglo-rapido
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Esta es la pantalla importante del ciclo.** Léela entera antes de seguir:
|
|
69
|
+
|
|
70
|
+
- **US atrasadas** — alguien cambió los criterios de aceptación después de que se
|
|
71
|
+
implementaran. Puedes cortar igual (a veces el criterio nuevo va en la próxima versión),
|
|
72
|
+
pero que sea una decisión y no un descuido.
|
|
73
|
+
- **Ramas sin US y sin prefijo exento** — entró trabajo que nadie va a poder rastrear a una
|
|
74
|
+
historia. Es la última oportunidad de verlo.
|
|
75
|
+
|
|
76
|
+
Con `--json` sale el mismo manifiesto estructurado, para scripts o para un asistente.
|
|
77
|
+
|
|
78
|
+
## Paso 2 · La versión — la firmas tú
|
|
79
|
+
|
|
80
|
+
dai **propone** un piso mirando los tipos de commit y lo dice:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
minor: 3 commit(s) feat: agregan funcionalidad
|
|
84
|
+
propuesta a partir de los TIPOS de commit — la regla del repo mira el COMPORTAMIENTO.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
La regla que manda mira el **comportamiento observable**: si algo mueve un valor por
|
|
88
|
+
omisión, agrega una opción o cambia lo que ve quien no configura nada, es *minor* aunque
|
|
89
|
+
todos los commits digan `fix:`. Esa decisión es tuya.
|
|
90
|
+
|
|
91
|
+
## Paso 3 · Cortar — `dai release cut`
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
dai release cut 0.5.0
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Crea la rama `release/0.5.0`, sube el número en los archivos que tu repositorio espeja,
|
|
98
|
+
escribe la entrada del CHANGELOG y hace el commit. **No habla hacia afuera**: no hay *push*,
|
|
99
|
+
ni tag, ni PR.
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
✓ branch release/0.5.0
|
|
103
|
+
✓ VERSION: 0.4.2 → 0.5.0
|
|
104
|
+
✓ package.json: 0.4.2 → 0.5.0
|
|
105
|
+
✓ CHANGELOG.md: entrada para 0.5.0
|
|
106
|
+
✓ commit chore(release): v0.5.0
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
> ¿Tu repositorio no tiene `VERSION` ni `package.json`? No pasa nada: el tag es la versión y
|
|
110
|
+
> dai te lo dice. ¿Trabajas sin rama de release, etiquetando directamente desde integración?
|
|
111
|
+
> Usa `--no-branch`.
|
|
112
|
+
|
|
113
|
+
## Paso 4 · El CHANGELOG — lo escribes tú
|
|
114
|
+
|
|
115
|
+
dai deja el material y las secciones vacías:
|
|
116
|
+
|
|
117
|
+
```markdown
|
|
118
|
+
## [0.5.0] — 2026-09-09
|
|
119
|
+
|
|
120
|
+
<!-- dai:manifiesto · el material de esta versión. Repartilo abajo y contá el porqué:
|
|
121
|
+
dai sabe qué entró; por qué importa lo sabés vos.
|
|
122
|
+
ACME-482 Checkout sin duplicado
|
|
123
|
+
ACME-491 Alta de póliza sin duplicar cliente
|
|
124
|
+
-->
|
|
125
|
+
|
|
126
|
+
### Agregado
|
|
127
|
+
### Cambiado
|
|
128
|
+
### Corregido
|
|
129
|
+
### Interno
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Reparte las historias en las secciones que correspondan, **explica por qué importaba cada
|
|
133
|
+
una** y borra el comentario. Un changelog que solo lista lo que entró no lo lee nadie; uno
|
|
134
|
+
que cuenta qué estaba mal se lee seis meses después.
|
|
135
|
+
|
|
136
|
+
Luego, el commit y la PR:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
git add CHANGELOG.md && git commit -m "docs(changelog): reparte el manifiesto de la 0.5.0"
|
|
140
|
+
dai pr --description-file notas.md
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
La base sale sola: una rama `release/` va contra tu rama de producción, y dai te lo marca y
|
|
144
|
+
te pide que escribas el nombre de la rama para confirmar.
|
|
145
|
+
|
|
146
|
+
## Paso 5 · Merge y publicación — tu firma
|
|
147
|
+
|
|
148
|
+
Apruebas y mergeas la PR, y ejecutas lo que tu repositorio use para publicar (`npm publish`,
|
|
149
|
+
un despliegue, lo que sea). **El ciclo no terminó acá**: faltan el tag, la nota de release y
|
|
150
|
+
el *back-merge* — los tres pasos que más se olvidan cuando esto se hace de memoria.
|
|
151
|
+
|
|
152
|
+
## Paso 6 · Cerrar — `dai release done`
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
dai release done 0.5.0
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
── Cerrar la versión 0.5.0 ──────────────────────────
|
|
160
|
+
tag: v0.5.0 → main @ 177719f3
|
|
161
|
+
release: nota en el forge
|
|
162
|
+
back-merge: main → develop
|
|
163
|
+
aviso: webex · webexapis.com
|
|
164
|
+
─────────────────────────────────────────────────────
|
|
165
|
+
✓ tag v0.5.0 creado y publicado
|
|
166
|
+
✓ release note publicada
|
|
167
|
+
✓ back-merge main → develop
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Y borra la rama `release/0.5.0` (local y remota): ya está mergeada, etiquetada y con el
|
|
171
|
+
back-merge hecho, así que no tiene más razón de existir — una rama de release que sobrevive
|
|
172
|
+
a su release es un fork. Si quieres conservarla, `--keep-branch`. Si git se niega a
|
|
173
|
+
borrarla, es porque tiene commits que no llegaron a producción: revísala.
|
|
174
|
+
|
|
175
|
+
Cada paso se reporta por separado a propósito: **una vez creado el tag, la versión existe**.
|
|
176
|
+
Si falla la nota de release, dai te lo dice y aclara que el tag ya está publicado, para que
|
|
177
|
+
completes solo lo que falta.
|
|
178
|
+
|
|
179
|
+
## Paso 7 · Estampar el despliegue — opcional
|
|
180
|
+
|
|
181
|
+
Cuando la versión llega a un ambiente:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
dai release stamp 0.5.0 --env prod --app acme-backend
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
── Estampar despliegue ───────────────────────────────
|
|
189
|
+
versión: v0.5.0 app: acme-backend ambiente: PROD
|
|
190
|
+
tracker: jira · 3 User Storie(s) en el release
|
|
191
|
+
─────────────────────────────────────────────────────
|
|
192
|
+
ACME-482 Checkout sin duplicado
|
|
193
|
+
ACME-491 Alta de póliza sin duplicar cliente
|
|
194
|
+
ACME-503 Recordar medio de pago
|
|
195
|
+
─────────────────────────────────────────────────────
|
|
196
|
+
⚠ esto escribe 3 comentario(s) en el tracker de todo el equipo. No se deshace.
|
|
197
|
+
¿Estampo 3 comentario(s)? (s/N)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Cada User Story recibe un comentario con versión, aplicación, ambiente y fecha. Desde ese
|
|
201
|
+
momento, el funcional abre el ticket y sabe dónde está su historia sin preguntar. Si una US
|
|
202
|
+
se implementa en varios repositorios, el ticket va acumulando la matriz: *backend en
|
|
203
|
+
producción, frontend en pre*.
|
|
204
|
+
|
|
205
|
+
**Es opcional.** Decir que no sale con código 0 y no rompe nada: la versión ya está hecha.
|
|
206
|
+
Si tu equipo prefiere no llenar los tickets, avisa solo al canal.
|
|
207
|
+
|
|
208
|
+
Volver a ejecutarlo **no duplica**: dai reconoce sus propios comentarios por
|
|
209
|
+
`(aplicación, versión, ambiente)` y saltea los que ya están. La misma versión en otro
|
|
210
|
+
ambiente sí es un evento nuevo y se estampa.
|
|
211
|
+
|
|
212
|
+
## Paso 8 · El aviso al equipo — opcional
|
|
213
|
+
|
|
214
|
+
Si declaras un canal, `done` y `stamp` avisan solos:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# .env.dai
|
|
218
|
+
DAI_NOTIFY=webex # discord | slack | webex | telegram | webhook | none
|
|
219
|
+
DAI_NOTIFY_WEBHOOK=https://… # SECRETO: quien lo tiene, puede publicar
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Antes de depender de él, pruébalo:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
dai release notify --test
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
El mensaje que sale es siempre el mismo, en cualquier canal:
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
🚀 Release desplegada · acme-backend v0.5.0 → PROD
|
|
232
|
+
Autor: Ada Lovelace · Fecha: 09/09/2026 09:15
|
|
233
|
+
|
|
234
|
+
Cambios principales:
|
|
235
|
+
• ACME-482 Checkout sin duplicado
|
|
236
|
+
• ACME-491 Alta de póliza sin duplicar cliente
|
|
237
|
+
• ACME-503 Recordar medio de pago
|
|
238
|
+
|
|
239
|
+
Ver release: https://…/releases/v0.5.0
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Las viñetas son las **User Stories**, no los commits: lo que el equipo quiere leer es qué
|
|
243
|
+
valor salió, no qué archivos se tocaron.
|
|
244
|
+
|
|
245
|
+
## El ciclo entero, resumido
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
dai release status # ¿dónde estoy?
|
|
249
|
+
dai release plan # ¿qué entra? ← la pantalla importante
|
|
250
|
+
dai release cut 0.5.0 # rama + número + CHANGELOG + commit
|
|
251
|
+
# … escribes el CHANGELOG …
|
|
252
|
+
dai pr # PR a producción (te pide confirmación)
|
|
253
|
+
# … mergeas y publicas: tu firma …
|
|
254
|
+
dai release done 0.5.0 # tag + nota + back-merge + aviso
|
|
255
|
+
dai release stamp 0.5.0 --env prod # opcional: avisar a cada US
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Si algo sale mal
|
|
259
|
+
|
|
260
|
+
| Síntoma | Qué hacer |
|
|
261
|
+
|---|---|
|
|
262
|
+
| `no existe el tag vX.Y.Z` al estampar | Cierra la versión primero (`dai release done`), o trae los tags con `git fetch --tags`. |
|
|
263
|
+
| `done` creó el tag pero falló la nota | El tag ya está: la versión existe. Publica la nota a mano con el comando que dai te deja impreso. |
|
|
264
|
+
| El manifiesto no muestra ninguna US | ¿Las ramas tenían `implements.yaml`? Sin link no hay trazabilidad que reportar. |
|
|
265
|
+
| El tracker no responde | `dai release plan --no-network` sale igual, avisando que no pudo verificar. |
|
|
266
|
+
| Salieron comentarios repetidos | dai no pudo leer los comentarios previos y lo avisó. Revisa el token del tracker. |
|
package/docs/tutoriales/index.md
CHANGED
|
@@ -11,6 +11,12 @@ Guías de **setup operativo** — lo que haces una vez por máquina para trabaja
|
|
|
11
11
|
+ `glab`, las skills en Copilot, OpenSpec y el ciclo completo sobre una US real
|
|
12
12
|
(`link-us` → `check` → `mr` → `stamp`).
|
|
13
13
|
|
|
14
|
+
## El día a día
|
|
15
|
+
|
|
16
|
+
- [**Ciclo de release**](./ciclo-de-release) — de "hay cosas para sacar" a "producción sabe
|
|
17
|
+
qué versión tiene y cada User Story lo dice": manifiesto, corte, tag, nota de release y el
|
|
18
|
+
aviso a cada historia. El *porqué* está en la [guía de releases](../guias/releases).
|
|
19
|
+
|
|
14
20
|
## Preparar el entorno
|
|
15
21
|
|
|
16
22
|
- [**Configurar git**](./configurar-git) — tu identidad (nombre + correo) para que los
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dforce2055/dai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "Metodología de desarrollo asistido por IA — CLI de acciones deterministas (trazabilidad QUÉ↔CÓMO).",
|
|
5
5
|
"repository": { "type": "git", "url": "git+https://github.com/dforce2055/dai.git" },
|
|
6
6
|
"homepage": "https://dforce2055.github.io/dai/",
|