mcp-phase-gate 2.0.1 → 2.2.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/README.es.md +146 -59
- package/README.md +130 -52
- package/build/cli.d.ts +14 -0
- package/build/cli.js +98 -0
- package/build/config.d.ts +27 -2
- package/build/config.js +151 -22
- package/build/confirmacion.d.ts +15 -0
- package/build/confirmacion.js +100 -0
- package/build/diagnostico.d.ts +83 -0
- package/build/diagnostico.js +394 -0
- package/build/index.js +28 -365
- package/build/log.d.ts +6 -0
- package/build/log.js +27 -0
- package/build/paths.d.ts +9 -0
- package/build/paths.js +71 -8
- package/build/phases.d.ts +13 -1
- package/build/phases.js +125 -17
- package/build/prompts-mcp.d.ts +33 -0
- package/build/prompts-mcp.js +194 -0
- package/build/recursos.d.ts +8 -0
- package/build/recursos.js +306 -0
- package/build/schemas.d.ts +106 -50
- package/build/schemas.js +151 -75
- package/build/servidor.d.ts +3 -0
- package/build/servidor.js +372 -0
- package/build/tasks.d.ts +8 -2
- package/build/tasks.js +218 -24
- package/package.json +1 -2
- package/prompts/comandos/f1.md +14 -0
- package/prompts/comandos/f2.md +12 -0
- package/prompts/comandos/f3.md +9 -0
- package/prompts/comandos/f4.md +10 -0
- package/prompts/comandos/f5.md +12 -0
- package/prompts/fase-1-descubrimiento.md +22 -6
- package/prompts/fase-2-decisiones.md +34 -5
- package/prompts/fase-3-plan-tecnico.md +77 -22
- package/prompts/fase-4-ejecucion.md +168 -34
- package/prompts/fase-5-auditoria-pr.md +24 -1
- package/prompts/global-rules.md +30 -1
package/build/tasks.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export type EstadoTarea = {
|
|
|
12
12
|
ultimoCambio: number | null;
|
|
13
13
|
};
|
|
14
14
|
export declare function estadoDeTarea(taskName: string, folderPath: string, phases: Map<number, PhaseFile>): EstadoTarea;
|
|
15
|
+
export declare function motivoDeProteccion(carpetaTarea: string, relativo: string, phases: Map<number, PhaseFile>, contextoInicial: string): string | null;
|
|
15
16
|
export declare function describeEstado(estado: EstadoTarea, phases: Map<number, PhaseFile>): string;
|
|
16
17
|
export declare function listarTareas(carpetaProyecto: string, phases: Map<number, PhaseFile>): EstadoTarea[];
|
|
17
18
|
export type TareaActiva = {
|
|
@@ -28,9 +29,14 @@ export type Salto = {
|
|
|
28
29
|
}[];
|
|
29
30
|
};
|
|
30
31
|
export declare function detectarSalto(fasePedida: number, estado: EstadoTarea, phases: Map<number, PhaseFile>): Salto | null;
|
|
31
|
-
export
|
|
32
|
+
export type OpcionesDeTarea = {
|
|
32
33
|
project?: string;
|
|
33
34
|
taskName?: string;
|
|
34
|
-
}
|
|
35
|
+
};
|
|
36
|
+
export declare function tareaDelPrompt(phases: Map<number, PhaseFile>, opciones?: OpcionesDeTarea): EstadoTarea | "ambigua" | null;
|
|
37
|
+
export declare function avisoDeCompuerta(fasePedida: number, phases: Map<number, PhaseFile>, opciones?: OpcionesDeTarea): string | null;
|
|
38
|
+
export declare function bloqueDeRecibos(fasePedida: number, estado: EstadoTarea, phases: Map<number, PhaseFile>): string | null;
|
|
39
|
+
export declare function tieneValidacionesPendientes(texto: string): boolean;
|
|
40
|
+
export declare function avisoDePendientes(fasePedida: number, estado: EstadoTarea, phases: Map<number, PhaseFile>): string | null;
|
|
35
41
|
export declare function avisoDeSalto(salto: Salto, taskName: string, phases: Map<number, PhaseFile>): string;
|
|
36
42
|
//# sourceMappingURL=tasks.d.ts.map
|
package/build/tasks.js
CHANGED
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
import * as fs from "fs";
|
|
15
15
|
import * as path from "path";
|
|
16
16
|
import { MODULE_DIR, stateDir, stateFile } from "./config.js";
|
|
17
|
-
import {
|
|
17
|
+
import { log } from "./log.js";
|
|
18
|
+
import { claveDeNombre, findProjectFolder, leerTextoAcotado, requireProjectFolder, tasksRootPath } from "./paths.js";
|
|
19
|
+
import { separarFrontmatter } from "./phases.js";
|
|
18
20
|
export function leerPunteros() {
|
|
19
21
|
const archivo = stateFile();
|
|
20
22
|
if (!fs.existsSync(archivo))
|
|
@@ -67,7 +69,7 @@ export function limpiarEstadoViejo() {
|
|
|
67
69
|
try {
|
|
68
70
|
if (fs.existsSync(viejo)) {
|
|
69
71
|
fs.unlinkSync(viejo);
|
|
70
|
-
|
|
72
|
+
log("info", `Estado en formato anterior eliminado: ${viejo}`);
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
catch {
|
|
@@ -120,6 +122,63 @@ export function estadoDeTarea(taskName, folderPath, phases) {
|
|
|
120
122
|
ultimoCambio,
|
|
121
123
|
};
|
|
122
124
|
}
|
|
125
|
+
// ─── Documentos congelados ──────────────────────────────────────────────────
|
|
126
|
+
// Un documento de fase se congela en cuanto una fase posterior está presente: reescribirlo
|
|
127
|
+
// después cambiaría la base sobre la que esa fase ya trabajó, sin que nada lo avise. Se puede
|
|
128
|
+
// reescribir con force, que el modelo solo usa con la confirmación explícita del usuario.
|
|
129
|
+
//
|
|
130
|
+
// Una fase está presente si existe su documento, o si declara recibos y su subcarpeta tiene al
|
|
131
|
+
// menos un .md: con varios lotes, la Fase 4 está en curso mucho antes de escribir su documento.
|
|
132
|
+
function presenciaDeFase(carpetaTarea, datos) {
|
|
133
|
+
if (datos.documento && fs.existsSync(path.join(carpetaTarea, datos.documento))) {
|
|
134
|
+
return `existe \`${datos.documento}\``;
|
|
135
|
+
}
|
|
136
|
+
if (datos.recibos) {
|
|
137
|
+
const subcarpeta = path.join(carpetaTarea, datos.recibos);
|
|
138
|
+
const entradas = fs.existsSync(subcarpeta) ? fs.readdirSync(subcarpeta, { withFileTypes: true }) : [];
|
|
139
|
+
if (entradas.some((entrada) => entrada.isFile() && entrada.name.toLowerCase().endsWith(".md"))) {
|
|
140
|
+
return `existen recibos en \`${datos.recibos}/\``;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
// Los nombres protegidos se comparan por su clave (claveDeNombre) y no como texto exacto. Sin
|
|
146
|
+
// ella un archivo nuevo esquivaba la protección: `00 - contexto inicial.md` o
|
|
147
|
+
// `04 - ejecución/L01 - x.md` en Windows y macOS, donde abren lo mismo que el nombre protegido, y
|
|
148
|
+
// en cualquier sistema un nombre en Unicode NFD, que se ve igual que el documento. En Linux, un
|
|
149
|
+
// nombre que solo difiere en mayúsculas de un documento protegido también pide force; no hace
|
|
150
|
+
// falta nunca.
|
|
151
|
+
const clave = claveDeNombre;
|
|
152
|
+
// Devuelve el motivo del rechazo, o null si se puede escribir sin force. `relativo` es la ruta
|
|
153
|
+
// dentro de la tarea, con "/" como separador (la de ubicarEnTarea).
|
|
154
|
+
export function motivoDeProteccion(carpetaTarea, relativo, phases, contextoInicial) {
|
|
155
|
+
const buscado = clave(relativo);
|
|
156
|
+
// Ni siquiera para crearlo: solo start_task lo escribe.
|
|
157
|
+
if (buscado === clave(contextoInicial)) {
|
|
158
|
+
return "El contexto inicial está siempre protegido: es el único documento que no se puede regenerar leyendo el código.";
|
|
159
|
+
}
|
|
160
|
+
const fases = [...phases.entries()].sort((a, b) => a[0] - b[0]);
|
|
161
|
+
const propia = fases.find(([, datos]) => (datos.documento && clave(datos.documento) === buscado) ||
|
|
162
|
+
(datos.recibos && buscado.startsWith(`${clave(datos.recibos)}/`)));
|
|
163
|
+
if (!propia)
|
|
164
|
+
return null; // no es un archivo de fase
|
|
165
|
+
// Crear un documento nunca pisa nada, ni siquiera cuando rellena un hueco. Se mira el nombre
|
|
166
|
+
// declarado y no el que llegó, que puede venir en otras mayúsculas. Un recibo nuevo sí se
|
|
167
|
+
// congela: con la Fase 5 presente, la ejecución ya se auditó.
|
|
168
|
+
const [numero, datos] = propia;
|
|
169
|
+
const esDocumento = datos.documento !== undefined && clave(datos.documento) === buscado;
|
|
170
|
+
if (esDocumento && !fs.existsSync(path.join(carpetaTarea, datos.documento)))
|
|
171
|
+
return null;
|
|
172
|
+
for (const [posterior, datosPosterior] of fases) {
|
|
173
|
+
if (posterior <= numero)
|
|
174
|
+
continue;
|
|
175
|
+
const presencia = presenciaDeFase(carpetaTarea, datosPosterior);
|
|
176
|
+
if (presencia) {
|
|
177
|
+
return `\`${relativo}\` está congelado: la Fase ${posterior} ya está en curso o cerrada (${presencia}).`;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
123
182
|
// Frase corta para el chat. Se le pasa el mapa de fases para poder nombrar la fase siguiente
|
|
124
183
|
// con su título real en vez de un número suelto.
|
|
125
184
|
export function describeEstado(estado, phases) {
|
|
@@ -140,13 +199,15 @@ export function describeEstado(estado, phases) {
|
|
|
140
199
|
// ─── Listado ────────────────────────────────────────────────────────────────
|
|
141
200
|
// Las tareas son las carpetas que hay bajo la subcarpeta de tareas del proyecto. Se listan
|
|
142
201
|
// del disco, así que también aparecen las que alguien creó a mano sin pasar por start_task.
|
|
202
|
+
// Las carpetas con punto no son tareas: con la subcarpeta vacía, .phase-gate/ o .git/ del
|
|
203
|
+
// proyecto cuelgan del mismo sitio que ellas.
|
|
143
204
|
export function listarTareas(carpetaProyecto, phases) {
|
|
144
205
|
const raiz = tasksRootPath(carpetaProyecto);
|
|
145
206
|
if (!fs.existsSync(raiz))
|
|
146
207
|
return [];
|
|
147
208
|
return fs
|
|
148
209
|
.readdirSync(raiz, { withFileTypes: true })
|
|
149
|
-
.filter((entrada) => entrada.isDirectory())
|
|
210
|
+
.filter((entrada) => entrada.isDirectory() && !entrada.name.startsWith("."))
|
|
150
211
|
.map((entrada) => estadoDeTarea(entrada.name, path.join(raiz, entrada.name), phases))
|
|
151
212
|
.sort((a, b) => (b.ultimoCambio ?? 0) - (a.ultimoCambio ?? 0));
|
|
152
213
|
}
|
|
@@ -181,34 +242,20 @@ export function detectarSalto(fasePedida, estado, phases) {
|
|
|
181
242
|
.map(([numero, datos]) => ({ fase: numero, documento: datos.documento }));
|
|
182
243
|
return { fasePedida, siguienteFase: estado.siguienteFase, faltan };
|
|
183
244
|
}
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
//
|
|
189
|
-
// Quién hace este trabajo es la decisión de diseño de la compuerta: lo hace el servidor, no el
|
|
190
|
-
// modelo. El puntero por proyecto ya está guardado aquí, así que pedírselo al modelo sería
|
|
191
|
-
// trabajo de más y una vía de fricción — si no lograra deducirlo acabaría preguntándoselo al
|
|
192
|
-
// usuario en cada fase, y escribir "/f4" para que te contesten "¿en qué tarea estamos?" es peor
|
|
193
|
-
// que no tener compuerta.
|
|
194
|
-
//
|
|
195
|
-
// `project` y `taskName` son solo anulación manual, para comprobar contra una tarea distinta de
|
|
196
|
-
// la activa. Sin ellos se usa el puntero; y cuando de verdad no se puede saber se dice en voz
|
|
197
|
-
// alta, en vez de callar como si todo estuviera en orden.
|
|
198
|
-
export function avisoDeCompuerta(fasePedida, phases, opciones = {}) {
|
|
245
|
+
// La tarea de la que habla una llamada a get_phase_prompt: la indicada, o la activa del proyecto.
|
|
246
|
+
// Sin proyecto solo se puede resolver si hay exactamente un candidato con tarea activa; con
|
|
247
|
+
// varios, se devuelve "ambigua" para que quien llama lo diga en vez de callar.
|
|
248
|
+
export function tareaDelPrompt(phases, opciones = {}) {
|
|
199
249
|
let carpetaProyecto;
|
|
200
250
|
if (opciones.project) {
|
|
201
251
|
carpetaProyecto = requireProjectFolder(opciones.project);
|
|
202
252
|
}
|
|
203
253
|
else {
|
|
204
|
-
// Sin proyecto solo se puede resolver si hay exactamente un candidato con tarea activa.
|
|
205
254
|
const conPuntero = Object.keys(leerPunteros());
|
|
206
255
|
if (conPuntero.length === 0)
|
|
207
256
|
return null; // Nada empezado: no hay nada que comprobar.
|
|
208
|
-
if (conPuntero.length > 1)
|
|
209
|
-
return
|
|
210
|
-
"proyectos con tarea activa. Pasa `project` para que se compruebe.");
|
|
211
|
-
}
|
|
257
|
+
if (conPuntero.length > 1)
|
|
258
|
+
return "ambigua";
|
|
212
259
|
const encontrada = findProjectFolder(conPuntero[0]);
|
|
213
260
|
if (!encontrada)
|
|
214
261
|
return null; // Puntero de un proyecto que ya no existe: se ignora.
|
|
@@ -217,11 +264,158 @@ export function avisoDeCompuerta(fasePedida, phases, opciones = {}) {
|
|
|
217
264
|
const estado = opciones.taskName
|
|
218
265
|
? listarTareas(carpetaProyecto, phases).find((tarea) => tarea.taskName === opciones.taskName)
|
|
219
266
|
: tareaActiva(carpetaProyecto, phases)?.estado;
|
|
267
|
+
return estado ?? null; // null: proyecto sin tareas, o un nombre de tarea que no existe.
|
|
268
|
+
}
|
|
269
|
+
export function avisoDeCompuerta(fasePedida, phases, opciones = {}) {
|
|
270
|
+
const estado = tareaDelPrompt(phases, opciones);
|
|
271
|
+
if (estado === "ambigua") {
|
|
272
|
+
return ("> Nota: no se comprobó la compuerta de fase porque no se indicó `project` y hay varios " +
|
|
273
|
+
"proyectos con tarea activa. Pasa `project` para que se compruebe.");
|
|
274
|
+
}
|
|
220
275
|
if (!estado)
|
|
221
|
-
return null;
|
|
276
|
+
return null;
|
|
222
277
|
const salto = detectarSalto(fasePedida, estado, phases);
|
|
223
278
|
return salto ? avisoDeSalto(salto, estado.taskName, phases) : null;
|
|
224
279
|
}
|
|
280
|
+
// ─── Recibos que ya existen ─────────────────────────────────────────────────
|
|
281
|
+
// Ninguna tool lista una carpeta, así que al retomar la Fase 4 en otro chat, o al auditar en la
|
|
282
|
+
// Fase 5 sin índice, el modelo no tenía cómo saber qué recibos hay ni cómo se llaman sus slugs.
|
|
283
|
+
// Se los da el servidor al final del prompt de la fase que los declara y de las posteriores.
|
|
284
|
+
export function bloqueDeRecibos(fasePedida, estado, phases) {
|
|
285
|
+
const lineas = [];
|
|
286
|
+
for (const [numero, datos] of [...phases.entries()].sort((a, b) => a[0] - b[0])) {
|
|
287
|
+
if (!datos.recibos || numero > fasePedida)
|
|
288
|
+
continue;
|
|
289
|
+
const subcarpeta = path.join(estado.folderPath, datos.recibos);
|
|
290
|
+
if (!fs.existsSync(subcarpeta))
|
|
291
|
+
continue;
|
|
292
|
+
const recibos = fs
|
|
293
|
+
.readdirSync(subcarpeta, { withFileTypes: true })
|
|
294
|
+
.filter((entrada) => entrada.isFile() && entrada.name.toLowerCase().endsWith(".md"))
|
|
295
|
+
.map((entrada) => entrada.name)
|
|
296
|
+
.sort();
|
|
297
|
+
for (const recibo of recibos)
|
|
298
|
+
lineas.push(`- Fase ${numero}: \`${datos.recibos}/${recibo}\``);
|
|
299
|
+
}
|
|
300
|
+
if (lineas.length === 0)
|
|
301
|
+
return null;
|
|
302
|
+
return [
|
|
303
|
+
`## RECIBOS QUE YA EXISTEN EN LA TAREA "${estado.taskName}"`,
|
|
304
|
+
"",
|
|
305
|
+
"Léelos con `read_central_doc` usando estas rutas tal cual:",
|
|
306
|
+
...lineas,
|
|
307
|
+
].join("\n");
|
|
308
|
+
}
|
|
309
|
+
// ─── Pendientes heredados ───────────────────────────────────────────────────
|
|
310
|
+
// Refuerzo del servidor para la regla global PENDIENTES HEREDADOS (#39): si la fase anterior dejó
|
|
311
|
+
// algo sin comprobar, el prompt de la siguiente llega con un aviso que lo nombra. Lee encabezados
|
|
312
|
+
// y frontmatter de documentos que el prompt ya obliga a escribir; no guarda estado ni cambia la
|
|
313
|
+
// deducción de fase. Si el modelo no escribió la sección, el aviso no sale y todo sigue igual.
|
|
314
|
+
//
|
|
315
|
+
// No contradice el rechazo de la #39 a un campo `Estado:`: aquello era hacer depender la
|
|
316
|
+
// compuerta de algo escrito por el modelo. Esto no bloquea ni cambia la fase: solo señala.
|
|
317
|
+
const ENCABEZADO_PENDIENTES = "## Validaciones pendientes";
|
|
318
|
+
// Lo que se escribe por costumbre en una sección que no tiene nada: no es un pendiente.
|
|
319
|
+
const SECCION_VACIA = new Set([
|
|
320
|
+
"",
|
|
321
|
+
"ninguna",
|
|
322
|
+
"ninguno",
|
|
323
|
+
"ninguna pendiente",
|
|
324
|
+
"nada",
|
|
325
|
+
"sin pendientes",
|
|
326
|
+
"no hay pendientes",
|
|
327
|
+
"fase cerrada sin pendientes",
|
|
328
|
+
"n/a",
|
|
329
|
+
"no aplica",
|
|
330
|
+
]);
|
|
331
|
+
// La sección reducida a su texto: sin viñetas ni numeración, sin énfasis de Markdown (_ * `),
|
|
332
|
+
// sin puntos suspensivos ni punto final, en una línea y en minúsculas. Así "- Ninguna.",
|
|
333
|
+
// "_Ninguna._" o "Ninguna..." cuentan igual que "Ninguna".
|
|
334
|
+
function textoDeSeccion(seccion) {
|
|
335
|
+
return seccion
|
|
336
|
+
.split("\n")
|
|
337
|
+
.map((linea) => linea.replace(/^\s*(?:[-*+]|\d+[.)])\s+/, ""))
|
|
338
|
+
.join(" ")
|
|
339
|
+
.replace(/[_*`]/g, "")
|
|
340
|
+
.replace(/\s+/g, " ")
|
|
341
|
+
.trim()
|
|
342
|
+
.replace(/(?:\.+|…)$/, "")
|
|
343
|
+
.trim()
|
|
344
|
+
.toLowerCase();
|
|
345
|
+
}
|
|
346
|
+
const ESTADOS_PENDIENTES = new Set(["con pendientes", "fallido", "descartado"]);
|
|
347
|
+
// La sección es el texto entre su encabezado y el siguiente "## " (o el final). Devuelve true si
|
|
348
|
+
// existe y tiene algo que no sea una forma de decir "ninguna".
|
|
349
|
+
export function tieneValidacionesPendientes(texto) {
|
|
350
|
+
const lineas = separarFrontmatter(texto).cuerpo.split("\n");
|
|
351
|
+
// El encabezado se compara sin distinguir mayúsculas: "## Validaciones Pendientes" también vale.
|
|
352
|
+
const buscado = ENCABEZADO_PENDIENTES.toLowerCase();
|
|
353
|
+
const inicio = lineas.findIndex((linea) => linea.trimEnd().normalize("NFC").toLowerCase() === buscado);
|
|
354
|
+
if (inicio === -1)
|
|
355
|
+
return false;
|
|
356
|
+
const resto = lineas.slice(inicio + 1);
|
|
357
|
+
const fin = resto.findIndex((linea) => linea.startsWith("## "));
|
|
358
|
+
const seccion = (fin === -1 ? resto : resto.slice(0, fin)).join("\n");
|
|
359
|
+
return !SECCION_VACIA.has(textoDeSeccion(seccion));
|
|
360
|
+
}
|
|
361
|
+
function leerSinFallar(ruta) {
|
|
362
|
+
try {
|
|
363
|
+
return leerTextoAcotado(ruta);
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
return null; // un documento ilegible no es un pendiente: el aviso es una ayuda, no un control
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
export function avisoDePendientes(fasePedida, estado, phases) {
|
|
370
|
+
const ordenadas = [...phases.entries()].sort((a, b) => a[0] - b[0]);
|
|
371
|
+
const lineas = [];
|
|
372
|
+
// La fase anterior es la de mayor número, por debajo de la pedida, cuyo documento existe.
|
|
373
|
+
const anterior = ordenadas
|
|
374
|
+
.filter(([numero, datos]) => numero < fasePedida && datos.documento)
|
|
375
|
+
.reverse()
|
|
376
|
+
.find(([, datos]) => fs.existsSync(path.join(estado.folderPath, datos.documento)));
|
|
377
|
+
if (anterior) {
|
|
378
|
+
const documento = anterior[1].documento;
|
|
379
|
+
const texto = leerSinFallar(path.join(estado.folderPath, documento));
|
|
380
|
+
if (texto !== null && tieneValidacionesPendientes(texto)) {
|
|
381
|
+
lineas.push(`- \`${documento}\` tiene una sección \`${ENCABEZADO_PENDIENTES}\`.`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// Los recibos de lote de las fases anteriores que quedaron con pendientes, fallidos o
|
|
385
|
+
// descartados. Con un solo lote, el recibo es el documento de la fase y se mira también.
|
|
386
|
+
for (const [numero, datos] of ordenadas) {
|
|
387
|
+
if (numero >= fasePedida || !datos.recibos)
|
|
388
|
+
continue;
|
|
389
|
+
const candidatos = [];
|
|
390
|
+
if (datos.documento)
|
|
391
|
+
candidatos.push(datos.documento);
|
|
392
|
+
const subcarpeta = path.join(estado.folderPath, datos.recibos);
|
|
393
|
+
if (fs.existsSync(subcarpeta)) {
|
|
394
|
+
for (const entrada of fs.readdirSync(subcarpeta, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
395
|
+
if (entrada.isFile() && entrada.name.toLowerCase().endsWith(".md"))
|
|
396
|
+
candidatos.push(`${datos.recibos}/${entrada.name}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
for (const relativo of candidatos) {
|
|
400
|
+
const ruta = path.join(estado.folderPath, ...relativo.split("/"));
|
|
401
|
+
if (!fs.existsSync(ruta))
|
|
402
|
+
continue;
|
|
403
|
+
const texto = leerSinFallar(ruta);
|
|
404
|
+
const valor = texto === null ? "" : (separarFrontmatter(texto).meta.estado ?? "").trim().toLowerCase();
|
|
405
|
+
if (ESTADOS_PENDIENTES.has(valor))
|
|
406
|
+
lineas.push(`- \`${relativo}\`: estado \`${valor}\`.`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (lineas.length === 0)
|
|
410
|
+
return null;
|
|
411
|
+
return [
|
|
412
|
+
"## PENDIENTES HEREDADOS (detectados por el servidor)",
|
|
413
|
+
"",
|
|
414
|
+
...lineas,
|
|
415
|
+
"",
|
|
416
|
+
"Nómbraselos al usuario antes de empezar (ver PENDIENTES HEREDADOS en las reglas globales).",
|
|
417
|
+
].join("\n");
|
|
418
|
+
}
|
|
225
419
|
export function avisoDeSalto(salto, taskName, phases) {
|
|
226
420
|
const titulo = phases.get(salto.siguienteFase)?.title;
|
|
227
421
|
const vaEn = titulo ? `Fase ${salto.siguienteFase} (${titulo})` : `Fase ${salto.siguienteFase}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-phase-gate",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Servidor MCP que parte cada requerimiento en cinco fases con compuerta y obliga a que cada una deje su documento en una documentación central.",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -43,7 +43,6 @@
|
|
|
43
43
|
"homepage": "https://github.com/Montse2308/mcp-phase-gate#readme",
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
46
|
-
"dotenv": "^17.4.2",
|
|
47
46
|
"zod": "^4.4.3"
|
|
48
47
|
},
|
|
49
48
|
"devDependencies": {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Vas a iniciar la FASE 1 (Descubrimiento) del flujo del mcp-phase-gate.
|
|
2
|
+
|
|
3
|
+
Datos de la tarea (tómalos del texto que escribí después del comando; si falta alguno, pregúntame antes de seguir):
|
|
4
|
+
- project: uno de los proyectos que ya existen. Si no lo reconoce, `start_task` te devuelve la lista; no inventes uno ni pases `crear_proyecto` salvo que yo te diga explícitamente que quiero estrenar un proyecto nuevo.
|
|
5
|
+
- task_name: nombre corto de la tarea
|
|
6
|
+
- initial_context: el correo / mensaje / requerimiento completo
|
|
7
|
+
|
|
8
|
+
Pasos:
|
|
9
|
+
1. Llama a la tool `start_task` del mcp-phase-gate con project, task_name e initial_context.
|
|
10
|
+
2. Llama a `get_phase_prompt(1)` indicándole también el `project` en el que estoy trabajando —con eso comprueba que no me esté saltando una fase— y asume ese rol de comportamiento.
|
|
11
|
+
3. Analiza el código actual de este repositorio para entender el impacto del requerimiento.
|
|
12
|
+
4. Guarda el análisis técnico y funcional con `write_central_doc`, usando el nombre de archivo exacto que te indicó `get_phase_prompt`.
|
|
13
|
+
|
|
14
|
+
No escribas código de producción todavía.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Vas a iniciar la FASE 2 (Decisiones) del flujo del mcp-phase-gate.
|
|
2
|
+
|
|
3
|
+
Pasos:
|
|
4
|
+
1. Si no sabes cuál es la tarea activa, llama a `get_active_task` (o pídeme project + task_name).
|
|
5
|
+
2. Llama a `get_phase_prompt(2)` indicándole también el `project` en el que estoy trabajando —con eso comprueba que no me esté saltando una fase— y asume ese rol.
|
|
6
|
+
3. Lee el documento de la Fase 1 con `read_central_doc`; `get_phase_prompt` te dio su nombre exacto.
|
|
7
|
+
4. Identifica lagunas técnicas, riesgos y dependencias con el código existente.
|
|
8
|
+
5. Hazme la lista de preguntas críticas de decisión, explicándome las implicaciones de cada una.
|
|
9
|
+
6. Detente y espera mis respuestas. No escribas código aún.
|
|
10
|
+
7. Cuando ya te haya respondido: valida cada decisión contra el código real antes de darla por cerrada y guarda el documento de la fase con `write_central_doc`, con el nombre que te indicó `get_phase_prompt` y la estructura que pide el prompt.
|
|
11
|
+
|
|
12
|
+
El documento de la Fase 2 es obligatorio, aunque las decisiones se hayan tomado conversando: el chat no viaja entre dispositivos, el documento sí.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Vas a iniciar la FASE 3 (Plan Técnico) del flujo del mcp-phase-gate.
|
|
2
|
+
|
|
3
|
+
Pasos:
|
|
4
|
+
1. Si no sabes la tarea activa, llama a `get_active_task` (o pídeme project + task_name).
|
|
5
|
+
2. Llama a `get_phase_prompt(3)` indicándole también el `project` en el que estoy trabajando —con eso comprueba que no me esté saltando una fase— y asume ese rol.
|
|
6
|
+
3. Lee los documentos de las fases anteriores con `read_central_doc`; `get_phase_prompt` te dio sus nombres exactos.
|
|
7
|
+
4. Comprueba contra el código real lo que el plan va a afirmar, ANTES de escribirlo.
|
|
8
|
+
5. Con las decisiones ya tomadas, genera el plan con su tabla de lotes, respetando "Cero Rupturas".
|
|
9
|
+
6. Guárdalo con `write_central_doc`, con el nombre que te indicó `get_phase_prompt`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Vas a iniciar la FASE 4 (Ejecución) del flujo del mcp-phase-gate.
|
|
2
|
+
|
|
3
|
+
Pasos:
|
|
4
|
+
1. Si no sabes la tarea activa, llama a `get_active_task` (o pídeme project + task_name).
|
|
5
|
+
2. Llama a `get_phase_prompt(4)` indicándole también el `project` en el que estoy trabajando —con eso comprueba que no me esté saltando una fase— y asume ese rol.
|
|
6
|
+
3. Lee el plan técnico de la Fase 3 con `read_central_doc` —`get_phase_prompt` te dio su nombre exacto— y nómbrame sus pendientes antes de tocar código.
|
|
7
|
+
4. Ejecútalo **lote por lote**, como indica el prompt: implementa un lote, verifícalo ejecutando, escribe su recibo, muéstrame archivos y mensaje de commit, y con mi OK haz el commit antes de abrir el siguiente. Nunca hagas push.
|
|
8
|
+
5. Con un solo lote, su recibo es el documento de la fase. Con más de uno, el documento de la fase es el índice y se escribe solo al cerrar el último.
|
|
9
|
+
|
|
10
|
+
El entregable de esta fase es el código funcionando, lote por lote; cada recibo es corto y se escribe al cerrar su lote.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Vas a iniciar la FASE 5 (Auditoría / Pre-PR) del flujo del mcp-phase-gate.
|
|
2
|
+
|
|
3
|
+
Commits a auditar: si escribí un rango después del comando, usa ese. Si no, tómalos de los recibos de la Fase 4. Si tampoco hay recibos, pregúntame cuáles antes de seguir.
|
|
4
|
+
|
|
5
|
+
Pasos:
|
|
6
|
+
1. Si no sabes la tarea activa, llama a `get_active_task` (o pídeme project + task_name): la auditoría se guarda en su carpeta.
|
|
7
|
+
2. Llama a `get_phase_prompt(5)` indicándole también el `project` en el que estoy trabajando —con eso comprueba que no me esté saltando una fase— y asume ese rol de auditor externo.
|
|
8
|
+
3. Inspecciona los commits indicados usando SOLO lectura (git diff / git log de solo lectura, leer archivos). Nunca hagas commits, push ni subas PRs; de eso me encargo yo.
|
|
9
|
+
4. Entrégame en el chat:
|
|
10
|
+
- La AUDITORÍA con el formato del revisor, dejando clarísimo si hay cambios BLOQUEANTES para subir el PR.
|
|
11
|
+
- La DESCRIPCIÓN del PR (título + cuerpo), contrastada contra el código real.
|
|
12
|
+
5. Guarda el documento de la fase con `write_central_doc`, con el nombre que te indicó `get_phase_prompt`: lleva la auditoría y la descripción del PR tal cual me las entregaste.
|
|
@@ -6,12 +6,14 @@ documento_inicial: 00 - Contexto Inicial.md
|
|
|
6
6
|
# Descubrimiento
|
|
7
7
|
|
|
8
8
|
## ROL DE COMPORTAMIENTO (GLOBAL)
|
|
9
|
+
<!-- ranura: rol -->
|
|
9
10
|
Eres un Arquitecto de Software y Analista de Sistemas Senior. Tu enfoque es entender el problema al 100% antes de proponer soluciones definitivas. Tu prioridad es la investigación profunda y el pensamiento crítico.
|
|
11
|
+
<!-- /ranura -->
|
|
10
12
|
|
|
11
13
|
## REGLAS DE LA FASE 1 (DESCUBRIMIENTO)
|
|
12
14
|
1. NO escribas código de producción.
|
|
13
15
|
2. Lee el código real y la documentación central antes de opinar. Cada afirmación del
|
|
14
|
-
documento
|
|
16
|
+
documento se ancla en la evidencia que definen las reglas globales (EVIDENCIA).
|
|
15
17
|
3. **La investigación se hace AQUÍ, no se difiere.** Ver la sección VERIFICACIÓN.
|
|
16
18
|
4. Genera el documento de análisis en la carpeta de la tarea (ej. `01 - Análisis Técnico.md`)
|
|
17
19
|
siguiendo el contrato de estructura de abajo.
|
|
@@ -31,17 +33,19 @@ No entrega plan de implementación.
|
|
|
31
33
|
| Listar las decisiones que hay que tomar | Tomar esas decisiones → **Fase 2** |
|
|
32
34
|
|
|
33
35
|
La línea es esta: **describir lo que existe es Fase 1; comprometerse a lo que se va a
|
|
34
|
-
cambiar es Fase 3.** Nombrar `
|
|
36
|
+
cambiar es Fase 3.** Nombrar `src/facturacion/calculo.ts:112` explicando qué hace ahí es
|
|
35
37
|
Fase 1. Poner ese mismo archivo bajo un encabezado "archivos a corregir" ya es Fase 3.
|
|
36
38
|
|
|
37
39
|
---
|
|
38
40
|
|
|
39
41
|
## VERIFICACIÓN: HAZLA EN ESTA FASE
|
|
40
42
|
|
|
43
|
+
<!-- ranura: verificacion -->
|
|
41
44
|
Si una hipótesis se puede comprobar ejecutando algo, **ejecútalo ahora** y reporta la
|
|
42
45
|
salida real: una consulta contra una réplica de solo lectura, una sesión de consola del
|
|
43
46
|
framework, recuperar una versión anterior de un archivo con `git show` para comparar
|
|
44
47
|
comportamientos, una medición cronometrada.
|
|
48
|
+
<!-- /ranura -->
|
|
45
49
|
|
|
46
50
|
Un diagnóstico medido vale mucho más que uno argumentado. Descartar una hipótesis
|
|
47
51
|
"porque no tiene sentido" no sirve; descartarla porque la mediste sí.
|
|
@@ -71,9 +75,13 @@ confiesa su propio error de método es en el que se puede confiar.
|
|
|
71
75
|
está cada consumidor. Una tabla suele ser lo más claro.
|
|
72
76
|
6. **Lo que NO se debe tocar**, con el porqué de cada punto. Es la regla de Cero Rupturas
|
|
73
77
|
hecha sección: si algo es tentador de cambiar pero es compartido, dilo aquí.
|
|
74
|
-
7. **Decisiones para la Fase 2.** Formato `D1`, `D2`, …
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
7. **Decisiones para la Fase 2.** Formato `D1`, `D2`, … La Fase 2 debe poder resolverse
|
|
79
|
+
escogiendo, no redactando. Cada decisión:
|
|
80
|
+
|
|
81
|
+
### D<n> — <tema>
|
|
82
|
+
- **Opciones:** (a) … (b) … (c) …
|
|
83
|
+
- **Recomendación:** … (marcada como recomendación, no como decisión)
|
|
84
|
+
- **Base:** ver BASE DE CADA DECISIÓN en las reglas globales.
|
|
77
85
|
|
|
78
86
|
### Secciones condicionales
|
|
79
87
|
|
|
@@ -88,7 +96,7 @@ documento sale corto solo: así es como se ajusta la profundidad, no recortando
|
|
|
88
96
|
| Cómo se verificó / metodología | hubo medición o ejecución. Debe ser reproducible por otro |
|
|
89
97
|
| Requisitos funcionales (`RF1..RFn`) | es funcionalidad nueva y el requerimiento es difuso |
|
|
90
98
|
| Opciones evaluadas | hay más de un camino viable |
|
|
91
|
-
| Validaciones pendientes | no hubo acceso para comprobar algo. Es una laguna
|
|
99
|
+
| Validaciones pendientes | no hubo acceso para comprobar algo, o una medición sale de una fuente cuya frescura no confirmaste. Es una laguna: va con el encabezado exacto `## Validaciones pendientes` y se dice también en el mensaje de cierre |
|
|
92
100
|
| Deuda técnica fuera de alcance | encontraste algo real pero ajeno al ticket |
|
|
93
101
|
| Predicción verificable | el diagnóstico implica algo futuro comprobable |
|
|
94
102
|
|
|
@@ -107,6 +115,7 @@ documento sale corto solo: así es como se ajusta la profundidad, no recortando
|
|
|
107
115
|
- Cuando descartes una opción, **reconoce primero su ventaja** y luego explica qué la mata.
|
|
108
116
|
Una opción descartada sin argumento parece un descuido.
|
|
109
117
|
- Si algo va a morder a quien implemente, déjaselo escrito de forma directa.
|
|
118
|
+
- Si la fase cierra con lagunas, dilo en el mensaje de cierre al usuario, no solo dentro de `## Validaciones pendientes`. Quien lea solo el chat tiene que enterarse.
|
|
110
119
|
|
|
111
120
|
---
|
|
112
121
|
|
|
@@ -121,11 +130,14 @@ documento sale corto solo: así es como se ajusta la profundidad, no recortando
|
|
|
121
130
|
la Fase 2 vive en el documento de la Fase 2, no aquí.
|
|
122
131
|
- Rellenar secciones condicionales que no aplican, con tal de que el documento se vea completo.
|
|
123
132
|
- Inventar números o estimar tiempos que no mediste.
|
|
133
|
+
- Una decisión `D<n>` sin línea **Base**, o marcada `medido` sin fuente y vigencia.
|
|
134
|
+
- Cerrar la fase con lagunas sin decirlo en el mensaje de cierre al usuario.
|
|
124
135
|
|
|
125
136
|
---
|
|
126
137
|
|
|
127
138
|
## MARCAS DE CALIDAD
|
|
128
139
|
|
|
140
|
+
<!-- ranura: ejemplos -->
|
|
129
141
|
Así se ve un análisis que sirve. Son ejemplos reales de documentos aprobados.
|
|
130
142
|
|
|
131
143
|
**El veredicto arriba y con número, no con adjetivos:**
|
|
@@ -152,6 +164,7 @@ Así se ve un análisis que sirve. Son ejemplos reales de documentos aprobados.
|
|
|
152
164
|
**Una predicción falsable:**
|
|
153
165
|
> "Mejorará solo durante el mes conforme se completen los registros, y volverá a colapsar
|
|
154
166
|
> el día 5 del mes siguiente."
|
|
167
|
+
<!-- /ranura -->
|
|
155
168
|
|
|
156
169
|
---
|
|
157
170
|
|
|
@@ -165,3 +178,6 @@ Así se ve un análisis que sirve. Son ejemplos reales de documentos aprobados.
|
|
|
165
178
|
6. ¿Me metí en terreno de la Fase 3 (archivos a modificar, criterios de aceptación) o de
|
|
166
179
|
la Fase 4 (pasos de prueba)?
|
|
167
180
|
7. ¿Las decisiones de la Fase 2 tienen opciones concretas y una recomendación marcada?
|
|
181
|
+
8. ¿Cada `D<n>` tiene su línea **Base**, y ninguna dice `medido` sin fuente y vigencia?
|
|
182
|
+
9. ¿Las recomendaciones con base `inferido` dicen qué las confirmaría o las refutaría?
|
|
183
|
+
10. Si la fase cerró con lagunas, ¿están bajo `## Validaciones pendientes` y las dije en el mensaje de cierre?
|
|
@@ -5,7 +5,9 @@ documento: 02 - Decisiones.md
|
|
|
5
5
|
# Decisiones
|
|
6
6
|
|
|
7
7
|
## ROL DE COMPORTAMIENTO (GLOBAL)
|
|
8
|
+
<!-- ranura: rol -->
|
|
8
9
|
Eres un Líder Técnico especializado en seguridad y escalabilidad. No asumes absolutamente nada. Piensas las cosas lo suficiente hasta dar con algo 100% seguro y confiable.
|
|
10
|
+
<!-- /ranura -->
|
|
9
11
|
|
|
10
12
|
## REGLAS DE LA FASE 2 (DECISIONES)
|
|
11
13
|
1. NO escribas código de producción.
|
|
@@ -20,9 +22,8 @@ Eres un Líder Técnico especializado en seguridad y escalabilidad. No asumes ab
|
|
|
20
22
|
|
|
21
23
|
## POR QUÉ EL DOCUMENTO ES OBLIGATORIO
|
|
22
24
|
|
|
23
|
-
El
|
|
24
|
-
|
|
25
|
-
conversación, en la práctica se perdió.
|
|
25
|
+
El chat no viaja entre sesiones ni entre dispositivos; el documento sí. Si una decisión solo
|
|
26
|
+
quedó dicha en la conversación, en la práctica se perdió.
|
|
26
27
|
|
|
27
28
|
Por eso: toda decisión cerrada se escribe, con su fecha. Aunque parezca obvia. Aunque se
|
|
28
29
|
haya resuelto en un intercambio de dos mensajes.
|
|
@@ -31,6 +32,12 @@ haya resuelto en un intercambio de dos mensajes.
|
|
|
31
32
|
|
|
32
33
|
## EL TRABAJO DE ESTA FASE, EN ORDEN
|
|
33
34
|
|
|
35
|
+
0. **Revisa los pendientes heredados de la Fase 1** (ver PENDIENTES HEREDADOS en las reglas
|
|
36
|
+
globales). Si su documento trae `## Validaciones pendientes`, cifras marcadas como no
|
|
37
|
+
revalidadas o una medición cuya fuente el propio documento pone en duda, díselo al usuario
|
|
38
|
+
ANTES de plantear ninguna decisión y pregúntale si se cierran primero. Una decisión tomada
|
|
39
|
+
sobre un número que la fase anterior marcó como dudoso no se puede dar por cerrada, aunque
|
|
40
|
+
el usuario responda.
|
|
34
41
|
1. **Recoge las decisiones abiertas** que dejó la Fase 1 y preséntalas con sus opciones y
|
|
35
42
|
sus implicaciones, para que el usuario pueda responder con seguridad.
|
|
36
43
|
2. **Verifica antes de cerrar.** Una respuesta del usuario no cierra la decisión: la
|
|
@@ -49,9 +56,17 @@ haya resuelto en un intercambio de dos mensajes.
|
|
|
49
56
|
|
|
50
57
|
## VALIDAR ANTES DE CERRAR
|
|
51
58
|
|
|
59
|
+
<!-- ranura: verificacion -->
|
|
52
60
|
Antes de dar por buena una decisión, comprueba lo que se pueda comprobar: consultas contra
|
|
53
61
|
una réplica de solo lectura, lectura del código del framework, barridos por el proyecto
|
|
54
62
|
buscando otros consumidores, conteos reales de volumen.
|
|
63
|
+
<!-- /ranura -->
|
|
64
|
+
|
|
65
|
+
**Empieza por las decisiones que la Fase 1 marcó como `inferido`**: son las que más
|
|
66
|
+
probablemente cambien. Las `medido` se revalidan solo si la fuente de esa medición era dudosa,
|
|
67
|
+
y en ese caso lo dices. Las `leído en código` y `documentado` se revalidan si el código o la
|
|
68
|
+
fuente pudieron cambiar desde entonces. Reverificar todo por igual gasta la fase en lo que ya
|
|
69
|
+
estaba resuelto.
|
|
55
70
|
|
|
56
71
|
Presenta esas comprobaciones en una tabla de tres columnas:
|
|
57
72
|
|
|
@@ -119,6 +134,7 @@ criterios verificables con el detalle técnico. La Fase 4 los ejecuta.
|
|
|
119
134
|
| Bug preexistente que se corrige de paso | apareció algo roto en la zona que se va a tocar |
|
|
120
135
|
| Deuda técnica fuera de alcance | encontraste algo real que se decidió no atender ahora |
|
|
121
136
|
| Traducción para quien pidió el ticket | el cambio altera una cifra que una persona no técnica usa para tomar decisiones, hace aparecer o desaparecer filas que hoy ve en pantalla, o cambia lo que esa persona ve o cómo lo interpreta. Ver la sección propia, más abajo |
|
|
137
|
+
| Validaciones pendientes | quedó algo sin comprobar o sin decidir. Encabezado exacto `## Validaciones pendientes`, y se dice también en el mensaje de cierre |
|
|
122
138
|
|
|
123
139
|
---
|
|
124
140
|
|
|
@@ -126,8 +142,8 @@ criterios verificables con el detalle técnico. La Fase 4 los ejecuta.
|
|
|
126
142
|
|
|
127
143
|
**Si la decisión cabe en una línea**, va en tabla:
|
|
128
144
|
|
|
129
|
-
| # | Tema | Decisión | Fundamento |
|
|
130
|
-
|
|
145
|
+
| # | Tema | Decisión | Fundamento | Base |
|
|
146
|
+
|---|---|---|---|---|
|
|
131
147
|
|
|
132
148
|
**Si necesita explicación**, va como bloque:
|
|
133
149
|
|
|
@@ -135,12 +151,17 @@ criterios verificables con el detalle técnico. La Fase 4 los ejecuta.
|
|
|
135
151
|
### D<n> — <tema>
|
|
136
152
|
- **Decisión:** qué se hace.
|
|
137
153
|
- **Por qué:** el fundamento, apoyado en código o en datos.
|
|
154
|
+
- **Base:** ver BASE DE CADA DECISIÓN en las reglas globales.
|
|
138
155
|
- **Implicación aceptada:** a qué se está renunciando al decidir esto.
|
|
139
156
|
```
|
|
140
157
|
|
|
141
158
|
No mezcles: agrupa primero las simples en una tabla y luego desarrolla las que lo
|
|
142
159
|
necesiten.
|
|
143
160
|
|
|
161
|
+
En esta fase, la **Base** es el fundamento con el que se comprobó que la decisión es segura,
|
|
162
|
+
no quién la tomó. Si la decisión es puramente de negocio y no hay nada técnico que comprobar,
|
|
163
|
+
la Base es `documentado (respuesta del usuario, <fecha>)`.
|
|
164
|
+
|
|
144
165
|
---
|
|
145
166
|
|
|
146
167
|
## LA IMPLICACIÓN ACEPTADA
|
|
@@ -199,11 +220,15 @@ Reglas propias de esta sección:
|
|
|
199
220
|
- Dar la fase por cerrada con decisiones críticas abiertas sin decirlo explícitamente.
|
|
200
221
|
- Escribir la traducción para quien pidió el ticket con jerga técnica, o rellenarla por
|
|
201
222
|
completitud cuando su disparador no se cumplió.
|
|
223
|
+
- Plantear decisiones sin haber nombrado antes los pendientes heredados de la Fase 1.
|
|
224
|
+
- Una decisión sin **Base**, o marcada `medido` sin fuente y vigencia.
|
|
225
|
+
- Dar por cerrada una decisión que descansa en un dato que la Fase 1 marcó como dudoso, aunque el usuario haya respondido.
|
|
202
226
|
|
|
203
227
|
---
|
|
204
228
|
|
|
205
229
|
## MARCAS DE CALIDAD
|
|
206
230
|
|
|
231
|
+
<!-- ranura: ejemplos -->
|
|
207
232
|
Ejemplos reales de documentos aprobados.
|
|
208
233
|
|
|
209
234
|
**La implicación aceptada, dicha como elección:**
|
|
@@ -228,6 +253,7 @@ Ejemplos reales de documentos aprobados.
|
|
|
228
253
|
**Un "no" justificado en serio, no despachado:**
|
|
229
254
|
> "No se encola porque los adjuntos son archivos temporales que el comando borra al
|
|
230
255
|
> terminar: el trabajador los procesaría después, cuando el archivo ya no existe."
|
|
256
|
+
<!-- /ranura -->
|
|
231
257
|
|
|
232
258
|
---
|
|
233
259
|
|
|
@@ -244,3 +270,6 @@ Ejemplos reales de documentos aprobados.
|
|
|
244
270
|
la traducción para quien pidió el ticket, sin que me la pidieran?
|
|
245
271
|
8. ¿Me metí en terreno de la Fase 3 (plan, criterios técnicos) o de la Fase 4 (pasos)?
|
|
246
272
|
9. ¿Declaré si la fase queda cerrada o si faltan respuestas?
|
|
273
|
+
10. ¿Abrí nombrando los pendientes heredados de la Fase 1, antes de plantear ninguna decisión?
|
|
274
|
+
11. ¿Ataqué primero las decisiones con base `inferido`?
|
|
275
|
+
12. ¿Cada decisión, en tabla o en bloque, tiene su **Base**?
|