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
|
|
312
|
-
if (!
|
|
313
|
-
return errorEnvelope(
|
|
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:
|
|
323
|
-
cockpit:
|
|
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
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
|
310
|
-
if (!
|
|
311
|
-
return
|
|
312
|
-
const
|
|
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) => {
|