elsabro 7.2.0 → 7.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/agents/elsabro-orchestrator.md +2 -0
- package/commands/elsabro/execute.md +346 -1471
- package/commands/elsabro/quick.md +11 -11
- package/commands/elsabro/start.md +156 -14
- package/flow-engine/src/callbacks.js +79 -0
- package/flow-engine/src/checkpoint.js +41 -0
- package/flow-engine/src/cli.js +214 -6
- package/flow-engine/src/executors.js +135 -4
- package/flow-engine/src/graph.js +30 -2
- package/flow-engine/src/template.js +152 -72
- package/flow-engine/tests/checkpoint.test.js +476 -0
- package/flow-engine/tests/execute-dispatcher.test.js +738 -0
- package/flow-engine/tests/executors-complex.test.js +259 -0
- package/flow-engine/tests/graph.test.js +193 -0
- package/flow-engine/tests/skill-install.test.js +254 -0
- package/flow-engine/tests/validation.test.js +137 -0
- package/flows/development-flow.json +23 -23
- package/hooks/skill-discovery.sh +0 -0
- package/package.json +1 -1
- package/references/SYSTEM_INDEX.md +1 -1
- package/references/agent-teams-integration.md +7 -15
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: execute
|
|
3
|
-
description: Ejecutar un plan
|
|
3
|
+
description: Ejecutar un plan via CLI flow engine con observabilidad
|
|
4
4
|
allowed-tools:
|
|
5
5
|
- Read
|
|
6
6
|
- Write
|
|
@@ -9,1615 +9,490 @@ allowed-tools:
|
|
|
9
9
|
- Glob
|
|
10
10
|
- Grep
|
|
11
11
|
- Task
|
|
12
|
-
- TaskCreate
|
|
13
|
-
- TaskUpdate
|
|
14
|
-
- TaskList
|
|
15
|
-
- TaskGet
|
|
16
12
|
- TeamCreate
|
|
17
13
|
- TeamDelete
|
|
18
14
|
- SendMessage
|
|
19
15
|
- AskUserQuestion
|
|
20
|
-
|
|
21
|
-
argument-hint: "[número de fase]"
|
|
16
|
+
argument-hint: "[numero de fase]"
|
|
22
17
|
sync:
|
|
23
18
|
reads: [".elsabro/state.json", ".planning/*-PLAN.md"]
|
|
24
|
-
writes: [".elsabro/state.json", ".elsabro/context.md"]
|
|
25
|
-
phases: ["initializing", "
|
|
19
|
+
writes: [".elsabro/state.json", ".elsabro/context.md", ".elsabro/telemetry/"]
|
|
20
|
+
phases: ["initializing", "stepping", "done"]
|
|
26
21
|
passes_context_to: ["verify-work"]
|
|
27
|
-
dispatcher:
|
|
28
|
-
exploration:
|
|
29
|
-
agents: [Explore, feature-dev:code-explorer, Plan]
|
|
30
|
-
model: haiku
|
|
31
|
-
parallel: true
|
|
32
|
-
min_agents: 3
|
|
33
|
-
method: "subagent"
|
|
34
|
-
implementation:
|
|
35
|
-
agents: [elsabro-executor, feature-dev:code-architect]
|
|
36
|
-
model: opus
|
|
37
|
-
parallel: true
|
|
38
|
-
min_agents: 2
|
|
39
|
-
method: "agent-team"
|
|
40
|
-
agent_team_config:
|
|
41
|
-
team_members: [elsabro-executor, elsabro-qa, elsabro-planner]
|
|
42
|
-
when: "always" # v4.2.0: Agent Teams obligatorio para 2+ agentes (Rule 8)
|
|
43
|
-
verification:
|
|
44
|
-
agents: [pr-review-toolkit:code-reviewer, pr-review-toolkit:silent-failure-hunter, pr-review-toolkit:pr-test-analyzer]
|
|
45
|
-
model: opus
|
|
46
|
-
parallel: true
|
|
47
|
-
min_agents: 3
|
|
48
|
-
method: "agent-team"
|
|
49
|
-
blocking: true
|
|
50
|
-
max_review_iterations: 5
|
|
51
22
|
---
|
|
52
23
|
|
|
53
24
|
# ELSABRO: Execute
|
|
54
25
|
|
|
55
|
-
|
|
56
|
-
|
|
26
|
+
Thin wrapper del CLI flow engine. Toda la logica de grafos, Agent Teams, y gates
|
|
27
|
+
vive en el engine y en `flows/development-flow.json`. Este archivo solo define el loop.
|
|
57
28
|
|
|
58
|
-
**
|
|
29
|
+
**Reglas**: @references/enforcement-rules.md
|
|
30
|
+
**State sync**: @references/state-sync.md
|
|
59
31
|
|
|
60
|
-
|
|
32
|
+
## 1. Inicializar
|
|
61
33
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
TaskCreate({
|
|
65
|
-
subject: "Initialize execute command",
|
|
66
|
-
description: "Leer estado y preparar ejecución",
|
|
67
|
-
activeForm: "Inicializando..."
|
|
68
|
-
})
|
|
69
|
-
TaskUpdate(id, status: "in_progress")
|
|
70
|
-
|
|
71
|
-
// 2. Leer estado existente
|
|
72
|
-
const state = Read(".elsabro/state.json") || createInitialState();
|
|
73
|
-
|
|
74
|
-
// 3. Verificar flujo en progreso
|
|
75
|
-
if (state.current_flow && state.current_flow.command !== "execute") {
|
|
76
|
-
AskUserQuestion({
|
|
77
|
-
questions: [{
|
|
78
|
-
question: `Hay un flujo de "${state.current_flow.command}" en progreso. ¿Continuar ese o empezar execute?`,
|
|
79
|
-
header: "Flujo",
|
|
80
|
-
options: [
|
|
81
|
-
{ label: "Continuar anterior", description: "Retomar " + state.current_flow.command },
|
|
82
|
-
{ label: "Empezar execute", description: "Pausar anterior" }
|
|
83
|
-
]
|
|
84
|
-
}]
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// 4. Actualizar estado
|
|
89
|
-
state.current_flow = { command: "execute", phase: "initializing", started_at: new Date().toISOString() };
|
|
90
|
-
state.elsabro_mode = true;
|
|
91
|
-
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
92
|
-
|
|
93
|
-
// 5. Completar task de inicialización
|
|
94
|
-
TaskUpdate(id, status: "completed")
|
|
95
|
-
```
|
|
34
|
+
Leer `.elsabro/state.json` siguiendo el protocolo de @references/state-sync.md.
|
|
35
|
+
Verificar flujo en progreso. Actualizar phase a "stepping".
|
|
96
36
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
103
|
-
|
|
104
|
-
// Antes de cada wave
|
|
105
|
-
state.current_flow.phase = "executing_wave_" + waveNumber;
|
|
106
|
-
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
37
|
+
```bash
|
|
38
|
+
FLOW="flows/development-flow.json"
|
|
39
|
+
TASK="[descripcion de la tarea del usuario]"
|
|
40
|
+
PROFILE="[default|yolo|careful|teams|bmad]"
|
|
41
|
+
COMPLEXITY="[low|medium|high]"
|
|
107
42
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
43
|
+
# Initialize flow and create checkpoint
|
|
44
|
+
node flow-engine/src/cli.js init --flow "$FLOW" --task "$TASK" --profile "$PROFILE" --complexity "$COMPLEXITY"
|
|
111
45
|
```
|
|
112
46
|
|
|
113
|
-
|
|
47
|
+
Guardar el flowId del resultado para todos los comandos siguientes.
|
|
114
48
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
artifact: null
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Pasar contexto a verify-work
|
|
125
|
-
state.context = state.context || {};
|
|
126
|
-
state.context.changed_files = [...changedFiles];
|
|
127
|
-
state.context.commits = [...commitIds];
|
|
128
|
-
state.context.tests_added = [...testFiles];
|
|
129
|
-
state.context.phase_completed = phaseNumber;
|
|
130
|
-
|
|
131
|
-
// Limpiar flujo y sugerir siguiente
|
|
132
|
-
state.current_flow = null;
|
|
133
|
-
state.suggested_next = "verify-work";
|
|
134
|
-
|
|
135
|
-
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
136
|
-
|
|
137
|
-
// Actualizar context.md legible
|
|
138
|
-
Write(".elsabro/context.md", generateHumanReadableContext(state));
|
|
139
|
-
```
|
|
140
|
-
</state_sync>
|
|
49
|
+
**Note on Skill Auto-Install:**
|
|
50
|
+
The development-flow.json includes a `skill_discovery` node (second node after entry) that automatically:
|
|
51
|
+
1. Calls `hooks/skill-discovery.sh` with task description and complexity
|
|
52
|
+
2. Discovers installed skills and recommends relevant ones from the Vercel Agent Skills Registry
|
|
53
|
+
3. Provides install commands for recommended skills
|
|
54
|
+
4. All discovery output is captured and available in `context.steps.discovery`
|
|
141
55
|
|
|
142
|
-
|
|
143
|
-
## Skill Discovery (Pre-Execution)
|
|
56
|
+
## 1.1. Stitch MCP Auto-Detection (Visual Projects)
|
|
144
57
|
|
|
145
|
-
**
|
|
58
|
+
**AFTER initialization**, check if this is a visual project and suggest Stitch MCP if UI work is needed:
|
|
146
59
|
|
|
147
60
|
```javascript
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
state.context = state.context || {};
|
|
153
|
-
state.context.available_skills = JSON.parse(discoveryResult).recommended || [];
|
|
154
|
-
|
|
155
|
-
// Informar al usuario
|
|
156
|
-
if (state.context.available_skills.length > 0) {
|
|
157
|
-
output(`Skills cargados: ${state.context.available_skills.map(s => s.name).join(", ")}`);
|
|
158
|
-
}
|
|
159
|
-
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
Los skills proveen patrones de código verificados, configuraciones de setup, y guides de integración durante la implementación.
|
|
163
|
-
</skill_discovery>
|
|
164
|
-
|
|
165
|
-
<skill_injection>
|
|
166
|
-
## Skill Injection (OBLIGATORIO despues de Discovery)
|
|
61
|
+
// Detectar frameworks visuales y verificar si Stitch está configurado
|
|
62
|
+
let isVisualProject = false;
|
|
63
|
+
let detectedFrameworks = [];
|
|
64
|
+
let hasStitchDesigns = false;
|
|
167
65
|
|
|
168
|
-
|
|
66
|
+
try {
|
|
67
|
+
const packageJson = Read("package.json");
|
|
68
|
+
const pkg = JSON.parse(packageJson);
|
|
69
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
169
70
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
// 2a. Try local ELSABRO skill first
|
|
180
|
-
let content = Read(`skills/${skill.id}.md`);
|
|
181
|
-
|
|
182
|
-
// 2b. Try global installed skill
|
|
183
|
-
if (!content) {
|
|
184
|
-
content = Read(`${HOME}/.claude/skills/${skill.id}.md`);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// 2c. If not found locally and has install_cmd → offer to install
|
|
188
|
-
if (!content && skill.install_cmd && skill.source === "skills-registry" && !registryDown) {
|
|
189
|
-
// Check registry accessibility
|
|
190
|
-
const checkResult = Bash(`bash ./hooks/skill-install.sh check`, { timeout: 20000 });
|
|
191
|
-
const check = JSON.parse(checkResult);
|
|
192
|
-
|
|
193
|
-
if (check.status === "ok") {
|
|
194
|
-
// Ask user permission
|
|
195
|
-
const answer = AskUserQuestion({
|
|
196
|
-
questions: [{
|
|
197
|
-
question: `Skill "${skill.id}" no esta instalado localmente pero esta disponible en el registry. Instalarlo?`,
|
|
198
|
-
header: "Skill Install",
|
|
199
|
-
options: [
|
|
200
|
-
{ label: "Instalar", description: `Ejecuta: ${skill.install_cmd}` },
|
|
201
|
-
{ label: "Omitir", description: "Continuar sin este skill" }
|
|
202
|
-
],
|
|
203
|
-
multiSelect: false
|
|
204
|
-
}]
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
if (answer === "Instalar") {
|
|
208
|
-
// Execute install
|
|
209
|
-
const installResult = Bash(`bash ./hooks/skill-install.sh install "${skill.install_cmd}"`, { timeout: 45000 });
|
|
210
|
-
const install = JSON.parse(installResult);
|
|
211
|
-
|
|
212
|
-
if (install.status === "ok") {
|
|
213
|
-
// Validate installed file
|
|
214
|
-
const validateResult = Bash(`bash ./hooks/skill-install.sh validate "${skill.id}"`, { timeout: 5000 });
|
|
215
|
-
const validation = JSON.parse(validateResult);
|
|
216
|
-
|
|
217
|
-
if (validation.status === "ok") {
|
|
218
|
-
content = Read(validation.path);
|
|
219
|
-
output(` + Skill "${skill.id}" instalado y cargado`);
|
|
220
|
-
} else {
|
|
221
|
-
output(` ! Skill "${skill.id}" instalado pero formato invalido — omitido`);
|
|
222
|
-
}
|
|
223
|
-
} else {
|
|
224
|
-
output(` ! Instalacion de "${skill.id}" fallo: ${install.message} — omitido`);
|
|
225
|
-
}
|
|
226
|
-
} else {
|
|
227
|
-
output(` - Skill "${skill.id}" omitido por usuario`);
|
|
228
|
-
}
|
|
229
|
-
} else {
|
|
230
|
-
output(` ! Registry no disponible — omitiendo instalacion de skills externos`);
|
|
231
|
-
registryDown = true; // Skip install attempts for remaining skills
|
|
232
|
-
}
|
|
233
|
-
}
|
|
71
|
+
// Frameworks visuales
|
|
72
|
+
const visualFrameworks = [
|
|
73
|
+
{ name: "react", display: "React" },
|
|
74
|
+
{ name: "next", display: "Next.js" },
|
|
75
|
+
{ name: "vue", display: "Vue" },
|
|
76
|
+
{ name: "nuxt", display: "Nuxt" },
|
|
77
|
+
{ name: "@angular/core", display: "Angular" },
|
|
78
|
+
{ name: "svelte", display: "Svelte" }
|
|
79
|
+
];
|
|
234
80
|
|
|
235
|
-
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
content: content
|
|
240
|
-
});
|
|
81
|
+
for (const framework of visualFrameworks) {
|
|
82
|
+
if (allDeps[framework.name]) {
|
|
83
|
+
detectedFrameworks.push(framework.display);
|
|
84
|
+
isVisualProject = true;
|
|
241
85
|
}
|
|
242
86
|
}
|
|
243
87
|
|
|
244
|
-
//
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
88
|
+
// Verificar si ya hay diseños de Stitch
|
|
89
|
+
try {
|
|
90
|
+
const stitchDesigns = Read(".planning/ui-designs/UI-DESIGNS.md");
|
|
91
|
+
hasStitchDesigns = !!stitchDesigns;
|
|
92
|
+
} catch (e) {
|
|
93
|
+
hasStitchDesigns = false;
|
|
94
|
+
}
|
|
95
|
+
} catch (e) {
|
|
96
|
+
// No package.json - continue without detection
|
|
251
97
|
}
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
**GATE**: Cuando lances agentes executors, INCLUIR los skills en el prompt:
|
|
255
|
-
```
|
|
256
|
-
Si state.context.loaded_skills tiene contenido, agregar al prompt de CADA executor:
|
|
257
|
-
|
|
258
|
-
"SKILLS DE REFERENCIA:
|
|
259
|
-
[Para cada skill cargado:]
|
|
260
|
-
### Skill: {name}
|
|
261
|
-
{contenido del archivo .md}
|
|
262
|
-
|
|
263
|
-
Usa estos skills como guia de patrones, estructura y mejores practicas."
|
|
264
|
-
```
|
|
265
|
-
</skill_injection>
|
|
266
|
-
|
|
267
|
-
<objective>
|
|
268
|
-
Ejecutar planes de una fase con:
|
|
269
|
-
- **Sistema de Tasks para tracking real** de waves y dependencias
|
|
270
|
-
- **Dispatcher inteligente con selección automática de modelo** (ver @references/task-dispatcher.md)
|
|
271
|
-
- **Ejecución por subagentes en paralelo** según tipo de tarea
|
|
272
|
-
- Investigación de patrones con Context7
|
|
273
|
-
- TDD cuando sea apropiado
|
|
274
|
-
- Commits atómicos por tarea
|
|
275
|
-
- Verificación automática con agregador
|
|
276
|
-
</objective>
|
|
277
|
-
|
|
278
|
-
<dispatcher_integration>
|
|
279
|
-
## Selección Automática de Modelo
|
|
280
|
-
|
|
281
|
-
**REGLA CRÍTICA:** Cada tarea se ejecuta con el modelo óptimo para su tipo.
|
|
282
|
-
|
|
283
|
-
```
|
|
284
|
-
┌─────────────────────────────────────────────────────────────────────────┐
|
|
285
|
-
│ MODELO SEGÚN FASE │
|
|
286
|
-
├─────────────────────────────────────────────────────────────────────────┤
|
|
287
|
-
│ FASE │ SUBAGENTES │ MODELO │
|
|
288
|
-
├────────────────────┼────────────────────────────────────┼───────────────┤
|
|
289
|
-
│ Exploración │ Explore │ HAIKU │
|
|
290
|
-
│ (pre-ejecución) │ feature-dev:code-explorer │ HAIKU │
|
|
291
|
-
│ │ Plan │ HAIKU │
|
|
292
|
-
│ │ (3 en paralelo obligatorio) │ │
|
|
293
|
-
├────────────────────┼────────────────────────────────────┼───────────────┤
|
|
294
|
-
│ Implementación │ elsabro-executor │ OPUS │
|
|
295
|
-
│ (waves) │ feature-dev:code-architect │ OPUS │
|
|
296
|
-
│ │ (2+ en paralelo por wave) │ │
|
|
297
|
-
├────────────────────┼────────────────────────────────────┼───────────────┤
|
|
298
|
-
│ Verificación │ pr-review-toolkit:code-reviewer │ OPUS │
|
|
299
|
-
│ (post-ejecución) │ pr-review-toolkit:silent-... │ OPUS │
|
|
300
|
-
│ │ pr-review-toolkit:pr-test-... │ OPUS │
|
|
301
|
-
│ │ (3 en paralelo obligatorio) │ │
|
|
302
|
-
└────────────────────┴────────────────────────────────────┴───────────────┘
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
### Por qué esta distribución
|
|
306
98
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
99
|
+
// Si es proyecto visual sin diseños, sugerir Stitch MCP
|
|
100
|
+
if (isVisualProject && !hasStitchDesigns) {
|
|
101
|
+
state.context = state.context || {};
|
|
102
|
+
state.context.is_visual_project = true;
|
|
103
|
+
state.context.visual_frameworks = detectedFrameworks;
|
|
311
104
|
|
|
312
|
-
|
|
313
|
-
|
|
105
|
+
// Actualizar estado
|
|
106
|
+
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
314
107
|
|
|
315
|
-
|
|
108
|
+
// Sugerir al usuario
|
|
109
|
+
output(`\n🎨 Proyecto ${detectedFrameworks.join("/")} detectado`);
|
|
110
|
+
output(`💡 Tip: Usa /elsabro:design-ui para generar diseños visuales con Stitch MCP antes de implementar\n`);
|
|
316
111
|
|
|
317
|
-
|
|
318
|
-
|
|
112
|
+
// Preguntar si quieren diseñar primero
|
|
113
|
+
const answer = AskUserQuestion({
|
|
114
|
+
questions: [{
|
|
115
|
+
question: "¿Quieres diseñar la UI visualmente antes de implementar?",
|
|
116
|
+
header: "Stitch MCP",
|
|
117
|
+
options: [
|
|
118
|
+
{ label: "Continuar con ejecución", description: "Implementar sin diseño visual previo" },
|
|
119
|
+
{ label: "Diseñar UI primero (Recomendado)", description: "Genera screens con Stitch MCP antes de codificar" }
|
|
120
|
+
],
|
|
121
|
+
multiSelect: false
|
|
122
|
+
}]
|
|
123
|
+
});
|
|
319
124
|
|
|
125
|
+
if (answer.includes("Diseñar UI primero")) {
|
|
126
|
+
// Interrumpir execute y redirigir a design-ui
|
|
127
|
+
output("→ Redirigiendo a /elsabro:design-ui...\n");
|
|
128
|
+
Skill("elsabro:design-ui", TASK);
|
|
129
|
+
return; // Exit execute
|
|
130
|
+
}
|
|
131
|
+
}
|
|
320
132
|
```
|
|
321
|
-
┌─────────────────────────────────────────────────────────────────────────┐
|
|
322
|
-
│ AGENT TEAMS — OBLIGATORIO (v4.2.0) │
|
|
323
|
-
├─────────────────────────────────────────────────────────────────────────┤
|
|
324
|
-
│ FASE │ MÉTODO │
|
|
325
|
-
├─────────────────────────────────────┼───────────────────────────────────┤
|
|
326
|
-
│ Exploración (HAIKU read-only) │ SUBAGENTS (excepción) │
|
|
327
|
-
│ Implementación (2+ agentes) │ AGENT TEAMS (obligatorio) │
|
|
328
|
-
│ Verificación/Review (2+ agentes) │ AGENT TEAMS (obligatorio) │
|
|
329
|
-
│ Planning (2+ agentes opus) │ AGENT TEAMS (obligatorio) │
|
|
330
|
-
│ Debugging (2+ agentes) │ AGENT TEAMS (obligatorio) │
|
|
331
|
-
│ Agente único │ SUBAGENT (no aplica teams) │
|
|
332
|
-
└─────────────────────────────────────┴───────────────────────────────────┘
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
**VIOLACIÓN CRÍTICA**: Lanzar 2+ agentes sin TeamCreate = ABORTAR OPERACIÓN.
|
|
336
|
-
Ver `/references/enforcement-rules.md` Regla 8.
|
|
337
|
-
|
|
338
|
-
### Flujo con Agent Teams
|
|
339
|
-
|
|
340
|
-
```javascript
|
|
341
|
-
// 1. Crear team (API oficial Claude Code)
|
|
342
|
-
TeamCreate({
|
|
343
|
-
team_name: "elsabro-impl",
|
|
344
|
-
description: "Implementation team for: " + task
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
// 2. Create tasks para el team
|
|
348
|
-
TaskCreate({ subject: "Analyze requirements", ... })
|
|
349
|
-
TaskCreate({ subject: "Implement core logic", ... })
|
|
350
|
-
TaskCreate({ subject: "Write tests", ... })
|
|
351
|
-
|
|
352
|
-
// 3. Spawn teammates
|
|
353
|
-
Task({
|
|
354
|
-
subagent_type: "elsabro-executor",
|
|
355
|
-
team_name: "elsabro-impl",
|
|
356
|
-
name: "executor-1",
|
|
357
|
-
prompt: "Implement core logic following TDD..."
|
|
358
|
-
})
|
|
359
|
-
Task({
|
|
360
|
-
subagent_type: "elsabro-qa",
|
|
361
|
-
team_name: "elsabro-impl",
|
|
362
|
-
name: "qa-1",
|
|
363
|
-
prompt: "Write comprehensive tests..."
|
|
364
|
-
})
|
|
365
133
|
|
|
366
|
-
|
|
367
|
-
// 5. When done, shutdown teammates then cleanup
|
|
368
|
-
SendMessage({ type: "shutdown_request", recipient: "executor-1", content: "Work complete" })
|
|
369
|
-
SendMessage({ type: "shutdown_request", recipient: "qa-1", content: "Work complete" })
|
|
370
|
-
TeamDelete()
|
|
371
|
-
```
|
|
134
|
+
## 2. Loop Principal
|
|
372
135
|
|
|
373
|
-
|
|
136
|
+
Ejecutar el siguiente loop hasta que `step` retorne `{ finished: true }`:
|
|
374
137
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
-
|
|
378
|
-
|
|
379
|
-
|
|
138
|
+
1. **STEP**: Ejecutar con Bash y parsear el JSON:
|
|
139
|
+
```bash
|
|
140
|
+
node flow-engine/src/cli.js step --flow "$FLOW"
|
|
141
|
+
```
|
|
142
|
+
Capturar el output JSON. Parsear `instruction` del resultado.
|
|
380
143
|
|
|
381
|
-
|
|
144
|
+
2. **CHECK**: Si `finished: true` -> ir a seccion 5. Si `error` -> mostrar al usuario, ofrecer retry/abort.
|
|
382
145
|
|
|
383
|
-
|
|
384
|
-
|--------|-------------|--------|
|
|
385
|
-
| elsabro-orchestrator | Team Lead | Siempre (coordina) |
|
|
386
|
-
| elsabro-executor | Teammate | Implementación |
|
|
387
|
-
| elsabro-planner | Teammate | Planning |
|
|
388
|
-
| elsabro-analyst | Teammate | Análisis |
|
|
389
|
-
| elsabro-qa | Teammate | Testing |
|
|
390
|
-
| elsabro-verifier | Teammate | Verificación |
|
|
391
|
-
| elsabro-debugger | Teammate | Debug (iter >= 2) |
|
|
146
|
+
3. **DISPATCH**: Ejecutar segun `instruction.type` (ver seccion 3). Guardar el resultado.
|
|
392
147
|
|
|
393
|
-
|
|
148
|
+
4. **OBSERVE**: Emitir senales de observabilidad (ver seccion 4).
|
|
394
149
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
</agent_teams_integration>
|
|
150
|
+
5. **COMPLETE**: Enviar resultado al CLI:
|
|
151
|
+
```bash
|
|
152
|
+
node flow-engine/src/cli.js complete --flow "$FLOW" --result '{"output": "..."}'
|
|
153
|
+
```
|
|
400
154
|
|
|
401
|
-
|
|
402
|
-
## Agent Teams Gate (OBLIGATORIO - NO NEGOCIABLE)
|
|
155
|
+
6. Volver al paso 1.
|
|
403
156
|
|
|
404
|
-
|
|
157
|
+
## 2.1. Error Handling: Runtime Status Validation
|
|
405
158
|
|
|
406
|
-
|
|
407
|
-
¿Se necesitan 2+ agentes en paralelo?
|
|
408
|
-
│
|
|
409
|
-
├─ SÍ → ¿Es exploración read-only (HAIKU)?
|
|
410
|
-
│ │
|
|
411
|
-
│ ├─ SÍ → Subagents OK (excepción)
|
|
412
|
-
│ │
|
|
413
|
-
│ └─ NO → ¿Se usó TeamCreate?
|
|
414
|
-
│ │
|
|
415
|
-
│ ├─ NO → EJECUTAR AHORA:
|
|
416
|
-
│ │ TeamCreate({ team_name, description })
|
|
417
|
-
│ │ Solo entonces: Task({ team_name, name, ... })
|
|
418
|
-
│ │
|
|
419
|
-
│ └─ SÍ → ¿Teammates spawned con team_name?
|
|
420
|
-
│ │
|
|
421
|
-
│ ├─ SÍ → Continuar con SendMessage para coordinación
|
|
422
|
-
│ └─ NO → VIOLACIÓN: Usar team_name en Task()
|
|
423
|
-
│
|
|
424
|
-
└─ NO (agente único) → Subagent OK
|
|
425
|
-
```
|
|
159
|
+
The flow engine validates all nodes before execution via `checkRuntimeStatus()`. This catches:
|
|
426
160
|
|
|
161
|
+
### NotImplementedError
|
|
162
|
+
Thrown when a node has `runtime_status: "not_implemented"`:
|
|
427
163
|
```javascript
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
**VIOLACIÓN CRÍTICA**: Lanzar 2+ agentes sin Agent Teams = ABORTAR OPERACIÓN
|
|
435
|
-
</agent_teams_gate>
|
|
436
|
-
|
|
437
|
-
<blocking_review_protocol>
|
|
438
|
-
## Protocolo de Review Bloqueante (v4.0.0)
|
|
439
|
-
|
|
440
|
-
**REGLA CRÍTICA:** El proceso NO PUEDE avanzar a post-mortem si hay issues de code review sin resolver.
|
|
441
|
-
|
|
442
|
-
### Flujo
|
|
443
|
-
|
|
444
|
-
```
|
|
445
|
-
parallel_review (3 agentes)
|
|
446
|
-
↓
|
|
447
|
-
review_check: ¿hay issues?
|
|
448
|
-
↓
|
|
449
|
-
┌────┴────┐
|
|
450
|
-
│ NO │ YES
|
|
451
|
-
│ issues │ issues
|
|
452
|
-
↓ ↓
|
|
453
|
-
verify fix_review_issues
|
|
454
|
-
final (max 5 iteraciones)
|
|
455
|
-
↓
|
|
456
|
-
parallel_review (re-check)
|
|
457
|
-
↓
|
|
458
|
-
¿issues == 0?
|
|
459
|
-
↓
|
|
460
|
-
┌─────┴─────┐
|
|
461
|
-
│ SÍ │ NO (5 iter agotadas)
|
|
462
|
-
↓ ↓
|
|
463
|
-
verify_final interrupt_blocked
|
|
464
|
-
(SIN opción "continuar con errores")
|
|
465
|
-
↓
|
|
466
|
-
┌───┴───┐───────┐
|
|
467
|
-
│ │ │
|
|
468
|
-
retry manual abort
|
|
469
|
-
↓ ↓ ↓
|
|
470
|
-
fix_review wait end_cancelled
|
|
471
|
-
_issues _manual
|
|
472
|
-
_review_fix
|
|
473
|
-
↓
|
|
474
|
-
parallel_review (re-check)
|
|
164
|
+
{
|
|
165
|
+
"id": "bmad_solution",
|
|
166
|
+
"type": "sequence",
|
|
167
|
+
"runtime_status": "not_implemented",
|
|
168
|
+
"gaps": ["BMAD solutioning logic not yet implemented", "Needs ADR generation"]
|
|
169
|
+
}
|
|
475
170
|
```
|
|
476
171
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
### Iteraciones de Review
|
|
486
|
-
|
|
487
|
-
```javascript
|
|
488
|
-
// Estado del review loop
|
|
489
|
-
state.reviewIteration = state.reviewIteration || 0;
|
|
490
|
-
state.reviewIteration++;
|
|
491
|
-
|
|
492
|
-
// Log de cada iteración
|
|
493
|
-
console.log(`[REVIEW] Iteración ${state.reviewIteration}/5`);
|
|
494
|
-
console.log(`[REVIEW] Issues encontrados: ${issueCount}`);
|
|
495
|
-
|
|
496
|
-
// Solo avanza cuando issues == 0
|
|
497
|
-
if (issueCount === 0) {
|
|
498
|
-
state.reviewIteration = 0; // Reset
|
|
499
|
-
// → verify_final
|
|
500
|
-
} else if (state.reviewIteration >= 5) {
|
|
501
|
-
// → interrupt_blocked (NO verify_final)
|
|
502
|
-
} else {
|
|
503
|
-
// → fix_review_issues → parallel_review
|
|
172
|
+
**Error format:**
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"error": "NotImplementedError",
|
|
176
|
+
"nodeId": "bmad_solution",
|
|
177
|
+
"message": "Node \"bmad_solution\" is not yet implemented. Gaps: BMAD solutioning logic not yet implemented; Needs ADR generation",
|
|
178
|
+
"gaps": ["BMAD solutioning logic not yet implemented", "Needs ADR generation"]
|
|
504
179
|
}
|
|
505
180
|
```
|
|
506
|
-
</blocking_review_protocol>
|
|
507
|
-
|
|
508
|
-
<process>
|
|
509
|
-
## Paso 0: Exploración Pre-Ejecución (HAIKU x3 paralelo)
|
|
510
181
|
|
|
511
|
-
**
|
|
182
|
+
**Dispatcher action:**
|
|
183
|
+
- STOP execution immediately
|
|
184
|
+
- Display error to user with gap details
|
|
185
|
+
- Offer options:
|
|
186
|
+
1. "Skip this node" (only if node has `optional: true`)
|
|
187
|
+
2. "Abort workflow"
|
|
188
|
+
3. "Report bug" (opens issue template with node info)
|
|
512
189
|
|
|
190
|
+
### DeprecatedNodeError
|
|
191
|
+
Thrown when a node has `runtime_status: "deprecated"`:
|
|
513
192
|
```javascript
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}) // → explore-files-id
|
|
521
|
-
|
|
522
|
-
TaskCreate({
|
|
523
|
-
subject: "Explore: Analyze patterns",
|
|
524
|
-
description: "Analizar patrones existentes en el codebase",
|
|
525
|
-
activeForm: "Analizando patrones...",
|
|
526
|
-
metadata: { type: "exploration", model: "haiku" }
|
|
527
|
-
}) // → explore-patterns-id
|
|
528
|
-
|
|
529
|
-
TaskCreate({
|
|
530
|
-
subject: "Explore: Map architecture",
|
|
531
|
-
description: "Mapear arquitectura del área afectada",
|
|
532
|
-
activeForm: "Mapeando arquitectura...",
|
|
533
|
-
metadata: { type: "exploration", model: "haiku" }
|
|
534
|
-
}) // → explore-arch-id
|
|
535
|
-
|
|
536
|
-
// Marcar todas como in_progress
|
|
537
|
-
TaskUpdate({ taskId: "explore-files-id", status: "in_progress" })
|
|
538
|
-
TaskUpdate({ taskId: "explore-patterns-id", status: "in_progress" })
|
|
539
|
-
TaskUpdate({ taskId: "explore-arch-id", status: "in_progress" })
|
|
540
|
-
|
|
541
|
-
// Lanzar 3 agentes HAIKU EN PARALELO (UN SOLO MENSAJE)
|
|
542
|
-
Task({
|
|
543
|
-
subagent_type: "Explore",
|
|
544
|
-
model: "haiku",
|
|
545
|
-
description: "Buscar archivos relacionados",
|
|
546
|
-
prompt: "Busca archivos relacionados con la fase [N]. Lista los 10 más relevantes."
|
|
547
|
-
}) |
|
|
548
|
-
Task({
|
|
549
|
-
subagent_type: "feature-dev:code-explorer",
|
|
550
|
-
model: "haiku",
|
|
551
|
-
description: "Analizar patrones",
|
|
552
|
-
prompt: "Analiza patrones de arquitectura en el área de la fase [N]."
|
|
553
|
-
}) |
|
|
554
|
-
Task({
|
|
555
|
-
subagent_type: "Plan",
|
|
556
|
-
model: "haiku",
|
|
557
|
-
description: "Mapear arquitectura",
|
|
558
|
-
prompt: "Mapea componentes y flujo de datos del área afectada."
|
|
559
|
-
})
|
|
560
|
-
|
|
561
|
-
// Marcar completed
|
|
562
|
-
TaskUpdate({ taskId: "explore-files-id", status: "completed" })
|
|
563
|
-
TaskUpdate({ taskId: "explore-patterns-id", status: "completed" })
|
|
564
|
-
TaskUpdate({ taskId: "explore-arch-id", status: "completed" })
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
**Resultado:** Contexto del codebase capturado antes de modificar nada.
|
|
568
|
-
|
|
569
|
-
## Paso 1: Descubrir Planes
|
|
570
|
-
|
|
571
|
-
```bash
|
|
572
|
-
ls .planning/*-PLAN.md
|
|
193
|
+
{
|
|
194
|
+
"id": "legacy_team_node",
|
|
195
|
+
"type": "team",
|
|
196
|
+
"runtime_status": "deprecated",
|
|
197
|
+
"deprecated_reason": "Use parallel nodes with Agent Teams instead (Rule 8)"
|
|
198
|
+
}
|
|
573
199
|
```
|
|
574
200
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
description: "Coordinar ejecución de todos los planes de la fase",
|
|
584
|
-
activeForm: "Ejecutando Fase [N]...",
|
|
585
|
-
metadata: { phase: N, type: "coordinator" }
|
|
586
|
-
}) // → phase-coordinator-id
|
|
201
|
+
**Error format:**
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"error": "DeprecatedNodeError",
|
|
205
|
+
"nodeId": "legacy_team_node",
|
|
206
|
+
"message": "Node \"legacy_team_node\" is deprecated: Use parallel nodes with Agent Teams instead (Rule 8)",
|
|
207
|
+
"reason": "Use parallel nodes with Agent Teams instead (Rule 8)"
|
|
208
|
+
}
|
|
587
209
|
```
|
|
588
210
|
|
|
589
|
-
|
|
590
|
-
|
|
211
|
+
**Dispatcher action:**
|
|
212
|
+
- STOP execution immediately
|
|
213
|
+
- Display deprecation warning with reason
|
|
214
|
+
- Suggest migration path if available in error
|
|
215
|
+
- Offer "Abort workflow"
|
|
591
216
|
|
|
592
|
-
|
|
593
|
-
// Para cada wave, crear task coordinadora
|
|
594
|
-
TaskCreate({
|
|
595
|
-
subject: "Execute Wave [W]",
|
|
596
|
-
description: "Plans: A, B, C en paralelo",
|
|
597
|
-
activeForm: "Ejecutando Wave [W]...",
|
|
598
|
-
metadata: { wave: W, plans: ["A", "B", "C"] }
|
|
599
|
-
}) // → wave-W-id
|
|
600
|
-
|
|
601
|
-
// Wave 2+ tiene dependencia de wave anterior
|
|
602
|
-
TaskUpdate({
|
|
603
|
-
taskId: "wave-2-id",
|
|
604
|
-
addBlockedBy: ["wave-1-id"]
|
|
605
|
-
})
|
|
606
|
-
```
|
|
217
|
+
### When to Continue vs Abort
|
|
607
218
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
subject: "Execute Plan [name]",
|
|
612
|
-
description: "TDD + commits para plan específico",
|
|
613
|
-
activeForm: "Ejecutando Plan [name]...",
|
|
614
|
-
metadata: { wave: W, plan: "name", type: "executor" }
|
|
615
|
-
}) // → plan-X-id
|
|
616
|
-
|
|
617
|
-
// Bloquear por wave coordinator
|
|
618
|
-
TaskUpdate({
|
|
619
|
-
taskId: "plan-X-id",
|
|
620
|
-
addBlockedBy: ["wave-W-id"]
|
|
621
|
-
})
|
|
622
|
-
```
|
|
219
|
+
**Continue (skip node) only if:**
|
|
220
|
+
- Node has `optional: true` explicitly set
|
|
221
|
+
- AND user explicitly chooses "Skip" option
|
|
623
222
|
|
|
624
|
-
|
|
223
|
+
**Always abort for:**
|
|
224
|
+
- Deprecated nodes (never optional)
|
|
225
|
+
- Not-implemented nodes without `optional: true`
|
|
226
|
+
- Any runtime status error in critical path nodes (entry, exit, condition, router)
|
|
625
227
|
|
|
626
|
-
|
|
228
|
+
### Telemetry Signal
|
|
627
229
|
|
|
628
|
-
|
|
629
|
-
```
|
|
630
|
-
|
|
230
|
+
When runtime status errors occur, emit:
|
|
231
|
+
```json
|
|
232
|
+
{
|
|
233
|
+
"ts": "2025-02-09T12:34:56Z",
|
|
234
|
+
"signal": "runtime_status_error",
|
|
235
|
+
"nodeId": "bmad_solution",
|
|
236
|
+
"error": "NotImplementedError",
|
|
237
|
+
"gaps": ["..."],
|
|
238
|
+
"action": "abort"
|
|
239
|
+
}
|
|
631
240
|
```
|
|
632
241
|
|
|
633
|
-
|
|
634
|
-
## ⛔ STOP — LEER ANTES DE LANZAR AGENTES PARALELOS
|
|
635
|
-
|
|
636
|
-
**REGLA NO NEGOCIABLE (Rule 8):** Si vas a lanzar 2+ Task() para implementacion o review:
|
|
242
|
+
## 3. Dispatch por Tipo de Instruccion
|
|
637
243
|
|
|
638
|
-
|
|
639
|
-
2. **DESPUES** → `Task({ ..., team_name: "elsabro-impl", name: "executor-1" })`
|
|
640
|
-
3. **AL FINAL** → `SendMessage({ type: "shutdown_request", ... })` para CADA teammate
|
|
641
|
-
4. **ULTIMO** → `TeamDelete()`
|
|
642
|
-
|
|
643
|
-
**Si ya lanzaste Task() sin TeamCreate: DETENTE AHORA. No continúes.**
|
|
644
|
-
Vuelve arriba, crea el team, y relanza los agentes CON team_name.
|
|
645
|
-
|
|
646
|
-
**NUNCA hagas esto:**
|
|
244
|
+
### type: "entry"
|
|
647
245
|
```javascript
|
|
648
|
-
//
|
|
649
|
-
|
|
650
|
-
|
|
246
|
+
// instruction: { type: "entry", nodeId, next }
|
|
247
|
+
// Entry nodes are auto-resolved by the engine - no dispatcher action needed
|
|
248
|
+
// The engine automatically transitions to the next node
|
|
249
|
+
// result: { next: instruction.next }
|
|
651
250
|
```
|
|
652
251
|
|
|
653
|
-
|
|
252
|
+
### type: "exit"
|
|
654
253
|
```javascript
|
|
655
|
-
//
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
// ... al completar ...
|
|
660
|
-
SendMessage({ type: "shutdown_request", recipient: "executor-1", content: "Done" })
|
|
661
|
-
SendMessage({ type: "shutdown_request", recipient: "qa-1", content: "Done" })
|
|
662
|
-
TeamDelete()
|
|
254
|
+
// instruction: { type: "exit", nodeId, outputs, status }
|
|
255
|
+
// Exit nodes are auto-resolved by the engine - no dispatcher action needed
|
|
256
|
+
// The engine resolves output templates and terminates the flow
|
|
257
|
+
// result: { outputs: resolvedOutputs, status: instruction.status || "completed" }
|
|
663
258
|
```
|
|
664
259
|
|
|
665
|
-
|
|
666
|
-
</IMPERATIVO_AGENT_TEAMS>
|
|
667
|
-
|
|
668
|
-
### Si hay múltiples planes en la wave → Paralelo (OPUS)
|
|
260
|
+
### type: "condition"
|
|
669
261
|
```javascript
|
|
670
|
-
//
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
// OBLIGATORIO: Crear Agent Team para implementación paralela (Rule 8)
|
|
676
|
-
TeamCreate({
|
|
677
|
-
team_name: "elsabro-impl",
|
|
678
|
-
description: "Implementation team for wave " + waveNumber
|
|
679
|
-
})
|
|
680
|
-
|
|
681
|
-
// MARCAR: Agent Teams usado (para GATE CHECK en siguiente_paso)
|
|
682
|
-
state.current_flow.parallel_agents_used = 3;
|
|
683
|
-
state.current_flow.agent_teams_used = true;
|
|
684
|
-
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
685
|
-
|
|
686
|
-
// Lanzar teammates OPUS en paralelo (UN SOLO MENSAJE)
|
|
687
|
-
// MODELO: OPUS porque escriben código
|
|
688
|
-
// SKILLS: Incluir loaded_skills en CADA prompt de executor
|
|
689
|
-
const skillContext = state.context.loaded_skills?.length > 0
|
|
690
|
-
? '\n\n## SKILLS DE REFERENCIA\n' + state.context.loaded_skills.map(s =>
|
|
691
|
-
'### Skill: ' + s.name + '\n' + s.content
|
|
692
|
-
).join('\n\n---\n\n') + '\n\nUsa los skills como referencia de patrones y estructura de archivos.'
|
|
693
|
-
: '';
|
|
694
|
-
|
|
695
|
-
Task({
|
|
696
|
-
subagent_type: "elsabro-executor",
|
|
697
|
-
model: "opus", // ← OPUS para implementación
|
|
698
|
-
team_name: "elsabro-impl",
|
|
699
|
-
name: "executor-1",
|
|
700
|
-
description: "Ejecutar Plan A",
|
|
701
|
-
prompt: `Implementa el Plan A siguiendo TDD.${skillContext}`
|
|
702
|
-
}) |
|
|
703
|
-
Task({
|
|
704
|
-
subagent_type: "elsabro-executor",
|
|
705
|
-
model: "opus", // ← OPUS para implementación
|
|
706
|
-
team_name: "elsabro-impl",
|
|
707
|
-
name: "executor-2",
|
|
708
|
-
description: "Ejecutar Plan B",
|
|
709
|
-
prompt: `Implementa el Plan B siguiendo TDD.${skillContext}`
|
|
710
|
-
}) |
|
|
711
|
-
Task({
|
|
712
|
-
subagent_type: "elsabro-executor",
|
|
713
|
-
model: "opus", // ← OPUS para implementación
|
|
714
|
-
team_name: "elsabro-impl",
|
|
715
|
-
name: "executor-3",
|
|
716
|
-
description: "Ejecutar Plan C",
|
|
717
|
-
prompt: `Implementa el Plan C siguiendo TDD.${skillContext}`
|
|
718
|
-
})
|
|
719
|
-
|
|
720
|
-
// Al completar cada plan
|
|
721
|
-
TaskUpdate({ taskId: "plan-A-id", status: "completed" })
|
|
262
|
+
// instruction: { type: "condition", nodeId, conditionResult, next }
|
|
263
|
+
// Condition nodes are auto-resolved by the engine - no dispatcher action needed
|
|
264
|
+
// The engine evaluates the condition and determines the next node (true/false branch)
|
|
265
|
+
// result: { next: instruction.next, conditionResult: instruction.conditionResult }
|
|
722
266
|
```
|
|
723
267
|
|
|
724
|
-
###
|
|
268
|
+
### type: "router"
|
|
725
269
|
```javascript
|
|
726
|
-
//
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
})
|
|
731
|
-
|
|
732
|
-
// Spawn 2 teammates OPUS en paralelo
|
|
733
|
-
Task({
|
|
734
|
-
subagent_type: "elsabro-executor",
|
|
735
|
-
model: "opus",
|
|
736
|
-
team_name: "elsabro-arch-validate",
|
|
737
|
-
name: "impl-1",
|
|
738
|
-
description: "Implementar cambios",
|
|
739
|
-
prompt: "..."
|
|
740
|
-
}) |
|
|
741
|
-
Task({
|
|
742
|
-
subagent_type: "feature-dev:code-architect",
|
|
743
|
-
model: "opus", // ← OPUS para decisiones arquitectónicas
|
|
744
|
-
team_name: "elsabro-arch-validate",
|
|
745
|
-
name: "architect-1",
|
|
746
|
-
description: "Validar arquitectura",
|
|
747
|
-
prompt: "Verifica que la implementación sigue los patrones del codebase..."
|
|
748
|
-
})
|
|
749
|
-
|
|
750
|
-
// Shutdown y cleanup
|
|
751
|
-
SendMessage({ type: "shutdown_request", recipient: "impl-1", content: "Validation complete" })
|
|
752
|
-
SendMessage({ type: "shutdown_request", recipient: "architect-1", content: "Validation complete" })
|
|
753
|
-
TeamDelete()
|
|
270
|
+
// instruction: { type: "router", nodeId, routeKey, next }
|
|
271
|
+
// Router nodes are auto-resolved by the engine - no dispatcher action needed
|
|
272
|
+
// The engine evaluates the condition, matches against routes, and determines next node
|
|
273
|
+
// result: { next: instruction.next, routeKey: instruction.routeKey }
|
|
754
274
|
```
|
|
755
275
|
|
|
756
|
-
###
|
|
276
|
+
### type: "agent"
|
|
757
277
|
```javascript
|
|
758
|
-
|
|
278
|
+
// instruction: { type: "agent", nodeId, agent, model, inputs }
|
|
759
279
|
Task({
|
|
760
|
-
subagent_type:
|
|
761
|
-
model:
|
|
762
|
-
|
|
763
|
-
prompt: "..."
|
|
764
|
-
})
|
|
765
|
-
TaskUpdate({ taskId: "plan-X-id", status: "completed" })
|
|
766
|
-
```
|
|
767
|
-
|
|
768
|
-
### Marcar wave como completed cuando todos sus planes terminen
|
|
769
|
-
```javascript
|
|
770
|
-
TaskUpdate({ taskId: "wave-W-id", status: "completed" })
|
|
771
|
-
// → Wave W+1 se desbloquea automáticamente
|
|
772
|
-
|
|
773
|
-
// MARCAR: código fue escrito (para code_review_gate)
|
|
774
|
-
state.current_flow.code_written = true;
|
|
775
|
-
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
776
|
-
```
|
|
777
|
-
|
|
778
|
-
## Paso 4: Verificar Fase (con Agregador)
|
|
779
|
-
|
|
780
|
-
### 4.1 Crear tasks de verificación
|
|
781
|
-
```javascript
|
|
782
|
-
TaskCreate({
|
|
783
|
-
subject: "Verify Functional Requirements",
|
|
784
|
-
activeForm: "Verificando funcionalidad...",
|
|
785
|
-
metadata: { type: "verifier", area: "functional" }
|
|
786
|
-
}) // → verify-functional-id
|
|
787
|
-
|
|
788
|
-
TaskCreate({
|
|
789
|
-
subject: "Verify Quality Metrics",
|
|
790
|
-
activeForm: "Verificando calidad...",
|
|
791
|
-
metadata: { type: "verifier", area: "quality" }
|
|
792
|
-
}) // → verify-quality-id
|
|
793
|
-
|
|
794
|
-
TaskCreate({
|
|
795
|
-
subject: "Verify Phase Completion",
|
|
796
|
-
activeForm: "Agregando resultados...",
|
|
797
|
-
metadata: { type: "aggregator" }
|
|
798
|
-
}) // → verify-aggregate-id
|
|
799
|
-
|
|
800
|
-
// Agregador espera a todos los verificadores
|
|
801
|
-
TaskUpdate({
|
|
802
|
-
taskId: "verify-aggregate-id",
|
|
803
|
-
addBlockedBy: ["verify-functional-id", "verify-quality-id"]
|
|
280
|
+
subagent_type: instruction.agent,
|
|
281
|
+
model: instruction.model,
|
|
282
|
+
prompt: JSON.stringify(instruction.inputs)
|
|
804
283
|
})
|
|
284
|
+
// result: { output: agentOutput }
|
|
805
285
|
```
|
|
806
286
|
|
|
807
|
-
###
|
|
808
|
-
|
|
809
|
-
**OBLIGATORIO:** Usar 3 agentes OPUS para verificación profunda.
|
|
810
|
-
|
|
287
|
+
### type: "parallel" con useAgentTeams: false
|
|
811
288
|
```javascript
|
|
812
|
-
//
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
metadata: { type: "verifier", area: "security", model: "opus" }
|
|
817
|
-
}) // → verify-security-id
|
|
818
|
-
|
|
819
|
-
// Actualizar agregador para esperar los 3
|
|
820
|
-
TaskUpdate({
|
|
821
|
-
taskId: "verify-aggregate-id",
|
|
822
|
-
addBlockedBy: ["verify-functional-id", "verify-quality-id", "verify-security-id"]
|
|
823
|
-
})
|
|
824
|
-
|
|
825
|
-
// Marcar todos como in_progress
|
|
826
|
-
TaskUpdate({ taskId: "verify-functional-id", status: "in_progress" })
|
|
827
|
-
TaskUpdate({ taskId: "verify-quality-id", status: "in_progress" })
|
|
828
|
-
TaskUpdate({ taskId: "verify-security-id", status: "in_progress" })
|
|
829
|
-
|
|
830
|
-
// NOTA: El Agent Team "elsabro-impl" ya fue creado en Paso 3.
|
|
831
|
-
// Reusar el mismo team para la fase de review (un team por sesión).
|
|
832
|
-
// Si el team fue eliminado, crear uno nuevo:
|
|
833
|
-
// TeamCreate({ team_name: "elsabro-review", description: "Review team" })
|
|
834
|
-
|
|
835
|
-
// Lanzar 3 reviewers como teammates EN PARALELO (UN SOLO MENSAJE)
|
|
836
|
-
Task({
|
|
837
|
-
subagent_type: "pr-review-toolkit:code-reviewer",
|
|
838
|
-
model: "opus", // ← OPUS para análisis profundo
|
|
839
|
-
team_name: "elsabro-impl",
|
|
840
|
-
name: "reviewer-1",
|
|
841
|
-
description: "Code review completo",
|
|
842
|
-
prompt: "Revisa los cambios buscando bugs, code smells, mejores prácticas..."
|
|
843
|
-
}) |
|
|
844
|
-
Task({
|
|
845
|
-
subagent_type: "pr-review-toolkit:silent-failure-hunter",
|
|
846
|
-
model: "opus", // ← OPUS para edge cases
|
|
847
|
-
team_name: "elsabro-impl",
|
|
848
|
-
name: "reviewer-2",
|
|
849
|
-
description: "Buscar errores ocultos",
|
|
850
|
-
prompt: "Busca errores silenciosos, try/catch que ocultan errores, fallbacks problemáticos..."
|
|
851
|
-
}) |
|
|
852
|
-
Task({
|
|
853
|
-
subagent_type: "pr-review-toolkit:pr-test-analyzer",
|
|
854
|
-
model: "opus", // ← OPUS para análisis de tests
|
|
855
|
-
team_name: "elsabro-impl",
|
|
856
|
-
name: "reviewer-3",
|
|
857
|
-
description: "Analizar cobertura de tests",
|
|
858
|
-
prompt: "Verifica que los tests cubren los cambios, busca casos críticos sin test..."
|
|
859
|
-
})
|
|
860
|
-
|
|
861
|
-
// Marcar completed al terminar
|
|
862
|
-
TaskUpdate({ taskId: "verify-functional-id", status: "completed" })
|
|
863
|
-
TaskUpdate({ taskId: "verify-quality-id", status: "completed" })
|
|
864
|
-
TaskUpdate({ taskId: "verify-security-id", status: "completed" })
|
|
865
|
-
|
|
866
|
-
// Agregador se desbloquea y genera reporte final
|
|
867
|
-
TaskUpdate({ taskId: "verify-aggregate-id", status: "in_progress" })
|
|
868
|
-
// ... genera VERIFICATION.md ...
|
|
869
|
-
TaskUpdate({ taskId: "verify-aggregate-id", status: "completed" })
|
|
870
|
-
|
|
871
|
-
// MARCAR: code review pasó (para code_review_gate en siguiente_paso)
|
|
872
|
-
if (reviewIssuesCount === 0) {
|
|
873
|
-
state.current_flow.code_review_passed = true;
|
|
874
|
-
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
289
|
+
// instruction: { type: "parallel", nodeId, useAgentTeams: false, branches }
|
|
290
|
+
// Todos haiku -> subagents sueltos (excepcion Rule 8)
|
|
291
|
+
for (const branch of instruction.branches) {
|
|
292
|
+
Task({ subagent_type: branch.agent, model: "haiku", prompt: JSON.stringify(branch.inputs) })
|
|
875
293
|
}
|
|
294
|
+
// result: branches.map(b => ({ id: b.id, output: branchOutput }))
|
|
876
295
|
```
|
|
877
296
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
### Si passed:
|
|
881
|
-
```
|
|
882
|
-
✓ Fase [N] completada
|
|
883
|
-
|
|
884
|
-
Lo que se hizo:
|
|
885
|
-
- [Resumen de tareas]
|
|
886
|
-
|
|
887
|
-
Archivos modificados:
|
|
888
|
-
- [Lista de archivos]
|
|
889
|
-
|
|
890
|
-
Commits creados:
|
|
891
|
-
- [Lista de commits]
|
|
892
|
-
|
|
893
|
-
¿Siguiente paso?
|
|
894
|
-
- Continuar con fase [N+1]: /elsabro:plan [N+1]
|
|
895
|
-
- Verificar manualmente: /elsabro:verify [N]
|
|
896
|
-
```
|
|
897
|
-
|
|
898
|
-
### Si gaps_found:
|
|
899
|
-
```
|
|
900
|
-
⚠ Fase [N] tiene gaps
|
|
901
|
-
|
|
902
|
-
Problemas encontrados:
|
|
903
|
-
- [Gap 1]
|
|
904
|
-
- [Gap 2]
|
|
905
|
-
|
|
906
|
-
Opciones:
|
|
907
|
-
1. Crear planes de corrección: /elsabro:plan [N] --gaps
|
|
908
|
-
2. Ver detalles: cat .planning/[N]-VERIFICATION.md
|
|
909
|
-
3. Continuar de todas formas
|
|
910
|
-
```
|
|
911
|
-
|
|
912
|
-
### Si human_needed:
|
|
913
|
-
```
|
|
914
|
-
👤 Se necesita verificación manual
|
|
915
|
-
|
|
916
|
-
Por favor prueba:
|
|
917
|
-
1. [Instrucción]
|
|
918
|
-
2. [Instrucción]
|
|
919
|
-
|
|
920
|
-
Cuando termines, dime si funcionó o qué problemas encontraste.
|
|
921
|
-
```
|
|
922
|
-
|
|
923
|
-
</process>
|
|
924
|
-
|
|
925
|
-
<agent_teams_cleanup>
|
|
926
|
-
## Agent Teams Cleanup (OBLIGATORIO)
|
|
927
|
-
|
|
928
|
-
**Al completar TODAS las fases de implementacion y review:**
|
|
929
|
-
|
|
297
|
+
### type: "parallel" con useAgentTeams: true
|
|
930
298
|
```javascript
|
|
931
|
-
//
|
|
932
|
-
|
|
933
|
-
for (const name of teammates) {
|
|
934
|
-
SendMessage({ type: "shutdown_request", recipient: name, content: "All phases complete" });
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
// 2. Eliminar team
|
|
938
|
-
TeamDelete();
|
|
939
|
-
|
|
940
|
-
// 3. Registrar en state
|
|
941
|
-
state.current_flow.agent_teams_cleanup = true;
|
|
942
|
-
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
943
|
-
```
|
|
944
|
-
|
|
945
|
-
**GATE**: No mostrar "Siguiente Paso" si agent_teams_cleanup !== true y hubo 2+ agentes.
|
|
946
|
-
</agent_teams_cleanup>
|
|
947
|
-
|
|
948
|
-
<code_review_gate>
|
|
949
|
-
## Code Review Gate (OBLIGATORIO - NO NEGOCIABLE)
|
|
950
|
-
|
|
951
|
-
**ANTES de mostrar "Siguiente Paso" o ofrecer commit, VERIFICAR:**
|
|
952
|
-
|
|
953
|
-
```
|
|
954
|
-
¿Se escribió/modificó código en este comando?
|
|
955
|
-
│
|
|
956
|
-
├─ NO → Continuar a Siguiente Paso
|
|
957
|
-
│
|
|
958
|
-
└─ SÍ → ¿Se ejecutó code review (Paso 4)?
|
|
959
|
-
│
|
|
960
|
-
├─ NO → ABORTAR. Volver a Paso 4.
|
|
961
|
-
│
|
|
962
|
-
└─ SÍ, issues == 0 → Continuar a Siguiente Paso
|
|
963
|
-
```
|
|
964
|
-
|
|
965
|
-
**VIOLACIÓN CRÍTICA**: Mostrar siguiente paso sin code review = ABORTAR OPERACIÓN
|
|
966
|
-
</code_review_gate>
|
|
967
|
-
|
|
968
|
-
<siguiente_paso>
|
|
969
|
-
## Siguiente Paso
|
|
299
|
+
// instruction: { type: "parallel", nodeId, useAgentTeams: true, team, branches }
|
|
300
|
+
// El engine ya compuso team de 5+ miembros via callbacks.js
|
|
970
301
|
|
|
971
|
-
|
|
302
|
+
TeamCreate({ team_name: instruction.team.name, description: instruction.team.description })
|
|
972
303
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
SI: Continuar
|
|
982
|
-
NO: DETENERME. Cargar skills y re-ejecutar.
|
|
983
|
-
|
|
984
|
-
[ ] Si escribi/modifique codigo → ¿ejecute code review?
|
|
985
|
-
SI: Continuar
|
|
986
|
-
NO: DETENERME. Ejecutar code review.
|
|
987
|
-
|
|
988
|
-
[ ] Si use Agent Teams → ¿ejecute SendMessage(shutdown) + TeamDelete?
|
|
989
|
-
SI: Continuar
|
|
990
|
-
NO: DETENERME. Hacer cleanup.
|
|
991
|
-
```
|
|
992
|
-
|
|
993
|
-
**Si alguna respuesta es NO: NO mostrar siguiente paso. Corregir primero.**
|
|
994
|
-
|
|
995
|
-
Al completar, establecer en state.json:
|
|
996
|
-
```javascript
|
|
997
|
-
// GATE CHECK 1: No escribir suggested_next sin code review
|
|
998
|
-
if (state.current_flow?.code_written && !state.current_flow?.code_review_passed) {
|
|
999
|
-
// VIOLACIÓN CRÍTICA — ABORTAR. Volver a code_review_gate.
|
|
1000
|
-
throw new Error("CODE_REVIEW_GATE: Cannot proceed without code review");
|
|
1001
|
-
}
|
|
1002
|
-
// GATE CHECK 2: No proceder sin Agent Teams para fases con 2+ agentes
|
|
1003
|
-
if (state.current_flow?.parallel_agents_used >= 2 && !state.current_flow?.agent_teams_used) {
|
|
1004
|
-
// VIOLACIÓN CRÍTICA — ABORTAR. Volver a agent_teams_gate.
|
|
1005
|
-
throw new Error("AGENT_TEAMS_GATE: Cannot proceed without Agent Teams for 2+ agents");
|
|
304
|
+
for (const member of instruction.team.members) {
|
|
305
|
+
Task({
|
|
306
|
+
subagent_type: member.agent,
|
|
307
|
+
model: member.model,
|
|
308
|
+
team_name: instruction.team.name,
|
|
309
|
+
name: member.name,
|
|
310
|
+
prompt: `Role: ${member.role}. Execute: ${instruction.nodeId}`
|
|
311
|
+
})
|
|
1006
312
|
}
|
|
1007
|
-
state.suggested_next = "verify-work";
|
|
1008
|
-
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
1009
|
-
```
|
|
1010
|
-
|
|
1011
|
-
Mostrar al usuario:
|
|
1012
|
-
```
|
|
1013
|
-
## Siguiente Paso
|
|
1014
|
-
|
|
1015
|
-
→ /elsabro:verify-work — verificar el trabajo completado
|
|
1016
|
-
→ /elsabro:progress — ver el progreso general del proyecto
|
|
1017
|
-
```
|
|
1018
|
-
</siguiente_paso>
|
|
1019
|
-
|
|
1020
|
-
<execution_protocol>
|
|
1021
|
-
## Protocolo de Ejecución
|
|
1022
|
-
|
|
1023
|
-
### Para Cada Tarea
|
|
1024
313
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
- Configuración → NO
|
|
1032
|
-
- UI simple → NO
|
|
1033
|
-
|
|
1034
|
-
3. **Si TDD:**
|
|
1035
|
-
```
|
|
1036
|
-
a. Escribir test que falla
|
|
1037
|
-
b. Verificar que falla por razón correcta
|
|
1038
|
-
c. Implementar código mínimo
|
|
1039
|
-
d. Verificar que test pasa
|
|
1040
|
-
e. Refactorizar si necesario
|
|
1041
|
-
f. Commit
|
|
1042
|
-
```
|
|
1043
|
-
|
|
1044
|
-
4. **Si no TDD:**
|
|
1045
|
-
```
|
|
1046
|
-
a. Implementar código
|
|
1047
|
-
b. Ejecutar <verify>
|
|
1048
|
-
c. Confirmar resultado esperado
|
|
1049
|
-
d. Commit
|
|
1050
|
-
```
|
|
1051
|
-
|
|
1052
|
-
### Commit por Tarea
|
|
1053
|
-
|
|
1054
|
-
```bash
|
|
1055
|
-
git add [archivos]
|
|
1056
|
-
git commit -m "feat([fase]-[plan]): [descripción]"
|
|
314
|
+
// Al completar todos:
|
|
315
|
+
for (const member of instruction.team.members) {
|
|
316
|
+
SendMessage({ type: "shutdown_request", recipient: member.name, content: "Done" })
|
|
317
|
+
}
|
|
318
|
+
TeamDelete()
|
|
319
|
+
// result: team outputs aggregated
|
|
1057
320
|
```
|
|
1058
|
-
</execution_protocol>
|
|
1059
|
-
|
|
1060
|
-
<parallel_execution>
|
|
1061
|
-
## Ejecución Paralela con Tasks
|
|
1062
321
|
|
|
1063
|
-
###
|
|
1064
|
-
- Múltiples planes en la misma wave
|
|
1065
|
-
- Tareas independientes (sin dependencias)
|
|
1066
|
-
|
|
1067
|
-
### Cómo paralelizar CON TRACKING
|
|
322
|
+
### type: "team"
|
|
1068
323
|
```javascript
|
|
1069
|
-
//
|
|
1070
|
-
|
|
1071
|
-
const planBTask = TaskCreate({ subject: "Plan B", activeForm: "Ejecutando Plan B..." })
|
|
1072
|
-
const planCTask = TaskCreate({ subject: "Plan C", activeForm: "Ejecutando Plan C..." })
|
|
1073
|
-
|
|
1074
|
-
// 2. Marcar todas como in_progress
|
|
1075
|
-
TaskUpdate({ taskId: planATask.id, status: "in_progress" })
|
|
1076
|
-
TaskUpdate({ taskId: planBTask.id, status: "in_progress" })
|
|
1077
|
-
TaskUpdate({ taskId: planCTask.id, status: "in_progress" })
|
|
1078
|
-
|
|
1079
|
-
// 3. Lanzar agentes EN UN SOLO MENSAJE
|
|
1080
|
-
Task(elsabro-executor) Plan A |
|
|
1081
|
-
Task(elsabro-executor) Plan B |
|
|
1082
|
-
Task(elsabro-executor) Plan C
|
|
1083
|
-
|
|
1084
|
-
// 4. Al completar cada uno
|
|
1085
|
-
TaskUpdate({ taskId: planATask.id, status: "completed" })
|
|
1086
|
-
// ...
|
|
1087
|
-
```
|
|
1088
|
-
|
|
1089
|
-
### Visualización automática (Ctrl+T)
|
|
1090
|
-
```
|
|
1091
|
-
📋 Execute Phase 3:
|
|
1092
|
-
✅ Wave 1 - Completed (3/3 plans)
|
|
1093
|
-
🔧 Wave 2 - Executing Plan D... (1/2 plans done)
|
|
1094
|
-
✅ Plan D - Completed
|
|
1095
|
-
⏳ Plan E - In Progress
|
|
1096
|
-
⏳ Wave 3 - Blocked by Wave 2
|
|
1097
|
-
⏳ Verification - Blocked by Wave 3
|
|
1098
|
-
```
|
|
324
|
+
// instruction: { type: "team", nodeId, team, coordinator, workers }
|
|
325
|
+
// Dedicated team node with coordinator-worker pattern
|
|
1099
326
|
|
|
1100
|
-
|
|
1101
|
-
- Planes con dependencias entre sí (usar `blockedBy`)
|
|
1102
|
-
- Plan 2 necesita output de Plan 1
|
|
1103
|
-
- Modifican los mismos archivos
|
|
327
|
+
TeamCreate({ team_name: instruction.team.name, description: instruction.team.description })
|
|
1104
328
|
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
329
|
+
// Spawn coordinator
|
|
330
|
+
Task({
|
|
331
|
+
subagent_type: instruction.coordinator.agent,
|
|
332
|
+
model: instruction.coordinator.model,
|
|
333
|
+
team_name: instruction.team.name,
|
|
334
|
+
name: instruction.coordinator.name,
|
|
335
|
+
prompt: `Role: Coordinator. ${instruction.coordinator.instructions}`
|
|
1111
336
|
})
|
|
1112
|
-
// Plan B no puede empezar hasta que Plan A complete
|
|
1113
|
-
```
|
|
1114
|
-
</parallel_execution>
|
|
1115
337
|
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
- Más de 2 agentes en wave: Considerar worktrees
|
|
1125
|
-
- Archivos compartidos entre agentes: OBLIGATORIO usar worktrees
|
|
1126
|
-
|
|
1127
|
-
### Flujo con Worktrees
|
|
1128
|
-
|
|
1129
|
-
```javascript
|
|
1130
|
-
// Antes de ejecutar wave paralela
|
|
1131
|
-
if (shouldUseWorktrees(wave, profile)) {
|
|
1132
|
-
const agents = wave.plans.map(p => p.agent);
|
|
1133
|
-
|
|
1134
|
-
// Crear worktrees
|
|
1135
|
-
Bash(`./scripts/setup-parallel-worktrees.sh create ${agents.join(' ')}`);
|
|
1136
|
-
|
|
1137
|
-
// OBLIGATORIO: Crear Agent Team para worktree paralelo (Rule 8)
|
|
1138
|
-
TeamCreate({
|
|
1139
|
-
team_name: "elsabro-worktree",
|
|
1140
|
-
description: "Parallel worktree execution for wave " + wave.id
|
|
338
|
+
// Spawn workers
|
|
339
|
+
for (const worker of instruction.workers) {
|
|
340
|
+
Task({
|
|
341
|
+
subagent_type: worker.agent,
|
|
342
|
+
model: worker.model,
|
|
343
|
+
team_name: instruction.team.name,
|
|
344
|
+
name: worker.name,
|
|
345
|
+
prompt: `Role: ${worker.role}. ${worker.instructions}`
|
|
1141
346
|
})
|
|
1142
|
-
|
|
1143
|
-
// Ejecutar agentes como teammates en sus worktrees
|
|
1144
|
-
for (let i = 0; i < agents.length; i++) {
|
|
1145
|
-
Task({
|
|
1146
|
-
subagent_type: agents[i],
|
|
1147
|
-
team_name: "elsabro-worktree",
|
|
1148
|
-
name: `wt-agent-${i + 1}`,
|
|
1149
|
-
prompt: `Trabaja en worktree: ../elsabro-worktrees/${agents[i]}-wt/`
|
|
1150
|
-
});
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
// Shutdown teammates y cleanup team
|
|
1154
|
-
for (let i = 0; i < agents.length; i++) {
|
|
1155
|
-
SendMessage({
|
|
1156
|
-
type: "shutdown_request",
|
|
1157
|
-
recipient: `wt-agent-${i + 1}`,
|
|
1158
|
-
content: "Worktree work complete"
|
|
1159
|
-
});
|
|
1160
|
-
}
|
|
1161
|
-
TeamDelete();
|
|
1162
|
-
|
|
1163
|
-
// Merge y cleanup worktrees al completar
|
|
1164
|
-
Bash(`./scripts/setup-parallel-worktrees.sh complete ${agents.join(' ')}`);
|
|
1165
347
|
}
|
|
1166
|
-
```
|
|
1167
|
-
|
|
1168
|
-
### Resolución de Conflictos
|
|
1169
|
-
|
|
1170
|
-
Si hay conflictos en el merge:
|
|
1171
|
-
1. Pausar ejecución
|
|
1172
|
-
2. Mostrar conflictos al usuario
|
|
1173
|
-
3. Opciones: resolver manual, descartar branch, o abortar
|
|
1174
|
-
</worktrees_integration>
|
|
1175
348
|
|
|
1176
|
-
|
|
1177
|
-
|
|
349
|
+
// Coordinator sends messages to workers via SendMessage
|
|
350
|
+
// (handled internally by the agent - dispatcher just spawns them)
|
|
1178
351
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
```javascript
|
|
1184
|
-
// Antes de cualquier wave, inicializar contratos
|
|
1185
|
-
const registryValidator = new ContractRegistryValidator();
|
|
1186
|
-
const sessionValidator = new ContractSessionValidator();
|
|
1187
|
-
const timeoutHandler = new ContractTimeoutHandler();
|
|
1188
|
-
const retryPolicy = new ContractRetryPolicy({
|
|
1189
|
-
maxAttempts: 3,
|
|
1190
|
-
baseDelayMs: 1000,
|
|
1191
|
-
backoffMultiplier: 2
|
|
1192
|
-
});
|
|
1193
|
-
const errorAggregator = new ContractErrorAggregator("quorum");
|
|
1194
|
-
const severityClassifier = new ContractSeverityClassifier();
|
|
1195
|
-
|
|
1196
|
-
// 1. Validar registry
|
|
1197
|
-
const agentsNeeded = dispatcher.exploration.agents;
|
|
1198
|
-
const batch = await registryValidator.validateBatch(agentsNeeded);
|
|
1199
|
-
if (!batch.canProceed) {
|
|
1200
|
-
return handleCriticalError("REGISTRY_MISSING", batch.details);
|
|
352
|
+
// Al completar:
|
|
353
|
+
SendMessage({ type: "shutdown_request", recipient: instruction.coordinator.name, content: "Done" })
|
|
354
|
+
for (const worker of instruction.workers) {
|
|
355
|
+
SendMessage({ type: "shutdown_request", recipient: worker.name, content: "Done" })
|
|
1201
356
|
}
|
|
1202
|
-
|
|
1203
|
-
//
|
|
1204
|
-
const session = await sessionValidator.load();
|
|
1205
|
-
if (!session.success) {
|
|
1206
|
-
return handleCriticalError("SESSION_INVALID", session);
|
|
1207
|
-
}
|
|
1208
|
-
```
|
|
1209
|
-
|
|
1210
|
-
### Error Handling por Fase
|
|
1211
|
-
|
|
1212
|
-
#### Fase Exploracion (HAIKU x3)
|
|
1213
|
-
```javascript
|
|
1214
|
-
// Policy: continue_all (exploracion es best-effort)
|
|
1215
|
-
errorAggregator.setPolicy("continue_all");
|
|
1216
|
-
|
|
1217
|
-
const results = await Promise.all([
|
|
1218
|
-
executeWithRetry("explore-1", () => Task(Explore)),
|
|
1219
|
-
executeWithRetry("explore-2", () => Task(CodeExplorer)),
|
|
1220
|
-
executeWithRetry("explore-3", () => Task(Plan))
|
|
1221
|
-
]);
|
|
1222
|
-
|
|
1223
|
-
const summary = await errorAggregator.aggregate("exploration", results);
|
|
1224
|
-
// continue_all: siempre continua, reporta al final
|
|
357
|
+
TeamDelete()
|
|
358
|
+
// result: { coordinatorOutput, workerOutputs }
|
|
1225
359
|
```
|
|
1226
360
|
|
|
1227
|
-
|
|
361
|
+
### type: "interrupt"
|
|
1228
362
|
```javascript
|
|
1229
|
-
//
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
// OBLIGATORIO: Crear Agent Team para wave de implementacion (Rule 8)
|
|
1236
|
-
TeamCreate({
|
|
1237
|
-
team_name: "elsabro-wave-impl",
|
|
1238
|
-
description: "Implementation wave " + wave.id
|
|
1239
|
-
})
|
|
1240
|
-
|
|
1241
|
-
const results = await Promise.all(
|
|
1242
|
-
wave.plans.map((plan, i) =>
|
|
1243
|
-
executeWithRetry(plan.id, () => Task({
|
|
1244
|
-
subagent_type: "elsabro-executor",
|
|
1245
|
-
team_name: "elsabro-wave-impl",
|
|
1246
|
-
name: `wave-executor-${i + 1}`,
|
|
1247
|
-
prompt: plan
|
|
1248
|
-
}))
|
|
1249
|
-
)
|
|
1250
|
-
);
|
|
1251
|
-
|
|
1252
|
-
// Shutdown teammates y cleanup team
|
|
1253
|
-
wave.plans.forEach((_, i) => {
|
|
1254
|
-
SendMessage({
|
|
1255
|
-
type: "shutdown_request",
|
|
1256
|
-
recipient: `wave-executor-${i + 1}`,
|
|
1257
|
-
content: "Wave complete"
|
|
1258
|
-
});
|
|
1259
|
-
});
|
|
1260
|
-
TeamDelete();
|
|
1261
|
-
|
|
1262
|
-
const summary = await errorAggregator.aggregate(wave.id, results);
|
|
1263
|
-
|
|
1264
|
-
if (summary.decision === "STOP") {
|
|
1265
|
-
// Quorum no alcanzado
|
|
1266
|
-
await notifyUserImmediately({
|
|
1267
|
-
severity: "CRITICAL",
|
|
1268
|
-
message: `Wave ${wave.id} fallo: ${summary.reason}`,
|
|
1269
|
-
options: ["retry", "debug", "abort"]
|
|
1270
|
-
});
|
|
1271
|
-
return;
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
timeoutHandler.completeTimeout(wave.id);
|
|
1275
|
-
}
|
|
363
|
+
// instruction: { type: "interrupt", nodeId, display, routes }
|
|
364
|
+
const answer = AskUserQuestion({
|
|
365
|
+
questions: [{ question: instruction.display.title, options: instruction.display.options }]
|
|
366
|
+
})
|
|
367
|
+
// result: routeKey seleccionado por usuario (maps to instruction.routes)
|
|
1276
368
|
```
|
|
1277
369
|
|
|
1278
|
-
|
|
370
|
+
### type: "sequence"
|
|
1279
371
|
```javascript
|
|
1280
|
-
//
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
const verificationResults = await Promise.all([
|
|
1284
|
-
executeWithRetry("verify-code", () => Task(CodeReviewer)),
|
|
1285
|
-
executeWithRetry("verify-silent", () => Task(SilentFailureHunter)),
|
|
1286
|
-
executeWithRetry("verify-tests", () => Task(TestAnalyzer))
|
|
1287
|
-
]);
|
|
1288
|
-
|
|
1289
|
-
const summary = await errorAggregator.aggregate("verification", verificationResults);
|
|
1290
|
-
|
|
1291
|
-
if (summary.decision === "STOP") {
|
|
1292
|
-
// Cualquier fallo en verificacion es critico
|
|
1293
|
-
await displayVerificationFailure(summary);
|
|
1294
|
-
return;
|
|
1295
|
-
}
|
|
1296
|
-
```
|
|
1297
|
-
|
|
1298
|
-
### Escalamiento de Errores
|
|
372
|
+
// instruction: { type: "sequence", nodeId, steps }
|
|
373
|
+
// Execute each step sequentially, capturing outputs
|
|
1299
374
|
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
│ ESCALAMIENTO DE ERRORES │
|
|
1303
|
-
├─────────────────────────────────────────────────────────────────────┤
|
|
1304
|
-
│ │
|
|
1305
|
-
│ Error detectado │
|
|
1306
|
-
│ ↓ │
|
|
1307
|
-
│ SeverityClassifier.classify(error) │
|
|
1308
|
-
│ ↓ │
|
|
1309
|
-
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
1310
|
-
│ │ CRITICAL: Notificar inmediatamente, STOP │ │
|
|
1311
|
-
│ │ HIGH: Notificar, ofrecer opciones [fix/debug/abort] │ │
|
|
1312
|
-
│ │ MEDIUM: Agregar a resumen, continuar │ │
|
|
1313
|
-
│ │ LOW: Log para debugging, continuar │ │
|
|
1314
|
-
│ └─────────────────────────────────────────────────────────────┘ │
|
|
1315
|
-
│ │
|
|
1316
|
-
└─────────────────────────────────────────────────────────────────────┘
|
|
1317
|
-
```
|
|
375
|
+
const stepOutputs = {};
|
|
376
|
+
const context = getCurrentContext(); // From checkpoint
|
|
1318
377
|
|
|
1319
|
-
|
|
378
|
+
for (const step of instruction.steps) {
|
|
379
|
+
const stepLabel = step.as || step.action || 'unnamed';
|
|
380
|
+
|
|
381
|
+
switch (step.action) {
|
|
382
|
+
case 'bash': {
|
|
383
|
+
// Resolve template variables in command (e.g., {{inputs.task}})
|
|
384
|
+
const resolvedCommand = resolveTemplate(step.command, context);
|
|
385
|
+
|
|
386
|
+
// Execute bash command with timeout
|
|
387
|
+
const bashResult = Bash({
|
|
388
|
+
command: resolvedCommand,
|
|
389
|
+
timeout: step.timeout || 120000,
|
|
390
|
+
description: `Execute ${stepLabel} - ${step.action}`
|
|
391
|
+
});
|
|
1320
392
|
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
throw new CriticalError(error, classification);
|
|
393
|
+
stepOutputs[stepLabel] = {
|
|
394
|
+
output: bashResult.stdout,
|
|
395
|
+
exitCode: bashResult.exitCode || 0,
|
|
396
|
+
stderr: bashResult.stderr || ''
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// Special handling for skill-discovery: parse JSON output
|
|
400
|
+
if (resolvedCommand.includes('skill-discovery.sh')) {
|
|
401
|
+
try {
|
|
402
|
+
const discoveryData = JSON.parse(bashResult.stdout);
|
|
403
|
+
stepOutputs[stepLabel].discovered = discoveryData.discovered || [];
|
|
404
|
+
stepOutputs[stepLabel].recommended = discoveryData.recommended || {};
|
|
405
|
+
|
|
406
|
+
// Log skill discovery execution
|
|
407
|
+
logInfo(`Skill discovery found ${discoveryData.discovered?.length || 0} installed skills`);
|
|
408
|
+
if (discoveryData.recommended?.skills?.length > 0) {
|
|
409
|
+
logInfo(`Recommended skills: ${discoveryData.recommended.skills.join(', ')}`);
|
|
410
|
+
}
|
|
411
|
+
} catch (e) {
|
|
412
|
+
logWarn(`Failed to parse skill-discovery output: ${e.message}`);
|
|
1342
413
|
}
|
|
1343
414
|
}
|
|
415
|
+
break;
|
|
1344
416
|
}
|
|
1345
|
-
);
|
|
1346
|
-
}
|
|
1347
|
-
```
|
|
1348
|
-
|
|
1349
|
-
### Guardar Estado de Errores
|
|
1350
|
-
|
|
1351
|
-
Al completar cada wave (exito o fallo):
|
|
1352
|
-
|
|
1353
|
-
```javascript
|
|
1354
|
-
// Actualizar state.json con errores para recovery
|
|
1355
|
-
state.errors = {
|
|
1356
|
-
count: allErrors.length,
|
|
1357
|
-
lastError: allErrors[allErrors.length - 1],
|
|
1358
|
-
byWave: errorsByWave,
|
|
1359
|
-
bySeverity: {
|
|
1360
|
-
CRITICAL: criticalErrors,
|
|
1361
|
-
HIGH: highErrors,
|
|
1362
|
-
MEDIUM: mediumErrors,
|
|
1363
|
-
LOW: lowErrors
|
|
1364
|
-
}
|
|
1365
|
-
};
|
|
1366
417
|
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
### Clasificacion de Errores (Referencia Rapida)
|
|
1378
|
-
|
|
1379
|
-
| Severity | Emoji | Accion |
|
|
1380
|
-
|----------|-------|--------|
|
|
1381
|
-
| CRITICAL | Parar | No puede continuar de ninguna forma |
|
|
1382
|
-
| HIGH | Preguntar | Ofrecer opciones: fix/debug/abort |
|
|
1383
|
-
| MEDIUM | Warning | Mostrar y continuar |
|
|
1384
|
-
| LOW | Info | Log y continuar |
|
|
1385
|
-
|
|
1386
|
-
### Reglas de Desviacion
|
|
1387
|
-
|
|
1388
|
-
| Situacion | Severity | Accion |
|
|
1389
|
-
|-----------|----------|--------|
|
|
1390
|
-
| Bug que impide continuar | HIGH | Auto-fix (3 intentos) |
|
|
1391
|
-
| Falta validacion critica | HIGH | Auto-add |
|
|
1392
|
-
| Falta dependencia | HIGH | Auto-install |
|
|
1393
|
-
| Cambio arquitectonico | HIGH | CHECKPOINT |
|
|
1394
|
-
| Tests fallan | HIGH | Auto-fix (5 intentos) |
|
|
1395
|
-
| Lint warnings | HIGH | Auto-fix (incluido en quality gate) |
|
|
1396
|
-
|
|
1397
|
-
### Actualizar Estado con Tasks
|
|
1398
|
-
|
|
1399
|
-
Despues de errores, actualizar tanto SESSION-STATE.json como Tasks:
|
|
1400
|
-
|
|
1401
|
-
```javascript
|
|
1402
|
-
// 1. Actualizar task con metadata de error
|
|
1403
|
-
TaskUpdate({
|
|
1404
|
-
taskId: "plan-X-id",
|
|
1405
|
-
metadata: {
|
|
1406
|
-
error: {
|
|
1407
|
-
code: "TESTS_FAILED",
|
|
1408
|
-
severity: "HIGH",
|
|
1409
|
-
at: "2024-01-20T15:25:00Z",
|
|
1410
|
-
attempt: 3,
|
|
1411
|
-
maxAttempts: 3
|
|
418
|
+
case 'agent': {
|
|
419
|
+
const resolvedInputs = resolveTemplate(step.inputs || {}, context);
|
|
420
|
+
const agentResult = Task({
|
|
421
|
+
subagent_type: step.agent,
|
|
422
|
+
model: step.model || 'sonnet',
|
|
423
|
+
prompt: JSON.stringify(resolvedInputs)
|
|
424
|
+
});
|
|
425
|
+
stepOutputs[stepLabel] = { result: agentResult };
|
|
426
|
+
break;
|
|
1412
427
|
}
|
|
1413
|
-
}
|
|
1414
|
-
})
|
|
1415
|
-
|
|
1416
|
-
// 2. Si retry exhausted, NO marcar completed
|
|
1417
|
-
// Crear subtask de fix
|
|
1418
|
-
TaskCreate({
|
|
1419
|
-
subject: "Fix: Tests failing in Plan X",
|
|
1420
|
-
description: "3 retry attempts exhausted, needs manual fix",
|
|
1421
|
-
activeForm: "Fixing test failures...",
|
|
1422
|
-
metadata: { type: "bugfix", parentPlan: "plan-X-id" }
|
|
1423
|
-
}) // → bugfix-id
|
|
1424
|
-
|
|
1425
|
-
// 3. Bloquear verificacion hasta que bugfix complete
|
|
1426
|
-
TaskUpdate({
|
|
1427
|
-
taskId: "verify-aggregate-id",
|
|
1428
|
-
addBlockedBy: ["bugfix-id"]
|
|
1429
|
-
})
|
|
1430
|
-
|
|
1431
|
-
// 4. Tambien actualizar SESSION-STATE.json para compatibilidad
|
|
1432
|
-
```
|
|
1433
|
-
|
|
1434
|
-
### Rollback de Estado con Tasks
|
|
1435
428
|
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
429
|
+
case 'read_files': {
|
|
430
|
+
const files = resolveTemplate(step.files, context);
|
|
431
|
+
const fileContents = [];
|
|
432
|
+
for (const filePath of files) {
|
|
433
|
+
try {
|
|
434
|
+
const content = Read({ file_path: filePath });
|
|
435
|
+
fileContents.push({ path: filePath, content });
|
|
436
|
+
} catch (e) {
|
|
437
|
+
if (!step.optional) throw e;
|
|
438
|
+
fileContents.push({ path: filePath, content: null, error: e.message });
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
stepOutputs[stepLabel] = { content: fileContents };
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
1441
444
|
|
|
1442
|
-
|
|
1443
|
-
|
|
445
|
+
default:
|
|
446
|
+
// Unimplemented step actions: log warning
|
|
447
|
+
logWarn(`Step action "${step.action}" not implemented in dispatcher, skipping`);
|
|
448
|
+
stepOutputs[stepLabel] = { skipped: true, reason: `Action not implemented` };
|
|
449
|
+
}
|
|
450
|
+
}
|
|
1444
451
|
|
|
1445
|
-
//
|
|
1446
|
-
TaskUpdate({ taskId: "plan-X-id", status: "completed" })
|
|
452
|
+
// result: stepOutputs with all step execution results
|
|
1447
453
|
```
|
|
1448
454
|
|
|
1449
|
-
|
|
455
|
+
## 4. Observabilidad (4 Senales)
|
|
1450
456
|
|
|
1451
|
-
|
|
1452
|
-
// Listar todas las tasks
|
|
1453
|
-
TaskList()
|
|
457
|
+
Despues de cada `step` y `complete`, emitir al log:
|
|
1454
458
|
|
|
1455
|
-
// Ver detalles de una task especifica
|
|
1456
|
-
TaskGet({ taskId: "plan-X-id" })
|
|
1457
459
|
```
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
<summary_creation>
|
|
1461
|
-
## Creación de Summary
|
|
1462
|
-
|
|
1463
|
-
Al completar un plan:
|
|
1464
|
-
|
|
1465
|
-
```markdown
|
|
1466
|
-
# Summary: [Nombre del Plan]
|
|
1467
|
-
|
|
1468
|
-
## Completado
|
|
1469
|
-
- [x] Tarea 1
|
|
1470
|
-
- [x] Tarea 2
|
|
1471
|
-
|
|
1472
|
-
## Archivos
|
|
1473
|
-
- path/to/file.ts
|
|
1474
|
-
- path/to/another.ts
|
|
1475
|
-
|
|
1476
|
-
## Commits
|
|
1477
|
-
- abc1234: feat(01-01): [descripción]
|
|
1478
|
-
- def5678: feat(01-01): [descripción]
|
|
1479
|
-
|
|
1480
|
-
## Verificación
|
|
1481
|
-
- [x] Build: npm run build
|
|
1482
|
-
- [x] Tests: npm run test
|
|
460
|
+
.elsabro/telemetry/execution-{flowId}.jsonl
|
|
1483
461
|
```
|
|
1484
|
-
</summary_creation>
|
|
1485
462
|
|
|
1486
|
-
|
|
1487
|
-
|
|
463
|
+
| Senal | Cuando | Datos |
|
|
464
|
+
|---|---|---|
|
|
465
|
+
| `node_skip_counter` | step auto-resuelve N nodos | `{ signal: "node_skip", skipped: N, nodeId }` (NOTE: requires cli.js patch to include nodesVisited in step output) |
|
|
466
|
+
| `gate_bypass_detector` | step resuelve condition/router | `{ signal: "gate_check", nodeId, result: true/false }` |
|
|
467
|
+
| `execution_time_anomaly` | complete tarda >120s | `{ signal: "time_anomaly", nodeId, duration_ms }` |
|
|
468
|
+
| `diff_size_vs_review_ratio` | complete de parallel_review | `{ signal: "review_ratio", files, comments }` |
|
|
1488
469
|
|
|
1489
|
-
|
|
1490
|
-
The CLI is at `flow-engine/src/cli.js` (also `npm run flow` or `elsabro-flow`).
|
|
1491
|
-
|
|
1492
|
-
### Quick Reference
|
|
470
|
+
Formato: una linea JSON por evento (JSON Lines). Append-only.
|
|
1493
471
|
|
|
1494
472
|
```bash
|
|
1495
|
-
|
|
1496
|
-
node flow-engine/src/cli.js validate --flow flows/development-flow.json
|
|
1497
|
-
|
|
1498
|
-
# Dry-run (full path, mock callbacks)
|
|
1499
|
-
node flow-engine/src/cli.js dry-run --flow flows/development-flow.json --task "feature" --profile default
|
|
1500
|
-
|
|
1501
|
-
# Step-based execution
|
|
1502
|
-
node flow-engine/src/cli.js init --flow flows/development-flow.json --task "build auth" --profile default
|
|
1503
|
-
node flow-engine/src/cli.js step --flow flows/development-flow.json
|
|
1504
|
-
node flow-engine/src/cli.js complete --flow flows/development-flow.json --result '{"output": "done"}'
|
|
1505
|
-
node flow-engine/src/cli.js status --flow flows/development-flow.json
|
|
473
|
+
echo '{"ts":"'$(date -u +%FT%TZ)'","signal":"node_skip","nodeId":"...","skipped":3}' >> .elsabro/telemetry/execution-${FLOW_ID}.jsonl
|
|
1506
474
|
```
|
|
1507
|
-
</engine_integration>
|
|
1508
475
|
|
|
1509
|
-
|
|
1510
|
-
## CLI-Driven Execution (v7.0.0)
|
|
476
|
+
## 5. Finalizar
|
|
1511
477
|
|
|
1512
|
-
|
|
1513
|
-
and stops at actionable nodes, emitting a JSON instruction. Claude follows the instruction,
|
|
1514
|
-
then calls `complete` to advance.
|
|
478
|
+
Cuando el loop retorna `{ finished: true }`:
|
|
1515
479
|
|
|
1516
|
-
|
|
480
|
+
1. Actualizar `.elsabro/state.json` siguiendo @references/state-sync.md:
|
|
481
|
+
- Agregar a `history`
|
|
482
|
+
- Pasar `context.changed_files`, `context.commits`, `context.tests_added` para verify-work
|
|
483
|
+
- Limpiar `current_flow`
|
|
484
|
+
- Establecer `suggested_next: "verify-work"`
|
|
485
|
+
2. Actualizar `.elsabro/context.md` con resumen legible
|
|
1517
486
|
|
|
1518
|
-
|
|
1519
|
-
init → [step → execute instruction → complete]* → step (finished)
|
|
1520
|
-
```
|
|
1521
|
-
|
|
1522
|
-
1. **init**: Creates checkpoint at entry node
|
|
1523
|
-
2. **step**: Auto-resolves through conditions/routers, stops at actionable node, returns instruction JSON
|
|
1524
|
-
3. **execute instruction**: Claude executes based on instruction type (see below)
|
|
1525
|
-
4. **complete**: Stores result, advances checkpoint to next node
|
|
1526
|
-
5. Repeat step→execute→complete until `step` returns `{ finished: true }`
|
|
1527
|
-
|
|
1528
|
-
### Instruction Types
|
|
1529
|
-
|
|
1530
|
-
#### `type: "agent"` → Launch single subagent
|
|
1531
|
-
```javascript
|
|
1532
|
-
// instruction: { type: "agent", nodeId, agent, model, inputs }
|
|
1533
|
-
Task({
|
|
1534
|
-
subagent_type: instruction.agent,
|
|
1535
|
-
model: instruction.model,
|
|
1536
|
-
prompt: `Execute task with inputs: ${JSON.stringify(instruction.inputs)}`
|
|
1537
|
-
})
|
|
1538
|
-
```
|
|
1539
|
-
|
|
1540
|
-
#### `type: "parallel"` with `useAgentTeams: false` → Launch subagents (haiku exploration)
|
|
1541
|
-
```javascript
|
|
1542
|
-
// instruction: { type: "parallel", nodeId, useAgentTeams: false, branches }
|
|
1543
|
-
// All-haiku branches → regular subagents, no team needed
|
|
1544
|
-
for (const branch of instruction.branches) {
|
|
1545
|
-
Task({
|
|
1546
|
-
subagent_type: branch.agent,
|
|
1547
|
-
model: "haiku",
|
|
1548
|
-
prompt: `Execute: ${JSON.stringify(branch.inputs)}`
|
|
1549
|
-
})
|
|
1550
|
-
}
|
|
1551
|
-
```
|
|
1552
|
-
|
|
1553
|
-
#### `type: "parallel"` with `useAgentTeams: true` → Use Agent Teams (min 5 members)
|
|
1554
|
-
```javascript
|
|
1555
|
-
// instruction: { type: "parallel", nodeId, useAgentTeams: true, team: { name, members[5+] }, branches }
|
|
1556
|
-
//
|
|
1557
|
-
// The engine composed a team of 5+ members:
|
|
1558
|
-
// - Core branch agents (from flow definition)
|
|
1559
|
-
// - Support agents (verifier, qa, debugger, etc.) padded to minimum 5
|
|
1560
|
-
//
|
|
1561
|
-
// Claude Code handles team lifecycle automatically:
|
|
1562
|
-
|
|
1563
|
-
TeamCreate({
|
|
1564
|
-
team_name: instruction.team.name,
|
|
1565
|
-
description: instruction.team.description
|
|
1566
|
-
})
|
|
487
|
+
## 6. Siguiente Paso
|
|
1567
488
|
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
subagent_type: member.agent,
|
|
1572
|
-
model: member.model,
|
|
1573
|
-
team_name: instruction.team.name,
|
|
1574
|
-
name: member.name,
|
|
1575
|
-
prompt: `Role: ${member.role}. Execute your part of ${instruction.nodeId}.`
|
|
1576
|
-
})
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
// When all complete:
|
|
1580
|
-
for (const member of instruction.team.members) {
|
|
1581
|
-
SendMessage({ type: "shutdown_request", recipient: member.name, content: "Done" })
|
|
1582
|
-
}
|
|
1583
|
-
TeamDelete()
|
|
1584
|
-
```
|
|
489
|
+
**Gate checks** (por referencia a @references/enforcement-rules.md):
|
|
490
|
+
- Rule 7: Si se escribio codigo -> code review debe haber pasado (el flow lo garantiza via parallel_review)
|
|
491
|
+
- Rule 8: Si se usaron 2+ agentes -> Agent Teams debe haberse usado (callbacks.js lo garantiza)
|
|
1585
492
|
|
|
1586
|
-
#### `type: "interrupt"` → Ask user
|
|
1587
|
-
```javascript
|
|
1588
|
-
// instruction: { type: "interrupt", nodeId, display, routes, reason }
|
|
1589
|
-
AskUserQuestion({
|
|
1590
|
-
questions: [{
|
|
1591
|
-
question: instruction.display.title,
|
|
1592
|
-
options: instruction.display.options.map(o => ({ label: o.label, description: o.id }))
|
|
1593
|
-
}]
|
|
1594
|
-
})
|
|
1595
|
-
// User's selection maps to a route key → pass as result to complete
|
|
1596
493
|
```
|
|
494
|
+
Siguiente Paso
|
|
1597
495
|
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
// instruction: { type: "sequence", nodeId, steps }
|
|
1601
|
-
for (const step of instruction.steps) {
|
|
1602
|
-
if (step.action === "bash") Bash(step.command)
|
|
1603
|
-
if (step.action === "agent") Task({ subagent_type: step.agent, ... })
|
|
1604
|
-
if (step.action === "read_files") Read(step.files)
|
|
1605
|
-
}
|
|
496
|
+
-> /elsabro:verify-work -- verificar el trabajo completado
|
|
497
|
+
-> /elsabro:progress -- ver el progreso general del proyecto
|
|
1606
498
|
```
|
|
1607
|
-
|
|
1608
|
-
### Team Compositions (from validate)
|
|
1609
|
-
|
|
1610
|
-
| Node | Core | Padded to 5 with | Total |
|
|
1611
|
-
|------|------|-------------------|-------|
|
|
1612
|
-
| `standard_analyze` | 4 haiku | NO TEAM (subagents) | 4 subagents |
|
|
1613
|
-
| `parallel_implementation` | executor, qa | + verifier, analyst, debugger | 5 |
|
|
1614
|
-
| `fix_issues` | debugger, error-detective, refactor | + verifier, qa | 5 |
|
|
1615
|
-
| `parallel_review` | code-reviewer, silent-failure, staff | + verifier, qa | 5 |
|
|
1616
|
-
|
|
1617
|
-
### Rules
|
|
1618
|
-
|
|
1619
|
-
1. **Minimum 5 teammates** — Every Agent Team has at least 5 members
|
|
1620
|
-
2. **Haiku exception** — All-haiku parallel nodes use subagents, not teams
|
|
1621
|
-
3. **One team at a time** — Cleanup before creating next team
|
|
1622
|
-
4. **Cleanup required** — SendMessage shutdown + TeamDelete after each team
|
|
1623
|
-
</cli_driven_execution>
|