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.
- package/README.md +15 -5
- package/dist/agents/agent-activation-service.js +13 -4
- package/dist/agents/agent-registry-service.js +8 -2
- package/dist/agents/agent-resolver.js +33 -3
- package/dist/agents/agent-seed-upgrade-service.js +231 -0
- package/dist/agents/boot-upgrade.js +59 -0
- package/dist/agents/canonical-agent-manifest.js +39 -18
- package/dist/agents/canonical-agent-projection.js +38 -4
- package/dist/agents/manager-profile-overlay-service.js +14 -0
- package/dist/agents/repositories/agent-seed-history.js +128 -0
- package/dist/cli/cli-help.js +14 -3
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/interactive-entrypoint-dispatcher.js +8 -0
- package/dist/cli/commands/mcp-dispatcher.js +7 -0
- package/dist/cli/commands/memory-sdd-dispatcher.js +71 -5
- package/dist/cli/commands/status-dispatcher.js +130 -0
- package/dist/cli/commands/workflow-dispatcher.js +11 -5
- package/dist/cli/dispatcher.js +11 -1
- package/dist/cli/product-resume-renderer.js +32 -0
- package/dist/cli/product-status-renderer.js +74 -0
- package/dist/cli/sdd-renderer.js +80 -3
- package/dist/code/cli/code-command.js +7 -4
- package/dist/code/reporting/summary.js +4 -1
- package/dist/code/runtime/code-runtime.js +27 -4
- package/dist/code/runtime/sdd-context.js +18 -2
- package/dist/governance/governance-report-builder.js +18 -7
- package/dist/mcp/claude-code-agent-config.js +10 -4
- package/dist/mcp/client-install-claude-code-contract.js +19 -4
- package/dist/mcp/client-install-claude-code.js +2 -2
- package/dist/mcp/control-plane.js +78 -5
- package/dist/mcp/provider-status.js +89 -88
- package/dist/mcp/schema.js +42 -8
- package/dist/mcp/stdio-server.js +6 -0
- package/dist/mcp/validation.js +77 -5
- package/dist/memory/sqlite/migrations/016_agent_seed_history.sql +15 -0
- package/dist/resume/product-resume.js +166 -0
- package/dist/runs/repositories/runs.js +12 -1
- package/dist/runs/run-service.js +62 -5
- package/dist/runs/schema.js +4 -0
- package/dist/sdd/schema.js +20 -0
- package/dist/sdd/sdd-continuation-plan.js +81 -0
- package/dist/sdd/sdd-workflow-service.js +146 -18
- package/dist/skills/skill-resolver.js +21 -4
- package/dist/status/product-status.js +117 -0
- package/docs/architecture.md +9 -1
- package/docs/cli.md +38 -13
- package/docs/code-runtime.md +3 -0
- package/docs/contributing.md +1 -1
- package/docs/glossary.md +2 -2
- package/docs/mcp.md +20 -6
- package/docs/project-health-audit-v1.9.1.md +126 -0
- package/docs/providers.md +4 -4
- package/docs/safety.md +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { summarizePayloadContent } from '../payload/payload-summary.js';
|
|
2
|
-
import {
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
463
|
+
if (reason === 'missing')
|
|
464
|
+
missingPrerequisiteTopicKeys.push(topicKey);
|
|
465
|
+
const blocker = {
|
|
413
466
|
phase: prerequisite,
|
|
414
467
|
topicKey,
|
|
415
|
-
reason
|
|
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({
|
|
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 {
|
|
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 `
|
|
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
|
-
|
|
538
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
+
}
|
package/docs/architecture.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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 --
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|