vgxness 1.12.0 → 1.14.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.
@@ -89,6 +89,7 @@ export class SkillResolver {
89
89
  ...(agent.value?.name !== undefined ? { agentName: agent.value.name } : {}),
90
90
  ...(input.workflow !== undefined ? { workflow: input.workflow } : {}),
91
91
  ...(input.phase !== undefined ? { phase: input.phase } : {}),
92
+ ...(input.intentSignals !== undefined && input.intentSignals.length > 0 ? { intentSignals: normalizedIntentSignals(input.intentSignals) } : {}),
92
93
  ...(input.providerAdapter !== undefined ? { providerAdapter: input.providerAdapter } : {}),
93
94
  ...(input.runId !== undefined ? { runId: input.runId } : {}),
94
95
  },
@@ -121,6 +122,8 @@ export class SkillResolver {
121
122
  }
122
123
  for (const phase of phases)
123
124
  targets.push({ targetType: 'workflow-phase', targetKey: phase });
125
+ for (const signal of normalizedIntentSignals(input.intentSignals))
126
+ targets.push({ targetType: 'intent-signal', targetKey: signal });
124
127
  if (input.providerAdapter !== undefined)
125
128
  targets.push({ targetType: 'provider-adapter', targetKey: input.providerAdapter });
126
129
  return dedupeTargets(targets);
@@ -229,6 +232,9 @@ function phaseTargetKeys(workflow, phase) {
229
232
  function isSddWorkflow(workflow) {
230
233
  return workflow?.trim().toLowerCase() === 'sdd';
231
234
  }
235
+ function normalizedIntentSignals(signals) {
236
+ return [...new Set((signals ?? []).map((signal) => signal.trim().toLowerCase()).filter(Boolean))];
237
+ }
232
238
  function stringMetadata(value) {
233
239
  return typeof value === 'string' && value.trim() ? value : undefined;
234
240
  }
@@ -33,9 +33,8 @@ export class SkillSeedService {
33
33
  if (!details.ok)
34
34
  return details;
35
35
  existingVersions.set(seed.name, details.value.versions.some((version) => version.version === seed.version));
36
- for (const attachment of details.value.attachments) {
36
+ for (const attachment of details.value.attachments)
37
37
  existingAttachments.set(`${seed.name}:${attachment.targetType}:${attachment.targetKey}`, true);
38
- }
39
38
  }
40
39
  }
41
40
  const transaction = this.database.transaction(() => {
@@ -74,11 +73,12 @@ export class SkillSeedService {
74
73
  const skill = this.registry.getSkillByName(manifest.project, manifest.scope, attachment.skill);
75
74
  if (!skill.ok)
76
75
  throw new SeedLoadError(skill.error);
77
- const targetKey = `${attachment.workflow}:${attachment.phase}`;
78
- const key = `${attachment.skill}:workflow-phase:${targetKey}`;
76
+ const targetType = attachmentTargetType(attachment);
77
+ const targetKey = attachmentTargetKey(attachment);
78
+ const key = `${attachment.skill}:${targetType}:${targetKey}`;
79
79
  const attachInput = {
80
80
  skillId: skill.value.id,
81
- targetType: 'workflow-phase',
81
+ targetType,
82
82
  targetKey,
83
83
  metadata: attachmentMetadata(attachment),
84
84
  };
@@ -138,7 +138,7 @@ function validateManifest(manifest) {
138
138
  const validation = validateAttachmentSeed(attachment, names);
139
139
  if (!validation.ok)
140
140
  return validation;
141
- const key = `${attachment.workflow}:${attachment.phase}:${attachment.skill}`;
141
+ const key = `${attachmentTargetType(attachment)}:${attachmentTargetKey(attachment)}:${attachment.skill}`;
142
142
  if (attachments.has(key))
143
143
  return validationFailure(`Duplicate skill seed attachment: ${key}`);
144
144
  attachments.add(key);
@@ -159,10 +159,18 @@ function validateSkillSeed(skill) {
159
159
  return rejectUnsupportedNativeInputs(skill.name, skill.metadata);
160
160
  }
161
161
  function validateAttachmentSeed(attachment, names) {
162
- if (!attachment.workflow?.trim())
163
- return validationFailure('Skill seed attachment workflow is required');
164
- if (!attachment.phase?.trim())
165
- return validationFailure('Skill seed attachment phase is required');
162
+ if (attachment.targetType !== undefined && !isSkillAttachmentTargetType(attachment.targetType))
163
+ return validationFailure(`Skill seed attachment targetType is invalid: ${String(attachment.targetType)}`);
164
+ const targetType = attachmentTargetType(attachment);
165
+ if (targetType === 'workflow-phase') {
166
+ if (!attachment.workflow?.trim())
167
+ return validationFailure('Skill seed attachment workflow is required');
168
+ if (!attachment.phase?.trim())
169
+ return validationFailure('Skill seed attachment phase is required');
170
+ }
171
+ else if (!attachment.targetKey?.trim()) {
172
+ return validationFailure(`Skill seed attachment targetKey is required for ${targetType}`);
173
+ }
166
174
  if (!attachment.skill?.trim())
167
175
  return validationFailure('Skill seed attachment skill is required');
168
176
  if (!names.has(attachment.skill))
@@ -186,20 +194,35 @@ function rejectUnsupportedNativeInputs(name, metadata) {
186
194
  function compatibilityFor(seed, attachments) {
187
195
  const matching = attachments.filter((attachment) => attachment.skill === seed.name);
188
196
  return {
189
- targets: ['workflow-phase'],
197
+ targets: [...new Set(matching.map(attachmentTargetType))],
190
198
  adapters: ['opencode'],
191
- workflows: [...new Set(matching.map((attachment) => attachment.workflow))],
192
- phases: [...new Set(matching.map((attachment) => attachment.phase))],
199
+ workflows: [...new Set(matching.map((attachment) => attachment.workflow).filter((workflow) => workflow !== undefined))],
200
+ phases: [...new Set(matching.map((attachment) => attachment.phase).filter((phase) => phase !== undefined))],
193
201
  };
194
202
  }
195
203
  function attachmentMetadata(attachment) {
196
- return {
204
+ const metadata = {
197
205
  ...(attachment.metadata ?? {}),
198
206
  ...(attachment.order !== undefined ? { order: attachment.order } : {}),
199
207
  seededBy: 'vgxness',
200
- workflow: attachment.workflow,
201
- phase: attachment.phase,
208
+ targetType: attachmentTargetType(attachment),
209
+ targetKey: attachmentTargetKey(attachment),
202
210
  };
211
+ if (attachment.workflow !== undefined)
212
+ metadata.workflow = attachment.workflow;
213
+ if (attachment.phase !== undefined)
214
+ metadata.phase = attachment.phase;
215
+ return metadata;
216
+ }
217
+ function attachmentTargetType(attachment) {
218
+ return attachment.targetType ?? 'workflow-phase';
219
+ }
220
+ function attachmentTargetKey(attachment) {
221
+ const targetType = attachmentTargetType(attachment);
222
+ return targetType === 'workflow-phase' ? `${attachment.workflow}:${attachment.phase}` : attachment.targetKey ?? '';
223
+ }
224
+ function isSkillAttachmentTargetType(value) {
225
+ return value === 'agent' || value === 'subagent' || value === 'workflow-phase' || value === 'provider-adapter' || value === 'intent-signal';
203
226
  }
204
227
  function validationFailure(message) {
205
228
  return { ok: false, error: { code: 'validation_failed', message } };
@@ -0,0 +1,403 @@
1
+ # Flujo SDD
2
+
3
+ > Versión en inglés: [SDD Flow](./sdd-flow.md).
4
+
5
+ > **Alcance:** este documento explica el flujo SDD completo en VGXNESS: desde una intención humana, pasando por artefactos de planeación, progreso de implementación, verificación y archivo. Es una guía práctica para operadores y acompaña a [Architecture](./architecture.md), [Safety](./safety.md), [CLI](./cli.md) y [MCP tools](./mcp.md).
6
+
7
+ VGXNESS trata SDD como estado real del producto, no solo como instrucciones para agentes. Cada fase produce un artefacto guardado localmente en SQLite, y el avance entre fases se controla mediante readiness explícito y, cuando aplica, aceptación humana explícita.
8
+
9
+ Las fases canónicas de SDD son:
10
+
11
+ ```text
12
+ explore → proposal → spec → design → tasks → apply-progress → verify → archive
13
+ ```
14
+
15
+ ## Modelo mental
16
+
17
+ ```text
18
+ Intención humana
19
+
20
+ Conversación en OpenCode / acción del operador por CLI
21
+
22
+ Superficie VGXNESS MCP o CLI
23
+
24
+ Servicios del control plane
25
+
26
+ Artefactos SDD, runs, memoria y checkpoints en SQLite
27
+ ```
28
+
29
+ La separación importante es:
30
+
31
+ ```text
32
+ Conversación ≠ estado
33
+ Draft ≠ aceptación
34
+ Plan ≠ ejecución
35
+ Preflight ≠ permiso automático
36
+ Provider status ≠ escritura de config del provider
37
+ CLI/MCP ≠ reglas de negocio duplicadas
38
+ ```
39
+
40
+ ## 1. Intención humana
41
+
42
+ El flujo empieza cuando el humano expresa un objetivo, normalmente dentro de OpenCode después de instalar el MCP de VGXNESS.
43
+
44
+ Ejemplo:
45
+
46
+ ```text
47
+ Mejorar el flujo de recuperación de runs interrumpidos para que VGXNESS sugiera cómo continuar de forma segura.
48
+ ```
49
+
50
+ Para cambios sustanciales, VGXNESS no debería saltar directo al código. El camino seguro es inspeccionar el estado SDD actual y elegir la siguiente fase válida.
51
+
52
+ Superficies útiles:
53
+
54
+ ```text
55
+ sdd_status
56
+ sdd_next
57
+ sdd_continue
58
+ agent_resolve
59
+ agent_activate
60
+ ```
61
+
62
+ Equivalentes por CLI para setup, diagnóstico, recuperación y scripting:
63
+
64
+ ```bash
65
+ vgxness sdd status --project <project> --change <change>
66
+ vgxness sdd next --project <project> --change <change>
67
+ vgxness sdd continue --project <project> --change <change>
68
+ ```
69
+
70
+ ## 2. `explore`: entender antes de elegir solución
71
+
72
+ Objetivo: investigar el problema, los límites actuales del código, decisiones previas, riesgos y posibles enfoques sin comprometerse todavía con una implementación.
73
+
74
+ Preguntas típicas:
75
+
76
+ - ¿Dónde vive la lógica relevante?
77
+ - ¿Qué herramientas CLI y MCP ya existen?
78
+ - ¿Qué servicio es dueño de la regla de dominio?
79
+ - ¿Qué restricciones de safety o storage aplican?
80
+ - ¿Qué riesgos harían difícil revisar el cambio?
81
+
82
+ Para recuperación de runs interrumpidos, la exploración podría revisar:
83
+
84
+ ```text
85
+ src/runs/*
86
+ src/sdd/*
87
+ src/mcp/control-plane.ts
88
+ src/cli/commands/*
89
+ docs/*
90
+ test/*
91
+ ```
92
+
93
+ El artefacto de fase se guarda con el topic key canónico:
94
+
95
+ ```text
96
+ sdd/{change}/explore
97
+ ```
98
+
99
+ Un agente puede marcar el artefacto como ready, pero readiness no es aceptación.
100
+
101
+ ## 3. Aceptación humana de `explore`
102
+
103
+ VGXNESS separa deliberadamente contenido generado y aprobación humana:
104
+
105
+ ```text
106
+ draft / ready ≠ accepted
107
+ ```
108
+
109
+ Solo una decisión humana de aceptación debería avanzar trabajo posterior que esté gobernado por gates. Esto evita que un agente apruebe silenciosamente su propia dirección.
110
+
111
+ Ejemplo CLI:
112
+
113
+ ```bash
114
+ vgxness sdd accept-artifact --project <project> --change <change> --phase explore
115
+ ```
116
+
117
+ ## 4. `proposal`: elegir dirección de producto
118
+
119
+ Objetivo: definir qué cambio debe hacerse y por qué.
120
+
121
+ Una buena propuesta responde:
122
+
123
+ - ¿Qué problema estamos resolviendo?
124
+ - ¿Quién se beneficia?
125
+ - ¿Qué está dentro del alcance?
126
+ - ¿Qué queda explícitamente fuera del alcance?
127
+ - ¿Qué riesgos o tradeoffs existen?
128
+ - ¿Cómo sabremos que funcionó?
129
+
130
+ Ejemplo de resumen de propuesta:
131
+
132
+ ```text
133
+ Agregar una superficie read-only de continuación para runs interrumpidos que combine runs failed/blocked/needs-human, el último checkpoint, la fase SDD asociada y una siguiente acción recomendada segura.
134
+ ```
135
+
136
+ Esto sigue siendo definición de dirección, no implementación.
137
+
138
+ ## 5. Aceptación humana de `proposal`
139
+
140
+ La propuesta es el contrato principal de alcance. Si es demasiado amplia, la implementación y la revisión se vuelven riesgosas.
141
+
142
+ Pregunta recomendada para revisión:
143
+
144
+ ```text
145
+ ¿Esto puede revisarse como un slice coherente o deberíamos dividirlo?
146
+ ```
147
+
148
+ Por ejemplo, un primer slice más seguro puede ser:
149
+
150
+ ```text
151
+ Diagnóstico read-only de runs interrumpidos antes de cualquier recuperación automática o ejecución de provider.
152
+ ```
153
+
154
+ Después de aceptar exactamente la propuesta, se pueden generar borradores downstream de planeación, pero esos artefactos siguen siendo drafts hasta que se revisen y acepten según la gobernanza.
155
+
156
+ ## 6. `spec`: definir comportamiento observable
157
+
158
+ Objetivo: especificar qué debe hacer el sistema sin sobreadaptarse a detalles de implementación.
159
+
160
+ Para recuperación de runs interrumpidos, una spec podría requerir:
161
+
162
+ - Runs con estado `failed`, `blocked` o `needs-human` aparecen como candidatos de recuperación.
163
+ - Cada candidato incluye run id, proyecto, workflow, fase, estado, último checkpoint, razón de fallo o bloqueo y siguiente acción recomendada.
164
+ - El estado vacío es explícito cuando no existen runs interrumpidos.
165
+ - La superficie es read-only y no reanuda providers ni muta estado de runs.
166
+
167
+ La spec debe incluir casos límite, por ejemplo:
168
+
169
+ - muchos runs interrumpidos;
170
+ - runs sin checkpoints;
171
+ - runs de otro proyecto;
172
+ - runs ligados a fases SDD ya aceptadas;
173
+ - metadata incompleta o inconsistente.
174
+
175
+ ## 7. `design`: decidir cómo construirlo
176
+
177
+ Objetivo: conectar la spec con la arquitectura existente.
178
+
179
+ Un buen diseño identifica:
180
+
181
+ - límites de servicio;
182
+ - cambios de repository/query;
183
+ - superficies CLI y MCP;
184
+ - agregados de schema;
185
+ - comportamiento de renderers;
186
+ - tests;
187
+ - necesidad de migraciones, si aplica;
188
+ - invariantes de safety.
189
+
190
+ Ejemplo de diseño:
191
+
192
+ ```text
193
+ Agregar un servicio de candidatos de resume respaldado por el repositorio de runs.
194
+ Exponer herramientas MCP read-only para listar e inspeccionar candidatos.
195
+ Exponer un comando CLI de recuperación/status que use el mismo servicio.
196
+ Mantener la generación de recomendaciones como no-mutante.
197
+ ```
198
+
199
+ Reglas arquitectónicas que preservar:
200
+
201
+ - CLI y MCP deben compartir servicios de dominio.
202
+ - Los renderers no deben reimplementar reglas de negocio.
203
+ - Las herramientas read-only deben seguir siendo no-mutantes.
204
+ - Las escrituras de configuración de provider requieren consentimiento humano explícito.
205
+ - Los artefactos SDD siguen respaldados por SQLite; no crear `openspec/`.
206
+
207
+ ## 8. `tasks`: hacer el diseño revisable
208
+
209
+ Objetivo: dividir el diseño en pasos pequeños de implementación.
210
+
211
+ Ejemplo de desglose:
212
+
213
+ ```text
214
+ 1. Agregar query de repositorio para runs interrumpidos.
215
+ 2. Agregar servicio de candidatos de resume.
216
+ 3. Agregar schema MCP y herramientas read-only.
217
+ 4. Agregar comando CLI o extender la superficie existente de recovery/status.
218
+ 5. Agregar salida de renderer para estados vacío, único candidato y múltiples candidatos.
219
+ 6. Agregar tests enfocados de servicio.
220
+ 7. Agregar tests de contrato CLI/MCP.
221
+ 8. Actualizar docs si cambia comportamiento visible para el usuario.
222
+ ```
223
+
224
+ Buenas tasks son pequeñas, testeables y fáciles de revisar.
225
+
226
+ ## 9. `apply-progress`: implementar con progreso trazable
227
+
228
+ Objetivo: hacer el cambio de código mientras se registra qué cambió, qué falta y qué evidencia existe.
229
+
230
+ Antes de implementar, revisar tamaño y riesgo:
231
+
232
+ - ¿El cambio toca varios subsistemas?
233
+ - ¿Altera storage o migraciones?
234
+ - ¿Cambia schemas MCP?
235
+ - ¿Cambia comportamiento de safety?
236
+ - ¿El diff es demasiado grande para una sola revisión?
237
+
238
+ Si el cambio es demasiado amplio, hay que dividirlo antes de implementar.
239
+
240
+ Durante la implementación, `apply-progress` debería capturar:
241
+
242
+ - trabajo completado;
243
+ - archivos o módulos modificados;
244
+ - tareas pendientes;
245
+ - resultados de tests;
246
+ - blockers conocidos;
247
+ - desviaciones del diseño aceptado.
248
+
249
+ `apply-progress` es un registro de progreso, no prueba de que la implementación sea correcta.
250
+
251
+ ## 10. Preflight para operaciones riesgosas
252
+
253
+ VGXNESS usa preflight checks para mantener explícitas las operaciones riesgosas.
254
+
255
+ Ejemplos de categorías riesgosas:
256
+
257
+ ```text
258
+ implementation-edit
259
+ shell
260
+ test-run
261
+ install
262
+ git-write
263
+ provider-tool
264
+ secrets
265
+ external-directory
266
+ ```
267
+
268
+ Ejemplo conceptual MCP:
269
+
270
+ ```text
271
+ run_preflight({
272
+ category: "test-run",
273
+ operation: "bun run verify:test",
274
+ workflow: "sdd",
275
+ phase: "apply-progress"
276
+ })
277
+ ```
278
+
279
+ Preflight es control consultivo/de planeación. No significa que la operación esté automáticamente aprobada o ejecutada.
280
+
281
+ ## 11. `verify`: comprobar la implementación independientemente
282
+
283
+ Objetivo: verificar la implementación contra la spec y el design aceptados, idealmente con contexto fresco de revisor/agente para cambios no triviales.
284
+
285
+ La verificación debe revisar:
286
+
287
+ - que la spec se cumpla;
288
+ - que los límites del diseño se respeten o las desviaciones estén justificadas;
289
+ - que los tests relevantes pasen;
290
+ - que superficies read-only no se hayan vuelto mutantes;
291
+ - que setup/config de providers sigan requiriendo consentimiento explícito;
292
+ - que storage, CLI y MCP sigan siendo consistentes.
293
+
294
+ Comandos típicos de verificación del repo:
295
+
296
+ ```bash
297
+ bun run verify:typecheck
298
+ bun run verify:test
299
+ bun run verify:bun-sqlite
300
+ bun run package:bun:evidence
301
+ ```
302
+
303
+ No todo cambio pequeño de docs o copy necesita la suite completa. Cambios de storage, schema CLI/MCP, setup de providers o packaging merecen verificación más estricta.
304
+
305
+ ## 12. `archive`: cerrar el cambio con contexto durable
306
+
307
+ Objetivo: preservar qué ocurrió para que trabajo futuro pueda recuperar contexto sin releer todo el hilo o diff.
308
+
309
+ Un artefacto de archive debería incluir:
310
+
311
+ - resultado final;
312
+ - comportamiento visible que cambió;
313
+ - archivos o módulos clave tocados;
314
+ - verificación realizada;
315
+ - riesgos residuales;
316
+ - trabajo de seguimiento;
317
+ - notas de rollback o recuperación, si aplica.
318
+
319
+ Ejemplo de resumen archive:
320
+
321
+ ```text
322
+ Change: recover-runs
323
+ Outcome: implemented a read-only interrupted-run recovery surface.
324
+ Verification: typecheck and focused service/CLI tests passed.
325
+ Residual risk: full package evidence was not run locally.
326
+ Follow-up: consider TUI integration after the CLI/MCP flow stabilizes.
327
+ ```
328
+
329
+ ## Boceto del flujo de herramientas
330
+
331
+ Dentro de OpenCode, el flujo normalmente se ve así conceptualmente:
332
+
333
+ ```text
334
+ sdd_status
335
+
336
+ sdd_continue
337
+
338
+ agent_activate(explore)
339
+
340
+ sdd_save_artifact(explore)
341
+
342
+ sdd_ready(explore)
343
+
344
+ humano acepta explore
345
+
346
+ agent_activate(proposal)
347
+
348
+ sdd_save_artifact(proposal)
349
+
350
+ sdd_ready(proposal)
351
+
352
+ humano acepta proposal
353
+
354
+ agent_activate(spec)
355
+
356
+ agent_activate(design)
357
+
358
+ agent_activate(tasks)
359
+
360
+ humano revisa/acepta artefactos con gate
361
+
362
+ agent_activate(apply)
363
+
364
+ run_preflight(...)
365
+
366
+ sdd_save_artifact(apply-progress)
367
+
368
+ agent_activate(verify)
369
+
370
+ sdd_save_artifact(verify)
371
+
372
+ archive
373
+ ```
374
+
375
+ ## Por qué existe este flujo
376
+
377
+ Un flujo agentic ingenuo sería:
378
+
379
+ ```text
380
+ leer código → editar código → correr tests → decir terminado
381
+ ```
382
+
383
+ VGXNESS usa SDD para hacerlo más seguro:
384
+
385
+ ```text
386
+ entender objetivo
387
+
388
+ elegir alcance
389
+
390
+ especificar comportamiento
391
+
392
+ diseñar el cambio
393
+
394
+ dividir en tareas
395
+
396
+ implementar con preflight y tracking de progreso
397
+
398
+ verificar independientemente
399
+
400
+ archivar contexto durable
401
+ ```
402
+
403
+ La estructura inicial cuesta un poco de tiempo, pero reduce scope creep oculto, diffs imposibles de revisar, automatización insegura y pérdida de contexto.