vgxness 1.20.0 → 1.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -308,19 +308,16 @@ function governanceSddServices(services) {
308
308
  };
309
309
  }
310
310
  function sddContinueEnvelope(input, services) {
311
- const next = services.sdd.getNext({ project: input.project, change: input.change });
312
- if (!next.ok)
313
- return errorEnvelope(next.error.code, next.error.message, 'vgxness_sdd_continue');
314
- const cockpit = services.sdd.getCockpit({ project: input.project, change: input.change });
315
- if (!cockpit.ok)
316
- return errorEnvelope(cockpit.error.code, cockpit.error.message, 'vgxness_sdd_continue');
311
+ const context = services.sdd.getContinuationContext({ project: input.project, change: input.change });
312
+ if (!context.ok)
313
+ return errorEnvelope(context.error.code, context.error.message, 'vgxness_sdd_continue');
317
314
  const relatedRun = services.runs.findRelatedInterruptedSddRun({ project: input.project, change: input.change });
318
315
  if (!relatedRun.ok)
319
316
  return errorEnvelope(relatedRun.error.code, relatedRun.error.message, 'vgxness_sdd_continue');
320
317
  return successEnvelope('vgxness_sdd_continue', sddContinuationPlanFrom({
321
318
  project: input.project,
322
- next: next.value,
323
- cockpit: cockpit.value,
319
+ next: context.value.next,
320
+ cockpit: context.value.cockpit,
324
321
  ...(relatedRun.value === undefined ? {} : { relatedRunContext: relatedRun.value }),
325
322
  }));
326
323
  }
@@ -70,23 +70,42 @@ export class OpenCodeHandoffPreviewService {
70
70
  return { summary: { ...summary, unavailableReason: reason }, inspection: notInspectedSdd(reason) };
71
71
  }
72
72
  const status = this.deps.sdd.getStatus({ project: input.project, change: input.change });
73
- const next = this.deps.sdd.getNext({ project: input.project, change: input.change });
74
- const inspection = { inspected: true, statusQueried: true, nextQueried: true, readinessQueried: false };
73
+ const inspection = { inspected: true, statusQueried: true, nextQueried: true, readinessQueried: input.phase !== undefined };
75
74
  if (status.ok)
76
75
  summary.status = status.value;
77
76
  else
78
77
  warnings.push(`SDD status unavailable: ${status.error.message}`);
79
- if (next.ok)
80
- summary.next = next.value;
81
- else
82
- warnings.push(`SDD next phase unavailable: ${next.error.message}`);
83
- if (input.phase !== undefined) {
84
- const ready = this.deps.sdd.getReady({ project: input.project, change: input.change, phase: input.phase });
85
- inspection.readinessQueried = true;
86
- if (ready.ok)
87
- summary.readiness = ready.value;
78
+ const continuation = this.deps.sdd.getContinuationContext?.({ project: input.project, change: input.change });
79
+ if (continuation !== undefined) {
80
+ if (continuation.ok) {
81
+ const continuationNext = continuationNextValue(continuation.value);
82
+ if (continuationNext !== undefined)
83
+ summary.next = continuationNext;
84
+ if (input.phase !== undefined) {
85
+ const continuationReadiness = continuationReadinessValue(continuation.value, input.phase);
86
+ if (continuationReadiness !== undefined)
87
+ summary.readiness = continuationReadiness;
88
+ }
89
+ }
90
+ else {
91
+ warnings.push(`SDD continuation context unavailable: ${continuation.error.message}`);
92
+ }
93
+ }
94
+ if (summary.next === undefined) {
95
+ const next = this.deps.sdd.getNext({ project: input.project, change: input.change });
96
+ if (next.ok)
97
+ summary.next = next.value;
88
98
  else
89
- warnings.push(`SDD readiness unavailable: ${ready.error.message}`);
99
+ warnings.push(`SDD next phase unavailable: ${next.error.message}`);
100
+ }
101
+ if (input.phase !== undefined) {
102
+ if (summary.readiness === undefined) {
103
+ const ready = this.deps.sdd.getReady({ project: input.project, change: input.change, phase: input.phase });
104
+ if (ready.ok)
105
+ summary.readiness = ready.value;
106
+ else
107
+ warnings.push(`SDD readiness unavailable: ${ready.error.message}`);
108
+ }
90
109
  }
91
110
  return { summary, inspection };
92
111
  }
@@ -108,6 +127,47 @@ export class OpenCodeHandoffPreviewService {
108
127
  function notInspectedSdd(reason) {
109
128
  return { inspected: false, statusQueried: false, nextQueried: false, readinessQueried: false, notInspectedReason: reason };
110
129
  }
130
+ function continuationNextValue(value) {
131
+ const record = asRecord(value);
132
+ if (record === undefined)
133
+ return undefined;
134
+ const next = record.next;
135
+ return next === null ? undefined : next;
136
+ }
137
+ function continuationReadinessValue(value, phase) {
138
+ const record = asRecord(value);
139
+ if (record === undefined)
140
+ return undefined;
141
+ return readinessFromPhaseEntries(asRecord(record.cockpit)?.phases, phase) ?? readinessFromPhaseEntries(record.phases, phase) ?? readinessFromPhaseEntries(asRecord(record.readModel)?.phases, phase) ?? readinessFromReadinessMap(record.readinessByPhase, phase) ?? readinessFromReadinessMap(record.phaseReadiness, phase) ?? matchingReadiness(record.readiness, phase);
142
+ }
143
+ function readinessFromPhaseEntries(value, phase) {
144
+ if (!Array.isArray(value))
145
+ return undefined;
146
+ for (const entry of value) {
147
+ const record = asRecord(entry);
148
+ if (record?.phase === phase)
149
+ return record.readiness === null ? undefined : record.readiness;
150
+ }
151
+ return undefined;
152
+ }
153
+ function readinessFromReadinessMap(value, phase) {
154
+ const record = asRecord(value);
155
+ if (record === undefined)
156
+ return undefined;
157
+ const readiness = record[phase];
158
+ return readiness === null ? undefined : readiness;
159
+ }
160
+ function matchingReadiness(value, phase) {
161
+ const record = asRecord(value);
162
+ if (record === undefined)
163
+ return undefined;
164
+ if (record.phase !== undefined && record.phase !== phase)
165
+ return undefined;
166
+ return value;
167
+ }
168
+ function asRecord(value) {
169
+ return typeof value === 'object' && value !== null ? value : undefined;
170
+ }
111
171
  function managerInput(input, scope) {
112
172
  return { project: input.project, scope, agentName: input.agentName ?? 'vgxness-manager', ...(input.agentId === undefined ? {} : { agentId: input.agentId }), ...(input.workspaceRoot === undefined ? {} : { workspaceRoot: input.workspaceRoot }), ...(input.maxSourceBytes === undefined ? {} : { maxSourceBytes: input.maxSourceBytes }), payloadMode: 'compact' };
113
173
  }
@@ -93,6 +93,25 @@ export class ProviderStatusService {
93
93
  readSdd(project, change) {
94
94
  if (this.deps.sdd === undefined)
95
95
  return { change };
96
+ const continuation = this.deps.sdd.getContinuationContext?.({ project, change });
97
+ if (continuation?.ok === true && isRecord(continuation.value)) {
98
+ const continuationNext = isRecord(continuation.value.next) ? continuation.value.next : undefined;
99
+ const continuationStatus = projectSddCockpitStatus(continuation.value.cockpit, change, continuationNext);
100
+ if (continuationNext !== undefined) {
101
+ if (continuationStatus !== undefined)
102
+ return { change, status: continuationStatus, next: continuationNext };
103
+ const status = this.deps.sdd.getStatus({ project, change });
104
+ return { change, ...(status.ok ? { status: status.value } : {}), next: continuationNext };
105
+ }
106
+ if (continuationStatus !== undefined) {
107
+ const next = this.deps.sdd.getNext({ project, change });
108
+ return {
109
+ change,
110
+ status: projectSddCockpitStatus(continuation.value.cockpit, change, next.ok && isRecord(next.value) ? next.value : undefined),
111
+ ...(next.ok && isRecord(next.value) ? { next: next.value } : {}),
112
+ };
113
+ }
114
+ }
96
115
  const status = this.deps.sdd.getStatus({ project, change });
97
116
  const next = this.deps.sdd.getNext({ project, change });
98
117
  return { change, ...(status.ok ? { status: status.value } : {}), ...(next.ok ? { next: next.value } : {}) };
@@ -257,6 +276,31 @@ function compactSdd(sdd, mode) {
257
276
  ...(sdd.next === undefined ? {} : { next: compactUnknown(sdd.next) }),
258
277
  };
259
278
  }
279
+ function projectSddCockpitStatus(cockpit, change, next) {
280
+ if (!isRecord(cockpit) || !Array.isArray(cockpit.phases))
281
+ return undefined;
282
+ const phases = cockpit.phases.map((phase) => projectSddPhaseStatus(phase));
283
+ if (phases.some((phase) => phase === undefined))
284
+ return undefined;
285
+ return {
286
+ change,
287
+ phases,
288
+ ...(next?.status === 'runnable' && typeof next.nextPhase === 'string' ? { nextReadyPhase: next.nextPhase } : {}),
289
+ };
290
+ }
291
+ function projectSddPhaseStatus(phase) {
292
+ if (!isRecord(phase) || typeof phase.phase !== 'string' || typeof phase.topicKey !== 'string' || typeof phase.present !== 'boolean')
293
+ return undefined;
294
+ return {
295
+ phase: phase.phase,
296
+ topicKey: phase.topicKey,
297
+ present: phase.present,
298
+ ...(typeof phase.state === 'string' ? { state: phase.state } : {}),
299
+ ...(typeof phase.accepted === 'boolean' ? { accepted: phase.accepted } : {}),
300
+ ...(typeof phase.legacy === 'boolean' ? { legacy: phase.legacy } : {}),
301
+ ...(typeof phase.artifactId === 'string' ? { artifactId: phase.artifactId } : {}),
302
+ };
303
+ }
260
304
  function compactUnknown(value) {
261
305
  if (!isRecord(value))
262
306
  return value;
@@ -80,57 +80,17 @@ export class SddWorkflowService {
80
80
  const snapshot = this.loadPhaseSnapshot(validated.value.project, validated.value.change);
81
81
  if (!snapshot.ok)
82
82
  return snapshot;
83
- const phases = snapshot.value.phases;
84
- const next = nextDecisionFromStatuses(validated.value.change, phases, this.options);
85
- const cockpitPhases = phases.map((phaseStatus) => {
86
- const readiness = {
87
- change: validated.value.change,
88
- phase: phaseStatus.phase,
89
- ...getReadinessFromStatuses(validated.value.change, phaseStatus.phase, phases, this.options),
90
- };
91
- const artifact = phaseStatus.present ? cockpitArtifactSummaryFromSnapshotItem(phaseStatus) : undefined;
92
- const blockers = cockpitBlockersForPhase(phaseStatus, readiness);
93
- const gates = phaseGateFromStatus(phaseStatus, readiness);
94
- return {
95
- phase: phaseStatus.phase,
96
- topicKey: phaseStatus.topicKey,
97
- present: phaseStatus.present,
98
- accepted: phaseStatus.accepted === true,
99
- legacy: phaseStatus.legacy === true,
100
- state: phaseStatus.state ?? 'missing',
101
- readiness,
102
- ...(artifact === undefined ? {} : { artifact }),
103
- blockers,
104
- gates,
105
- };
106
- });
107
- const artifacts = cockpitPhases.map((phase) => phase.artifact).filter((artifact) => artifact !== undefined);
108
- const aggregateBlockers = dedupeCockpitBlockers([
109
- ...cockpitPhases.flatMap((phase) => phase.blockers),
110
- ...(next.missingPrerequisiteTopicKeys ?? []).map((topicKey) => blockerFromTopicKey(validated.value.change, topicKey)),
111
- ...(next.blockedPrerequisites ?? []).map((blocker) => ({
112
- kind: 'readiness',
113
- phase: blocker.phase,
114
- topicKey: blocker.topicKey,
115
- reason: blocker.reason,
116
- ...(blocker.artifactId === undefined ? {} : { artifactId: blocker.artifactId }),
117
- })),
118
- ]);
119
- const cockpit = {
120
- project: validated.value.project,
121
- change: validated.value.change,
122
- ...(next.nextPhase === undefined ? {} : { actionablePhase: next.nextPhase }),
123
- next,
124
- recommendedAction: cockpitRecommendedAction(next, aggregateBlockers),
125
- phases: cockpitPhases,
126
- artifacts,
127
- acceptedCount: cockpitPhases.filter((phase) => phase.accepted).length,
128
- legacyCount: cockpitPhases.filter((phase) => phase.legacy).length,
129
- aggregateBlockers,
130
- gates: cockpitGatesFromPhases(cockpitPhases, next),
131
- inspectCommand: `vgxness sdd cockpit --project ${validated.value.project} --change ${validated.value.change} --json`,
132
- };
133
- return ok(cockpit);
83
+ return ok(this.cockpitFromSnapshot(snapshot.value));
84
+ }
85
+ getContinuationContext(input) {
86
+ const validated = validateProjectAndChange(input.project, input.change);
87
+ if (!validated.ok)
88
+ return validated;
89
+ const snapshot = this.loadPhaseSnapshot(validated.value.project, validated.value.change);
90
+ if (!snapshot.ok)
91
+ return snapshot;
92
+ const cockpit = this.cockpitFromSnapshot(snapshot.value);
93
+ return ok({ next: cockpit.next, cockpit });
134
94
  }
135
95
  getGovernanceSnapshot(input) {
136
96
  const validated = validateProjectAndChange(input.project, input.change);
@@ -306,13 +266,10 @@ export class SddWorkflowService {
306
266
  },
307
267
  });
308
268
  }
309
- const listed = this.memory.listArtifactsByTopicPrefix(validated.value.project, `sdd/${validated.value.change}/`, this.context);
310
- if (!listed.ok)
311
- return listed;
312
- const artifactsByTopicKey = new Map(listed.value.map((artifact) => [artifact.topicKey, artifact]));
313
- const artifacts = sddPhases
314
- .map((phase) => artifactsByTopicKey.get(sddTopicKey(validated.value.change, phase)))
315
- .filter((artifact) => artifact !== undefined);
269
+ const snapshot = this.loadPhaseSnapshot(validated.value.project, validated.value.change);
270
+ if (!snapshot.ok)
271
+ return snapshot;
272
+ const artifacts = snapshot.value.phases.flatMap((phase) => (phase.artifact === undefined ? [] : [phase.artifact]));
316
273
  return ok({ project: validated.value.project, change: validated.value.change, artifacts });
317
274
  }
318
275
  validatePhaseInput(input) {
@@ -358,6 +315,58 @@ export class SddWorkflowService {
358
315
  });
359
316
  return ok({ project, change, phases });
360
317
  }
318
+ cockpitFromSnapshot(snapshot) {
319
+ const phases = snapshot.phases;
320
+ const next = nextDecisionFromStatuses(snapshot.change, phases, this.options);
321
+ const cockpitPhases = phases.map((phaseStatus) => {
322
+ const readiness = {
323
+ change: snapshot.change,
324
+ phase: phaseStatus.phase,
325
+ ...getReadinessFromStatuses(snapshot.change, phaseStatus.phase, phases, this.options),
326
+ };
327
+ const artifact = phaseStatus.present ? cockpitArtifactSummaryFromSnapshotItem(phaseStatus) : undefined;
328
+ const blockers = cockpitBlockersForPhase(phaseStatus, readiness);
329
+ const gates = phaseGateFromStatus(phaseStatus, readiness);
330
+ return {
331
+ phase: phaseStatus.phase,
332
+ topicKey: phaseStatus.topicKey,
333
+ present: phaseStatus.present,
334
+ accepted: phaseStatus.accepted === true,
335
+ legacy: phaseStatus.legacy === true,
336
+ state: phaseStatus.state ?? 'missing',
337
+ readiness,
338
+ ...(artifact === undefined ? {} : { artifact }),
339
+ blockers,
340
+ gates,
341
+ };
342
+ });
343
+ const artifacts = cockpitPhases.map((phase) => phase.artifact).filter((artifact) => artifact !== undefined);
344
+ const aggregateBlockers = dedupeCockpitBlockers([
345
+ ...cockpitPhases.flatMap((phase) => phase.blockers),
346
+ ...(next.missingPrerequisiteTopicKeys ?? []).map((topicKey) => blockerFromTopicKey(snapshot.change, topicKey)),
347
+ ...(next.blockedPrerequisites ?? []).map((blocker) => ({
348
+ kind: 'readiness',
349
+ phase: blocker.phase,
350
+ topicKey: blocker.topicKey,
351
+ reason: blocker.reason,
352
+ ...(blocker.artifactId === undefined ? {} : { artifactId: blocker.artifactId }),
353
+ })),
354
+ ]);
355
+ return {
356
+ project: snapshot.project,
357
+ change: snapshot.change,
358
+ ...(next.nextPhase === undefined ? {} : { actionablePhase: next.nextPhase }),
359
+ next,
360
+ recommendedAction: cockpitRecommendedAction(next, aggregateBlockers),
361
+ phases: cockpitPhases,
362
+ artifacts,
363
+ acceptedCount: cockpitPhases.filter((phase) => phase.accepted).length,
364
+ legacyCount: cockpitPhases.filter((phase) => phase.legacy).length,
365
+ aggregateBlockers,
366
+ gates: cockpitGatesFromPhases(cockpitPhases, next),
367
+ inspectCommand: `vgxness sdd cockpit --project ${snapshot.project} --change ${snapshot.change} --json`,
368
+ };
369
+ }
361
370
  }
362
371
  export function nextDecisionFromStatuses(change, phases, options = {}) {
363
372
  const blockedPresentPhase = phases.find((status) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vgxness",
3
- "version": "1.20.0",
3
+ "version": "1.20.1",
4
4
  "description": "CLI and MCP control plane for guided AI-agent workflows, SDD, memory, and OpenCode setup.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "repository": {