@dforce2055/dai 0.10.0 → 0.11.0

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.
@@ -0,0 +1,183 @@
1
+ # ADR-0018 — El alcance de `stamp` lo decide la rama, el gate de CI se ejecuta, y editar el QUÉ es un comando
2
+
3
+ - **Estado:** aceptado
4
+ - **Fecha:** 2026-07-22
5
+ - **Decide:** lead / arquitecto de la metodología
6
+
7
+ ## Contexto
8
+
9
+ Tres agujeros con la misma raíz: **dai suponía cosas sobre el QUÉ que nunca escribió en
10
+ ningún lado** — que un repo tiene una sola US viva, que toda rama implementa una US, y que
11
+ la US, una vez publicada, no se vuelve a tocar. Las tres suposiciones fallan en el primer
12
+ sprint real.
13
+
14
+ ### 1. `dai stamp` estampaba todo el repo
15
+
16
+ `cmdStamp` recorría el repo entero con `discoverImplements(cwd)` —**archivados
17
+ incluidos**— y le dejaba un comentario de cobertura a cada US que encontrara. Cerrar una
18
+ historia dejaba esto en el tracker:
19
+
20
+ ```
21
+ ➜ backend git:(feature/331qtr-historia-motor) dai stamp
22
+ ✓ 86acme482 → task 86acme482 (comentario) (✅ al día)
23
+ ✓ 86acme483 → task 86acme483 (comentario) (✅ al día)
24
+ ✓ 86acme484 → task 86acme484 (comentario) (✅ al día)
25
+ ✓ 86acme485 → task 86acme485 (comentario) (✅ al día)
26
+ ```
27
+
28
+ Tres de esos cuatro comentarios son ruido en el ticket de otra persona. Y un comentario
29
+ en un tracker **no se deshace**: queda en el historial de la US, en la notificación por
30
+ mail de quien la sigue, y en el Slack del canal conectado.
31
+
32
+ Lo llamativo es que `dai check` **ya** filtraba los archivados (`includeArchived: false`,
33
+ ADR-0010) y `stamp` no. La divergencia era un descuido, no una decisión.
34
+
35
+ ### 2. `governance/ci-rules.md` prometía un gate que no existía
36
+
37
+ El documento decía, en una tabla, que el CI **bloquea** el PR si una rama de producto no
38
+ tiene `implements.yaml`. No había ningún comando que hiciera eso. No estaba en `ci.yml`,
39
+ no estaba en el CLI, no estaba en ningún template. Era una regla escrita que nadie
40
+ aplicaba — el peor estado posible: el equipo cree que está protegido y no lo está.
41
+
42
+ Y al ir a implementarla apareció el problema real, el que probablemente explica por qué
43
+ nunca se implementó: **la regla como estaba escrita es demasiado dura**. Un `chore/` de
44
+ bump de dependencias, un `docs/` de typo, un `hotfix/` de las tres de la mañana no
45
+ tienen US, no deberían tenerla, y un gate que los bloquea se desactiva la primera semana.
46
+ Un gate desactivado protege exactamente igual que un gate que no existe, pero además
47
+ enseña al equipo que los checks de dai son un obstáculo.
48
+
49
+ ### 3. Editar una US publicada era copiar y pegar
50
+
51
+ `dai publish` creaba la US y `dai check` detectaba que había cambiado. En el medio no
52
+ había nada: para **editarla** el PO abría el navegador y escribía en un textarea sin
53
+ validación, o el dev editaba el `us.md` local y el tracker quedaba viejo. El formato
54
+ canónico (`templates/formato-us.md`) existía como documento y no como chequeo, así que una
55
+ US podía volver al tracker sin criterios y nadie se enteraba hasta que `dai link-us`
56
+ fallaba, sprints después.
57
+
58
+ ## Decisión
59
+
60
+ ### 1. El alcance de `stamp` sale del nombre de la rama, y ante la duda se pregunta
61
+
62
+ `dai stamp` deja de recorrer el repo. La decisión vive en un módulo puro
63
+ (`cli/lib/branch-scope.mjs`) que responde **qué estampar y por qué**:
64
+
65
+ | Situación | Qué hace |
66
+ |---|---|
67
+ | `dai stamp ABC-482` | esas US, aunque el change esté archivado |
68
+ | `dai stamp --all` | todo (el comportamiento viejo, ahora explícito) |
69
+ | la rama nombra una US del repo | **solo esa** |
70
+ | hay una sola US viva | esa |
71
+ | varias US vivas y la rama no dice cuál | **no estampa: pregunta** |
72
+
73
+ Los changes archivados **salen del default**. Se alcanzan con `--all` o nombrando el ID
74
+ —que es justo el caso de estampar después del merge.
75
+
76
+ > **El default es preguntar, no estampar de más.** Es la asimetría del costo: no estampar
77
+ > se arregla corriendo el comando otra vez; estampar de más deja cuatro comentarios que
78
+ > no se borran. Cuando no hay TTY (CI), en vez de preguntar **falla** y pide el ID
79
+ > explícito — que es lo que un pipeline debería estar pasando de todos modos.
80
+
81
+ El matcheo rama↔US es deliberadamente conservador: `feature/331qtr-historia-motor` **no**
82
+ matchea `86acme482` aunque ambos sean alfanuméricos. Se comparan candidatos del nombre
83
+ contra los IDs que el repo realmente declara, y si no hay coincidencia exacta se pregunta.
84
+
85
+ Para el gate —que no tiene contra qué comparar, justamente porque el `implements.yaml` no
86
+ existe— un key de tracker se reconoce por **MAYÚSCULAS + guion + números** (`ABC-482`). El
87
+ case es lo único que lo separa de una palabra del slug con un número pegado: dogfoodeando
88
+ esto, la rama `feat/issues-22-26` hacía que el gate sugiriera `dai link-us issues-22`, o
89
+ sea mandar al dev a crear un link inventado. `dai link-us` preserva el case del key, y los
90
+ keys de tracker son mayúsculas por convención en Jira, GitLab y Azure Boards.
91
+
92
+ ### 2. El gate es un comando, y sabe qué ramas no exigir
93
+
94
+ ```bash
95
+ dai check --ci # 0 = pasa · 1 = falta el link · 2 = el QUÉ cambió
96
+ ```
97
+
98
+ Lee el nombre de la rama y aplica `branch-naming.md`:
99
+
100
+ - `feature/`, `feat/` → **siempre** exige `implements.yaml`
101
+ - `chore/`, `docs/`, `ci/`, `build/`, `test/`, `refactor/`, `style/`, `release/`,
102
+ `hotfix/`, `revert/` → **exentas**
103
+ - `fix/` y cualquier otro prefijo → exige **solo si el nombre trae un ID** de tracker
104
+ - sin prefijo (`main`, `develop`) → no es rama de trabajo
105
+
106
+ > El nombre de la rama **es** la declaración del tipo de trabajo. Si el gate te bloquea
107
+ > un chore, la respuesta no es inventarle una US: es renombrar la rama. Esto convierte
108
+ > `branch-naming.md` de convención sugerida en algo con consecuencia, sin agregar
109
+ > ceremonia nueva.
110
+
111
+ En CI, la rama de una PR no se saca de git: `HEAD` es un merge commit detached. El
112
+ comando lee `GITHUB_HEAD_REF` / `CI_MERGE_REQUEST_SOURCE_BRANCH_NAME` / equivalentes.
113
+
114
+ ### 3. El gate no bloquea por falta de red ni de credencial
115
+
116
+ `--no-network` valida el link y no compara contra la US viva; es el **default del
117
+ template** (`templates/ci-dai-gate.yml`). Con los secrets del tracker cargados se saca el
118
+ flag y el gate detecta además las US atrasadas.
119
+
120
+ Mismo criterio dentro del modo completo: si el tracker no responde, **avisa y no
121
+ bloquea**. Un gate que se pone rojo porque venció un token enseña al equipo a mirar para
122
+ otro lado, y ahí perdimos los dos checks — el de red y el que sí importaba.
123
+
124
+ ### 4. Editar el QUÉ es un comando, y el `spec_version` lo decide la persona
125
+
126
+ El tercer agujero del mismo origen: **la US vivía en el tracker y no había forma de
127
+ editarla sin copiar y pegar.** El PO abría el navegador, escribía en un textarea sin
128
+ validación, y el `.md` del repo quedaba viejo — o al revés. Dos comandos, un camino:
129
+
130
+ ```bash
131
+ dai edit-us <ID> # la baja del tracker → $EDITOR → valida → preview → guarda (PO)
132
+ dai update-us <ID> # ya tenés el .md escrito (lo refinaste implementando) → guarda (dev)
133
+ ```
134
+
135
+ No son dos implementaciones: `edit-us` termina llamando al mismo tramo que `update-us`
136
+ (`pushUS`), así que las dos puertas dan **el mismo preview y la misma confirmación**. La
137
+ única diferencia es de dónde sale el markdown.
138
+
139
+ **La validación bloquea tres cosas y avisa del resto.** Frenan: sin título, sin sección
140
+ de criterios, sección vacía. Nada más — y no por permisividad: son exactamente las tres
141
+ sin las cuales no hay `ac_hash`, y sin `ac_hash` no hay link QUÉ↔CÓMO. Que un criterio no
142
+ sea Gherkin completo, o que el título tenga doce palabras, son **avisos**: dai opina en su
143
+ dominio (la trazabilidad) y sugiere en el resto. `--strict` sube los avisos a errores para
144
+ quien quiera esa política; no es el default, porque un gate que rechaza US legítimas se
145
+ esquiva editando en el navegador y volvemos al punto de partida.
146
+
147
+ Un formato inválido **no tira lo escrito**: te devuelve al editor con los errores a la
148
+ vista, tantas veces como haga falta, y salir sin guardar deja tu `.md` intacto.
149
+
150
+ **El `spec_version` se propone, no se impone.** Si el `ac_hash` se movió, dai pregunta:
151
+
152
+ | El PO responde | Qué significa | Qué pasa |
153
+ |---|---|---|
154
+ | `s` | cambio **material** | `v1` → `v2`; los repos con `v1` se marcan atrasados |
155
+ | `n` | cambio **editorial** | se queda en `v1`; nadie se marca atrasado |
156
+
157
+ Es la línea que ya estaba escrita en [METODOLOGIA §4](../METODOLOGIA.md) —*el número
158
+ comunica, el hash detecta*— llevada a una pregunta concreta. La máquina sabe **que** algo
159
+ cambió; solo la persona sabe **si importa**. Automatizarlo en cualquiera de las dos
160
+ direcciones rompe algo: subirlo siempre infla la versión con cada typo y entrena al equipo
161
+ a ignorar los ⚠️; no subirlo nunca deja el número mintiendo. `--bump` / `--no-bump` cubren
162
+ el caso no interactivo.
163
+
164
+ ## Consecuencias
165
+
166
+ - `dai stamp` sin argumentos **cambia de comportamiento**: antes estampaba todo, ahora
167
+ estampa una. Es un cambio incompatible en el papel, pero el comportamiento viejo era el
168
+ bug; quien lo quiera tiene `--all`. Va en una **minor** por eso.
169
+ - En CI hay que pasar el ID (`dai stamp ABC-482`). `governance/ci-rules.md` lo dice.
170
+ - `branch-naming.md` pasa a tener consecuencia mecánica. Un repo con otras convenciones de
171
+ prefijo va a ver ramas "exentas" que él considera de producto — el escape es nombrar la
172
+ rama con el ID, que es lo que `dai link-us` hace solo.
173
+ - El gate no valida tests ni lint: eso ya lo hace el CI del repo y dai no se mete
174
+ (ver `dai` como herramienta, no como mandato).
175
+ - Los adaptadores de PM ahora devuelven `raw` (el markdown completo de la US) además del
176
+ parseo. Es lo que `edit-us` abre; antes solo teníamos título + hash, que alcanza para
177
+ detectar drift pero no para editar.
178
+ - `dai edit-us` le da al **PO** un comando de terminal, cuando hasta ahora su superficie
179
+ eran skills en el asistente y el tracker. Es opcional —seguir editando en el navegador
180
+ funciona igual— pero es el único camino donde el formato se valida ANTES de guardar.
181
+ - La validación de formato vive en el CLI (`us-format.mjs`), no en la skill. Es lo
182
+ mecánico y verificable; el criterio de si un criterio es *bueno* sigue en
183
+ `/grill-user-story`, que interroga (ADR-0002).
@@ -23,6 +23,7 @@ decisión cambia, se escribe un ADR nuevo que supersede al viejo. Molde en
23
23
  | [0015](0015-jira-corporativo.md) | `dai publish` en Jira corporativo: campos propios declarados, `--parent`/`--issuetype`, TLS con CA (nunca apagar la verificación) | aceptado |
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
+ | [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 |
26
27
 
27
28
  > Estas son las decisiones que cierran las "Decisiones abiertas" de
28
29
  > [`METODOLOGIA.md §7`](../METODOLOGIA.md) y las enmiendas al
@@ -14,11 +14,25 @@ la IA hace dos cosas, en orden:
14
14
  o `descartar` (no vale la pena ahora). Un "no lo construyas" es un éxito del gate,
15
15
  no una falla.
16
16
 
17
+ ## Cuando la US ya existe y hay que editarla
18
+
19
+ El refinamiento no termina cuando la US entra al sprint: aparece un criterio que faltaba,
20
+ o una regla que nadie había dicho. `dai edit-us <ID>` cierra ese ciclo sin copiar y pegar:
21
+ baja la US del tracker, la abre en el editor del PO, **valida el formato** (título +
22
+ criterios + Gherkin completo), muestra qué cambia, pregunta si el cambio es **material**
23
+ (sube `spec_version`, los repos atrasados se marcan solos) o **editorial** (no sube nada),
24
+ y recién con la confirmación escribe.
25
+
26
+ Si el que refina es el **dev** —un criterio que aparece escribiendo el test— es el mismo
27
+ camino por la otra puerta: `dai update-us <ID>` empuja el `us.md` del change, con el mismo
28
+ preview y la misma confirmación ([ADR-0018](../adr/0018-alcance-de-stamp-y-gate-de-ci.md)).
29
+
17
30
  ## Herramientas
18
31
 
19
32
  - `/grill-user-story` → US en formato [`formato-us.md`](../../templates/formato-us.md),
20
33
  publicada en Jira/ClickUp (o `.md` de fallback).
21
34
  - `/grill-intent` → `openspec/intents/<fecha-slug>/intent.md`
35
+ - `dai edit-us <ID>` → trae la US del tracker, la editas, dai valida el formato y la devuelve.
22
36
  - Gate de entrada: [`definition-of-ready.md`](../../templates/definition-of-ready.md).
23
37
 
24
38
  ## Qué firma el humano
@@ -11,13 +11,22 @@ cobertura inversa** en el tracker: qué repo/change implementa la US, contra qu
11
11
  versión, con estado ✅/⚠️ y links (branch + commit-ancla).
12
12
 
13
13
  ```bash
14
- dai check # ¿estoy atrasado respecto de la US? (read-only, gate de PR)
15
- dai stamp # escribe la cobertura en el tracker (branch + commit)
14
+ dai check # ¿estoy atrasado respecto de la US? (read-only)
15
+ dai check --ci # el mismo chequeo como GATE del PR: 0 pasa · 1 falta el link · 2 atrasado
16
+ dai stamp # escribe la cobertura en el tracker (branch + commit)
16
17
  ```
17
18
 
19
+ `dai stamp` sin argumentos deduce **qué US** estampar del nombre de tu rama, y si el repo
20
+ tiene varias vivas y no puede saberlo, **pregunta** antes de escribir: un comentario en un
21
+ tracker no se deshace. **En un pipeline pasá el ID** — `dai stamp ABC-482` — que además es
22
+ lo único que un CI sabe con certeza ([ADR-0018](../adr/0018-alcance-de-stamp-y-gate-de-ci.md)).
23
+
18
24
  ## Herramientas
19
25
 
20
26
  - `dai check` / `dai stamp` — mismos comandos los corra un humano o el CI.
27
+ - `dai check --ci` — el gate de [`ci-rules.md`](../../governance/ci-rules.md) ejecutable:
28
+ exige el link en las ramas de producto y deja pasar `chore/`, `docs/`, `release/`…
29
+ Workflow listo para copiar: [`templates/ci-dai-gate.yml`](../../templates/ci-dai-gate.yml).
21
30
  - Contenido del stamp: [ADR-0005](../adr/0005-superficie-comandos-y-stamp.md).
22
31
 
23
32
  ## Qué firma el humano
@@ -28,6 +37,8 @@ dai stamp # escribe la cobertura en el tracker (branch + commit)
28
37
  ## Antipatrones
29
38
 
30
39
  - **Actualizar el estado del ticket a mano** → desincronización garantizada.
40
+ - **`dai stamp --all` en el CI** → le deja un comentario a cada US del repo, incluidas las
41
+ de sprints viejos. En un pipeline el ID va explícito.
31
42
  - **Escribir el link en los dos lados** → se desincroniza al primer cambio (Art. 9).
32
43
  - **Guardar solo la branch en el stamp** → 404 al borrarse; va con commit-ancla.
33
44
  - **Creer que hace falta "un CI en Jira"** → es un comando; el tracker no ejecuta nada.
package/docs/glosario.md CHANGED
@@ -54,7 +54,7 @@
54
54
 
55
55
  | Término | Qué es |
56
56
  |---|---|
57
- | **CI (Integración Continua)** | La automatización que corre en cada *push* / PR: compila, corre los tests y valida el repo. **En dai**, el CI ejecuta `dai check` como *gate* del PR (valida que el link exista y que el `ac_hash` coincida con la US viva) y, al mergear, `dai stamp` (estampa la cobertura inversa en el tracker — nadie la escribe a mano). Qué valida, en [`governance/ci-rules.md`](../governance/ci-rules.md). |
57
+ | **CI (Integración Continua)** | La automatización que corre en cada *push* / PR: compila, corre los tests y valida el repo. **En dai**, el CI ejecuta `dai check --ci` como *gate* del PR (valida que el link exista y que el `ac_hash` coincida con la US viva; las ramas `chore/`/`docs/` quedan exentas) y, al mergear, `dai stamp <ID>` (estampa la cobertura inversa en el tracker — nadie la escribe a mano). Qué valida, en [`governance/ci-rules.md`](../governance/ci-rules.md); workflow listo para copiar en [`templates/ci-dai-gate.yml`](../templates/ci-dai-gate.yml). |
58
58
  | **CD (Despliegue Continuo)** | La automatización que lleva la versión a los ambientes (`dev` / `test` / `pre` / `prod`) y reporta a cuál llegó. **En dai**, el CD alimenta la **matriz repo × ambiente**: *implementado ≠ desplegado* — el CI dice "el repo implementó `@v3`", el CD dice "`@v3` está viva en `pre`". |
59
59
  | **CI/CD** | Juntos, la "plomería" que **blinda** el método sin depender de que la gente se acuerde (enforcement, no vigilancia). En dai es **opcional** (ADR-0003): aparece sobre todo en **N3** (organización grande). En **N1/N2** los mismos comandos (`dai check`, `dai stamp`) corren a mano o por un git-hook — la trazabilidad es idéntica; solo cambia **quién** los dispara. dai **no trae** su propio CI/CD: se apoya en el pipeline que la organización ya tenga. |
60
60
 
package/docs/guias/dev.md CHANGED
@@ -41,6 +41,9 @@
41
41
  8. **Verificas el DoD** → [`definition-of-done.md`](../../templates/definition-of-done.md) antes de mergear.
42
42
  9. **Merge → se estampa la cobertura** con `dai stamp` (lo corres tú tras mergear, o el CI
43
43
  si la org lo tiene automatizado — mismo comando, ADR-0003). El estado se **deriva** (Art. 10).
44
+ `dai stamp` deduce **qué US** estampar del nombre de tu rama; si el repo tiene varias
45
+ vivas y no puede saberlo, te pregunta antes de escribir en el tracker. En un pipeline
46
+ pasa el ID: `dai stamp ABC-482` ([ADR-0018](../adr/0018-alcance-de-stamp-y-gate-de-ci.md)).
44
47
  10. **(Opcional) Limpias la rama** → `dai done` te devuelve a la base (default `main`, o
45
48
  `--base develop`), hace `fetch --prune` + `pull` y borra la rama local **solo si está
46
49
  mergeada**. Higiene del repo tras el merge, sin riesgo de perder trabajo sin integrar.
@@ -57,6 +60,23 @@ Si el PO sube la US a `v2`, tu `implements.yaml` (que apunta a `v1`) se marca
57
60
  **atrasado** solo. Abres una nueva iteración contra `v2` y vuelves al paso 3. Nadie
58
61
  te avisa: el link versionado lo hace [Art. 11](../MANIFIESTO.md#art-11).
59
62
 
63
+ ### Cuando el que cambia el QUÉ eres tú
64
+
65
+ Implementando aparece un criterio que la US no decía, y lo escribes en el `us.md` del
66
+ change. Ahí el tracker queda viejo y tu `ac_hash` deja de coincidir con nada:
67
+
68
+ ```bash
69
+ dai update-us ABC-482 # empuja tu us.md al tracker + re-estampa el ac_hash local
70
+ ```
71
+
72
+ Te muestra qué va a cambiar allá arriba y **pide confirmación** antes de pisar la US
73
+ (`--dry-run` para solo mirar, `--yes` para saltar la pregunta). Sin `--us` toma el `us.md`
74
+ que está junto a tu `implements.yaml`.
75
+
76
+ > Que un criterio nuevo pase por el tracker no es burocracia: es lo que hace que el
77
+ > **PO se entere** de que la historia creció. Editar solo el `us.md` local deja el QUÉ
78
+ > partido en dos versiones y ninguna es la buena.
79
+
60
80
  ## Tus herramientas
61
81
 
62
82
  - `/link-us`
@@ -66,4 +86,5 @@ te avisa: el link versionado lo hace [Art. 11](../MANIFIESTO.md#art-11).
66
86
  - `/tdd` — la disciplina TDD que aplica el paso anterior
67
87
  - `/dai-review`
68
88
  - `dai check` · `dai pr` · `dai stamp` · `dai done` (limpieza, opcional)
89
+ - `dai update-us` — empuja al tracker una US que refinaste implementando
69
90
  - `definition-of-done.md`
package/docs/guias/po.md CHANGED
@@ -31,7 +31,45 @@
31
31
  válido es *"no lo construyas"* — eso es el gate haciendo su trabajo (Art. 4).
32
32
  4. **Verificas el [DoR](../../templates/definition-of-ready.md)** → antes de que entre al sprint, la US cumple el
33
33
  [`definition-of-ready.md`](../../templates/definition-of-ready.md).
34
- 5. **Demo** → aceptas o rechazas contra los mismos criterios que ya eran tests.
34
+ 5. **Editas una US que ya existe** → `dai edit-us ABC-482`. La **baja del gestor**, te la
35
+ abre en tu editor, **valida el formato** cuando guardas, te muestra qué cambia y
36
+ recién ahí la sube. Ver [Cuando el QUÉ cambia](#cuando-el-que-cambia) abajo.
37
+ 6. **Demo** → aceptas o rechazas contra los mismos criterios que ya eran tests.
38
+
39
+ ## Cuando el QUÉ cambia {#cuando-el-que-cambia}
40
+
41
+ Un criterio que faltaba, una regla que aparece a mitad del sprint. La US **vive en el
42
+ gestor**, no en un `.md` que alguien tiene que acordarse de sincronizar — así que dai la
43
+ trae, te deja editarla y la devuelve:
44
+
45
+ ```bash
46
+ dai edit-us ABC-482
47
+ ```
48
+
49
+ 1. **Baja** la US del gestor (Jira / ClickUp) a un `.md`.
50
+ 2. **La abre** en tu `$EDITOR`. Escribes en markdown, con el
51
+ [molde canónico](../../templates/formato-us.md) delante.
52
+ 3. **Valida el formato** al guardar: que tenga título, que tenga criterios, y que cada
53
+ criterio sea Gherkin completo (`Dado / Cuando / Entonces`). Si algo no da, **te
54
+ devuelve al editor** — no te tira lo escrito.
55
+ 4. **Te muestra qué cambia** allá arriba: título, cuántos criterios, el `ac_hash`.
56
+ 5. **Te pregunta si subir el `spec_version`** — y esta es la decisión tuya, no de dai:
57
+
58
+ | Tu cambio | Respondes | Qué pasa |
59
+ |---|---|---|
60
+ | Un criterio nuevo, una regla distinta (**material**) | `s` → `v1` → `v2` | los repos que implementaron `v1` se marcan **atrasados** solos |
61
+ | Un typo, redacción más clara (**editorial**) | `n` → se queda en `v1` | nadie se marca atrasado |
62
+
63
+ dai no puede distinguir las dos cosas mirando el hash: sabe *que* cambió, no *si
64
+ importa*. Eso lo sabes tú.
65
+ 6. **Confirmas** y recién ahí escribe en el gestor. Sin confirmación no se toca nada.
66
+
67
+ > **`--dry-run`** te muestra todo el preview sin escribir. Úsalo la primera vez.
68
+
69
+ Solo dos cosas te **frenan**: que la US no tenga título, o que no tenga criterios. Sin
70
+ criterios no hay `ac_hash`, y sin `ac_hash` la US no se puede linkear a ningún repo — no
71
+ es una regla de estilo, es lo que sostiene la trazabilidad. Todo lo demás (un criterio
72
+ que no es Gherkin, un título largo) es un **aviso**: dai te lo dice y sigue.
35
73
 
36
74
  ## La trampa a evitar
37
75
 
@@ -52,5 +90,6 @@ el CI (Art. 10).
52
90
  - `/grill-epic` — algo grande → una épica partida en varias US
53
91
  - `/grill-user-story` — una US funcional y testeable
54
92
  - `/grill-intent` — Gate 0: desafía el problema de la US antes del spec
93
+ - `dai edit-us <ID>` — traes una US del gestor, la editas, dai valida y la devuelve
55
94
  - el gestor de proyectos
56
95
  - [`definition-of-ready.md`](../../templates/definition-of-ready.md)
@@ -12,15 +12,45 @@
12
12
 
13
13
  ## Qué valida el CI en cada PR/MR
14
14
 
15
- | Check | Regla | Si falla |
16
- |---|---|---|
17
- | **Link presente** | Toda rama de producto (`feature/ABC-###-*`) tiene `implements.yaml`. | ❌ bloquea el PR |
18
- | **ID válido** | El `id` matchea el formato del gestor (`ABC-\d+`) y el ID existe. | ❌ bloquea |
19
- | **`ac_hash` calculado** | El CI (re)calcula el hash de los criterios de la US y lo compara. | ⚠️ marca atrasado si no coincide |
20
- | **Tests verdes** | La suite de la US pasa. | ❌ bloquea |
21
- | **Estándares** | Lint + tipos + convenciones del repo. | ❌ bloquea |
15
+ Las dos primeras filas **las ejecuta un comando**, no la buena voluntad:
22
16
 
23
- > Ramas `chore/`/`fix/` sin US no requieren `implements.yaml` (ver `branch-naming.md`).
17
+ ```bash
18
+ dai check --ci # salidas: 0 pasa · 1 falta el link · 2 el QUÉ cambió
19
+ ```
20
+
21
+ Hay un workflow listo para copiar en [`templates/ci-dai-gate.yml`](../templates/ci-dai-gate.yml).
22
+ Si tu CI no es GitHub Actions, el contrato es el mismo: un comando y su código de salida.
23
+
24
+ | Check | Regla | Quién lo hace | Si falla |
25
+ |---|---|---|---|
26
+ | **Link presente** | Toda rama de producto (`feature/`) tiene `implements.yaml`. | `dai check --ci` | ❌ bloquea (exit 1) |
27
+ | **`ac_hash` al día** | Recalcula el hash de los criterios de la US viva y lo compara. | `dai check --ci` | ❌ bloquea (exit 2) |
28
+ | **ID resoluble** | El `id` del link existe en el gestor. | `dai check --ci` | ❌ bloquea (exit 2) |
29
+ | **Tests verdes** | La suite de la US pasa. | el CI del repo | ❌ bloquea |
30
+ | **Estándares** | Lint + tipos + convenciones del repo. | el CI del repo | ❌ bloquea |
31
+
32
+ ### Qué ramas quedan exentas
33
+
34
+ Un gate que le exige US a todo se desactiva a la semana, y entonces no protege nada.
35
+ Por eso `dai check --ci` decide **leyendo el nombre de la rama** (`branch-naming.md`):
36
+
37
+ | Prefijo | ¿Exige `implements.yaml`? |
38
+ |---|---|
39
+ | `feature/`, `feat/` | **Sí, siempre** — es trabajo de producto |
40
+ | `chore/`, `docs/`, `ci/`, `build/`, `test/`, `refactor/`, `style/`, `release/`, `hotfix/`, `revert/` | **No** — trabajo sin US |
41
+ | `fix/` y cualquier otro prefijo | **Solo si el nombre trae un ID** (`fix/ABC-482-…`). Sin ID, no |
42
+ | `main`, `develop` (sin prefijo) | No — no es una rama de trabajo |
43
+
44
+ > Si tu PR es una corrección o un chore y el gate te lo bloquea, la respuesta **no** es
45
+ > inventarle una US: es renombrar la rama con el prefijo que corresponde. El nombre de la
46
+ > rama es la declaración de qué tipo de trabajo es.
47
+
48
+ ### Sin credenciales del tracker
49
+
50
+ `dai check --ci --no-network` valida el link y **no** compara contra la US viva. Es el
51
+ default del template: un gate que falla porque un token venció o porque el CI no tiene
52
+ salida a internet enseña al equipo a ignorarlo. Con los secrets cargados, sacá el flag y
53
+ el gate detecta además las US atrasadas.
24
54
 
25
55
  ## Qué hace el CI al mergear
26
56
 
@@ -28,6 +58,15 @@
28
58
  2. **Estampa la cobertura inversa** en el gestor: en el ticket `ABC-###`, deja
29
59
  "implementado por `<repo>` @ `<version>` (`ac_hash`) ✅". **Nadie lo escribe a
30
60
  mano** ([Art. 10](../docs/MANIFIESTO.md#art-10)).
61
+
62
+ ```bash
63
+ dai stamp ABC-482 # explícito: lo que corresponde en CI
64
+ dai stamp # local: deduce la US de la rama; si hay varias, pregunta
65
+ ```
66
+
67
+ > En CI **pasá el ID**. `dai stamp` sin argumentos es para el dev en su máquina:
68
+ > deduce la US del nombre de la rama y, si no puede, pregunta en vez de estampar de
69
+ > más. Un comentario en el tracker no se deshace.
31
70
  3. **Actualiza el índice/router central** de la federación: la fila `ABC-### → { repos }`.
32
71
 
33
72
  ## Qué hace el CD al desplegar (solo N3 · organización grande)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dforce2055/dai",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
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/",
@@ -30,7 +30,7 @@ falló. El CLI lo caza antes.
30
30
  ### Cómo postear (elige la primera disponible)
31
31
 
32
32
  1. **`dai forge review <ref> --from <archivo> --yes`** — la forma. Review inline:
33
- resumen + un comentario por línea. Usa `GITHUB_TOKEN`/`GITLAB_TOKEN` del `.env`.
33
+ resumen + un comentario por línea. Usa `GITHUB_TOKEN`/`GITLAB_TOKEN` de `.env.dai` o `.env`.
34
34
  2. **MCP del forge / `dai forge comment`** — fallback si no hay token, o si necesitas
35
35
  dejar un comentario suelto sin anclar. Pierdes el inline y la validación.
36
36
 
@@ -60,11 +60,11 @@ la forma. Nunca reescribas el formato inline.
60
60
  1. **Armar la épica** con el formato de `templates/epica.md`: metadata (`ID`, autor,
61
61
  estado, US que la componen) + objetivo + alcance (in/out) + lista de US + métricas +
62
62
  dependencias.
63
- 2. **Publicar en el tracker.** **No asumas el tracker: lee `DAI_PM` del `.env` primero.**
63
+ 2. **Publicar en el tracker.** **No asumas el tracker: lee `DAI_PM` primero**, de `.env.dai` o `.env` (dai lee los dos; `.env.dai` gana clave por clave).
64
64
  Tres caminos, mismo contenido:
65
65
  - **Con MCP** (`jira` → MCP de Atlassian · `clickup` → MCP de ClickUp): crea el ticket
66
66
  de épica; las US hijas se crean como tickets vinculados (o quedan listadas).
67
- - **Sin MCP, con token** (`DAI_PM=jira` + token en `.env`): escribe la épica como `.md`
67
+ - **Sin MCP, con token** (`DAI_PM=jira` + token en `.env.dai` o `.env`): escribe la épica como `.md`
68
68
  y publícala con **`dai publish <epica.md> --issuetype Epic`**. Devuelve el key. Si el
69
69
  proyecto exige campos propios, van con `--field alias=valor` (los declarados en
70
70
  `.dai/jira-fields.json`; `dai doctor` te los lista). Después, cada US hija se cuelga
@@ -36,7 +36,7 @@ Pull *up* from the story's "I want Y" to the problem underneath, and pressure-te
36
36
  1. **Read** the user story and the constitution.
37
37
  2. **Challenge, one axis at a time.** grill-me discipline: ask, listen, push, move on. Don't dump the five questions at once.
38
38
  3. **Reach a verdict** — `a-spec` (problem survived, go to propose), `reframe` (the real problem is different, back to `grill-user-story`), or `descartar` (not worth solving now, stop and record why).
39
- 4. **Emit** the filled `intent.md` (default location `openspec/intents/<YYYYMMDD-slug>/intent.md`; adjust to where the manifesto puts intents). Embed the tracker ID (the issue/task key — Jira or ClickUp, per `DAI_PM` in the repo's `.env`) and the source story.
39
+ 4. **Emit** the filled `intent.md` (default location `openspec/intents/<YYYYMMDD-slug>/intent.md`; adjust to where the manifesto puts intents). Embed the tracker ID (the issue/task key — Jira or ClickUp, per `DAI_PM` in the repo's `.env.dai` or `.env` — dai reads both, `.env.dai` wins) and the source story.
40
40
 
41
41
  ## Hand-off
42
42
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: grill-user-story
3
- description: "Interroga a un PO o analista funcional para producir una User Story funcional y testeable siguiendo el formato del modelo de trazabilidad — o pule una US vaga existente. Se queda a nivel funcional/usuario y se niega a derivar en diseño técnico o a emitir una US con criterios no testeables. Al terminar, PUBLICA la US en el tracker configurado del repo (Jira o ClickUp, según DAI_PM del .env) usando su MCP; si no hay MCP/token, deja un .md con el mismo formato para copiar y pegar. Invocar como /grill-user-story, opcionalmente con un título, un ID/URL del tracker, y/o una US rústica existente. Usar antes de opsx:propose, o cuando alguien dice \"necesito una US\", \"convierte esto en una US como corresponde\", o \"esta historia está muy vaga\"."
3
+ description: "Interroga a un PO o analista funcional para producir una User Story funcional y testeable siguiendo el formato del modelo de trazabilidad — o pule una US vaga existente. Se queda a nivel funcional/usuario y se niega a derivar en diseño técnico o a emitir una US con criterios no testeables. Al terminar la PUBLICA en el tracker configurado del repo (Jira o ClickUp, según DAI_PM de su .env.dai o .env): si la US es NUEVA la crea (MCP o `dai publish`); si YA tiene key la actualiza con `dai edit-us --no-editor`, que valida el formato y pregunta si sube el spec_version antes de pisar nada. Sin MCP ni token, deja un .md con el mismo formato para copiar y pegar. Invocar como /grill-user-story, opcionalmente con un título, un ID/URL del tracker, y/o una US rústica existente. Usar antes de opsx:propose, o cuando alguien dice \"necesito una US\", \"convierte esto en una US como corresponde\", o \"esta historia está muy vaga\"."
4
4
  ---
5
5
 
6
6
  # grill-user-story
@@ -31,15 +31,17 @@ Si no viene nada, pedir título y si hay un borrador existente.
31
31
 
32
32
  1. **Cortar en lo técnico.** Si la charla deriva a tablas, endpoints, migraciones o elección de framework, frenar: "eso es diseño, no ahora". La US nombra *qué necesita el usuario*, nunca *cómo se construye*.
33
33
  2. **Cortar en lo no testeable.** Nunca emitir un criterio que no pueda volverse un test. "El usuario tiene una buena experiencia" se rechaza. "Un carrito vacío no se puede finalizar" se acepta — funcional y verificable. Empujar cada AC hasta que sea observable.
34
- 3. **Publicar, no pedir.** Si el tracker está configurado (`DAI_PM=jira|clickup` con token en `.env`), **CREA el ticket tú** (por MCP o con `dai publish`). **Nunca** cierres pidiéndole al usuario que te "pase el ID/URL del ticket" como si ya existiera el ticket lo creas en este paso. Solo pides un ID existente si el usuario dijo explícitamente que está refinando una US ya creada.
34
+ 3. **Publicar, no pedir.** Si el tracker está configurado (`DAI_PM=jira|clickup` con token en `.env.dai` o `.env`), **dejá la US publicada tú**. **Nunca** cierres pidiéndole al usuario que te "pase el ID/URL del ticket" como si ya existiera, ni que copie y pegue el markdown al navegador. Dos caminos según si la US ya tiene key:
35
+ - **No tiene key → la CREÁS** (MCP o `dai publish`).
36
+ - **Ya tiene key → la ACTUALIZÁS con `dai edit-us <ID> --no-editor`**, nunca pisando el ticket a mano por MCP. El comando valida el formato antes de escribir, muestra qué cambia y pregunta si sube el `spec_version` — controles que por MCP no existen.
35
37
 
36
38
  ## Proceso
37
39
 
38
- 0. **Detectar el tracker (SIEMPRE, antes de nada).** Lee el archivo `.env` del repo y observa `DAI_PM`:
40
+ 0. **Detectar el tracker (SIEMPRE, antes de nada).** Lee la config de dai del repo y observa `DAI_PM`. **Está en `.env.dai` o en el `.env` del equipo: mirá los dos.** dai carga ambos, y si una clave está en los dos gana `.env.dai` (ADR-0017). Un repo que tiene todo en `.env` funciona igual — no hace falta migrar nada:
39
41
  - `DAI_PM=jira` → publicas en **Jira** (base: `DAI_JIRA_BASE_URL`), vía el MCP de Atlassian/Jira.
40
42
  - `DAI_PM=clickup` → publicas en **ClickUp**, vía el MCP de ClickUp.
41
- - `DAI_PM=md` (o sin `.env`) → no hay tracker: dejas la US como `.md`.
42
- **No asumas el tracker** — depende del `.env`. Si no hay `.env`, pregunta cuál usa el equipo.
43
+ - `DAI_PM=md` (o sin config) → no hay tracker: dejas la US como `.md`.
44
+ **No asumas el tracker** — depende de la config. Si no hay `.env.dai` ni `.env`, pregunta cuál usa el equipo.
43
45
  1. **Resolver inputs.** Obtener título e ID del tracker (si existe). Si se refina, leer la US y anotar — para uno mismo — qué secciones del formato faltan o están flojas: rol genérico ("el usuario"), falta el "para", sin flujo de excepción, solución prefijada, ACs vagos.
44
46
  2. **Grillar, un eje por vez.** No tirar un cuestionario — preguntar, escuchar, profundizar, y seguir. Cubrir en orden:
45
47
  - **Título** — corto, **3 a 6 palabras**, nombra la capacidad (de ahí sale la branch). Si el título es una frase larga, acórtalo: el detalle va en la descripción, no en el título. Ej: "Confirmar acciones con consecuencias", no "Confirmación deliberada antes de ejecutar acciones con consecuencias".
@@ -49,11 +51,18 @@ Si no viene nada, pedir título y si hay un borrador existente.
49
51
  - **Criterios de aceptación** — convertir cada flujo en una condición funcional y testeable, en Gherkin. Aplicar el corte #2 acá, fuerte.
50
52
  - **Fuera de scope** — qué NO hace esta historia (mata el scope creep más adelante).
51
53
  3. **Chequeo de tamaño (INVEST).** Si los flujos y ACs desbordan y la historia se dispersa, es demasiado grande para una sola US. No seguir empujando: **promoverla a una épica** con `/grill-epic` (que la parte en varias US independientes) y después volver acá para grillar cada US hija. Una US que no entra en un sprint es la señal de que hay una épica adentro.
52
- 4. **Publicar la US en el tracker (PASO OBLIGATORIO, ver abajo).** No termines con "¿la guardo y/o disparo el siguiente paso?": el trabajo de esta skill **incluye dejar la US publicada** en el tracker (con su key), no solo redactarla.
54
+ 4. **Dejar la US en el tracker (PASO OBLIGATORIO, ver abajo).** No termines con "¿la guardo y/o disparo el siguiente paso?": el trabajo de esta skill **incluye dejar la US publicada** en el tracker (con su key), no solo redactarla. Si la US ya existía, actualizarla cuenta como publicarla.
53
55
 
54
- ## Salida — publicar en el tracker (según `DAI_PM`), con fallback a .md
56
+ ## Salida — dejar la US en el tracker (según `DAI_PM`), con fallback a .md
55
57
 
56
- La US se produce UNA vez con el formato de `../../templates/formato-us.md`. Lo que cambia es **dónde se publica** lo definió el paso 0:
58
+ La US se produce UNA vez con el formato de `../../templates/formato-us.md`. Lo que cambia es **dónde va** y **si se crea o se actualiza**.
59
+
60
+ > **Primero: ¿la US ya tiene key?**
61
+ > - **No** (nació en esta sesión) → **CREAR** — pasos 1–4 de abajo.
62
+ > - **Sí** (el usuario trajo `ABC-482`, o vos la creaste antes en esta misma charla) →
63
+ > **ACTUALIZAR** — saltá a *"Si la US ya existe"*.
64
+
65
+ ### Si la US es nueva (crear)
57
66
 
58
67
  1. **Armar la US** completa: metadata de trazabilidad (`ID`, `spec_version: v1`, autor, repos esperados) + historia + contexto + casos de uso + criterios Gherkin + fuera de scope + reglas + dependencias + métricas.
59
68
  2. **Publicar en el tracker que dice `DAI_PM`:**
@@ -62,10 +71,10 @@ La US se produce UNA vez con el formato de `../../templates/formato-us.md`. Lo q
62
71
  - Confirmar al usuario con el link al ticket publicado.
63
72
  - **Importante:** los criterios SIEMPRE van bajo `## Criterios de aceptación` en la descripción — así `dai link-us`/`check` los encuentran, sin importar el tracker.
64
73
  3. **Fallback SIN MCP → publicar con el CLI (`dai publish`):**
65
- - Si NO hay MCP del tracker conectado (pero sí `DAI_PM=jira|clickup` + token en `.env`):
74
+ - Si NO hay MCP del tracker conectado (pero sí `DAI_PM=jira|clickup` + token en `.env.dai` o `.env`):
66
75
  escribe la US como `.md` (formato `formato-us.md`) y publícala con el comando:
67
76
  **`dai publish <ruta-del-md>`** → crea el issue/tarea vía REST y devuelve el key.
68
- (Jira necesita además `DAI_JIRA_PROJECT` en el `.env` — la clave del **proyecto**,
77
+ (Jira necesita además `DAI_JIRA_PROJECT` — la clave del **proyecto**,
69
78
  `PROJ`, no la de un ticket.)
70
79
  - **Si la US pertenece a una épica:** `dai publish <us.md> --parent <KEY-de-la-épica>`.
71
80
  - **Si el proyecto exige campos propios** (típico en Jira corporativo): van con
@@ -82,6 +91,45 @@ La US se produce UNA vez con el formato de `../../templates/formato-us.md`. Lo q
82
91
  atajo publica igual, pero el `## Criterios de aceptación` puede quedar mal formado y
83
92
  **el link QUÉ↔CÓMO se rompe en silencio**. Si el error nombra un campo obligatorio que
84
93
  falta, decláralo en `.dai/jira-fields.json` (molde en `.dai/templates/`) y reintenta.
94
+ ### Si la US ya existe (refinar) — `dai edit-us`
95
+
96
+ Cuando la US **ya tiene key**, el trabajo no es crearla sino **pisarla con la versión
97
+ refinada**. Eso NO se hace por MCP ni editando el ticket en el navegador: se hace con
98
+
99
+ ```bash
100
+ dai edit-us <ID> --us <ruta-del-md> --no-editor
101
+ ```
102
+
103
+ Escribí la US refinada al `.md` y pasáselo. `--no-editor` es lo que hace que el comando
104
+ sirva desde una skill: no abre `$EDITOR` (vos ya editaste), pero **conserva todo lo demás**.
105
+
106
+ Qué te da ese camino, que por MCP no existe:
107
+
108
+ 1. **Valida el formato antes de escribir.** Frena si falta el `# Título`, la sección
109
+ `## Criterios de aceptación`, o si está vacía — las tres cosas sin las cuales no hay
110
+ `ac_hash` y el link QUÉ↔CÓMO se rompe. Y **avisa** si un criterio no es Gherkin
111
+ completo o se metió en el CÓMO. Si frena, **arreglá el `.md` y reintentá**: no busques
112
+ otra vía para publicar igual, el chequeo está para eso.
113
+ 2. **Muestra qué cambia** en el tracker (título, cuántos criterios, `ac_hash`) antes de
114
+ tocarlo.
115
+ 3. **Pregunta si sube el `spec_version`** — y esta pregunta **es de la persona, no tuya**:
116
+ - **material** (un criterio nuevo, una regla distinta) → sube `v1` → `v2`, y los repos
117
+ que implementaron `v1` se marcan **atrasados** solos.
118
+ - **editorial** (typo, redacción más clara) → se queda en `v1`, nadie se marca atrasado.
119
+
120
+ Con `--yes` el comando asume material y sube la versión. **No pongas `--yes` sin
121
+ preguntar**: marcar repos como atrasados de más entrena al equipo a ignorar los ⚠️.
122
+ Preguntale al PO cuál de las dos es, y recién entonces corré con `--bump` o `--no-bump`.
123
+ 4. **Re-estampa el `ac_hash`** del `implements.yaml` si el repo ya implementaba esa US,
124
+ para que `dai check` no la marque atrasada por la propia edición.
125
+
126
+ Usá **`--dry-run` primero** para mostrarle el preview a la persona, y corré en firme
127
+ recién cuando lo apruebe. Si el backend es `md` (sin tracker), el mismo comando actualiza
128
+ el `.md` canónico.
129
+
130
+ > Si preferís no traer nada del tracker porque ya tenés la US entera escrita,
131
+ > `dai update-us <ID> --us <md>` es el mismo camino sin el paso de bajarla.
132
+
85
133
  5. **Estado.** Dejar la US en `pulida`. Ofrecer que el dev siga con `dai link-us <ID>` → `opsx:propose` (lado técnico).
86
134
 
87
135
  ## Hand-off