@chllming/wave-orchestration 0.8.2 → 0.8.4
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/CHANGELOG.md +40 -2
- package/README.md +47 -11
- package/docs/README.md +6 -2
- package/docs/concepts/what-is-a-wave.md +1 -1
- package/docs/plans/architecture-hardening-migration.md +8 -1
- package/docs/plans/current-state.md +17 -7
- package/docs/plans/end-state-architecture.md +82 -69
- package/docs/plans/examples/wave-example-live-proof.md +1 -1
- package/docs/plans/migration.md +235 -61
- package/docs/plans/wave-orchestrator.md +37 -11
- package/docs/reference/cli-reference.md +39 -15
- package/docs/reference/coordination-and-closure.md +30 -6
- package/docs/reference/npmjs-trusted-publishing.md +5 -4
- package/docs/reference/sample-waves.md +4 -4
- package/package.json +1 -1
- package/releases/manifest.json +39 -0
- package/scripts/wave-orchestrator/agent-state.mjs +0 -491
- package/scripts/wave-orchestrator/autonomous.mjs +10 -6
- package/scripts/wave-orchestrator/{launcher-closure.mjs → closure-engine.mjs} +190 -74
- package/scripts/wave-orchestrator/control-cli.mjs +8 -0
- package/scripts/wave-orchestrator/coord-cli.mjs +8 -0
- package/scripts/wave-orchestrator/{launcher-derived-state.mjs → derived-state-engine.mjs} +34 -146
- package/scripts/wave-orchestrator/feedback.mjs +11 -1
- package/scripts/wave-orchestrator/{launcher-gates.mjs → gate-engine.mjs} +395 -139
- package/scripts/wave-orchestrator/human-input-resolution.mjs +348 -0
- package/scripts/wave-orchestrator/human-input-workflow.mjs +104 -0
- package/scripts/wave-orchestrator/implementation-engine.mjs +120 -0
- package/scripts/wave-orchestrator/launcher-runtime.mjs +5 -6
- package/scripts/wave-orchestrator/launcher.mjs +271 -724
- package/scripts/wave-orchestrator/projection-writer.mjs +256 -0
- package/scripts/wave-orchestrator/reconcile-format.mjs +32 -0
- package/scripts/wave-orchestrator/reducer-snapshot.mjs +297 -0
- package/scripts/wave-orchestrator/replay.mjs +3 -1
- package/scripts/wave-orchestrator/result-envelope.mjs +589 -0
- package/scripts/wave-orchestrator/retry-control.mjs +5 -0
- package/scripts/wave-orchestrator/{launcher-retry.mjs → retry-engine.mjs} +267 -18
- package/scripts/wave-orchestrator/role-helpers.mjs +51 -0
- package/scripts/wave-orchestrator/{launcher-supervisor.mjs → session-supervisor.mjs} +178 -103
- package/scripts/wave-orchestrator/shared.mjs +1 -0
- package/scripts/wave-orchestrator/traces.mjs +10 -1
- package/scripts/wave-orchestrator/wave-files.mjs +11 -9
- package/scripts/wave-orchestrator/wave-state-reducer.mjs +52 -5
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
REPO_ROOT,
|
|
4
|
+
readJsonOrNull,
|
|
5
|
+
toIsoTimestamp,
|
|
6
|
+
writeJsonAtomic,
|
|
7
|
+
} from "./shared.mjs";
|
|
8
|
+
|
|
9
|
+
export const ENVELOPE_VALID_ROLES = [
|
|
10
|
+
"implementation",
|
|
11
|
+
"integration",
|
|
12
|
+
"documentation",
|
|
13
|
+
"cont-qa",
|
|
14
|
+
"cont-eval",
|
|
15
|
+
"security",
|
|
16
|
+
"deploy",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function inferEnvelopeRole(agent, summary) {
|
|
20
|
+
const candidate = String(agent?.role || summary?.role || "").trim().toLowerCase();
|
|
21
|
+
if (candidate && ENVELOPE_VALID_ROLES.includes(candidate)) {
|
|
22
|
+
return candidate;
|
|
23
|
+
}
|
|
24
|
+
if (summary?.integration) {
|
|
25
|
+
return "integration";
|
|
26
|
+
}
|
|
27
|
+
if (summary?.docClosure) {
|
|
28
|
+
return "documentation";
|
|
29
|
+
}
|
|
30
|
+
if (summary?.eval) {
|
|
31
|
+
return "cont-eval";
|
|
32
|
+
}
|
|
33
|
+
if (summary?.security) {
|
|
34
|
+
return "security";
|
|
35
|
+
}
|
|
36
|
+
if (summary?.gate || summary?.verdict) {
|
|
37
|
+
return "cont-qa";
|
|
38
|
+
}
|
|
39
|
+
if (summary?.deploy) {
|
|
40
|
+
return "deploy";
|
|
41
|
+
}
|
|
42
|
+
if (summary?.proof || summary?.docDelta || Array.isArray(summary?.components)) {
|
|
43
|
+
return "implementation";
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function toSummaryRelativePath(filePath) {
|
|
49
|
+
const normalized = String(filePath || "").trim();
|
|
50
|
+
if (!normalized) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return path.isAbsolute(normalized) ? path.relative(REPO_ROOT, normalized) : normalized;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function toLegacyProofState(state) {
|
|
57
|
+
if (state === "satisfied") {
|
|
58
|
+
return "met";
|
|
59
|
+
}
|
|
60
|
+
if (state === "partial") {
|
|
61
|
+
return "gap";
|
|
62
|
+
}
|
|
63
|
+
if (state === "failed") {
|
|
64
|
+
return "failed";
|
|
65
|
+
}
|
|
66
|
+
return "not_applicable";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function agentEnvelopePath({ lane, waveNumber, attempt, agentId }) {
|
|
70
|
+
const safeLane = lane || "main";
|
|
71
|
+
const safeWave = waveNumber ?? 0;
|
|
72
|
+
const safeAttempt = attempt ?? 1;
|
|
73
|
+
const safeAgent = agentId || "unknown";
|
|
74
|
+
return `.tmp/${safeLane}-wave-launcher/results/wave-${safeWave}/attempt-${safeAttempt}/${safeAgent}.json`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizePositiveAttempt(value) {
|
|
78
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
79
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function inferResultsDirFromStatusPath(statusPath) {
|
|
83
|
+
const normalized = String(statusPath || "").trim();
|
|
84
|
+
if (!normalized) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const statusDir = path.dirname(normalized);
|
|
88
|
+
if (path.basename(statusDir) === "status") {
|
|
89
|
+
return path.join(path.dirname(statusDir), "results");
|
|
90
|
+
}
|
|
91
|
+
return path.join(statusDir, "results");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function runScopedEnvelopePath({ resultsDir, lane, waveNumber, attempt, agentId }) {
|
|
95
|
+
const safeWave = waveNumber ?? 0;
|
|
96
|
+
const safeAttempt = normalizePositiveAttempt(attempt) ?? 1;
|
|
97
|
+
const safeAgent = agentId || "unknown";
|
|
98
|
+
if (resultsDir) {
|
|
99
|
+
return path.join(resultsDir, `wave-${safeWave}`, `attempt-${safeAttempt}`, `${safeAgent}.json`);
|
|
100
|
+
}
|
|
101
|
+
return path.join(
|
|
102
|
+
REPO_ROOT,
|
|
103
|
+
agentEnvelopePath({
|
|
104
|
+
lane,
|
|
105
|
+
waveNumber: safeWave,
|
|
106
|
+
attempt: safeAttempt,
|
|
107
|
+
agentId: safeAgent,
|
|
108
|
+
}),
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function agentEnvelopePathFromStatusPath(statusPath) {
|
|
113
|
+
if (statusPath.endsWith(".summary.json")) {
|
|
114
|
+
return statusPath.replace(/\.summary\.json$/i, ".envelope.json");
|
|
115
|
+
}
|
|
116
|
+
if (statusPath.endsWith(".status")) {
|
|
117
|
+
return statusPath.replace(/\.status$/i, ".envelope.json");
|
|
118
|
+
}
|
|
119
|
+
return `${statusPath}.envelope.json`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function buildAgentResultEnvelope(agent, summary, options = {}) {
|
|
123
|
+
const safeAgent = agent || {};
|
|
124
|
+
const safeSummary = summary || {};
|
|
125
|
+
const safeOptions = options || {};
|
|
126
|
+
|
|
127
|
+
const agentId = safeAgent.agentId || safeSummary.agentId || null;
|
|
128
|
+
const role = inferEnvelopeRole(safeAgent, safeSummary);
|
|
129
|
+
|
|
130
|
+
const proof = safeSummary.proof || {};
|
|
131
|
+
const proofSection = {
|
|
132
|
+
state: proof.state === "met"
|
|
133
|
+
? "satisfied"
|
|
134
|
+
: proof.state === "gap"
|
|
135
|
+
? "partial"
|
|
136
|
+
: proof.state === "failed"
|
|
137
|
+
? "failed"
|
|
138
|
+
: "not_applicable",
|
|
139
|
+
completion: proof.completion || null,
|
|
140
|
+
durability: proof.durability || null,
|
|
141
|
+
proofLevel: proof.proof || null,
|
|
142
|
+
detail: proof.detail || null,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const deliverables = Array.isArray(safeSummary.deliverables)
|
|
146
|
+
? safeSummary.deliverables.map((deliverable) => ({
|
|
147
|
+
path: deliverable.path || null,
|
|
148
|
+
exists: deliverable.exists === true,
|
|
149
|
+
sha256: deliverable.sha256 || null,
|
|
150
|
+
}))
|
|
151
|
+
: [];
|
|
152
|
+
const proofArtifacts = Array.isArray(safeSummary.proofArtifacts)
|
|
153
|
+
? safeSummary.proofArtifacts.map((artifact) => ({
|
|
154
|
+
path: artifact.path || null,
|
|
155
|
+
kind: artifact.kind || null,
|
|
156
|
+
exists: artifact.exists === true,
|
|
157
|
+
sha256: artifact.sha256 || null,
|
|
158
|
+
requiredFor: artifact.requiredFor || null,
|
|
159
|
+
}))
|
|
160
|
+
: [];
|
|
161
|
+
const gaps = Array.isArray(safeSummary.gaps)
|
|
162
|
+
? safeSummary.gaps.map((gap) => ({
|
|
163
|
+
kind: gap.kind || null,
|
|
164
|
+
detail: gap.detail || null,
|
|
165
|
+
}))
|
|
166
|
+
: [];
|
|
167
|
+
const unresolvedBlockers = Array.isArray(safeSummary.unresolvedBlockers)
|
|
168
|
+
? safeSummary.unresolvedBlockers.map((blocker) =>
|
|
169
|
+
typeof blocker === "object"
|
|
170
|
+
? {
|
|
171
|
+
kind: blocker.kind || null,
|
|
172
|
+
detail: blocker.detail || null,
|
|
173
|
+
blocking: blocker.blocking || null,
|
|
174
|
+
}
|
|
175
|
+
: { kind: null, detail: String(blocker), blocking: null })
|
|
176
|
+
: [];
|
|
177
|
+
const riskNotes = Array.isArray(safeSummary.riskNotes) ? safeSummary.riskNotes : [];
|
|
178
|
+
const facts = Array.isArray(safeSummary.facts)
|
|
179
|
+
? safeSummary.facts.map((fact) => ({
|
|
180
|
+
factId: fact.factId || null,
|
|
181
|
+
kind: fact.kind || null,
|
|
182
|
+
content: fact.content || null,
|
|
183
|
+
}))
|
|
184
|
+
: [];
|
|
185
|
+
|
|
186
|
+
const envelope = {
|
|
187
|
+
schemaVersion: 2,
|
|
188
|
+
agentId,
|
|
189
|
+
waveNumber: safeOptions.waveNumber ?? safeSummary.waveNumber ?? null,
|
|
190
|
+
attempt: safeOptions.attempt ?? safeSummary.attempt ?? null,
|
|
191
|
+
completedAt: safeOptions.completedAt || safeSummary.completedAt || toIsoTimestamp(),
|
|
192
|
+
exitCode:
|
|
193
|
+
typeof safeOptions.exitCode === "number"
|
|
194
|
+
? safeOptions.exitCode
|
|
195
|
+
: typeof safeSummary.exitCode === "number"
|
|
196
|
+
? safeSummary.exitCode
|
|
197
|
+
: 0,
|
|
198
|
+
role,
|
|
199
|
+
proof: proofSection,
|
|
200
|
+
deliverables,
|
|
201
|
+
proofArtifacts,
|
|
202
|
+
gaps,
|
|
203
|
+
unresolvedBlockers,
|
|
204
|
+
riskNotes,
|
|
205
|
+
facts,
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
if (role === "implementation") {
|
|
209
|
+
const docDelta = safeSummary.docDelta || {};
|
|
210
|
+
const components = Array.isArray(safeSummary.components)
|
|
211
|
+
? safeSummary.components.map((component) => ({
|
|
212
|
+
componentId: component.componentId || null,
|
|
213
|
+
level: component.level || null,
|
|
214
|
+
state: component.state || null,
|
|
215
|
+
detail: component.detail || null,
|
|
216
|
+
}))
|
|
217
|
+
: [];
|
|
218
|
+
envelope.implementation = {
|
|
219
|
+
docDelta: {
|
|
220
|
+
state: docDelta.state || "none",
|
|
221
|
+
paths: Array.isArray(docDelta.paths) ? docDelta.paths : [],
|
|
222
|
+
detail: docDelta.detail || null,
|
|
223
|
+
},
|
|
224
|
+
components,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (role === "integration") {
|
|
229
|
+
const integration = safeSummary.integration || {};
|
|
230
|
+
envelope.integration = {
|
|
231
|
+
state: integration.state || null,
|
|
232
|
+
claims: integration.claims || 0,
|
|
233
|
+
conflicts: integration.conflicts || 0,
|
|
234
|
+
blockers: integration.blockers || 0,
|
|
235
|
+
detail: integration.detail || null,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (role === "documentation") {
|
|
240
|
+
const docClosure = safeSummary.docDelta || safeSummary.docClosure || {};
|
|
241
|
+
envelope.documentation = {
|
|
242
|
+
docClosure: {
|
|
243
|
+
state: docClosure.state || "no-change",
|
|
244
|
+
paths: Array.isArray(docClosure.paths) ? docClosure.paths : [],
|
|
245
|
+
detail: docClosure.detail || null,
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (role === "cont-qa") {
|
|
251
|
+
const verdict = safeSummary.verdict || {};
|
|
252
|
+
const gate = safeSummary.gate || {};
|
|
253
|
+
const gateClaims = {
|
|
254
|
+
architecture: gate.architecture || null,
|
|
255
|
+
integration: gate.integration || null,
|
|
256
|
+
durability: gate.durability || null,
|
|
257
|
+
live: gate.live || null,
|
|
258
|
+
docs: gate.docs || null,
|
|
259
|
+
detail: gate.detail || null,
|
|
260
|
+
};
|
|
261
|
+
envelope.contQa = {
|
|
262
|
+
verdict: {
|
|
263
|
+
verdict: verdict.verdict || null,
|
|
264
|
+
detail: verdict.detail || null,
|
|
265
|
+
},
|
|
266
|
+
...(Object.values(gateClaims).some((value) => value !== null) ? { gateClaims } : {}),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (role === "cont-eval") {
|
|
271
|
+
const evalSection = safeSummary.eval || {};
|
|
272
|
+
envelope.contEval = {
|
|
273
|
+
state: evalSection.state || null,
|
|
274
|
+
targets: evalSection.targets || 0,
|
|
275
|
+
benchmarks: evalSection.benchmarks || 0,
|
|
276
|
+
regressions: evalSection.regressions || 0,
|
|
277
|
+
targetIds: Array.isArray(evalSection.targetIds) ? evalSection.targetIds : [],
|
|
278
|
+
benchmarkIds: Array.isArray(evalSection.benchmarkIds) ? evalSection.benchmarkIds : [],
|
|
279
|
+
detail: evalSection.detail || null,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (role === "security") {
|
|
284
|
+
const security = safeSummary.security || {};
|
|
285
|
+
envelope.security = {
|
|
286
|
+
state: security.state || null,
|
|
287
|
+
findings: security.findings || 0,
|
|
288
|
+
approvals: security.approvals || 0,
|
|
289
|
+
detail: security.detail || null,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (role === "deploy") {
|
|
294
|
+
const deploy = safeSummary.deploy || {};
|
|
295
|
+
envelope.deploy = {
|
|
296
|
+
state: deploy.state || "not_applicable",
|
|
297
|
+
environment: deploy.environment || null,
|
|
298
|
+
healthCheck: deploy.healthCheck || null,
|
|
299
|
+
rolloutArtifact: deploy.rolloutArtifact || null,
|
|
300
|
+
detail: deploy.detail || null,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return envelope;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export function synthesizeLegacyEnvelope(agent, legacySummary, options = {}) {
|
|
308
|
+
const envelope = buildAgentResultEnvelope(agent, legacySummary, options);
|
|
309
|
+
envelope._synthesizedFromLegacy = true;
|
|
310
|
+
return envelope;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function projectLegacySummaryFromEnvelope(envelope, options = {}) {
|
|
314
|
+
if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
const role = inferEnvelopeRole(options.agent || {}, envelope);
|
|
318
|
+
const proof =
|
|
319
|
+
envelope.proof && typeof envelope.proof === "object"
|
|
320
|
+
? {
|
|
321
|
+
state: toLegacyProofState(envelope.proof.state),
|
|
322
|
+
completion: envelope.proof.completion || null,
|
|
323
|
+
durability: envelope.proof.durability || null,
|
|
324
|
+
proof: envelope.proof.proofLevel || null,
|
|
325
|
+
detail: envelope.proof.detail || null,
|
|
326
|
+
}
|
|
327
|
+
: null;
|
|
328
|
+
const summary = {
|
|
329
|
+
agentId: envelope.agentId || options.agent?.agentId || null,
|
|
330
|
+
role,
|
|
331
|
+
waveNumber: envelope.waveNumber ?? options.waveNumber ?? null,
|
|
332
|
+
attempt: envelope.attempt ?? options.attempt ?? null,
|
|
333
|
+
completedAt: envelope.completedAt || null,
|
|
334
|
+
exitCode: typeof envelope.exitCode === "number" ? envelope.exitCode : 0,
|
|
335
|
+
logPath: toSummaryRelativePath(options.logPath),
|
|
336
|
+
reportPath: toSummaryRelativePath(options.reportPath),
|
|
337
|
+
proof,
|
|
338
|
+
deliverables: Array.isArray(envelope.deliverables)
|
|
339
|
+
? envelope.deliverables.map((deliverable) => ({
|
|
340
|
+
path: deliverable.path || null,
|
|
341
|
+
exists: deliverable.exists === true,
|
|
342
|
+
sha256: deliverable.sha256 || null,
|
|
343
|
+
}))
|
|
344
|
+
: [],
|
|
345
|
+
proofArtifacts: Array.isArray(envelope.proofArtifacts)
|
|
346
|
+
? envelope.proofArtifacts.map((artifact) => ({
|
|
347
|
+
path: artifact.path || null,
|
|
348
|
+
kind: artifact.kind || null,
|
|
349
|
+
exists: artifact.exists === true,
|
|
350
|
+
sha256: artifact.sha256 || null,
|
|
351
|
+
requiredFor: artifact.requiredFor || null,
|
|
352
|
+
}))
|
|
353
|
+
: [],
|
|
354
|
+
gaps: Array.isArray(envelope.gaps)
|
|
355
|
+
? envelope.gaps.map((gap) => ({
|
|
356
|
+
kind: gap.kind || null,
|
|
357
|
+
detail: gap.detail || null,
|
|
358
|
+
}))
|
|
359
|
+
: [],
|
|
360
|
+
unresolvedBlockers: Array.isArray(envelope.unresolvedBlockers)
|
|
361
|
+
? envelope.unresolvedBlockers.map((blocker) => ({
|
|
362
|
+
kind: blocker.kind || null,
|
|
363
|
+
detail: blocker.detail || null,
|
|
364
|
+
blocking: blocker.blocking ?? null,
|
|
365
|
+
}))
|
|
366
|
+
: [],
|
|
367
|
+
riskNotes: Array.isArray(envelope.riskNotes) ? envelope.riskNotes.slice() : [],
|
|
368
|
+
facts: Array.isArray(envelope.facts)
|
|
369
|
+
? envelope.facts.map((fact) => ({
|
|
370
|
+
factId: fact.factId || null,
|
|
371
|
+
kind: fact.kind || null,
|
|
372
|
+
content: fact.content || null,
|
|
373
|
+
}))
|
|
374
|
+
: [],
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
if (envelope.implementation) {
|
|
378
|
+
summary.docDelta = {
|
|
379
|
+
state: envelope.implementation.docDelta?.state || "none",
|
|
380
|
+
paths: Array.isArray(envelope.implementation.docDelta?.paths)
|
|
381
|
+
? envelope.implementation.docDelta.paths
|
|
382
|
+
: [],
|
|
383
|
+
detail: envelope.implementation.docDelta?.detail || null,
|
|
384
|
+
};
|
|
385
|
+
summary.components = Array.isArray(envelope.implementation.components)
|
|
386
|
+
? envelope.implementation.components.map((component) => ({
|
|
387
|
+
componentId: component.componentId || null,
|
|
388
|
+
level: component.level || null,
|
|
389
|
+
state: component.state || null,
|
|
390
|
+
detail: component.detail || null,
|
|
391
|
+
}))
|
|
392
|
+
: [];
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (envelope.documentation?.docClosure) {
|
|
396
|
+
summary.docClosure = {
|
|
397
|
+
state: envelope.documentation.docClosure.state || "no-change",
|
|
398
|
+
paths: Array.isArray(envelope.documentation.docClosure.paths)
|
|
399
|
+
? envelope.documentation.docClosure.paths
|
|
400
|
+
: [],
|
|
401
|
+
detail: envelope.documentation.docClosure.detail || null,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (envelope.integration) {
|
|
406
|
+
summary.integration = {
|
|
407
|
+
state: envelope.integration.state || null,
|
|
408
|
+
claims: envelope.integration.claims || 0,
|
|
409
|
+
conflicts: envelope.integration.conflicts || 0,
|
|
410
|
+
blockers: envelope.integration.blockers || 0,
|
|
411
|
+
detail: envelope.integration.detail || null,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (envelope.contEval) {
|
|
416
|
+
summary.eval = {
|
|
417
|
+
state: envelope.contEval.state || null,
|
|
418
|
+
targets: envelope.contEval.targets || 0,
|
|
419
|
+
benchmarks: envelope.contEval.benchmarks || 0,
|
|
420
|
+
regressions: envelope.contEval.regressions || 0,
|
|
421
|
+
targetIds: Array.isArray(envelope.contEval.targetIds) ? envelope.contEval.targetIds : [],
|
|
422
|
+
benchmarkIds: Array.isArray(envelope.contEval.benchmarkIds) ? envelope.contEval.benchmarkIds : [],
|
|
423
|
+
detail: envelope.contEval.detail || null,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (envelope.contQa) {
|
|
428
|
+
const gateClaims = envelope.contQa.gateClaims || null;
|
|
429
|
+
if (gateClaims && Object.values(gateClaims).some((value) => value !== null)) {
|
|
430
|
+
summary.gate = {
|
|
431
|
+
architecture: gateClaims.architecture || null,
|
|
432
|
+
integration: gateClaims.integration || null,
|
|
433
|
+
durability: gateClaims.durability || null,
|
|
434
|
+
live: gateClaims.live || null,
|
|
435
|
+
docs: gateClaims.docs || null,
|
|
436
|
+
detail: gateClaims.detail || null,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
summary.verdict = {
|
|
440
|
+
verdict: envelope.contQa.verdict?.verdict || null,
|
|
441
|
+
detail: envelope.contQa.verdict?.detail || null,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (envelope.security) {
|
|
446
|
+
summary.security = {
|
|
447
|
+
state: envelope.security.state || null,
|
|
448
|
+
findings: envelope.security.findings || 0,
|
|
449
|
+
approvals: envelope.security.approvals || 0,
|
|
450
|
+
detail: envelope.security.detail || null,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (envelope.deploy) {
|
|
455
|
+
summary.deploy = {
|
|
456
|
+
state: envelope.deploy.state || "not_applicable",
|
|
457
|
+
environment: envelope.deploy.environment || null,
|
|
458
|
+
healthCheck: envelope.deploy.healthCheck || null,
|
|
459
|
+
rolloutArtifact: envelope.deploy.rolloutArtifact || null,
|
|
460
|
+
detail: envelope.deploy.detail || null,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return summary;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function requiredRoleSection(role) {
|
|
468
|
+
switch (role) {
|
|
469
|
+
case "implementation":
|
|
470
|
+
return "implementation";
|
|
471
|
+
case "integration":
|
|
472
|
+
return "integration";
|
|
473
|
+
case "documentation":
|
|
474
|
+
return "documentation";
|
|
475
|
+
case "cont-qa":
|
|
476
|
+
return "contQa";
|
|
477
|
+
case "cont-eval":
|
|
478
|
+
return "contEval";
|
|
479
|
+
case "security":
|
|
480
|
+
return "security";
|
|
481
|
+
case "deploy":
|
|
482
|
+
return "deploy";
|
|
483
|
+
default:
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export function validateResultEnvelope(envelope, options = {}) {
|
|
489
|
+
const errors = [];
|
|
490
|
+
if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
|
|
491
|
+
return {
|
|
492
|
+
valid: false,
|
|
493
|
+
errors: ["Envelope must be a JSON object."],
|
|
494
|
+
envelope: null,
|
|
495
|
+
role: null,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
if (Number(envelope.schemaVersion) !== 2) {
|
|
499
|
+
errors.push(`Unsupported envelope schema version: ${envelope.schemaVersion ?? "missing"}.`);
|
|
500
|
+
}
|
|
501
|
+
const expectedAgentId = String(options.agent?.agentId || "").trim() || null;
|
|
502
|
+
const agentId = String(envelope.agentId || "").trim() || null;
|
|
503
|
+
if (!agentId) {
|
|
504
|
+
errors.push("Envelope is missing agentId.");
|
|
505
|
+
} else if (expectedAgentId && agentId !== expectedAgentId) {
|
|
506
|
+
errors.push(`Envelope agentId ${agentId} does not match expected agent ${expectedAgentId}.`);
|
|
507
|
+
}
|
|
508
|
+
const role = inferEnvelopeRole(options.agent || {}, envelope);
|
|
509
|
+
if (!role) {
|
|
510
|
+
errors.push("Envelope role could not be inferred.");
|
|
511
|
+
}
|
|
512
|
+
const section = requiredRoleSection(role);
|
|
513
|
+
if (section && (!envelope[section] || typeof envelope[section] !== "object")) {
|
|
514
|
+
errors.push(`Envelope is missing required ${section} payload.`);
|
|
515
|
+
}
|
|
516
|
+
if (!envelope.proof || typeof envelope.proof !== "object") {
|
|
517
|
+
errors.push("Envelope is missing proof payload.");
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
valid: errors.length === 0,
|
|
521
|
+
errors,
|
|
522
|
+
envelope,
|
|
523
|
+
role,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
export function resolveRunEnvelopeContext(runInfo, wave = null, options = {}) {
|
|
528
|
+
const lane = options.lane || runInfo?.lane || wave?.lane || "main";
|
|
529
|
+
const waveNumber = options.waveNumber ?? runInfo?.wave ?? wave?.wave ?? 0;
|
|
530
|
+
const statusRecord = options.statusRecord || null;
|
|
531
|
+
const attempt =
|
|
532
|
+
normalizePositiveAttempt(
|
|
533
|
+
options.attempt ?? statusRecord?.attempt ?? runInfo?.lastLaunchAttempt,
|
|
534
|
+
) || 1;
|
|
535
|
+
const agentId = options.agentId || runInfo?.agent?.agentId || "unknown";
|
|
536
|
+
const resultsDir =
|
|
537
|
+
options.resultsDir ||
|
|
538
|
+
runInfo?.resultsDir ||
|
|
539
|
+
runInfo?.lanePaths?.resultsDir ||
|
|
540
|
+
inferResultsDirFromStatusPath(runInfo?.statusPath);
|
|
541
|
+
return {
|
|
542
|
+
lane,
|
|
543
|
+
waveNumber,
|
|
544
|
+
attempt,
|
|
545
|
+
agentId,
|
|
546
|
+
resultsDir,
|
|
547
|
+
envelopePath: runScopedEnvelopePath({
|
|
548
|
+
resultsDir,
|
|
549
|
+
lane,
|
|
550
|
+
waveNumber,
|
|
551
|
+
attempt,
|
|
552
|
+
agentId,
|
|
553
|
+
}),
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function normalizeEnvelopeForRunWrite(envelope, context) {
|
|
558
|
+
return {
|
|
559
|
+
...envelope,
|
|
560
|
+
agentId: envelope?.agentId || context.agentId || null,
|
|
561
|
+
waveNumber: envelope?.waveNumber ?? context.waveNumber ?? null,
|
|
562
|
+
attempt: envelope?.attempt ?? context.attempt ?? null,
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
export function writeAgentResultEnvelopeForRun(runInfo, wave, envelope, options = {}) {
|
|
567
|
+
const context = resolveRunEnvelopeContext(runInfo, wave, options);
|
|
568
|
+
const payload = normalizeEnvelopeForRunWrite(envelope, context);
|
|
569
|
+
writeJsonAtomic(context.envelopePath, payload);
|
|
570
|
+
return context.envelopePath;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
export function readAgentResultEnvelopeForRun(runInfo, wave, options = {}) {
|
|
574
|
+
const context = resolveRunEnvelopeContext(runInfo, wave, options);
|
|
575
|
+
const payload = readJsonOrNull(context.envelopePath);
|
|
576
|
+
return payload && typeof payload === "object" ? payload : null;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
export function writeAgentResultEnvelope(statusPath, envelope) {
|
|
580
|
+
const envelopePath = agentEnvelopePathFromStatusPath(statusPath);
|
|
581
|
+
writeJsonAtomic(envelopePath, envelope);
|
|
582
|
+
return envelopePath;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
export function readAgentResultEnvelope(statusPath) {
|
|
586
|
+
const envelopePath = agentEnvelopePathFromStatusPath(statusPath);
|
|
587
|
+
const payload = readJsonOrNull(envelopePath);
|
|
588
|
+
return payload && typeof payload === "object" ? payload : null;
|
|
589
|
+
}
|
|
@@ -194,6 +194,11 @@ export function resolveRetryOverrideAgentIds(waveDefinition, lanePaths, override
|
|
|
194
194
|
if (resumePhase === "integrating") {
|
|
195
195
|
return [lanePaths?.integrationAgentId || "A8"];
|
|
196
196
|
}
|
|
197
|
+
if (resumePhase === "security-review") {
|
|
198
|
+
return agents
|
|
199
|
+
.filter((agent) => isSecurityReviewAgent(agent))
|
|
200
|
+
.map((agent) => agent.agentId);
|
|
201
|
+
}
|
|
197
202
|
if (resumePhase === "docs-closure") {
|
|
198
203
|
return [lanePaths?.documentationAgentId || "A9"];
|
|
199
204
|
}
|