@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 CHANGED
@@ -27,6 +27,22 @@ DAI_PM=md
27
27
  # rama como producción: no adivina cuál es.
28
28
  # DAI_BRANCH_PROD=main
29
29
 
30
+ # ── Aviso de release a un canal de equipo (opcional) ─────────────────────────
31
+ # Tercer adaptador de dai, con la misma forma que DAI_PM: una variable elige el canal y el
32
+ # canal trae las suyas. Por default no hay aviso: dai no habla hacia afuera sin pedirlo.
33
+ # discord | slack | webex | telegram | webhook | none
34
+ # `webhook` es el genérico: manda los campos del release en JSON a cualquier endpoint
35
+ # (Teams, Mattermost, un sistema interno) e incluye además el texto ya armado.
36
+ # DAI_NOTIFY=webex
37
+
38
+ # El endpoint del canal. ⚠️ ES LA CREDENCIAL: quien lo tiene, puede postear. Nunca lo
39
+ # commitees; dai muestra el host, nunca la URL. Probalo con `dai release notify --test`.
40
+ # DAI_NOTIFY_WEBHOOK=
41
+
42
+ # Solo para telegram: no tiene webhooks de entrada, así que el endpoint es el bot y hace
43
+ # falta decir a qué chat va el mensaje.
44
+ # DAI_NOTIFY_CHAT_ID=
45
+
30
46
  # ── Backend md (local, offline) ──────────────────────────────────────────────
31
47
  # Carpeta donde viven las US como <ID>.md (p. ej. ABC-482.md).
32
48
  DAI_MD_US_DIR=.dai/us
package/CHANGELOG.md CHANGED
@@ -3,6 +3,131 @@
3
3
  Formato basado en [Keep a Changelog](https://keepachangelog.com/). Versionado semver
4
4
  (ver `VERSION`).
5
5
 
6
+ ## [0.15.1] — 2026-09-10
7
+
8
+ **Una US puede estar impecable y dai igual no leerla. El molde de trazabilidad pide que la
9
+ metadata viva en una tabla, y cuando la US se escribe a mano en Jira esa tabla es una tabla
10
+ de verdad — que dai no sabía leer. El `spec_version` declarado se perdía en el camino y el
11
+ link quedaba estampado en `pendiente`, con un mensaje que mandaba a agregar una fila que ya
12
+ estaba ahí. Esta versión le enseña a dai a leer y escribir tablas.**
13
+
14
+ ### Corregido
15
+ - **El `spec_version` que la US declaraba y dai no veía.** `adfToMarkdown` no tenía caso
16
+ para las tablas de Jira Cloud, así que caían al `default` y cada celda terminaba en su
17
+ propia línea: `spec_version` en una, `v1` en la siguiente. Como el regex que lee la
18
+ versión no cruza saltos de línea —a propósito, para no capturar un `v2` de veinte líneas
19
+ más abajo— no encontraba nada, y el link se estampaba en `version: pendiente`. El aviso
20
+ era el peor de todos: uno que **acusa al usuario** de no haber hecho algo que sí hizo.
21
+ Ahora una fila de tabla llega como una fila de markdown. El `ac_hash` **no se mueve** —la
22
+ metadata vive arriba del bloque de criterios—, así que ningún `implements.yaml` ya
23
+ estampado queda atrasado por este cambio: lo único que cambia es que `pendiente` pasa a
24
+ ser la versión que la US declara. Es el hermano del #46: aquel era el campo escrito
25
+ `specversion`, este es el campo escrito en la tabla que el propio molde pide.
26
+ - **La otra mitad del mismo bug: dai tampoco escribía tablas.** `markdownToAdf` mandaba la
27
+ metadata del molde —y el comentario de cobertura de `dai release stamp`, que también es
28
+ una tabla— como párrafos con pipes adentro. Ilegibles, y encima invitaban a rehacerlos
29
+ como tabla de Jira a mano, que es justo lo que del otro lado no se sabía leer. Ahora
30
+ viajan como nodo `table` y el ida y vuelta de `dai edit-us` no destruye la metadata.
31
+ - **`dai release done` avisaba de un problema inexistente.** Miraba `origin/release/X.Y.Z`,
32
+ una ref local que sobrevive desactualizada hasta el próximo prune, en vez de preguntarle
33
+ al remoto. Cuando GitHub ya había borrado la rama al mergear —su comportamiento por
34
+ default— intentaba borrar algo que no estaba y reportaba un ⚠ sobre un estado que era el
35
+ correcto. Ahora pregunta con `git ls-remote --heads`, que es autoritativo, limpia la ref
36
+ fantasma y explica en una línea por qué no hubo nada que borrar. Un aviso falso es caro:
37
+ enseña a ignorar los avisos.
38
+
39
+ ### Cambiado
40
+ - **`dai release plan --json` expone `counts.stale`, antes `counts.atrasadas`.** Era una
41
+ clave en español en una salida que lee un script — la única que quedaba — y sigue el
42
+ glosario que ADR-0008 ya había fijado (*atrasado* → *stale*). Si escribiste algo contra
43
+ el `--json` de la 0.15.0 (publicada un día antes), es el único nombre que tenés que
44
+ cambiar.
45
+
46
+ ### Interno
47
+ - **Convención de idioma, ahora escrita.** El repo siempre fue código en inglés y
48
+ comentarios en español, pero no estaba en ningún lado: al escribir el ciclo de release se
49
+ rompió sistemáticamente (25% de identificadores en español en lo nuevo, contra ~2%
50
+ preexistente). Se renombraron 209 identificadores al inglés y la regla quedó como sexta
51
+ regla de oro en `CONTRIBUTING.md`, con una excepción deliberada: **`autor` se queda**. No
52
+ es código, es un campo del `implements.yaml` y parte del contrato del método (ADR-0004);
53
+ renombrarlo sería romper un contrato para ganar consistencia interna.
54
+ - **ADR-0008** documenta el relevamiento que falta para poder ejecutar el i18n.
55
+ - **484 tests** (+9 desde la 0.15.0): las tablas ADF en las dos direcciones —incluido el
56
+ ida y vuelta markdown → ADF → markdown, que es el que protege la metadata de `dai
57
+ edit-us`— y la primera cobertura de integración de `dai release done`, que era casi todo
58
+ efecto y no tenía ninguna: el ciclo completo contra un repo con remoto de verdad, el caso
59
+ del forge que se adelantó, y las dos negativas a taguear.
60
+
61
+ ## [0.15.0] — 2026-09-09
62
+
63
+ **Un equipo puede tener el link QUÉ↔CÓMO perfecto y seguir sin poder contestar la pregunta
64
+ que hace el negocio: "¿esto ya está en producción?". La trazabilidad llegaba hasta la PR y
65
+ se cortaba justo ahí. Esta versión agrega el último eslabón — la versión desplegada — y con
66
+ él, el ciclo completo para cortarla, cerrarla y contarla.**
67
+
68
+ ### Agregado
69
+ - **`dai release plan` — el manifiesto de la versión.** Qué User Stories entran entre el
70
+ último tag y la rama de integración, en qué estado está cada una, y qué entró **sin**
71
+ declarar US. Resuelve las US leyendo los `implements.yaml` **tal como estaban en cada
72
+ commit del rango**, no por el nombre de la rama: el link viaja con el código, así que la
73
+ respuesta sobrevive a que la branch se borre y a que el change se archive — el estado
74
+ normal del repo cuando llegás a cortar, días después del merge. Es el dato del que
75
+ dependen los otros cuatro comandos; `--json` es lo que consume la skill.
76
+ - **`dai release cut <X.Y.Z>` — preparar, sin hablar hacia afuera.** Rama de release, número
77
+ en los archivos que el repo espeja, entrada del CHANGELOG y commit. Ni push, ni tag, ni
78
+ PR: todo lo que pasa ANTES de la firma humana.
79
+ - **`dai release done <X.Y.Z>` — cerrar, después del merge.** Tag anotado, release note en
80
+ el forge, back-merge a integración, borrado de la rama de release y aviso al canal. Existe
81
+ como comando separado porque los dos pasos que más se olvidan cuando la ceremonia se hace
82
+ a mano viven en esta mitad, la que queda después de la firma. Se llama `done` y no
83
+ `finish` por lo mismo que `dai done` cierra el trabajo de una branch: mismo verbo, distinto
84
+ sustantivo.
85
+ - **`dai release stamp <X.Y.Z> --env <ambiente>` — que cada US sepa dónde está.** Deja en
86
+ cada historia del release un comentario con versión, app, ambiente y fecha, y con eso el
87
+ funcional lee el ticket en vez de preguntar. Una US federada en varios repos acumula sola
88
+ su matriz. Es **opcional** y decir que no sale con 0: cuando el comando corre, el tag ya
89
+ existe y la versión está hecha.
90
+ - **`dai release status`** — dónde estás en el ciclo: versión declarada vs último tag, qué
91
+ falta promover, si quedó un back-merge pendiente, qué ramas de release sobrevivieron.
92
+ **`dai release notify --test`** — probar el canal antes de depender de él, porque un
93
+ webhook no se puede validar sin postear y fingir que sí sería justo lo que dai no hace.
94
+ - **Aviso de release a un canal de equipo** (`DAI_NOTIFY`): discord, slack, webex, telegram
95
+ o un `webhook` genérico para Teams, Mattermost o un sistema interno. Apagado por default:
96
+ dai no habla hacia afuera sin que se lo pidan. El endpoint **es la credencial**, así que
97
+ vive en el `.env.dai` y dai muestra el host, nunca la URL — tampoco en los errores.
98
+ - **Skill `/dai-release`** — conduce el ciclo confirmando paso a paso. No recalcula el
99
+ manifiesto: lo pide y lo narra. Si su memoria y el CLI se contradicen, gana el CLI.
100
+ - **[Guía de releases](docs/guias/releases.md)** con el *porqué* y
101
+ **[tutorial del ciclo completo](docs/tutoriales/ciclo-de-release.md)**. Una estrategia de
102
+ branching que el equipo no entiende se abandona en dos sprints, así que la documentación
103
+ entra en la misma versión que los comandos, no después.
104
+
105
+ ### Corregido
106
+ - **`dai pr` no podía abrir la PR de una `feature/` en un repo sin User Stories.** Le pasa a
107
+ cualquier repo de tooling o librería interna que use dai para versionar sin gestionar sus
108
+ propias historias — a este, sin ir más lejos, donde el mensaje mandaba a renombrar la
109
+ branch a `chore/`. Lo que distingue un olvido de un repo que no trabaja así es si la
110
+ branch **nombra un ticket**. El gate de CI no se toca.
111
+ - **Un aviso que aparece siempre no avisa nada.** El manifiesto marcaba cada branch sin US
112
+ como "entró trabajo sin link", incluso en repos donde ninguna branch va a declarar una
113
+ jamás. Ahora el hallazgo se reporta solo si el repo trabaja con User Stories — salvo que
114
+ la branch nombre un ticket, que ahí sí es un olvido.
115
+ - **`dai pr` fallaba al actualizar una PR por un motivo que no era suyo.** `gh pr edit`
116
+ resuelve por GraphQL y arrastra campos deprecados del servidor, así que devolvía un error
117
+ sobre *Projects (classic)* cuando lo único que se quería era cambiar el body. Ahora
118
+ reintenta por REST con la misma autenticación; se intenta callado y solo se reporta si el
119
+ plan B también falla.
120
+
121
+ ### Interno
122
+ - **475 tests** (+68 desde la 0.14.0): el manifiesto y su resolución por commit, el corte y
123
+ el cierre, el gate de alcance del estampado y su idempotencia por (app, versión,
124
+ ambiente), y el adaptador de canal — incluidos tres tests que fallan si el endpoint se
125
+ filtra en algún mensaje.
126
+ - El adaptador de PM suma `comment(id, markdown)` y `listComments(id)` en los tres backends:
127
+ sin poder leer sus propios comentarios, dai no puede saber qué ya estampó.
128
+ - Esta versión se cortó con los comandos nuevos, y el dogfooding devolvió tres de las
129
+ correcciones de arriba.
130
+
6
131
  ## [0.14.0] — 2026-09-08
7
132
 
8
133
  **`dai pr` proponía mergear a `main` en un repo donde `main` despliega a producción, y el
@@ -918,6 +1043,8 @@ ClickUp y Jira Cloud.
918
1043
  - Tests de las rutas de red (jira/clickup/forge) con `fetch` mockeado. Sin links rotos;
919
1044
  `files` de npm sin tests ni secretos.
920
1045
 
1046
+ [0.15.1]: https://github.com/dforce2055/dai/releases/tag/v0.15.1
1047
+ [0.15.0]: https://github.com/dforce2055/dai/releases/tag/v0.15.0
921
1048
  [0.14.0]: https://github.com/dforce2055/dai/releases/tag/v0.14.0
922
1049
  [0.13.3]: https://github.com/dforce2055/dai/releases/tag/v0.13.3
923
1050
  [0.13.2]: https://github.com/dforce2055/dai/releases/tag/v0.13.2
package/CONTRIBUTING.md CHANGED
@@ -41,6 +41,28 @@ git config core.hooksPath .githooks # valida convención de commits + autoría
41
41
  (Cursor, Copilot, Claude, …). Un agente puede ayudarte, pero el cambio lo firmas
42
42
  tú: con tu identidad y sin el trailer del agente. Un check de CI lo bloquea en cada
43
43
  PR (ver [`governance/human-authorship.md`](governance/human-authorship.md)).
44
+ 6. **Código en inglés, comentarios en español.** Los identificadores —variables,
45
+ funciones, constantes, claves de objeto— van en **inglés**; los comentarios y la
46
+ documentación, en **español**. Y todo lo que sale por una interfaz de máquina
47
+ (`--json`, el payload de un webhook, los códigos de estado internos) va en inglés
48
+ también: es una API, la lee un script, y traducirla después rompe a quien la consume.
49
+
50
+ ```js
51
+ // El endpoint ES la credencial: quien lo tiene, postea. ← comentario en español
52
+ export function describeTarget(cfg) { ← código en inglés
53
+ const target = new URL(cfg.endpoint).host;
54
+ ```
55
+
56
+ **La excepción, y es una sola:** los nombres de campo del `implements.yaml` son
57
+ parte del contrato del método, no del código. `autor:` se llama así en el schema
58
+ (ADR-0004) y en todos los repos que ya lo usan; renombrarlo sería romper el
59
+ contrato para ganar consistencia interna, que es el peor cambio posible.
60
+
61
+ Lo que **todavía no** cumple esta regla son los **mensajes al usuario**, que hoy son
62
+ literales en español repartidos por todo el CLI. Eso no es un descuido: es el
63
+ refactor que [ADR-0008](docs/adr/0008-estrategia-de-i18n.md) tiene planificado como
64
+ fase 3 (`DAI_LANG` + un catálogo `t(key)` sin dependencias). Mientras tanto, escribí
65
+ los mensajes en español y **no inventes un mecanismo de traducción propio**.
44
66
 
45
67
  ## Flujo (la propia metodología)
46
68
 
package/README.md CHANGED
@@ -209,6 +209,7 @@ flowchart TD
209
209
  | `dai forge review <ref> --from <review.json>` `[--dry-run\|--yes]` | **review inline**: un resumen + un comentario anclado a cada `archivo:línea`, clasificado low/medium/high. **Valida cada posición contra el diff** (descarta lo que el modelo inventó) antes de postear; sin `--yes` muestra el preview y no postea nada. Modo desatendido: `--min-severity`/`--min-confidence`/`--max-comments`. El review sale con `event: COMMENT`, nunca `APPROVE` ([ADR-0016](docs/adr/0016-review-inline.md)) |
210
210
  | `dai forge comment <ref> --body-file <f>` · `dai forge pr <ref>` | comentar / leer una PR/MR (GitHub/GitLab) — el fallback simple, sin anclar |
211
211
  | `dai ac-hash <us.md>` | calcula el hash de los criterios de aceptación de una US |
212
+ | `dai release plan` · `cut <X.Y.Z>` · `done <X.Y.Z>` · `stamp <X.Y.Z> --env <amb>` · `status` · `notify --test` | **el ciclo de versión** ([ADR-0019](docs/adr/0019-ciclo-de-version-y-aviso-de-release.md)). `plan` arma el **manifiesto**: qué US entran, cuáles quedaron **atrasadas** y qué se coló **sin US** — más el bump que **propone** (la versión la firma una persona: un cambio de default es minor aunque todo sea `fix:`). `cut` prepara (rama, número, entrada del CHANGELOG con el material para repartir, commit) y **no habla hacia afuera**; `done` cierra tras el merge (tag + release note + back-merge + aviso) — los dos pasos que más se olvidan. `stamp` le avisa a **cada US** en qué versión y ambiente salió: muestra el alcance real, es **idempotente** por (app, versión, ambiente) y **es opcional** (decir que no sale con 0). El tag es la versión; `VERSION`/`package.json` son espejos y puede no haber ninguno. Guía: [releases](docs/guias/releases.md) · Tutorial: [ciclo de release](docs/tutoriales/ciclo-de-release.md) |
212
213
  | `dai help [<comando>]` · `dai <comando> --help` | ayuda del CLI. Pedir ayuda **nunca ejecuta el comando**: sale por `stdout` y termina con 0. Valen `--help`, `-h` y `dai <comando> help` — las tres formas, en todos los comandos |
213
214
  | `dai doctor` · `dai docs <dest>` · `dai version` | diagnóstico del entorno (incluye **version-drift** del scaffold) · copiar la doc (sin los assets del sitio; los links a las capturas apuntan al sitio publicado) · versión (`dai version` avisa si tu repo quedó atrás) |
214
215
 
@@ -243,7 +244,7 @@ flowchart TD
243
244
  > en [`skills/`](skills/).
244
245
 
245
246
  Skills (se invocan en el asistente): `/doc-to-backlog` · `/grill-intent` · `/grill-epic` · `/grill-user-story` · `/link-us` ·
246
- `/tdd` · `/dai-review`. Config del tracker (`md`\|`jira`\|`clickup`) y tokens: en `.env.dai`
247
+ `/tdd` · `/dai-review` · `/dai-release`. Config del tracker (`md`\|`jira`\|`clickup`) y tokens: en `.env.dai`
247
248
  (no versionado; el `.env` del equipo no se toca — [ADR-0017](docs/adr/0017-env-dai.md)) —
248
249
  ver [`.env.dai.example`](.env.dai.example). Auth (SSH + tokens): [ADR-0007](docs/adr/0007-modelo-de-autenticacion.md).
249
250
 
@@ -279,7 +280,7 @@ mi-repo/
279
280
  ├── CLAUDE.md · Constitución del proyecto (auto-cargada por Claude)
280
281
  ├── .env.dai · tu tracker (NO versionado, completa el token) + .env.dai.example (plantilla, sí versionada)
281
282
  ├── .claude/skills/ · Las skills, locales al repo (el equipo las hereda)
282
- │ └── doc-to-backlog · grill-intent · grill-epic · grill-user-story · link-us · tdd · dai-review
283
+ │ └── doc-to-backlog · grill-intent · grill-epic · grill-user-story · link-us · tdd · dai-review · dai-release
283
284
  ├── .github/
284
285
  │ ├── copilot-instructions.md · La constitución, auto-inyectada en cada chat de Copilot
285
286
  │ ├── skills/ · Las mismas skills, en formato Copilot nativo (SKILL.md)
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.14.0
1
+ 0.15.1