vgxness 1.9.1 → 1.9.3

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.
Files changed (54) hide show
  1. package/README.md +15 -5
  2. package/dist/agents/agent-activation-service.js +13 -4
  3. package/dist/agents/agent-registry-service.js +8 -2
  4. package/dist/agents/agent-resolver.js +33 -3
  5. package/dist/agents/agent-seed-upgrade-service.js +231 -0
  6. package/dist/agents/boot-upgrade.js +59 -0
  7. package/dist/agents/canonical-agent-manifest.js +39 -18
  8. package/dist/agents/canonical-agent-projection.js +38 -4
  9. package/dist/agents/manager-profile-overlay-service.js +14 -0
  10. package/dist/agents/repositories/agent-seed-history.js +128 -0
  11. package/dist/cli/cli-help.js +14 -3
  12. package/dist/cli/commands/index.js +1 -0
  13. package/dist/cli/commands/interactive-entrypoint-dispatcher.js +8 -0
  14. package/dist/cli/commands/mcp-dispatcher.js +7 -0
  15. package/dist/cli/commands/memory-sdd-dispatcher.js +71 -5
  16. package/dist/cli/commands/status-dispatcher.js +130 -0
  17. package/dist/cli/commands/workflow-dispatcher.js +11 -5
  18. package/dist/cli/dispatcher.js +11 -1
  19. package/dist/cli/product-resume-renderer.js +32 -0
  20. package/dist/cli/product-status-renderer.js +74 -0
  21. package/dist/cli/sdd-renderer.js +80 -3
  22. package/dist/code/cli/code-command.js +7 -4
  23. package/dist/code/reporting/summary.js +4 -1
  24. package/dist/code/runtime/code-runtime.js +27 -4
  25. package/dist/code/runtime/sdd-context.js +18 -2
  26. package/dist/governance/governance-report-builder.js +18 -7
  27. package/dist/mcp/claude-code-agent-config.js +10 -4
  28. package/dist/mcp/client-install-claude-code-contract.js +19 -4
  29. package/dist/mcp/client-install-claude-code.js +2 -2
  30. package/dist/mcp/control-plane.js +78 -5
  31. package/dist/mcp/provider-status.js +89 -88
  32. package/dist/mcp/schema.js +42 -8
  33. package/dist/mcp/stdio-server.js +6 -0
  34. package/dist/mcp/validation.js +77 -5
  35. package/dist/memory/sqlite/migrations/016_agent_seed_history.sql +15 -0
  36. package/dist/resume/product-resume.js +166 -0
  37. package/dist/runs/repositories/runs.js +12 -1
  38. package/dist/runs/run-service.js +62 -5
  39. package/dist/runs/schema.js +4 -0
  40. package/dist/sdd/schema.js +20 -0
  41. package/dist/sdd/sdd-continuation-plan.js +81 -0
  42. package/dist/sdd/sdd-workflow-service.js +146 -18
  43. package/dist/skills/skill-resolver.js +21 -4
  44. package/dist/status/product-status.js +117 -0
  45. package/docs/architecture.md +9 -1
  46. package/docs/cli.md +38 -13
  47. package/docs/code-runtime.md +3 -0
  48. package/docs/contributing.md +1 -1
  49. package/docs/glossary.md +2 -2
  50. package/docs/mcp.md +20 -6
  51. package/docs/project-health-audit-v1.9.1.md +126 -0
  52. package/docs/providers.md +4 -4
  53. package/docs/safety.md +1 -1
  54. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { summarizePayloadContent } from '../payload/payload-summary.js';
2
- import { isSddPhase, normalizeSddArtifact, sddPhases, sddPrerequisites, sddTopicKey, } from './schema.js';
2
+ import { normalizeSddArtifact, normalizeSddPhaseInput, sddPhases, sddPrerequisites, sddTopicKey, } from './schema.js';
3
3
  const defaultContext = { actor: 'sdd-workflow-service' };
4
4
  const validChangePattern = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
5
5
  function readAutoAdvanceFromEnv() {
@@ -103,7 +103,7 @@ export class SddWorkflowService {
103
103
  const artifacts = cockpitPhases.map((phase) => phase.artifact).filter((artifact) => artifact !== undefined);
104
104
  const aggregateBlockers = dedupeCockpitBlockers([
105
105
  ...cockpitPhases.flatMap((phase) => phase.blockers),
106
- ...next.missingArtifactTopicKeys.map((topicKey) => blockerFromTopicKey(validated.value.change, topicKey)),
106
+ ...(next.missingPrerequisiteTopicKeys ?? []).map((topicKey) => blockerFromTopicKey(validated.value.change, topicKey)),
107
107
  ...(next.blockedPrerequisites ?? []).map((blocker) => ({
108
108
  kind: 'readiness',
109
109
  phase: blocker.phase,
@@ -175,7 +175,7 @@ export class SddWorkflowService {
175
175
  if (status === 'accepted')
176
176
  return validationFailure('Cannot overwrite an accepted SDD artifact; supersede it under a new topic key.');
177
177
  if (status === 'rejected')
178
- return validationFailure('Rejected SDD artifact must be re-opened with an explicit decision before saving.');
178
+ return validationFailure('Rejected SDD artifact must be re-opened via SddWorkflowService.reopenArtifact before saving.');
179
179
  if (status === 'superseded')
180
180
  return validationFailure('Cannot overwrite a superseded SDD artifact.');
181
181
  }
@@ -198,7 +198,7 @@ export class SddWorkflowService {
198
198
  const existingEnvelope = normalizeSddArtifact(existing.value);
199
199
  const existingStatus = existingEnvelope.metadata.status;
200
200
  if (existingStatus === 'rejected')
201
- return validationFailure('Rejected SDD artifact must be re-opened with an explicit decision before accepting.');
201
+ return validationFailure('Rejected SDD artifact must be re-opened via SddWorkflowService.reopenArtifact before accepting.');
202
202
  if (existingStatus === 'superseded')
203
203
  return validationFailure('Cannot accept a superseded SDD artifact; create a new artifact under a different topic key instead.');
204
204
  const acceptance = {
@@ -223,6 +223,47 @@ export class SddWorkflowService {
223
223
  },
224
224
  }, this.context);
225
225
  }
226
+ reopenArtifact(input) {
227
+ const validated = this.validatePhaseInput(input);
228
+ if (!validated.ok)
229
+ return validated;
230
+ if (input.reopenedBy === undefined)
231
+ return validationFailure('SDD artifact reopen requires an explicit human actor');
232
+ if (input.reopenedBy.type !== 'human')
233
+ return validationFailure('SDD artifact reopen must be performed by a human actor');
234
+ if (input.reopenedBy.id === undefined || input.reopenedBy.id.trim().length === 0)
235
+ return validationFailure('SDD artifact reopen human actor id must not be empty');
236
+ const topicKey = sddTopicKey(validated.value.change, validated.value.phase);
237
+ const existing = this.memory.getArtifact(validated.value.project, topicKey, this.context);
238
+ if (!existing.ok)
239
+ return existing;
240
+ const existingEnvelope = normalizeSddArtifact(existing.value);
241
+ const existingStatus = existingEnvelope.metadata.status;
242
+ if (existingStatus !== 'rejected')
243
+ return validationFailure(`SDD artifact reopen is only valid for rejected artifacts (current status: ${existingStatus}).`);
244
+ const reopenedAt = input.reopenedAt ?? new Date().toISOString();
245
+ const reopenRecord = {
246
+ actor: {
247
+ type: 'human',
248
+ id: input.reopenedBy.id,
249
+ ...(input.reopenedBy.displayName === undefined ? {} : { displayName: input.reopenedBy.displayName }),
250
+ },
251
+ reopenedAt,
252
+ ...(input.note === undefined ? {} : { note: input.note }),
253
+ };
254
+ return this.memory.saveArtifact({
255
+ project: existing.value.project,
256
+ topicKey: existing.value.topicKey,
257
+ phase: existing.value.phase,
258
+ content: existing.value.content,
259
+ metadata: {
260
+ ...existingEnvelope.metadata,
261
+ status: 'draft',
262
+ reopen: reopenRecord,
263
+ updatedAt: reopenedAt,
264
+ },
265
+ }, this.context);
266
+ }
226
267
  getArtifact(input) {
227
268
  const validated = this.validatePhaseInput(input);
228
269
  if (!validated.ok)
@@ -273,9 +314,10 @@ export class SddWorkflowService {
273
314
  const validated = validateProjectAndChange(input.project, input.change);
274
315
  if (!validated.ok)
275
316
  return validated;
276
- if (!isSddPhase(input.phase))
317
+ const phase = normalizeSddPhaseInput(input.phase);
318
+ if (phase === undefined)
277
319
  return validationFailure(`Unknown SDD phase: ${input.phase}`);
278
- return { ok: true, value: { ...validated.value, phase: input.phase } };
320
+ return { ok: true, value: { ...validated.value, phase } };
279
321
  }
280
322
  getPhaseStatuses(project, change) {
281
323
  const snapshot = this.loadPhaseSnapshot(project, change);
@@ -326,9 +368,11 @@ export function nextDecisionFromStatuses(change, phases, options = {}) {
326
368
  status: 'blocked',
327
369
  nextPhase: blockedPresentPhase.phase,
328
370
  missingArtifactTopicKeys: readiness.missingArtifactTopicKeys,
371
+ missingPrerequisiteTopicKeys: readiness.missingPrerequisiteTopicKeys ?? [],
329
372
  blockedPrerequisites: blockers,
373
+ blockerGuidance: readiness.blockerGuidance ?? [],
330
374
  reason: `${blockedPresentPhase.phase} is blocked by prerequisite artifacts that are not accepted: ${formatBlockers(blockers)}.`,
331
- recommendedAction: `Create, restore, or accept ${readiness.missingArtifactTopicKeys.join(', ')} before continuing.`,
375
+ recommendedAction: blockedRecommendedAction(readiness, 'continuing'),
332
376
  };
333
377
  }
334
378
  const nextMissingPhase = phases.find((status) => !status.present)?.phase;
@@ -340,6 +384,7 @@ export function nextDecisionFromStatuses(change, phases, options = {}) {
340
384
  status: 'blocked',
341
385
  nextPhase: firstUnacceptedPhase.phase,
342
386
  missingArtifactTopicKeys: [firstUnacceptedPhase.topicKey],
387
+ missingPrerequisiteTopicKeys: [],
343
388
  blockedPrerequisites: [
344
389
  {
345
390
  phase: firstUnacceptedPhase.phase,
@@ -348,8 +393,9 @@ export function nextDecisionFromStatuses(change, phases, options = {}) {
348
393
  ...(firstUnacceptedPhase.artifactId === undefined ? {} : { artifactId: firstUnacceptedPhase.artifactId }),
349
394
  },
350
395
  ],
396
+ blockerGuidance: [blockerGuidanceForPhaseStatus(firstUnacceptedPhase)],
351
397
  reason: `${firstUnacceptedPhase.phase} is present but ${firstUnacceptedPhase.state ?? 'draft'}; accepted governance status is required before the change is complete.`,
352
- recommendedAction: `Accept or replace ${firstUnacceptedPhase.topicKey} before archiving this SDD change.`,
398
+ recommendedAction: `${blockerActionForStatus(firstUnacceptedPhase)} before archiving this SDD change.`,
353
399
  };
354
400
  }
355
401
  return {
@@ -369,9 +415,11 @@ export function nextDecisionFromStatuses(change, phases, options = {}) {
369
415
  status: 'blocked',
370
416
  nextPhase: nextMissingPhase,
371
417
  missingArtifactTopicKeys: readiness.missingArtifactTopicKeys,
418
+ missingPrerequisiteTopicKeys: readiness.missingPrerequisiteTopicKeys ?? [],
372
419
  blockedPrerequisites: blockers,
420
+ blockerGuidance: readiness.blockerGuidance ?? [],
373
421
  reason: `${nextMissingPhase} is blocked by prerequisite artifacts that are not accepted: ${formatBlockers(blockers)}.`,
374
- recommendedAction: `Create, restore, or accept ${readiness.missingArtifactTopicKeys.join(', ')} before running ${nextMissingPhase}.`,
422
+ recommendedAction: blockedRecommendedAction(readiness, `running ${nextMissingPhase}`),
375
423
  };
376
424
  }
377
425
  return {
@@ -398,7 +446,9 @@ function statusFromPhases(change, phases, options = {}) {
398
446
  function getReadinessFromStatuses(change, phase, phases, options = {}) {
399
447
  const satisfiedPrerequisites = [];
400
448
  const missingArtifactTopicKeys = [];
449
+ const missingPrerequisiteTopicKeys = [];
401
450
  const blockedPrerequisites = [];
451
+ const blockerGuidance = [];
402
452
  const useAutoAdvance = options.autoAdvance === true;
403
453
  for (const prerequisite of sddPrerequisites[phase]) {
404
454
  const status = phases.find((candidate) => candidate.phase === prerequisite);
@@ -408,19 +458,26 @@ function getReadinessFromStatuses(change, phase, phases, options = {}) {
408
458
  continue;
409
459
  }
410
460
  const topicKey = status?.topicKey ?? sddTopicKey(change, prerequisite);
461
+ const reason = status === undefined ? 'missing' : blockerReasonForStatus(status);
411
462
  missingArtifactTopicKeys.push(topicKey);
412
- blockedPrerequisites.push({
463
+ if (reason === 'missing')
464
+ missingPrerequisiteTopicKeys.push(topicKey);
465
+ const blocker = {
413
466
  phase: prerequisite,
414
467
  topicKey,
415
- reason: status === undefined ? 'missing' : blockerReasonForStatus(status),
468
+ reason,
416
469
  ...(status?.artifactId === undefined ? {} : { artifactId: status.artifactId }),
417
- });
470
+ };
471
+ blockedPrerequisites.push(blocker);
472
+ blockerGuidance.push(blockerGuidanceForBlocker(blocker, phase));
418
473
  }
419
474
  return {
420
475
  ready: blockedPrerequisites.length === 0,
421
476
  satisfiedPrerequisites,
422
477
  missingArtifactTopicKeys,
478
+ missingPrerequisiteTopicKeys,
423
479
  blockedPrerequisites,
480
+ blockerGuidance,
424
481
  };
425
482
  }
426
483
  function compactArtifactProjection(artifact, change, phase, readiness) {
@@ -477,13 +534,20 @@ function compactGovernanceArtifact(artifact) {
477
534
  function cockpitBlockersForPhase(status, readiness) {
478
535
  const blockers = [];
479
536
  if (!status.present)
480
- blockers.push({ kind: 'missing-topic-key', phase: status.phase, topicKey: status.topicKey, reason: 'Canonical SDD artifact is missing.' });
537
+ blockers.push({
538
+ kind: 'missing-topic-key',
539
+ phase: status.phase,
540
+ topicKey: status.topicKey,
541
+ reason: 'Canonical SDD artifact is missing.',
542
+ action: blockerActionForBlocker({ phase: status.phase, topicKey: status.topicKey, reason: 'missing' }),
543
+ });
481
544
  if (status.present && status.accepted !== true)
482
545
  blockers.push({
483
546
  kind: 'unaccepted-phase',
484
547
  phase: status.phase,
485
548
  topicKey: status.topicKey,
486
549
  reason: `${status.phase} is ${status.state ?? 'draft'}; explicit human acceptance is required.`,
550
+ action: blockerActionForStatus(status),
487
551
  ...(status.artifactId === undefined ? {} : { artifactId: status.artifactId }),
488
552
  });
489
553
  if (status.legacy === true)
@@ -492,6 +556,7 @@ function cockpitBlockersForPhase(status, readiness) {
492
556
  phase: status.phase,
493
557
  topicKey: status.topicKey,
494
558
  reason: 'Artifact has legacy or missing governance metadata and is treated as not accepted.',
559
+ action: blockerActionForStatus(status),
495
560
  ...(status.artifactId === undefined ? {} : { artifactId: status.artifactId }),
496
561
  });
497
562
  for (const blocker of readiness.blockedPrerequisites ?? []) {
@@ -500,6 +565,7 @@ function cockpitBlockersForPhase(status, readiness) {
500
565
  phase: blocker.phase,
501
566
  topicKey: blocker.topicKey,
502
567
  reason: blocker.reason,
568
+ action: blockerActionForBlocker(blocker),
503
569
  ...(blocker.artifactId === undefined ? {} : { artifactId: blocker.artifactId }),
504
570
  });
505
571
  }
@@ -507,7 +573,13 @@ function cockpitBlockersForPhase(status, readiness) {
507
573
  }
508
574
  function blockerFromTopicKey(change, topicKey) {
509
575
  const phase = phaseFromTopicKey(change, topicKey);
510
- return { kind: 'missing-topic-key', phase, topicKey, reason: 'Required SDD topic key is missing or not accepted.' };
576
+ return {
577
+ kind: 'missing-topic-key',
578
+ phase,
579
+ topicKey,
580
+ reason: 'Required SDD topic key is missing or not accepted.',
581
+ action: blockerActionForBlocker({ phase, topicKey, reason: 'missing' }),
582
+ };
511
583
  }
512
584
  function dedupeCockpitBlockers(blockers) {
513
585
  const seen = new Set();
@@ -526,16 +598,72 @@ function cockpitRecommendedAction(next, blockers) {
526
598
  return next.recommendedAction;
527
599
  const legacy = blockers.find((blocker) => blocker.kind === 'legacy-artifact');
528
600
  if (legacy !== undefined)
529
- return `Review and explicitly accept or replace legacy artifact ${legacy.topicKey}.`;
601
+ return legacy.action ?? `Inspect and explicitly accept or replace legacy artifact ${legacy.topicKey}.`;
530
602
  const unaccepted = blockers.find((blocker) => blocker.kind === 'unaccepted-phase');
531
603
  if (unaccepted !== undefined)
532
- return `Accept or replace ${unaccepted.topicKey} before continuing.`;
604
+ return unaccepted.action ?? `Accept or replace ${unaccepted.topicKey} before continuing.`;
533
605
  return next.recommendedAction;
534
606
  }
607
+ function blockedRecommendedAction(readiness, context) {
608
+ const firstGuidance = readiness.blockerGuidance?.[0];
609
+ if (firstGuidance !== undefined)
610
+ return `${firstGuidance.action} before ${context}.`;
611
+ const firstBlocker = readiness.blockedPrerequisites?.[0];
612
+ if (firstBlocker !== undefined)
613
+ return `${blockerActionForBlocker(firstBlocker)} before ${context}.`;
614
+ return `Resolve prerequisite blockers before ${context}.`;
615
+ }
616
+ function blockerGuidanceForPhaseStatus(status) {
617
+ return blockerGuidanceForBlocker({
618
+ phase: status.phase,
619
+ topicKey: status.topicKey,
620
+ reason: blockerReasonForStatus(status),
621
+ ...(status.artifactId === undefined ? {} : { artifactId: status.artifactId }),
622
+ });
623
+ }
624
+ function blockerGuidanceForBlocker(blocker, blockedPhase) {
625
+ return {
626
+ phase: blocker.phase,
627
+ topicKey: blocker.topicKey,
628
+ reason: blocker.reason,
629
+ action: blockerActionForBlocker(blocker, blockedPhase),
630
+ ...(blocker.artifactId === undefined ? {} : { artifactId: blocker.artifactId }),
631
+ };
632
+ }
633
+ function blockerActionForStatus(status) {
634
+ return blockerActionForBlocker({
635
+ phase: status.phase,
636
+ topicKey: status.topicKey,
637
+ reason: blockerReasonForStatus(status),
638
+ ...(status.artifactId === undefined ? {} : { artifactId: status.artifactId }),
639
+ });
640
+ }
641
+ function blockerActionForBlocker(blocker, blockedPhase) {
642
+ switch (blocker.reason) {
643
+ case 'missing':
644
+ return `Run or create the ${blocker.phase} SDD phase artifact at ${blocker.topicKey}`;
645
+ case 'draft':
646
+ if (blockedPhase !== undefined && isDraftRunPlanningPhase(blockedPhase))
647
+ return `Accept ${blocker.topicKey} if it is ready, or intentionally continue planning ${blockedPhase} with --draft-run; draft-run is planning-only, human acceptance is still required, and apply-progress remains gated`;
648
+ return `Accept ${blocker.topicKey}, or continue and re-run the ${blocker.phase} phase if the draft is incomplete`;
649
+ case 'accepted':
650
+ return `Inspect ${blocker.topicKey}; it is marked accepted but still appears in blocker data`;
651
+ case 'legacy':
652
+ return `Inspect ${blocker.topicKey} and explicitly accept or replace the legacy artifact`;
653
+ case 'rejected':
654
+ return `Reopen ${blocker.topicKey} for revision, or replace it with a new accepted artifact`;
655
+ case 'superseded':
656
+ return `Use the current replacement for ${blocker.topicKey}, or create and accept a new canonical ${blocker.phase} artifact`;
657
+ }
658
+ }
659
+ function isDraftRunPlanningPhase(phase) {
660
+ return phase === 'explore' || phase === 'proposal' || phase === 'spec' || phase === 'design' || phase === 'tasks';
661
+ }
535
662
  function phaseFromTopicKey(change, topicKey) {
536
663
  const suffix = topicKey.slice(`sdd/${change}/`.length);
537
- if (isSddPhase(suffix))
538
- return suffix;
664
+ const phase = normalizeSddPhaseInput(suffix);
665
+ if (phase !== undefined)
666
+ return phase;
539
667
  return 'explore';
540
668
  }
541
669
  function blockerReasonForStatus(status) {
@@ -1,3 +1,4 @@
1
+ import { normalizeSddPhaseInput } from '../sdd/schema.js';
1
2
  export class SkillResolver {
2
3
  skills;
3
4
  agents;
@@ -113,10 +114,13 @@ export class SkillResolver {
113
114
  targets.push({ targetType: agent.mode, targetKey: agent.name });
114
115
  targets.push({ targetType: agent.mode, targetKey: `${agent.project}/${agent.scope}/${agent.name}` });
115
116
  }
116
- if (input.workflow !== undefined && input.phase !== undefined)
117
- targets.push({ targetType: 'workflow-phase', targetKey: `${input.workflow}:${input.phase}` });
118
- if (input.phase !== undefined)
119
- targets.push({ targetType: 'workflow-phase', targetKey: input.phase });
117
+ const phases = phaseTargetKeys(input.workflow, input.phase);
118
+ if (input.workflow !== undefined) {
119
+ for (const phase of phases)
120
+ targets.push({ targetType: 'workflow-phase', targetKey: `${input.workflow}:${phase}` });
121
+ }
122
+ for (const phase of phases)
123
+ targets.push({ targetType: 'workflow-phase', targetKey: phase });
120
124
  if (input.providerAdapter !== undefined)
121
125
  targets.push({ targetType: 'provider-adapter', targetKey: input.providerAdapter });
122
126
  return dedupeTargets(targets);
@@ -212,6 +216,19 @@ function dedupeTargets(targets) {
212
216
  return true;
213
217
  });
214
218
  }
219
+ function phaseTargetKeys(workflow, phase) {
220
+ if (phase === undefined)
221
+ return [];
222
+ if (!isSddWorkflow(workflow))
223
+ return [phase];
224
+ const normalized = normalizeSddPhaseInput(phase);
225
+ if (normalized === 'apply-progress')
226
+ return ['apply-progress', 'apply'];
227
+ return normalized === undefined || normalized === phase ? [phase] : [normalized, phase];
228
+ }
229
+ function isSddWorkflow(workflow) {
230
+ return workflow?.trim().toLowerCase() === 'sdd';
231
+ }
215
232
  function stringMetadata(value) {
216
233
  return typeof value === 'string' && value.trim() ? value : undefined;
217
234
  }
@@ -0,0 +1,117 @@
1
+ import { basename, resolve } from 'node:path';
2
+ export function buildProductStatus(input) {
3
+ const project = resolveProject(input);
4
+ const baseSafety = [
5
+ 'Read-only cockpit: does not execute providers, edit files, write provider config, or mutate SDD artifacts.',
6
+ 'Human acceptance remains explicit; saved artifacts do not imply acceptance.',
7
+ ];
8
+ if (input.change === undefined) {
9
+ return {
10
+ version: 1,
11
+ kind: 'product-status',
12
+ project,
13
+ status: [`Project: ${project.value} (${project.source === 'flag' ? 'from --project' : 'inferred from cwd'})`, 'Change: not selected'],
14
+ blockers: ['No --change was provided, so VGXNESS cannot inspect SDD phase state yet.'],
15
+ next: ['Choose the active change id, then re-run status for the SDD cockpit view.'],
16
+ command: `vgxness status --project ${project.value} --change <change>`,
17
+ safety: ['Did not open the local memory store because no change was selected.', ...baseSafety],
18
+ };
19
+ }
20
+ if (input.databaseError !== undefined) {
21
+ return blockedStatus({
22
+ project,
23
+ change: input.change,
24
+ blocker: `Unable to read local memory store${input.databasePath === undefined ? '' : ` at ${input.databasePath}`}: ${input.databaseError}`,
25
+ next: 'Check --db, VGXNESS_DB_PATH, or run setup/status commands with the installed Bun runtime.',
26
+ command: `vgxness status --project ${project.value} --change ${input.change} --db <path>`,
27
+ safety: baseSafety,
28
+ });
29
+ }
30
+ if (input.sdd === undefined) {
31
+ return blockedStatus({
32
+ project,
33
+ change: input.change,
34
+ blocker: 'SDD cockpit service is not available for this status request.',
35
+ next: 'Use the installed CLI or provide a readable local memory store.',
36
+ command: `vgxness sdd cockpit --project ${project.value} --change ${input.change}`,
37
+ safety: baseSafety,
38
+ });
39
+ }
40
+ const cockpit = input.sdd.getCockpit({ project: project.value, change: input.change });
41
+ if (!cockpit.ok) {
42
+ return blockedStatus({
43
+ project,
44
+ change: input.change,
45
+ blocker: cockpit.error.message,
46
+ next: 'Review the project/change flags and local memory store, then re-run status.',
47
+ command: `vgxness sdd cockpit --project ${project.value} --change ${input.change}`,
48
+ safety: baseSafety,
49
+ });
50
+ }
51
+ const relatedRunContext = findRelatedRunContext({
52
+ project: project.value,
53
+ change: input.change,
54
+ ...(input.runs === undefined ? {} : { runs: input.runs }),
55
+ ...(input.explicitDatabasePath === undefined ? {} : { explicitDatabasePath: input.explicitDatabasePath }),
56
+ });
57
+ return fromSddCockpit(cockpit.value, project, baseSafety, relatedRunContext);
58
+ }
59
+ function resolveProject(input) {
60
+ if (input.project !== undefined && input.project.trim().length > 0)
61
+ return { value: input.project.trim(), source: 'flag' };
62
+ const directoryName = basename(resolve(input.cwd));
63
+ return { value: directoryName.length > 0 ? directoryName : 'unknown-project', source: 'cwd' };
64
+ }
65
+ function fromSddCockpit(cockpit, project, baseSafety, relatedRunContext) {
66
+ const nextLabel = cockpit.next.nextPhase === undefined ? cockpit.next.status : `${cockpit.next.status} / ${cockpit.next.nextPhase}`;
67
+ const command = cockpit.next.status === 'runnable' && cockpit.next.nextPhase !== undefined
68
+ ? `vgxness code sdd ${cockpit.change} ${cockpit.next.nextPhase} --project ${cockpit.project} --save-artifact`
69
+ : `vgxness sdd cockpit --project ${cockpit.project} --change ${cockpit.change} --json`;
70
+ return {
71
+ version: 1,
72
+ kind: 'product-status',
73
+ project,
74
+ change: cockpit.change,
75
+ status: [
76
+ `Project: ${cockpit.project}`,
77
+ `Change: ${cockpit.change}`,
78
+ `Next: ${nextLabel}`,
79
+ `Accepted: ${cockpit.acceptedCount}/${cockpit.phases.length}`,
80
+ `Legacy artifacts: ${cockpit.legacyCount}`,
81
+ ],
82
+ blockers: cockpit.aggregateBlockers.length === 0
83
+ ? ['none']
84
+ : cockpit.aggregateBlockers.map((blocker) => `${blocker.phase}: ${blocker.reason} at ${blocker.topicKey}${blocker.action === undefined ? '' : `; action=${blocker.action}`}`),
85
+ next: [cockpit.recommendedAction],
86
+ command,
87
+ sddNextStatus: cockpit.next.status,
88
+ ...(relatedRunContext === undefined ? {} : { relatedRunContext }),
89
+ safety: baseSafety,
90
+ };
91
+ }
92
+ function findRelatedRunContext(input) {
93
+ if (input.runs === undefined)
94
+ return undefined;
95
+ const relatedRun = input.runs.findRelatedInterruptedSddRun({ project: input.project, change: input.change });
96
+ if (!relatedRun.ok || relatedRun.value === undefined)
97
+ return undefined;
98
+ const dbFlag = input.explicitDatabasePath === undefined ? '' : ` --db ${input.explicitDatabasePath}`;
99
+ return {
100
+ ...relatedRun.value,
101
+ recommendation: 'Related interrupted run found by checkpoint changeId; inspect or resume it before starting duplicate SDD work. Advisory only.',
102
+ resumeCommand: `vgxness resume --project ${input.project} --run-id ${relatedRun.value.runId}${dbFlag}`,
103
+ };
104
+ }
105
+ function blockedStatus(input) {
106
+ return {
107
+ version: 1,
108
+ kind: 'product-status',
109
+ project: input.project,
110
+ change: input.change,
111
+ status: [`Project: ${input.project.value}`, `Change: ${input.change}`, 'Status: blocked'],
112
+ blockers: [input.blocker],
113
+ next: [input.next],
114
+ command: input.command,
115
+ safety: input.safety,
116
+ };
117
+ }
@@ -91,6 +91,14 @@ Gentle-AI/`gentle-pi` are strong references for the configurator and agent-behav
91
91
  | `Adapter` | Translator between the neutral `vgxness` model and a provider-specific config/runtime. |
92
92
  | `PermissionPolicy` | Rules defining what an agent may do, what requires approval, and what must be denied. |
93
93
 
94
+ ### Agent registry self-healing
95
+
96
+ The canonical agent manifest in `src/agents/canonical-agent-manifest.ts` is the source of truth for built-in agents (1 manager + 10 SDD subagents). At every CLI/MCP boot, `src/agents/boot-upgrade.ts` reconciles the live `agents` table with that manifest via `src/agents/agent-seed-upgrade-service.ts`: missing rows are created, drifted rows (changed `vgxnessPromptContractVersion`, instructions, adapters, or description) are overwritten, and matches are recorded as noop. Each non-noop outcome is appended to the `agent_seed_history` table for audit. Operators can opt out with `VGXNESS_SKIP_AGENT_SEED_AUTO_UPGRADE=1`. This eliminates the long-standing drift between the canonical prompt contract and stored agent rows after a version bump.
97
+
98
+ ### Manager overlay parity across providers
99
+
100
+ A user-saved `manager_profile_overlay` flows to both providers symmetrically. OpenCode consults the overlay inside `OpenCodeManagerPayloadService.applyManagerOverlayWhenAvailable` (`src/providers/opencode/manager-payload.ts:152-156`), which calls `ManagerProfileOverlayService.resolveEffectiveManager` before producing the OpenCode manager payload. Claude Code was previously canonical-only; the fix routes the overlay through `withEffectiveManagerInstructions` (`src/agents/canonical-agent-projection.ts`) into the render path (`expectedClaudeCodeRenderedAgents` / `expectedClaudeCodeRenderedUserAgents`), with the caller (`mcp install claude` in `src/cli/commands/mcp-dispatcher.ts`) computing the effective instructions via `computeEffectiveManagerInstructions(database)` and passing them into `installClaudeCodeMcpClient`. As a result, `vgxness mcp install claude` writes the user's customized instructions into `.claude/agents/vgxness-manager.md` instead of silently falling back to canonical.
101
+
94
102
  ## Storage model
95
103
 
96
104
  `vgxness` should keep local storage split by scope.
@@ -617,7 +625,7 @@ The code runtime layers a second, finer-grained decision on top of the policy ev
617
625
 
618
626
  CLI surface groups are documented in [CLI reference](./cli.md). The plural form is canonical — singular shortcuts are not added.
619
627
 
620
- MCP tools mirror the same core services for agent use. The full, current list of 38 tools is in [MCP tools](./mcp.md) and `SUPPORTED_VGX_MCP_TOOL_NAMES` (`src/mcp/schema.ts`); treat that array as the source of truth. The CLI and TUI are human/operator control surfaces. MCP is the agent-facing control surface. Provider integrations and the code runtime sit on the execution plane.
628
+ MCP tools mirror the same core services for agent use. The full, current list of 41 tools is in [MCP tools](./mcp.md) and `SUPPORTED_VGX_MCP_TOOL_NAMES` (`src/mcp/schema.ts`); treat that array as the source of truth. The CLI and TUI are human/operator control surfaces. MCP is the agent-facing control surface. Provider integrations and the code runtime sit on the execution plane.
621
629
 
622
630
  ## Evaluation strategy
623
631
 
package/docs/cli.md CHANGED
@@ -24,7 +24,7 @@ vgx --help
24
24
  vgxness mcp start --db <temp-memory.sqlite> # then call verification_plan with { changeType: 'mcp' }
25
25
  ```
26
26
 
27
- `vgxness setup plan` and `vgxness setup status` are human-readable and read-only by default; pass `--json` when you need parseable automation output. The MCP plan is read-only. Installed-package setup plans should use `vgxness mcp start` and must not mention `npm run cli`, `tsx`, or repository-relative source paths.
27
+ `vgxness setup plan` and `vgxness setup status` are human-readable and do not write provider config by default; local VGXNESS store initialization may occur when the selected SQLite store is needed. Pass `--json` when you need parseable automation output. The MCP plan does not write provider config. Installed-package setup plans should use `vgxness mcp start` and must not mention `npm run cli`, `tsx`, or repository-relative source paths.
28
28
 
29
29
  Only apply the OpenCode user/global config after reviewing the plan (use `--scope project` only when you intentionally want project config):
30
30
 
@@ -59,14 +59,31 @@ vgxness setup plan # human-readable, read-only plan
59
59
  vgxness setup status # human-readable, read-only setup status
60
60
  vgxness setup apply --yes # writes OpenCode config only after explicit consent
61
61
  vgxness doctor # human-readable readiness checks
62
- vgxness sdd next --project <project> --change <change>
62
+ vgxness status --project <project> --change <change>
63
+ vgxness next --project <project> --change <change>
64
+ vgxness resume --project <project> --run-id <id>
63
65
  vgxness setup rollback --backup <path>
64
66
  ```
65
67
 
66
- `setup plan`, `setup status`, and non-TTY `init` planning are read-only and do not write provider config. They are human-readable by default; pass `--json` when you need parseable automation output. With the default global database, the OpenCode MCP command is `vgxness mcp start`; for custom/project-local DBs it includes `--db <path>`. The default OpenCode target is `$HOME/.config/opencode/opencode.json`; pass `--scope project` to opt into `<workspace>/.opencode/opencode.json`. `setup apply --yes` is the explicit OpenCode provider-config write path. `setup rollback --backup <path>` validates a VGXNESS/OpenCode backup such as `opencode.json.backup-<timestamp>`, creates a pre-rollback backup of the current target when present, restores the selected backup byte-for-byte, and recommends `vgxness doctor`.
68
+ `setup plan`, `setup status`, and non-TTY `init` planning do not write provider config. Local VGXNESS store initialization may occur when the selected SQLite store is needed. They are human-readable by default; pass `--json` when you need parseable automation output. With the default global database, the OpenCode MCP command is `vgxness mcp start`; for custom/project-local DBs it includes `--db <path>`. The default OpenCode target is `$HOME/.config/opencode/opencode.json`; pass `--scope project` to opt into `<workspace>/.opencode/opencode.json`. `setup apply --yes` is the explicit OpenCode provider-config write path. `setup rollback --backup <path>` validates a VGXNESS/OpenCode backup such as `opencode.json.backup-<timestamp>`, creates a pre-rollback backup of the current target when present, restores the selected backup byte-for-byte, and recommends `vgxness doctor`.
67
69
 
68
70
  `vgxness init` prompts in English when stdin/stdout are TTYs: project name, DB location, provider, OpenCode scope, install mode, then shows the plan and asks `Apply this setup? Type "yes" to continue:`. Any answer other than `yes` exits successfully without writes. In CI/non-TTY without `--yes`, `init` returns the read-only plan and never waits for input. `vgxness doctor`, `vgxness sdd status`, `vgxness sdd next`, `vgxness sdd get-artifact`, and `vgxness sdd list-artifacts` are also human-readable by default; use `--json` for scripts.
69
71
 
72
+ ## Daily workflow front doors
73
+
74
+ Use these top-level commands before dropping into lower-level SDD or run inspection commands:
75
+
76
+ | Question | Command | Purpose |
77
+ |---|---|---|
78
+ | Where am I? | `vgxness status --project <project> --change <change>` | Shows the SDD cockpit state, blockers, next recommendation, and safety notes. |
79
+ | What should I do now? | `vgxness next --project <project> --change <change>` | Narrows the cockpit to the next action and why it is safe or blocked. |
80
+ | How do I continue a change? | `vgxness sdd continue --project <project> --change <change>` | Builds a read-only continuation plan for the selected SDD change. |
81
+ | How do I continue interrupted work? | `vgxness resume --project <project> [--run-id <id>]` | Lists or inspects interrupted runs and suggests manual continuation commands without retrying anything. |
82
+
83
+ Without `--change`, `--project`, or `--run-id`, these commands stay orientation-only and do not open the local memory store. With a selected change, project, or run, top-level `status`, `next`, and `resume` inspect SQLite read-only; formal `sdd` commands are non-provider, non-run-executing planning/artifact commands that may use the normal local store path. Pass `--json` for automation.
84
+
85
+ The usual SDD operator loop is: inspect `status`, ask `next`, run `sdd continue` for the copyable continuation plan, then either run `vgxness code sdd ...` for phase work or `vgxness resume ...` for an interrupted run. After a draft is ready, use `sdd accept-artifact` for explicit human acceptance; if an artifact was rejected, use `sdd reopen-artifact` before revising it. Related interrupted run hints in `status`, `next`, and `sdd continue` are advisory only; they help avoid duplicate work but do not retry or execute runs.
86
+
70
87
  ## Bun-first repository verification
71
88
 
72
89
  VGXNESS uses Bun as the canonical installed runtime and verification path for repository development and CI. Node.js `>=22` remains development/build/test tooling for TypeScript, `node:test`, and helper scripts. npm package consumer metadata remains supported, but the product runtime engine is `engines.bun`.
@@ -112,7 +129,7 @@ Release-candidate checklist, without publishing:
112
129
 
113
130
  1. `bun install --frozen-lockfile` completes on a clean checkout.
114
131
  2. `bun run check:bun-lock` passes.
115
- 3. `bun run verify:typecheck`, `bun run verify:test`, and `bun run verify:bun-sqlite` pass.
132
+ 3. `bun run verify:typecheck`, `bun run verify:test`, `bun run verify:test:bun-storage`, and `bun run verify:bun-sqlite` pass.
116
133
  4. `bun run package:bun:evidence -- --require-pass` passes on supported CI OSes.
117
134
  5. Uploaded JSON shows `status: pass`, artifact/install smoke pass, no retired scripts, no `package-lock.json`, `publicationAttempted: false`, and `releaseReadiness.status: pass`.
118
135
  6. Version, release notes, proprietary license boundary, and rollback criteria are reviewed.
@@ -226,7 +243,9 @@ For scriptable status, use explicit read-only commands:
226
243
 
227
244
  ```bash
228
245
  bun run cli:bun -- setup status --project vgxness
229
- bun run cli:bun -- sdd status --project vgxness --change my-change
246
+ bun run cli:bun -- status --project vgxness --change my-change
247
+ bun run cli:bun -- next --project vgxness --change my-change
248
+ bun run cli:bun -- resume --project vgxness --run-id <id>
230
249
  ```
231
250
 
232
251
  Provider support shown in the Installation surface is:
@@ -283,7 +302,7 @@ vgxness code inspect "<question>" \
283
302
  --memory off|ask|auto
284
303
  ```
285
304
 
286
- SDD-aware mode is exposed through `vgxness code sdd <change> <phase>`; pass `--save-artifact` only when persistence is intended, and pass `--change-id`/`--phase` to scope work. Read-only phases stay artifact-oriented; `apply-progress` may expose edit and shell tools; `verify` may expose verification shell tools.
305
+ SDD-aware mode is exposed through `vgxness code sdd <change> <phase>`; pass `--save-artifact` only when persistence is intended, and pass `--change-id`/`--phase` to scope work. Read-only phases stay artifact-oriented; `apply-progress` may expose edit and shell tools; `verify` may expose verification shell tools. `--draft-run` lets planning phases use draft prerequisites for planning only; human acceptance is still required, and `apply-progress` remains gated. `apply` is accepted as a user-facing alias for `apply-progress`.
287
306
 
288
307
  Useful events-only output for piping into the OpenTUI shell or your own tooling:
289
308
 
@@ -312,7 +331,7 @@ bun run cli:bun -- <workflow> run --project <name> --intent <text> [--phase <nam
312
331
  bun run cli:bun -- <workflow> execute --run-id <id> --confirm [--override-escalation] [--workspace <path>] [--db <path>] [--executor safe-non-dispatching|provider|command]
313
332
  ```
314
333
 
315
- `preview` is read-only: it returns the workflow registry plus optional planner output, does not open or write the database, does not create runs, does not call providers, does not edit files, and does not write provider config. Use it to choose between `quickfix`, `debug`, `sdd`, or another workflow before recording state.
334
+ `preview` is read-only: it returns the workflow registry plus optional planner output, does not open or write the database, does not create runs, does not call providers, does not edit files, and does not write provider config. Use it to choose between `quickfix`, `debug`, `sdd`, or another workflow before recording state. Other status/plan surfaces may open or initialize the selected VGXNESS SQLite store; that local store state is separate from provider config.
316
335
 
317
336
  `run` records the selected workflow intent and a `workflow-run-planned` checkpoint in the local SQLite store. It selects or validates an agent, stores the run id, and reports any planner recommendation such as SDD escalation. It does not dispatch providers, edit files, write provider config, create subagents, or mutate SDD artifacts.
318
337
 
@@ -433,11 +452,11 @@ A rejected approval is recorded in the run audit trail and prevents the matching
433
452
 
434
453
  ### SDD workflow commands vs formal SDD commands
435
454
 
436
- `sdd preview`, `sdd run`, and `sdd execute` are workflow commands and use the three-mode workflow contract above. `sdd status`, `sdd ready`, `sdd save-artifact`, `sdd accept-artifact`, `sdd get-artifact`, `sdd list-artifacts`, and `sdd next` are formal SDD artifact/status commands. Formal SDD commands read or write SDD artifact state in the selected local SQLite store; they do not execute providers or continue workflow runs.
455
+ `sdd preview`, `sdd run`, and `sdd execute` are workflow commands and use the three-mode workflow contract above. `sdd status`, `sdd ready`, `sdd continue`, `sdd save-artifact`, `sdd accept-artifact`, `sdd reopen-artifact`, `sdd get-artifact`, `sdd list-artifacts`, and `sdd next` are formal SDD artifact/status commands. Formal SDD commands read or write SDD artifact state in the selected local SQLite store; they do not execute providers or retry workflow runs. `sdd continue` is read-only and returns a continuation plan; it does not create runs, mutate artifacts, or call providers.
437
456
 
438
457
  ## MCP setup preview and doctor
439
458
 
440
- Use this flow to manually verify MCP readiness after implementation. Setup preview is read-only: it does **not** install anything, create `.opencode/` or `.claude/`, or write provider/user/global config.
459
+ Use this flow to manually verify MCP readiness after implementation. Setup preview does **not** install anything, create `.opencode/` or `.claude/`, or write provider/user/global config. Local VGXNESS store initialization may occur when a command needs the selected SQLite store.
441
460
 
442
461
  ```bash
443
462
  bun run cli:bun -- mcp setup --preview --provider opencode --db <path>
@@ -549,18 +568,24 @@ Use `sdd` commands to inspect local SDD artifact state and save, read, or list p
549
568
  bun run cli:bun -- sdd status --project vgxness --change sdd-workflow-engine --db /tmp/vgxness-memory.sqlite
550
569
  bun run cli:bun -- sdd ready --project vgxness --change sdd-workflow-engine --phase spec --db /tmp/vgxness-memory.sqlite
551
570
  bun run cli:bun -- sdd save-artifact --project vgxness --change sdd-workflow-engine --phase proposal --content "# Proposal" --db /tmp/vgxness-memory.sqlite
571
+ bun run cli:bun -- sdd continue --project vgxness --change sdd-workflow-engine --db /tmp/vgxness-memory.sqlite
572
+ bun run cli:bun -- code sdd sdd-workflow-engine spec --project vgxness --draft-run --save-artifact --db /tmp/vgxness-memory.sqlite
552
573
  bun run cli:bun -- sdd accept-artifact --project vgxness --change sdd-workflow-engine --phase proposal --actor user --note "Approved for spec" --db /tmp/vgxness-memory.sqlite
574
+ bun run cli:bun -- resume --project vgxness --db /tmp/vgxness-memory.sqlite
553
575
  bun run cli:bun -- sdd get-artifact --project vgxness --change sdd-workflow-engine --phase proposal --db /tmp/vgxness-memory.sqlite
554
576
  bun run cli:bun -- sdd list-artifacts --project vgxness --change sdd-workflow-engine --db /tmp/vgxness-memory.sqlite
555
577
  bun run cli:bun -- sdd next --project vgxness --change sdd-workflow-engine --db /tmp/vgxness-memory.sqlite
578
+ bun run cli:bun -- sdd reopen-artifact --project vgxness --change sdd-workflow-engine --phase design --actor user --note "Revise rejected design" --db /tmp/vgxness-memory.sqlite
556
579
  VGXNESS_DB_PATH=/tmp/vgxness-memory.sqlite bun run cli:bun -- sdd list-artifacts --project vgxness --change sdd-workflow-engine
557
580
  ```
558
581
 
559
- Current phases are `explore`, `proposal`, `spec`, `design`, `tasks`, `apply-progress`, `verify`, and `archive`. Each artifact is stored under `sdd/{change}/{phase}` in the local SQLite memory store. `sdd status` reports which phase artifacts are present and the next ready missing phase. `sdd next` reports the next SDD action for a project/change. `sdd status`, `sdd next`, `sdd get-artifact`, and `sdd list-artifacts` are human-readable by default; pass `--json` for automation/stable structured output. `sdd ready` reports satisfied prerequisites and missing artifact topic keys for one phase. `sdd get-artifact` shows artifact metadata and then full content after `--- Content ---`; `sdd list-artifacts` shows one compact row for every canonical SDD phase in phase order and does not print full artifact content.
582
+ Current canonical phases are `explore`, `proposal`, `spec`, `design`, `tasks`, `apply-progress`, `verify`, and `archive`. `apply` is accepted as a user-facing input alias, but artifacts, topic keys, readiness/status/cockpit JSON, and internal metadata continue to use canonical `apply-progress`. Each artifact is stored under `sdd/{change}/{phase}` in the local SQLite memory store. `sdd status` reports which phase artifacts are present, blocker-specific next actions, and related interrupted run hints when available. `sdd next` reports the next SDD action for a project/change. `sdd continue` turns that state into a read-only continuation plan, including draft-run suggestions for planning phases and related interrupted run context when a matching stopped run exists. `sdd status`, `sdd next`, `sdd continue`, `sdd get-artifact`, and `sdd list-artifacts` are human-readable by default; pass `--json` for automation/stable structured output. `sdd ready` reports satisfied prerequisites and missing artifact topic keys for one phase. `sdd get-artifact` shows artifact metadata and then full content after `--- Content ---`; `sdd list-artifacts` shows one compact row for every canonical SDD phase in phase order and does not print full artifact content.
583
+
584
+ `sdd accept-artifact` records explicit human-only acceptance of an existing artifact through the same SDD workflow service used by MCP. It requires `--project`, `--change`, `--phase`, and `--actor`; `--display-name` defaults to the actor id. `--accepted-at` is optional, but when supplied it must be an ISO date-time with an explicit timezone (`Z` or offset) and is normalized to UTC `toISOString()` output. The default output is a human-readable confirmation with a JSON hint; `--json` returns a content-free success object with project/change/phase/topic key/artifact id/status/acceptedBy/acceptedAt and optional note. `save-artifact` creates or updates draft content only and does not imply acceptance. `sdd reopen-artifact` moves a rejected artifact back to draft after an explicit human decision, so the artifact can be revised and accepted again.
560
585
 
561
- `sdd accept-artifact` records explicit human-only acceptance of an existing artifact through the same SDD workflow service used by MCP. It requires `--project`, `--change`, `--phase`, and `--actor`; `--display-name` defaults to the actor id. `--accepted-at` is optional, but when supplied it must be an ISO date-time with an explicit timezone (`Z` or offset) and is normalized to UTC `toISOString()` output. The default output is a human-readable confirmation with a JSON hint; `--json` returns a content-free success object with project/change/phase/topic key/artifact id/status/acceptedBy/acceptedAt and optional note. `save-artifact` creates or updates draft content only and does not imply acceptance.
586
+ Use `resume --project <project>` when you do not know the run id. It lists recent interrupted candidates for that project from the selected database. Use `resume --project <project> --run-id <id>` to inspect one candidate; this is still read-only and advisory.
562
587
 
563
- The SDD CLI is status/persistence only. It does **not** execute providers, continue workflows, create `openspec/`, or write `.opencode/`, `.claude/`, or user/global provider config.
588
+ The SDD CLI is status, planning, and artifact persistence only. It does **not** execute providers, retry workflow runs, create `openspec/`, or write `.opencode/`, `.claude/`, or user/global provider config.
564
589
 
565
590
  ## Agent resolution
566
591
 
@@ -953,7 +978,7 @@ For more on schema, scopes, and lifecycle, see [Storage](./storage.md).
953
978
 
954
979
  - [Architecture](./architecture.md) — current-state architecture and core domain model.
955
980
  - [Code runtime](./code-runtime.md) — `vgxness code` modes, tools, providers, and approval flow.
956
- - [MCP tools](./mcp.md) — full reference for the 38 MCP tools exposed to agents.
981
+ - [MCP tools](./mcp.md) — full reference for the 41 MCP tools exposed to agents.
957
982
  - [Safety model](./safety.md) — permission categories, approval flow, redactors, and runtime gates.
958
983
  - [Storage](./storage.md) — SQLite schema, scopes, and lifecycle.
959
984
  - [Providers](./providers.md) — adapter contract and how to add a new provider.