@dforce2055/dai 0.8.2 → 0.10.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.
- package/{.env.example → .env.dai.example} +3 -3
- package/CHANGELOG.md +81 -0
- package/README.md +41 -29
- package/VERSION +1 -1
- package/cli/dai.mjs +175 -44
- package/cli/lib/bootstrap.mjs +42 -7
- package/cli/lib/env.mjs +12 -0
- package/cli/lib/forge-api.mjs +89 -2
- package/cli/lib/pm-clickup.mjs +2 -2
- package/cli/lib/pm-jira.mjs +2 -2
- package/cli/lib/review-findings.mjs +195 -0
- package/cli/lib/skills-source.mjs +8 -0
- package/docs/EJEMPLO-END-TO-END.md +52 -43
- package/docs/MANIFIESTO.md +2 -2
- package/docs/METODOLOGIA.md +20 -15
- package/docs/PROBAR.md +13 -14
- package/docs/SCRUM-CON-IA.md +10 -10
- package/docs/adr/0003-deteccion-y-estampado-son-comandos.md +1 -1
- package/docs/adr/0006-distribucion-y-licencia.md +1 -1
- package/docs/adr/0013-skills-externas-install-from.md +11 -4
- package/docs/adr/0015-jira-corporativo.md +1 -1
- package/docs/adr/0016-review-inline.md +128 -0
- package/docs/adr/0017-env-dai.md +64 -0
- package/docs/adr/README.md +2 -0
- package/docs/detalle/01-refinamiento.md +6 -5
- package/docs/detalle/03-ramas.md +2 -2
- package/docs/detalle/04-tdd.md +15 -9
- package/docs/detalle/06-code-review.md +8 -5
- package/docs/detalle/08-daily.md +1 -1
- package/docs/detalle/README.md +1 -1
- package/docs/glosario.md +2 -2
- package/docs/guias/dev.md +10 -7
- package/docs/guias/index.md +12 -0
- package/docs/guias/lead.md +1 -1
- package/docs/guias/po.md +13 -7
- package/docs/index.md +35 -0
- package/docs/public/favicon.svg +12 -0
- package/docs/public/logo-link.svg +12 -0
- package/docs/public/logo.svg +12 -0
- package/docs/public/tutoriales/clickup-1-settings.png +0 -0
- package/docs/public/tutoriales/clickup-2-api.png +0 -0
- package/docs/public/tutoriales/clickup-3-generate-copy.png +0 -0
- package/docs/public/tutoriales/jira-1-avatar.png +0 -0
- package/docs/public/tutoriales/jira-2-seguridad-tokens.png +0 -0
- package/docs/public/tutoriales/jira-3-crear-token.png +0 -0
- package/docs/public/tutoriales/jira-4-nombre-vencimiento.png +0 -0
- package/docs/public/tutoriales/jira-5-copiar.png +0 -0
- package/docs/tutoriales/claves-ssh.md +93 -0
- package/docs/tutoriales/configurar-git.md +53 -0
- package/docs/tutoriales/index.md +18 -0
- package/docs/tutoriales/instalar-glab.md +74 -0
- package/docs/tutoriales/token-clickup.md +73 -0
- package/docs/tutoriales/token-jira.md +74 -0
- package/package.json +9 -3
- package/skills/dai-review/SKILL.md +96 -42
package/cli/lib/pm-jira.mjs
CHANGED
|
@@ -15,7 +15,7 @@ const trim = (b) => String(b || "").replace(/\/+$/, "");
|
|
|
15
15
|
// error de config más común: `dai init` deja DAI_JIRA_PROJECT vacío y quien lo completa
|
|
16
16
|
// suele pegar la épica que tiene a mano. Jira responde un 400 que no lo explica.
|
|
17
17
|
export function assertProjectKey(key) {
|
|
18
|
-
if (!key) throw new Error("falta DAI_JIRA_PROJECT en el .env (la clave del proyecto donde crear el issue).");
|
|
18
|
+
if (!key) throw new Error("falta DAI_JIRA_PROJECT en el .env.dai (la clave del proyecto donde crear el issue).");
|
|
19
19
|
const k = String(key).trim();
|
|
20
20
|
const m = k.match(/^([A-Za-z][A-Za-z0-9_]*)-\d+$/);
|
|
21
21
|
if (m) {
|
|
@@ -136,7 +136,7 @@ export function createHint(status, body = "") {
|
|
|
136
136
|
|
|
137
137
|
export function jiraAdapter(env) {
|
|
138
138
|
const base = env.DAI_JIRA_BASE_URL;
|
|
139
|
-
if (!base) throw new Error("falta DAI_JIRA_BASE_URL en el .env (backend jira).");
|
|
139
|
+
if (!base) throw new Error("falta DAI_JIRA_BASE_URL en el .env.dai (backend jira).");
|
|
140
140
|
return {
|
|
141
141
|
kind: "jira",
|
|
142
142
|
async fetchUS(id) {
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// dai · hallazgos de un review inline: parseo, validación contra el diff, filtros y render.
|
|
2
|
+
// Puro y sin red (ADR-0002: lo mecánico en el CLI, el criterio en la skill).
|
|
3
|
+
//
|
|
4
|
+
// El contrato con la skill es un `review.json`. La skill lo escribe, el humano lo lee y
|
|
5
|
+
// lo edita, y el CLI lo valida y lo postea. Ese archivo ES la puerta humana: es
|
|
6
|
+
// diff-eable, editable a mano y auditable, y no ata el flujo a ningún asistente.
|
|
7
|
+
|
|
8
|
+
export const SEVERITIES = ["low", "medium", "high"];
|
|
9
|
+
const SEV = {
|
|
10
|
+
low: { emoji: "🔵", label: "Low" },
|
|
11
|
+
medium: { emoji: "🟡", label: "Medium" },
|
|
12
|
+
high: { emoji: "🔴", label: "High" },
|
|
13
|
+
};
|
|
14
|
+
const rank = (s) => SEVERITIES.indexOf(s);
|
|
15
|
+
|
|
16
|
+
// ── Parseo del review.json ───────────────────────────────────────────────────
|
|
17
|
+
// Errores accionables: un LLM se equivoca, y "Unexpected token" no le dice dónde.
|
|
18
|
+
|
|
19
|
+
function fail(msg) {
|
|
20
|
+
throw new Error(`review.json inválido: ${msg}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function parseFindings(text) {
|
|
24
|
+
let j;
|
|
25
|
+
try {
|
|
26
|
+
j = typeof text === "string" ? JSON.parse(text) : text;
|
|
27
|
+
} catch (e) {
|
|
28
|
+
fail(`no es JSON válido (${e.message}).`);
|
|
29
|
+
}
|
|
30
|
+
if (!j || typeof j !== "object" || Array.isArray(j)) fail("la raíz tiene que ser un objeto.");
|
|
31
|
+
if (!Array.isArray(j.findings)) fail("falta el array 'findings' (puede ir vacío, pero tiene que estar).");
|
|
32
|
+
|
|
33
|
+
const findings = j.findings.map((f, i) => {
|
|
34
|
+
const at = `findings[${i}]`;
|
|
35
|
+
if (!f || typeof f !== "object") fail(`${at}: tiene que ser un objeto.`);
|
|
36
|
+
if (typeof f.path !== "string" || !f.path.trim()) fail(`${at}: falta 'path' (ruta del archivo, relativa a la raíz del repo).`);
|
|
37
|
+
if (!Number.isInteger(f.line) || f.line < 1) fail(`${at} (${f.path}): 'line' tiene que ser un entero ≥ 1, vino ${JSON.stringify(f.line)}.`);
|
|
38
|
+
if (typeof f.body !== "string" || !f.body.trim()) fail(`${at} (${f.path}:${f.line}): falta 'body' (el hallazgo, en prosa).`);
|
|
39
|
+
const side = f.side === undefined ? "RIGHT" : String(f.side).toUpperCase();
|
|
40
|
+
if (side !== "RIGHT" && side !== "LEFT") fail(`${at} (${f.path}:${f.line}): 'side' tiene que ser RIGHT o LEFT, vino ${JSON.stringify(f.side)}.`);
|
|
41
|
+
if (!SEVERITIES.includes(f.severity)) fail(`${at} (${f.path}:${f.line}): 'severity' tiene que ser ${SEVERITIES.join(" | ")}, vino ${JSON.stringify(f.severity)}.`);
|
|
42
|
+
const confidence = f.confidence === undefined ? 1 : f.confidence;
|
|
43
|
+
if (typeof confidence !== "number" || confidence < 0 || confidence > 1) {
|
|
44
|
+
fail(`${at} (${f.path}:${f.line}): 'confidence' tiene que ser un número entre 0 y 1, vino ${JSON.stringify(f.confidence)}.`);
|
|
45
|
+
}
|
|
46
|
+
return { path: f.path.trim(), line: f.line, side, severity: f.severity, confidence, body: f.body.trim() };
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
us: j.us ?? null,
|
|
51
|
+
version: j.version ?? null,
|
|
52
|
+
checkStatus: j.checkStatus ?? null,
|
|
53
|
+
dod: j.dod ?? null,
|
|
54
|
+
summary: typeof j.summary === "string" ? j.summary.trim() : "",
|
|
55
|
+
good: Array.isArray(j.good) ? j.good.filter((g) => typeof g === "string" && g.trim()) : [],
|
|
56
|
+
findings,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── Posiciones válidas según el diff ─────────────────────────────────────────
|
|
61
|
+
// El anti-alucinación. Un LLM inventa números de línea con una facilidad pasmosa, y el
|
|
62
|
+
// forge responde 422 sin decir cuál falló. Como el diff ya lo tenemos local, se verifica
|
|
63
|
+
// acá antes de salir a la red.
|
|
64
|
+
//
|
|
65
|
+
// Devuelve: Map<path, { right: Set<line>, left: Set<line> }>. Las líneas de contexto
|
|
66
|
+
// cuentan de los dos lados: el forge acepta comentarlas, son parte del hunk.
|
|
67
|
+
|
|
68
|
+
export function diffPositions(diff) {
|
|
69
|
+
const files = new Map();
|
|
70
|
+
let cur = null, newLine = 0, oldLine = 0;
|
|
71
|
+
|
|
72
|
+
for (const raw of String(diff ?? "").split(/\r?\n/)) {
|
|
73
|
+
// Ojo el orden: '+++ ' y '--- ' empiezan con '+' y '-'. Van ANTES que las de contenido.
|
|
74
|
+
if (raw.startsWith("+++ ")) {
|
|
75
|
+
const p = raw.slice(4).trim().replace(/\t.*$/, "");
|
|
76
|
+
if (p === "/dev/null") { cur = null; continue; } // archivo borrado: no hay lado derecho
|
|
77
|
+
cur = { right: new Set(), left: new Set() };
|
|
78
|
+
files.set(stripDiffPrefix(p), cur);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (raw.startsWith("--- ")) continue;
|
|
82
|
+
if (raw.startsWith("@@")) {
|
|
83
|
+
const m = raw.match(/^@@+ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
|
|
84
|
+
if (m) { oldLine = Number(m[1]); newLine = Number(m[2]); }
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (!cur) continue;
|
|
88
|
+
if (raw.startsWith("+")) cur.right.add(newLine++);
|
|
89
|
+
else if (raw.startsWith("-")) cur.left.add(oldLine++);
|
|
90
|
+
else if (raw.startsWith(" ")) { cur.right.add(newLine++); cur.left.add(oldLine++); }
|
|
91
|
+
// "", "index …", "diff --git …", "similarity …": se ignoran
|
|
92
|
+
}
|
|
93
|
+
return files;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// "b/src/x.ts" → "src/x.ts". git usa a//b/ como prefijos del diff.
|
|
97
|
+
function stripDiffPrefix(p) {
|
|
98
|
+
return p.replace(/^[ab]\//, "");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── Validación ───────────────────────────────────────────────────────────────
|
|
102
|
+
// Un hallazgo que no apunta al diff NO se postea. Se reporta, no se descarta en silencio.
|
|
103
|
+
|
|
104
|
+
export function validateFindings(findings, positions) {
|
|
105
|
+
const valid = [], rejected = [];
|
|
106
|
+
for (const f of findings) {
|
|
107
|
+
const pos = positions.get(f.path);
|
|
108
|
+
if (!pos) {
|
|
109
|
+
rejected.push({ finding: f, reason: "el archivo no aparece en el diff" });
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const lines = f.side === "LEFT" ? pos.left : pos.right;
|
|
113
|
+
if (!lines.has(f.line)) {
|
|
114
|
+
rejected.push({ finding: f, reason: `la línea ${f.line} no es parte del diff (lado ${f.side})` });
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
valid.push(f);
|
|
118
|
+
}
|
|
119
|
+
return { valid, rejected };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── Filtros ──────────────────────────────────────────────────────────────────
|
|
123
|
+
// Lo que habilita el modo desatendido con red: por debajo del umbral, el hallazgo no se
|
|
124
|
+
// postea pero se lista en el resumen. Nada se cae en silencio.
|
|
125
|
+
|
|
126
|
+
export function filterFindings(findings, { minSeverity = "low", minConfidence = 0, maxComments = Infinity } = {}) {
|
|
127
|
+
if (!SEVERITIES.includes(minSeverity)) throw new Error(`--min-severity: valores ${SEVERITIES.join(" | ")}, vino '${minSeverity}'.`);
|
|
128
|
+
const kept = [], suppressed = [];
|
|
129
|
+
// Ordenado por severidad y después por confianza: si hay tope, se cortan los menos graves.
|
|
130
|
+
const sorted = [...findings].sort((a, b) => rank(b.severity) - rank(a.severity) || b.confidence - a.confidence);
|
|
131
|
+
for (const f of sorted) {
|
|
132
|
+
if (rank(f.severity) < rank(minSeverity)) { suppressed.push({ finding: f, reason: `severidad ${f.severity} < ${minSeverity}` }); continue; }
|
|
133
|
+
if (f.confidence < minConfidence) { suppressed.push({ finding: f, reason: `confianza ${f.confidence} < ${minConfidence}` }); continue; }
|
|
134
|
+
if (kept.length >= maxComments) { suppressed.push({ finding: f, reason: `tope de --max-comments (${maxComments})` }); continue; }
|
|
135
|
+
kept.push(f);
|
|
136
|
+
}
|
|
137
|
+
return { kept, suppressed };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ── Render ───────────────────────────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
// El cuerpo de un comentario inline. Lleva marca de dai a propósito: el comentario se
|
|
143
|
+
// postea con el token del humano, así que el forge lo atribuye a él SIN badge de bot. Sin
|
|
144
|
+
// esta línea, el compañero ve un juicio sobre su código firmado por una persona, sin
|
|
145
|
+
// forma de saber que lo escribió una máquina.
|
|
146
|
+
export function renderFindingBody(f) {
|
|
147
|
+
const { emoji, label } = SEV[f.severity];
|
|
148
|
+
return `${emoji} **${label}** — ${f.body}\n\n<sub>🤖 <code>dai-review</code> · revisión asistida por IA, revisada y firmada por un humano</sub>`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const bullets = (arr) => (arr.length ? arr.map((x) => `- ${x}`).join("\n") : "- — ninguno —");
|
|
152
|
+
|
|
153
|
+
function detailsList(title, items) {
|
|
154
|
+
if (!items.length) return null;
|
|
155
|
+
const rows = items.map(({ finding: f, reason }) => `- \`${f.path}:${f.line}\` (${f.severity}) — ${reason}\n > ${f.body.split("\n")[0]}`);
|
|
156
|
+
return `<details>\n<summary>${title} (${items.length})</summary>\n\n${rows.join("\n")}\n\n</details>`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// El cuerpo del review (el "Pull request overview"). Mismo encabezado de metodología que
|
|
160
|
+
// renderReviewComment — los reviews del equipo se siguen leyendo igual.
|
|
161
|
+
export function renderReviewSummary(r, { kept = [], suppressed = [], rejected = [] } = {}) {
|
|
162
|
+
const head = r.us
|
|
163
|
+
? `**US:** \`${r.us}\`${r.version ? ` @ ${r.version}` : ""} · \`dai check\`: ${r.checkStatus || "?"}`
|
|
164
|
+
: "**US:** _(este PR no declara implementar una US)_";
|
|
165
|
+
|
|
166
|
+
const counts = SEVERITIES.slice().reverse()
|
|
167
|
+
.map((s) => ({ s, n: kept.filter((f) => f.severity === s).length }))
|
|
168
|
+
.filter(({ n }) => n > 0)
|
|
169
|
+
.map(({ s, n }) => `${n} ${SEV[s].emoji} ${SEV[s].label}`);
|
|
170
|
+
|
|
171
|
+
const hallazgos = kept.length
|
|
172
|
+
? `Dejé **${kept.length}** ${kept.length === 1 ? "comentario" : "comentarios"} en línea: ${counts.join(" · ")}.`
|
|
173
|
+
: "Sin comentarios en línea: no encontré nada concreto que marcar.";
|
|
174
|
+
|
|
175
|
+
return [
|
|
176
|
+
"## 🤖 dai-review",
|
|
177
|
+
"",
|
|
178
|
+
head,
|
|
179
|
+
r.dod ? `**Definition of Done:** ${r.dod}` : null,
|
|
180
|
+
"",
|
|
181
|
+
r.summary || null,
|
|
182
|
+
r.summary ? "" : null,
|
|
183
|
+
"### Hallazgos",
|
|
184
|
+
hallazgos,
|
|
185
|
+
"",
|
|
186
|
+
"### ✅ Lo que está bien",
|
|
187
|
+
bullets(r.good),
|
|
188
|
+
"",
|
|
189
|
+
detailsList("Suprimidos por el filtro", suppressed),
|
|
190
|
+
detailsList("Descartados: no apuntan al diff", rejected),
|
|
191
|
+
"",
|
|
192
|
+
"---",
|
|
193
|
+
"_Revisión asistida por dai. La aprobación la firma un humano (Art. 5 del manifiesto)._",
|
|
194
|
+
].filter((l) => l !== null).join("\n").replace(/\n{3,}/g, "\n\n") + "\n";
|
|
195
|
+
}
|
|
@@ -15,6 +15,14 @@ export function parseSource(src) {
|
|
|
15
15
|
const hash = raw.lastIndexOf("#");
|
|
16
16
|
if (hash > 0) { ref = raw.slice(hash + 1) || null; loc = raw.slice(0, hash); }
|
|
17
17
|
|
|
18
|
+
// Paquete npm: `npm:@scope/pkg[@version]`. dai hace `npm pack` a un temp, respetando el
|
|
19
|
+
// `.npmrc` del repo (así resuelve registries privados con scope). La versión va en el
|
|
20
|
+
// propio spec (`@1.2.3`), no como ref con '#'.
|
|
21
|
+
if (loc.startsWith("npm:")) {
|
|
22
|
+
const spec = loc.slice(4).trim();
|
|
23
|
+
if (!spec) throw new Error("fuente npm vacía (usá npm:@scope/paquete)");
|
|
24
|
+
return { type: "npm", location: spec, ref: null };
|
|
25
|
+
}
|
|
18
26
|
// Path local explícito (./ ../ / ~/).
|
|
19
27
|
if (/^(\.\.?\/|\/|~\/)/.test(loc)) return { type: "path", location: loc, ref };
|
|
20
28
|
// git por sintaxis de URL (https, ssh, scp git@host:…).
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
|
|
13
13
|
| Paso | Skill / evento | Artefacto que produce |
|
|
14
14
|
|---|---|---|
|
|
15
|
-
| 1 Refinamiento | `grill-
|
|
15
|
+
| 1 Refinamiento | `grill-user-story` → `grill-intent` (Gate 0) | la **US** (`ABC-482`) + `intent.md` |
|
|
16
16
|
| 2 Planning | `opsx:propose` | `proposal.md` + `design.md` + `tasks.md` + `specs/` |
|
|
17
17
|
| 3 Rama | `link-us` | branch + `implements.yaml` |
|
|
18
|
-
| 4
|
|
18
|
+
| 4 Implementación | `opsx:apply` (con TDD) | test (RED) → código (GREEN), por el agente |
|
|
19
19
|
| 5 Smoke | skill de smoke | escenario end-to-end verde |
|
|
20
20
|
| 6 Code review | review propio del dev → `dai pr` → partner (con `/dai-review`) | PR (código + spec) + aprobación |
|
|
21
21
|
| 7 Merge | `dai stamp` | cobertura estampada en el tracker |
|
|
@@ -37,34 +37,9 @@ ABC-482 · "Arreglar lo del carrito que a veces deja comprar de más"
|
|
|
37
37
|
|
|
38
38
|
Impresentable para implementar. Acá entra la IA.
|
|
39
39
|
|
|
40
|
-
### 1b ·
|
|
40
|
+
### 1b · `grill-user-story` produce la US
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
[`MANIFIESTO.md`](MANIFIESTO.md)).
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
IA: ¿Qué duele HOY, concretamente? "Comprar de más" es un síntoma.
|
|
47
|
-
PO: Un cliente finalizó la compra de un producto que ya no tenía stock, y quedó
|
|
48
|
-
una orden que no podemos cumplir. También pasa que finalizan con el carrito
|
|
49
|
-
vacío y se genera una orden fantasma.
|
|
50
|
-
IA: ¿Quién siente el dolor? "El sistema" no es un usuario.
|
|
51
|
-
PO: El comprador (recibe un error después de comprar) y depósito, que recibe
|
|
52
|
-
órdenes que no puede despachar.
|
|
53
|
-
IA: ¿Y si no lo hacemos? (costo de no actuar)
|
|
54
|
-
PO: Órdenes que se cancelan a mano, clientes enojados. Pasó bastante el último mes.
|
|
55
|
-
IA: La solución implícita es "validar el carrito al finalizar". ¿Es el salto correcto,
|
|
56
|
-
o el problema real es que el stock que muestra el carrito no es confiable?
|
|
57
|
-
PO: No, validar al finalizar es lo correcto: el stock ya existe, falta chequearlo
|
|
58
|
-
en el momento de la compra.
|
|
59
|
-
|
|
60
|
-
Veredicto: a-spec ✅ (problema real, usuario concreto, costo claro, sin solution-lock)
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
→ produce `openspec/intents/20260703-checkout-invalido/intent.md` con ese veredicto.
|
|
64
|
-
|
|
65
|
-
### 1c · `grill-user-story` produce la US
|
|
66
|
-
|
|
67
|
-
Con el problema validado, la skill interroga hasta que la US es **testeable por
|
|
42
|
+
Acá entra la IA: `grill-user-story` interroga al PO hasta que la US es **testeable por
|
|
68
43
|
construcción** ([`formato-us.md`](../templates/formato-us.md)) y la publica en el gestor:
|
|
69
44
|
|
|
70
45
|
```markdown
|
|
@@ -113,6 +88,33 @@ para **recibir los productos que elijo sin sorpresas**.
|
|
|
113
88
|
> Nota que la US **no dice** tablas, endpoints ni framework — solo el QUÉ. Y cada
|
|
114
89
|
> AC es un test en potencia ([Art. 3](./MANIFIESTO.md#art-3) del manifiesto).
|
|
115
90
|
|
|
91
|
+
### 1c · Gate 0 — `grill-intent` desafía el problema
|
|
92
|
+
|
|
93
|
+
Con la US ya formada, **antes** de invertir en el spec, el Gate 0 cuestiona el *problema*
|
|
94
|
+
detrás (contra el [`MANIFIESTO.md`](MANIFIESTO.md)):
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
IA: ¿Qué duele HOY, concretamente? "Comprar de más" es un síntoma.
|
|
98
|
+
PO: Un cliente finalizó la compra de un producto que ya no tenía stock, y quedó
|
|
99
|
+
una orden que no podemos cumplir. También pasa que finalizan con el carrito
|
|
100
|
+
vacío y se genera una orden fantasma.
|
|
101
|
+
IA: ¿Quién siente el dolor? "El sistema" no es un usuario.
|
|
102
|
+
PO: El comprador (recibe un error después de comprar) y depósito, que recibe
|
|
103
|
+
órdenes que no puede despachar.
|
|
104
|
+
IA: ¿Y si no lo hacemos? (costo de no actuar)
|
|
105
|
+
PO: Órdenes que se cancelan a mano, clientes enojados. Pasó bastante el último mes.
|
|
106
|
+
IA: La solución implícita es "validar el carrito al finalizar". ¿Es el salto correcto,
|
|
107
|
+
o el problema real es que el stock que muestra el carrito no es confiable?
|
|
108
|
+
PO: No, validar al finalizar es lo correcto: el stock ya existe, falta chequearlo
|
|
109
|
+
en el momento de la compra.
|
|
110
|
+
|
|
111
|
+
Veredicto: a-spec ✅ (problema real, usuario concreto, costo claro, sin solution-lock)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
→ produce `openspec/intents/20260703-checkout-invalido/intent.md` con ese veredicto. Si
|
|
115
|
+
hubiera dado *reframe* o *don't build*, la US volvería al PO **antes** de gastar un solo
|
|
116
|
+
artefacto de spec — ese es el punto del Gate 0.
|
|
117
|
+
|
|
116
118
|
---
|
|
117
119
|
|
|
118
120
|
## Paso 2 — Planning: `opsx:propose` deriva el CÓMO
|
|
@@ -175,9 +177,10 @@ autor: D. Force (dev)
|
|
|
175
177
|
|
|
176
178
|
---
|
|
177
179
|
|
|
178
|
-
## Paso 4 —
|
|
180
|
+
## Paso 4 — Implementación: el agente construye con TDD (`/opsx:apply`)
|
|
179
181
|
|
|
180
|
-
|
|
182
|
+
`/opsx:apply` implementa las tareas del change, un test a la vez. Vertical slice del AC-2
|
|
183
|
+
(el guard del carrito vacío). **Primero el test (RED):**
|
|
181
184
|
|
|
182
185
|
```typescript
|
|
183
186
|
test("un carrito vacío no se puede finalizar", async () => {
|
|
@@ -205,8 +208,9 @@ export async function finalizarCompra(id: CarritoId) {
|
|
|
205
208
|
// ▶ VERDE. Repetir el ciclo para AC-1 y AC-3.
|
|
206
209
|
```
|
|
207
210
|
|
|
208
|
-
> El test verifica por la **interfaz pública**
|
|
209
|
-
> espía lo interno. Sobrevive a un refactor
|
|
211
|
+
> El test (que escribió el agente) verifica por la **interfaz pública**
|
|
212
|
+
> (`finalizarCompra`, `ordenesDe`), no espía lo interno. Sobrevive a un refactor
|
|
213
|
+
> (Art. 7 + skill `tdd`). El dev revisa cada slice: es responsable del código.
|
|
210
214
|
|
|
211
215
|
---
|
|
212
216
|
|
|
@@ -239,21 +243,26 @@ $ dai pr --assignee mgomez
|
|
|
239
243
|
✓ PR #123 creada → …/pull/123 (base: main · US: ABC-482 @ v1 · dai check ✅)
|
|
240
244
|
```
|
|
241
245
|
|
|
242
|
-
El **partner** revisa la PR. Se apoya en la skill `/dai-review` para un primer pase
|
|
243
|
-
comentario
|
|
246
|
+
El **partner** revisa la PR. Se apoya en la skill `/dai-review` para un primer pase: deja
|
|
247
|
+
un **review inline** (resumen + un comentario anclado por línea, low/medium/high). La skill
|
|
248
|
+
le muestra el preview y **espera su OK antes de postear**; después el partner **firma**
|
|
249
|
+
aprobación o rechazo:
|
|
244
250
|
|
|
245
251
|
```
|
|
246
|
-
🤖 /dai-review
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
252
|
+
🤖 /dai-review — resumen
|
|
253
|
+
US: ABC-482 @ v1 · dai check: ✅ al día · DoD: 5/5
|
|
254
|
+
1 comentario en línea: 1 🔵 Low.
|
|
255
|
+
|
|
256
|
+
🔵 Low — src/checkout/errors.ts:12
|
|
257
|
+
CarritoVacioError y SinStockError podrían extender un DomainError común,
|
|
258
|
+
como el resto del módulo.
|
|
251
259
|
|
|
252
|
-
👤 M. Gómez (partner):
|
|
260
|
+
👤 M. Gómez (partner): reviso el preview, lo posteo, y apruebo tras el ajuste.
|
|
253
261
|
```
|
|
254
262
|
|
|
255
263
|
> El dev revisa su propio código; un partner distinto revisa la PR y **firma** (Art. 5).
|
|
256
|
-
>
|
|
264
|
+
> El review sale con el nombre y el token del partner (nunca `APPROVE` automático): la
|
|
265
|
+
> skill asiste, la persona firma.
|
|
257
266
|
|
|
258
267
|
---
|
|
259
268
|
|
|
@@ -282,7 +291,7 @@ ABC-482 · implementado por (lo estampó dai stamp)
|
|
|
282
291
|
## Paso 8 — Daily *(humano, a propósito)*
|
|
283
292
|
|
|
284
293
|
> *"Ayer cerré ABC-482, la validación del checkout con el guard de carrito. Hoy
|
|
285
|
-
>
|
|
294
|
+
> tomo ABC-490. Sin trabas."* — La IA no genera esto; el equipo se sincroniza (Art. 6).
|
|
286
295
|
|
|
287
296
|
## Paso 9 — Review / Demo
|
|
288
297
|
|
package/docs/MANIFIESTO.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> **Qué es esto.** La *constitución* de la metodología: los principios inmutables
|
|
4
4
|
> contra los que se mide toda decisión. Cuando `grill-intent` desafía un problema,
|
|
5
5
|
> lo hace contra estos artículos. Cuando dudas si algo "va con el método", la
|
|
6
|
-
> respuesta está
|
|
6
|
+
> respuesta está aquí. Es corto a propósito — un manifiesto que no se puede citar de
|
|
7
7
|
> memoria no gobierna nada.
|
|
8
8
|
>
|
|
9
9
|
> **Cómo se usa.** Es el input de constitución que las skills asumen (junto con
|
|
@@ -56,7 +56,7 @@ El *daily* y la *retro* se hacen a mano, a propósito. Son donde el equipo se
|
|
|
56
56
|
apropia del proceso y lo entiende. La IA puede darles datos; no los reemplaza.
|
|
57
57
|
|
|
58
58
|
<a id="art-7"></a>**Art. 7 — No vibe coding.**
|
|
59
|
-
No se improvisa código sobre una idea vaga. Toda implementación
|
|
59
|
+
No se improvisa código sobre una idea vaga. Toda implementación parte de una US
|
|
60
60
|
bien definida, pasa por un design, y se construye con tests. La disciplina no es
|
|
61
61
|
opcional: es lo que separa este método de "pedirle cosas a un chat".
|
|
62
62
|
|
package/docs/METODOLOGIA.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **Fuente de verdad única.** Este documento es el maestro. Los dos HTML
|
|
4
4
|
> (`desarrollo-asistido-por-ia.html` federado y `-equipos-compactos.html`) son
|
|
5
|
-
> **vistas de presentación** derivadas de
|
|
5
|
+
> **vistas de presentación** derivadas de aquí — si algo contradice a este `.md`,
|
|
6
6
|
> gana este `.md`. Versionado por git para que no driftee.
|
|
7
7
|
|
|
8
8
|
Una sola metodología para dos escalas opuestas:
|
|
@@ -57,9 +57,9 @@ identidad el día que se crea el ticket/change.
|
|
|
57
57
|
### 2.2 Formato linkeable garantizado
|
|
58
58
|
|
|
59
59
|
El QUÉ se produce con una forma mínima **testeable**: `id · spec_version · autor ·
|
|
60
|
-
criterios de aceptación en Gherkin`. Es exactamente lo que
|
|
61
|
-
`grill-
|
|
62
|
-
a qué linkear.
|
|
60
|
+
criterios de aceptación en Gherkin`. Es exactamente lo que asegura la skill
|
|
61
|
+
`grill-user-story` (y después el Gate 0 de `grill-intent` desafía el problema; ver
|
|
62
|
+
`formato-us.md`). Sin esa forma, no hay a qué linkear.
|
|
63
63
|
|
|
64
64
|
### 2.3 El link se autora una sola vez, del lado del CÓMO
|
|
65
65
|
|
|
@@ -122,8 +122,10 @@ federación.**
|
|
|
122
122
|
|
|
123
123
|
El CÓMO se construye con **test primero**, en *vertical slices* (un test → una
|
|
124
124
|
implementación → repetir), verificando por la **interfaz pública**, no espiando lo
|
|
125
|
-
interno. Un buen test lee como una spec y sobrevive a un refactor.
|
|
126
|
-
`
|
|
125
|
+
interno. Un buen test lee como una spec y sobrevive a un refactor. **Quien lo ejecuta
|
|
126
|
+
es el agente**, dentro de `/opsx:apply` (la disciplina la encapsula la skill `tdd`); el
|
|
127
|
+
dev decide qué comportamientos importa testear y es responsable de revisar el resultado
|
|
128
|
+
([Art. 7](MANIFIESTO.md#art-7)). Ver `skills/tdd/`.
|
|
127
129
|
|
|
128
130
|
---
|
|
129
131
|
|
|
@@ -134,7 +136,7 @@ duele, no antes.**
|
|
|
134
136
|
|
|
135
137
|
| | **N1 · Solo / 1 repo** | **N2 · Equipo compacto** | **N3 · Federado** |
|
|
136
138
|
|---|---|---|---|
|
|
137
|
-
| Caso típico | equipo chico
|
|
139
|
+
| Caso típico | equipo chico empezando, un dev | equipo chico | organización grande |
|
|
138
140
|
| El QUÉ vive en | `proposal.md` de OpenSpec | ClickUp (US) → el change la referencia | Jira (`ABC-###`, hub) |
|
|
139
141
|
| El link vive en | la carpeta del change (co-localizado) | `implements.yaml` en el repo | `implements.yaml` versionado |
|
|
140
142
|
| Inversa la genera | un comando local | comando / CI liviano | CI estampa cobertura + CD reporta ambiente |
|
|
@@ -178,13 +180,13 @@ El artefacto no desaparece; se aligera la ceremonia alrededor.
|
|
|
178
180
|
|
|
179
181
|
```
|
|
180
182
|
① Nace la idea → ticket vago en el PM (o intención suelta en N1)
|
|
181
|
-
②
|
|
182
|
-
③ Se pule el QUÉ → /grill-user-story → US testeable (formato-us.md),
|
|
183
|
+
② Se pule el QUÉ → /grill-user-story → US testeable (formato-us.md),
|
|
183
184
|
publicada en Jira/ClickUp (o .md si no hay MCP)
|
|
185
|
+
③ Gate 0: ¿problema OK? → /grill-intent → veredicto: a-spec / reframe / descartar
|
|
184
186
|
④ Se abre el CÓMO → /link-us ABC-### → branch + implements.yaml ligados al ID
|
|
185
187
|
⑤ Se arma el change → opsx:explore → opsx:propose (proposal/design/tasks + specs)
|
|
186
|
-
⑥ Se implementa → TDD (red
|
|
187
|
-
⑦ Se promueve → opsx:
|
|
188
|
+
⑥ Se implementa → opsx:apply → el agente aplica las tareas con TDD (red→green→refactor)
|
|
189
|
+
⑦ Se promueve → opsx:archive; el CI estampa cobertura en el PM
|
|
188
190
|
⑧ Se despliega → el CD reporta a qué ambiente (dev/test/pre/prod) fue la versión
|
|
189
191
|
```
|
|
190
192
|
|
|
@@ -203,11 +205,14 @@ El artefacto no desaparece; se aligera la ceremonia alrededor.
|
|
|
203
205
|
|
|
204
206
|
| Skill | Lado | Qué hace |
|
|
205
207
|
|---|---|---|
|
|
206
|
-
| `
|
|
208
|
+
| `doc-to-backlog` | QUÉ | Un documento (PDF/Word) → backlog candidato de épicas + US para priorizar. |
|
|
209
|
+
| `grill-epic` | QUÉ | Algo grande → una épica partida en varias US. |
|
|
207
210
|
| `grill-user-story` | QUÉ | Interroga hasta producir una US testeable (INVEST + Gherkin). Publica en Jira/ClickUp o deja `.md`. |
|
|
211
|
+
| `grill-intent` | QUÉ | Gate 0: con la US ya formada, desafía el *problema* antes de escribir spec. Veredicto: a-spec / reframe / descartar. |
|
|
208
212
|
| `link-us` | CÓMO | Crea branch + `implements.yaml` desde el ID del PM. El link, correcto por construcción. |
|
|
209
|
-
| `
|
|
210
|
-
| `
|
|
213
|
+
| `opsx:*` | CÓMO | OpenSpec: explore → propose → apply (el agente implementa con TDD) → archive. Lo provee OpenSpec. |
|
|
214
|
+
| `tdd` | CÓMO | La disciplina red-green-refactor (vertical slices, tests por interfaz pública) que aplica `opsx:apply`. |
|
|
215
|
+
| `dai-review` | CÓMO | Review inline de una PR/MR ajena: resumen + un comentario por línea, con gate humano de OK antes de postear. |
|
|
211
216
|
|
|
212
217
|
El **adaptador de PM** es un seam único: las skills del QUÉ publican en Jira **o**
|
|
213
218
|
ClickUp **o** dejan un `.md` según qué MCP/token haya. Es la **misma** skill con
|
|
@@ -228,7 +233,7 @@ de N3 queda **una** decisión sin resolver; el resto se cerró al construir la h
|
|
|
228
233
|
|
|
229
234
|
**Ya resueltas** (al construir dai):
|
|
230
235
|
- **Adaptador de PM configurable** → `getAdapter` (backends `md`/`jira`/`clickup`, token del
|
|
231
|
-
`.env`); modelo de auth en [ADR-0007](adr/0007-modelo-de-autenticacion.md).
|
|
236
|
+
`.env.dai` no versionado — [ADR-0017](adr/0017-env-dai.md)); modelo de auth en [ADR-0007](adr/0007-modelo-de-autenticacion.md).
|
|
232
237
|
- **Formato de `implements.yaml`** → congelado en [ADR-0004](adr/0004-ubicacion-y-schema-implements.md)
|
|
233
238
|
(schema + ubicación + descubrimiento por glob).
|
|
234
239
|
- **Escritura multi-repo en el tracker** → `dai stamp` deja un **comentario por repo** (no se
|
package/docs/PROBAR.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Probar dai
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
**
|
|
5
|
-
**
|
|
3
|
+
La forma más rápida de conocer dai: instala el CLI y corre el ciclo completo en tu
|
|
4
|
+
máquina. Empieza **sin credenciales** (backend `md`) para ver el flujo entero en un par de
|
|
5
|
+
minutos, y después pruébalo **contra tu tracker real** (ClickUp o Jira).
|
|
6
6
|
|
|
7
7
|
> Esto es la guía para **usar** dai por primera vez. Para verlo funcionando sobre una
|
|
8
8
|
> US real (narrado), mira [`EJEMPLO-END-TO-END.md`](EJEMPLO-END-TO-END.md).
|
|
@@ -10,19 +10,17 @@ No hace falta publicar en npm para probarlo. Se prueba local. Recomendado: hacer
|
|
|
10
10
|
## Instalar el CLI
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
npm i -g @dforce2055/dai
|
|
14
|
-
# — o, si clonaste el repo —
|
|
15
|
-
git clone https://github.com/dforce2055/dai && cd dai && npm link
|
|
16
|
-
|
|
13
|
+
npm i -g @dforce2055/dai
|
|
17
14
|
dai --version
|
|
18
15
|
```
|
|
19
16
|
|
|
20
|
-
##
|
|
17
|
+
## Paso 1 — sin credenciales (backend `md`)
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
Recorre el loop completo `link-us → check → stamp` sin red ni tokens: todo local. Perfecto
|
|
20
|
+
para ver cómo funciona antes de conectar nada.
|
|
23
21
|
|
|
24
22
|
```bash
|
|
25
|
-
# 1. Repo de prueba + bootstrap (dai init deja el .env; elige "md" cuando pregunte)
|
|
23
|
+
# 1. Repo de prueba + bootstrap (dai init deja el .env.dai; elige "md" cuando pregunte)
|
|
26
24
|
mkdir /tmp/dai-test && cd /tmp/dai-test
|
|
27
25
|
git init && git commit --allow-empty -m init
|
|
28
26
|
git remote add origin git@github.com:TU-USUARIO/dai-test.git # para los links de branch/commit
|
|
@@ -48,9 +46,9 @@ dai check # ⚠️ ATRASADO (exit 1) → sugiere: dai li
|
|
|
48
46
|
dai stamp # con md, deja .dai/us/<slug>.coverage.md
|
|
49
47
|
```
|
|
50
48
|
|
|
51
|
-
|
|
49
|
+
Con esto ya viste el ciclo entero. Ahora conéctalo a tu tracker real.
|
|
52
50
|
|
|
53
|
-
##
|
|
51
|
+
## Paso 2 — contra tu tracker real (ClickUp)
|
|
54
52
|
|
|
55
53
|
**Preparar ClickUp:**
|
|
56
54
|
|
|
@@ -62,7 +60,7 @@ Si esto anda, el flujo está bien. Pasa al tracker real.
|
|
|
62
60
|
**Config y flujo:**
|
|
63
61
|
|
|
64
62
|
```bash
|
|
65
|
-
cat > .env <<'EOF'
|
|
63
|
+
cat > .env.dai <<'EOF'
|
|
66
64
|
DAI_PM=clickup
|
|
67
65
|
DAI_CLICKUP_TOKEN=pk_XXXXXXXX
|
|
68
66
|
EOF
|
|
@@ -79,7 +77,8 @@ dai check # ⚠️ ATRASADO (lo detectó solo)
|
|
|
79
77
|
dai stamp # deja un COMENTARIO en la tarea con la cobertura
|
|
80
78
|
```
|
|
81
79
|
|
|
82
|
-
> El `.env`
|
|
80
|
+
> El `.env.dai` **no se versiona**: el token no se commitea, y el `.env` del equipo no se
|
|
81
|
+
> toca ([ADR-0017](adr/0017-env-dai.md); modelo de auth en [ADR-0007](adr/0007-modelo-de-autenticacion.md)).
|
|
83
82
|
|
|
84
83
|
## Troubleshooting
|
|
85
84
|
|
package/docs/SCRUM-CON-IA.md
CHANGED
|
@@ -49,9 +49,9 @@
|
|
|
49
49
|
|---|---|
|
|
50
50
|
| **Clásico** | El PO parte épicas en Historias de Usuario y les pone criterios de aceptación. |
|
|
51
51
|
| **Dolor** | US vagas, no testeables ("el usuario quiere un botón"). El malentendido se descubre tarde, ya implementado. |
|
|
52
|
-
| **Con IA** | `grill-
|
|
52
|
+
| **Con IA** | `grill-user-story` **interroga** al PO hasta que la US es testeable por construcción (INVEST + Gherkin); y después el Gate 0 de `grill-intent` desafía el *problema* detrás (veredicto: a-spec / reframe / descartar). La IA no *escribe* la US: la *saca a preguntas*. La US nace con ID estable y criterios hasheables. |
|
|
53
53
|
| **Humano (HITL)** | El PO responde y decide. La IA no inventa requerimientos: los pule. |
|
|
54
|
-
| **Herramienta** | `grill-
|
|
54
|
+
| **Herramienta** | `grill-user-story`, `grill-intent`, `formato-us.md` |
|
|
55
55
|
| **Detalle →** | [`detalle/01-refinamiento.md`](detalle/01-refinamiento.md) |
|
|
56
56
|
|
|
57
57
|
#### 2. Sprint Planning: comprometer US y derivar tareas
|
|
@@ -76,19 +76,19 @@
|
|
|
76
76
|
| **Clásico** | El dev crea una rama para trabajar la US. |
|
|
77
77
|
| **Dolor** | Nombres inconsistentes, ramas que no se sabe a qué US pertenecen → trazabilidad rota desde el commit uno. |
|
|
78
78
|
| **Con IA** | `link-us ABC-###` crea la rama **desde el ID de la US, sin tipearlo a mano**, y genera el `implements.yaml`. La rama *es* el link: correcto por construcción. |
|
|
79
|
-
| **Humano (HITL)** | El dev elige qué US
|
|
79
|
+
| **Humano (HITL)** | El dev elige qué US toma. |
|
|
80
80
|
| **Herramienta** | `link-us` |
|
|
81
81
|
| **Detalle →** | [`detalle/03-ramas.md`](detalle/03-ramas.md) |
|
|
82
82
|
|
|
83
|
-
#### 4. Implementación con TDD
|
|
83
|
+
#### 4. Implementación (`/opsx:apply`, con TDD)
|
|
84
84
|
|
|
85
85
|
| | |
|
|
86
86
|
|---|---|
|
|
87
87
|
| **Clásico** | El dev codea. Idealmente con tests. |
|
|
88
88
|
| **Dolor** | Se codea primero y se testea "si queda tiempo" (nunca queda). Vibe coding. |
|
|
89
|
-
| **Con IA** |
|
|
90
|
-
| **Humano (HITL)** | El dev decide qué comportamientos importa testear y revisa
|
|
91
|
-
| **Herramienta** | `tdd` |
|
|
89
|
+
| **Con IA** | El **agente implementa** con `/opsx:apply`: aplica las tareas de `opsx:propose` en *vertical slices* (un test → el código mínimo → repetir), escribiendo el test como spec ejecutable **antes** del código y verificando por la interfaz pública (la disciplina TDD la encapsula la skill `tdd`). Anti vibe-coding real. |
|
|
90
|
+
| **Humano (HITL)** | El dev decide qué comportamientos importa testear, valida cada slice y **es responsable del código** (no la IA) — lo revisa en el paso 6. |
|
|
91
|
+
| **Herramienta** | `opsx:apply` (con la disciplina de `tdd`) |
|
|
92
92
|
| **Detalle →** | [`detalle/04-tdd.md`](detalle/04-tdd.md) |
|
|
93
93
|
|
|
94
94
|
#### 5. Smoke test de la US
|
|
@@ -108,9 +108,9 @@
|
|
|
108
108
|
|---|---|
|
|
109
109
|
| **Clásico** | Un compañero revisa el PR/MR antes de mergear. |
|
|
110
110
|
| **Dolor** | Depende de que el partner tenga tiempo y ganas; reviews superficiales que dejan pasar lo importante. |
|
|
111
|
-
| **Con IA** | La IA hace el **primer pase** (
|
|
111
|
+
| **Con IA** | La IA hace el **primer pase**: un **review inline** (corre `dai check` + valida el DoD, y deja un resumen + **un comentario por línea**, low/medium/high). Muestra el preview y **espera el OK del partner antes de postear**; el partner revisa lo que importa, no el ruido. |
|
|
112
112
|
| **Humano (HITL)** | El partner aprueba o rechaza. La IA sugiere; la persona decide y firma. |
|
|
113
|
-
| **Herramienta** | `dai-review` |
|
|
113
|
+
| **Herramienta** | `dai-review` (`dai forge review`) |
|
|
114
114
|
| **Detalle →** | [`detalle/06-code-review.md`](detalle/06-code-review.md) |
|
|
115
115
|
|
|
116
116
|
#### 7. Merge + trazabilidad automática
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
| 1 Refinamiento | ●●● | La US testeable es la base de todo. |
|
|
171
171
|
| 2 Planning | ●● | El design y las tareas se derivan de la US. |
|
|
172
172
|
| 3 Rama | ●●● | El link correcto por construcción. |
|
|
173
|
-
| 4
|
|
173
|
+
| 4 Implementación | ●●● | El agente construye con TDD; el corazón del anti vibe-coding. |
|
|
174
174
|
| 5 Smoke | ●● | Cierre verificable de la US. |
|
|
175
175
|
| 6 Code review | ●● | Primer pase automático, humano decide. |
|
|
176
176
|
| 7 Merge + trazabilidad | ●●● | La matriz se deriva sola. |
|
|
@@ -49,7 +49,7 @@ Pasar de distribuido a automático es mover la invocación, no reescribir nada.
|
|
|
49
49
|
|
|
50
50
|
## Consecuencias
|
|
51
51
|
|
|
52
|
-
- ✅ **No hace falta ningún CI para empezar.** La metodología
|
|
52
|
+
- ✅ **No hace falta ningún CI para empezar.** La metodología empieza con un dev
|
|
53
53
|
tipeando `dai check` / `dai stamp`. El [Art. 14](../MANIFIESTO.md#art-14) queda respetado.
|
|
54
54
|
- ✅ **Rampa de adopción continua:** manual → git-hook → CI, siempre el mismo comando.
|
|
55
55
|
- ✅ **Honra el [Art. 10](../MANIFIESTO.md#art-10):** el humano *dispara* la derivación; no *escribe a mano* el
|
|
@@ -20,7 +20,7 @@ más fuerte de eso es el **copyleft**: quien distribuya un `dai` modificado debe
|
|
|
20
20
|
publicar sus cambios bajo la misma licencia. La GPL convierte "las mejoras vuelven"
|
|
21
21
|
en cláusula, no en deseo.
|
|
22
22
|
|
|
23
|
-
El downside típico de la GPL **no aplica**
|
|
23
|
+
El downside típico de la GPL **no aplica** aquí: `dai` es un **CLI que se ejecuta**,
|
|
24
24
|
no una librería que se embebe. Usar `dai` sobre tu código —aunque sea propietario y
|
|
25
25
|
comercial— **no genera ninguna obligación**; el copyleft solo se activa si alguien
|
|
26
26
|
**forkea y redistribuye** una versión modificada. Y *libre ≠ gratis*: se puede
|