@chllming/wave-orchestration 0.7.1 → 0.7.2
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 +15 -0
- package/README.md +8 -8
- package/docs/plans/component-cutover-matrix.json +50 -3
- package/docs/plans/current-state.md +1 -1
- package/docs/plans/end-state-architecture.md +927 -0
- package/docs/plans/examples/wave-example-live-proof.md +1 -1
- package/docs/plans/migration.md +2 -2
- package/docs/plans/waves/wave-1.md +376 -0
- package/docs/plans/waves/wave-2.md +292 -0
- package/docs/plans/waves/wave-3.md +342 -0
- package/docs/plans/waves/wave-4.md +391 -0
- package/docs/plans/waves/wave-5.md +382 -0
- package/docs/plans/waves/wave-6.md +321 -0
- package/docs/reference/npmjs-trusted-publishing.md +2 -2
- package/docs/reference/sample-waves.md +4 -4
- package/package.json +1 -1
- package/releases/manifest.json +19 -0
- package/scripts/wave-orchestrator/agent-state.mjs +447 -33
- package/scripts/wave-orchestrator/artifact-schemas.mjs +81 -0
- package/scripts/wave-orchestrator/control-cli.mjs +7 -1
- package/scripts/wave-orchestrator/coordination.mjs +11 -10
- package/scripts/wave-orchestrator/human-input-workflow.mjs +289 -0
- package/scripts/wave-orchestrator/install.mjs +22 -0
- package/scripts/wave-orchestrator/launcher-derived-state.mjs +915 -0
- package/scripts/wave-orchestrator/launcher-gates.mjs +1061 -0
- package/scripts/wave-orchestrator/launcher-retry.mjs +873 -0
- package/scripts/wave-orchestrator/launcher-supervisor.mjs +704 -0
- package/scripts/wave-orchestrator/launcher.mjs +153 -2922
- package/scripts/wave-orchestrator/task-entity.mjs +557 -0
- package/scripts/wave-orchestrator/wave-files.mjs +11 -2
- package/scripts/wave-orchestrator/wave-state-reducer.mjs +566 -0
- package/wave.config.json +1 -1
|
@@ -0,0 +1,1061 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
agentSummaryPathFromStatusPath,
|
|
5
|
+
buildAgentExecutionSummary,
|
|
6
|
+
readAgentExecutionSummary,
|
|
7
|
+
validateContQaSummary,
|
|
8
|
+
validateContEvalSummary,
|
|
9
|
+
validateImplementationSummary,
|
|
10
|
+
validateDocumentationClosureSummary,
|
|
11
|
+
validateSecuritySummary,
|
|
12
|
+
validateIntegrationSummary,
|
|
13
|
+
writeAgentExecutionSummary,
|
|
14
|
+
} from "./agent-state.mjs";
|
|
15
|
+
import {
|
|
16
|
+
REPO_ROOT,
|
|
17
|
+
readFileTail,
|
|
18
|
+
readJsonOrNull,
|
|
19
|
+
readStatusRecordIfPresent,
|
|
20
|
+
parseVerdictFromText,
|
|
21
|
+
REPORT_VERDICT_REGEX,
|
|
22
|
+
WAVE_VERDICT_REGEX,
|
|
23
|
+
toIsoTimestamp,
|
|
24
|
+
writeJsonAtomic,
|
|
25
|
+
} from "./shared.mjs";
|
|
26
|
+
import {
|
|
27
|
+
isSecurityReviewAgent,
|
|
28
|
+
resolveSecurityReviewReportPath,
|
|
29
|
+
isContEvalImplementationOwningAgent,
|
|
30
|
+
isContEvalReportOnlyAgent,
|
|
31
|
+
} from "./role-helpers.mjs";
|
|
32
|
+
import {
|
|
33
|
+
augmentSummaryWithProofRegistry,
|
|
34
|
+
} from "./proof-registry.mjs";
|
|
35
|
+
import {
|
|
36
|
+
agentRequiresProofCentricValidation,
|
|
37
|
+
waveRequiresProofCentricValidation,
|
|
38
|
+
validateWaveComponentPromotions,
|
|
39
|
+
validateWaveComponentMatrixCurrentLevels,
|
|
40
|
+
} from "./wave-files.mjs";
|
|
41
|
+
import {
|
|
42
|
+
isOpenCoordinationStatus,
|
|
43
|
+
openClarificationLinkedRequests,
|
|
44
|
+
buildCoordinationResponseMetrics,
|
|
45
|
+
} from "./coordination-store.mjs";
|
|
46
|
+
import {
|
|
47
|
+
parseStructuredSignalsFromLog,
|
|
48
|
+
} from "./dashboard-state.mjs";
|
|
49
|
+
|
|
50
|
+
function resolveRunReportPath(wave, runInfo) {
|
|
51
|
+
if (!wave || !runInfo?.agent) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (runInfo.agent.agentId === (wave.contQaAgentId || "A0") && wave.contQaReportPath) {
|
|
55
|
+
return path.resolve(REPO_ROOT, wave.contQaReportPath);
|
|
56
|
+
}
|
|
57
|
+
if (runInfo.agent.agentId === (wave.contEvalAgentId || "E0") && wave.contEvalReportPath) {
|
|
58
|
+
return path.resolve(REPO_ROOT, wave.contEvalReportPath);
|
|
59
|
+
}
|
|
60
|
+
if (isSecurityReviewAgent(runInfo.agent)) {
|
|
61
|
+
const securityReportPath = resolveSecurityReviewReportPath(runInfo.agent);
|
|
62
|
+
return securityReportPath ? path.resolve(REPO_ROOT, securityReportPath) : null;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function materializeAgentExecutionSummaryForRun(wave, runInfo) {
|
|
68
|
+
const statusRecord = readStatusRecordIfPresent(runInfo.statusPath);
|
|
69
|
+
if (!statusRecord) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const reportPath = resolveRunReportPath(wave, runInfo);
|
|
73
|
+
const summary = buildAgentExecutionSummary({
|
|
74
|
+
agent: runInfo.agent,
|
|
75
|
+
statusRecord,
|
|
76
|
+
logPath: runInfo.logPath,
|
|
77
|
+
reportPath,
|
|
78
|
+
});
|
|
79
|
+
writeAgentExecutionSummary(runInfo.statusPath, summary);
|
|
80
|
+
if (runInfo?.previewPath && fs.existsSync(runInfo.previewPath)) {
|
|
81
|
+
const previewPayload = readJsonOrNull(runInfo.previewPath);
|
|
82
|
+
if (previewPayload && typeof previewPayload === "object") {
|
|
83
|
+
const nextLimits =
|
|
84
|
+
previewPayload.limits && typeof previewPayload.limits === "object" && !Array.isArray(previewPayload.limits)
|
|
85
|
+
? { ...previewPayload.limits }
|
|
86
|
+
: {};
|
|
87
|
+
const observedTurnLimit = Number(summary?.terminationObservedTurnLimit);
|
|
88
|
+
if (Number.isFinite(observedTurnLimit) && observedTurnLimit > 0) {
|
|
89
|
+
nextLimits.observedTurnLimit = observedTurnLimit;
|
|
90
|
+
nextLimits.observedTurnLimitSource = "runtime-log";
|
|
91
|
+
if (runInfo.agent.executorResolved?.id === "codex") {
|
|
92
|
+
const existingNotes = Array.isArray(nextLimits.notes) ? nextLimits.notes.slice() : [];
|
|
93
|
+
const observedNote = `Observed runtime stop at ${observedTurnLimit} turns from executor log output.`;
|
|
94
|
+
if (!existingNotes.includes(observedNote)) {
|
|
95
|
+
existingNotes.push(observedNote);
|
|
96
|
+
}
|
|
97
|
+
nextLimits.notes = existingNotes;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
writeJsonAtomic(runInfo.previewPath, {
|
|
101
|
+
...previewPayload,
|
|
102
|
+
limits: nextLimits,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return summary;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function readRunExecutionSummary(runInfo, wave = null) {
|
|
110
|
+
const applyProofRegistry = (summary) =>
|
|
111
|
+
runInfo?.proofRegistry ? augmentSummaryWithProofRegistry(runInfo.agent, summary, runInfo.proofRegistry) : summary;
|
|
112
|
+
const statusRecord = runInfo?.statusPath ? readStatusRecordIfPresent(runInfo.statusPath) : null;
|
|
113
|
+
const summaryReadOptions =
|
|
114
|
+
wave && runInfo?.logPath
|
|
115
|
+
? {
|
|
116
|
+
agent: runInfo.agent,
|
|
117
|
+
statusPath: runInfo.statusPath,
|
|
118
|
+
statusRecord,
|
|
119
|
+
logPath: runInfo.logPath,
|
|
120
|
+
reportPath: resolveRunReportPath(wave, runInfo),
|
|
121
|
+
}
|
|
122
|
+
: {};
|
|
123
|
+
if (runInfo?.summary && typeof runInfo.summary === "object") {
|
|
124
|
+
return applyProofRegistry(runInfo.summary);
|
|
125
|
+
}
|
|
126
|
+
if (runInfo?.summaryPath && fs.existsSync(runInfo.summaryPath)) {
|
|
127
|
+
return applyProofRegistry(readAgentExecutionSummary(runInfo.summaryPath, summaryReadOptions));
|
|
128
|
+
}
|
|
129
|
+
if (runInfo?.statusPath && fs.existsSync(agentSummaryPathFromStatusPath(runInfo.statusPath))) {
|
|
130
|
+
return applyProofRegistry(readAgentExecutionSummary(runInfo.statusPath, summaryReadOptions));
|
|
131
|
+
}
|
|
132
|
+
if (wave && runInfo?.statusPath && runInfo?.logPath && fs.existsSync(runInfo.statusPath)) {
|
|
133
|
+
return applyProofRegistry(materializeAgentExecutionSummaryForRun(wave, runInfo));
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function materializeAgentExecutionSummaries(wave, agentRuns) {
|
|
139
|
+
return Object.fromEntries(
|
|
140
|
+
agentRuns.map((runInfo) => [runInfo.agent.agentId, materializeAgentExecutionSummaryForRun(wave, runInfo)]),
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function readWaveContQaGate(wave, agentRuns, options = {}) {
|
|
145
|
+
const mode = String(options.mode || "compat").trim().toLowerCase();
|
|
146
|
+
const strict = mode === "live";
|
|
147
|
+
const contQaAgentId = options.contQaAgentId || wave.contQaAgentId || "A0";
|
|
148
|
+
const contQaRun =
|
|
149
|
+
agentRuns.find((run) => run.agent.agentId === contQaAgentId) ?? null;
|
|
150
|
+
if (!contQaRun) {
|
|
151
|
+
return {
|
|
152
|
+
ok: false,
|
|
153
|
+
agentId: contQaAgentId,
|
|
154
|
+
statusCode: "missing-cont-qa",
|
|
155
|
+
detail: `Agent ${contQaAgentId} is missing.`,
|
|
156
|
+
logPath: null,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
const summary = readRunExecutionSummary(contQaRun, strict ? wave : null);
|
|
160
|
+
if (summary) {
|
|
161
|
+
const validation = validateContQaSummary(contQaRun.agent, summary, { mode });
|
|
162
|
+
return {
|
|
163
|
+
ok: validation.ok,
|
|
164
|
+
agentId: contQaRun.agent.agentId,
|
|
165
|
+
statusCode: validation.statusCode,
|
|
166
|
+
detail: validation.detail,
|
|
167
|
+
logPath: summary.logPath || path.relative(REPO_ROOT, contQaRun.logPath),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
if (strict) {
|
|
171
|
+
return {
|
|
172
|
+
ok: false,
|
|
173
|
+
agentId: contQaRun.agent.agentId,
|
|
174
|
+
statusCode: "missing-wave-gate",
|
|
175
|
+
detail: `Missing structured cont-QA summary for ${contQaRun.agent.agentId}.`,
|
|
176
|
+
logPath: path.relative(REPO_ROOT, contQaRun.logPath),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const contQaReportPath = wave.contQaReportPath
|
|
180
|
+
? path.resolve(REPO_ROOT, wave.contQaReportPath)
|
|
181
|
+
: null;
|
|
182
|
+
const reportText =
|
|
183
|
+
contQaReportPath && fs.existsSync(contQaReportPath)
|
|
184
|
+
? fs.readFileSync(contQaReportPath, "utf8")
|
|
185
|
+
: "";
|
|
186
|
+
const reportVerdict = parseVerdictFromText(reportText, REPORT_VERDICT_REGEX);
|
|
187
|
+
if (reportVerdict.verdict) {
|
|
188
|
+
return {
|
|
189
|
+
ok: reportVerdict.verdict === "pass",
|
|
190
|
+
agentId: contQaRun.agent.agentId,
|
|
191
|
+
statusCode: reportVerdict.verdict === "pass" ? "pass" : `cont-qa-${reportVerdict.verdict}`,
|
|
192
|
+
detail: reportVerdict.detail || "Verdict read from cont-QA report.",
|
|
193
|
+
logPath: path.relative(REPO_ROOT, contQaRun.logPath),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
const logVerdict = parseVerdictFromText(
|
|
197
|
+
readFileTail(contQaRun.logPath, 30000),
|
|
198
|
+
WAVE_VERDICT_REGEX,
|
|
199
|
+
);
|
|
200
|
+
if (logVerdict.verdict) {
|
|
201
|
+
return {
|
|
202
|
+
ok: logVerdict.verdict === "pass",
|
|
203
|
+
agentId: contQaRun.agent.agentId,
|
|
204
|
+
statusCode: logVerdict.verdict === "pass" ? "pass" : `cont-qa-${logVerdict.verdict}`,
|
|
205
|
+
detail: logVerdict.detail || "Verdict read from cont-QA log marker.",
|
|
206
|
+
logPath: path.relative(REPO_ROOT, contQaRun.logPath),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
ok: false,
|
|
211
|
+
agentId: contQaRun.agent.agentId,
|
|
212
|
+
statusCode: "missing-cont-qa-verdict",
|
|
213
|
+
detail: contQaReportPath
|
|
214
|
+
? `Missing Verdict line in ${path.relative(REPO_ROOT, contQaReportPath)} and no [wave-verdict] marker in ${path.relative(REPO_ROOT, contQaRun.logPath)}.`
|
|
215
|
+
: `Missing cont-QA report path and no [wave-verdict] marker in ${path.relative(REPO_ROOT, contQaRun.logPath)}.`,
|
|
216
|
+
logPath: path.relative(REPO_ROOT, contQaRun.logPath),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function readWaveContEvalGate(wave, agentRuns, options = {}) {
|
|
221
|
+
const mode = String(options.mode || "compat").trim().toLowerCase();
|
|
222
|
+
const strict = mode === "live";
|
|
223
|
+
const contEvalAgentId = options.contEvalAgentId || wave.contEvalAgentId || "E0";
|
|
224
|
+
const contEvalRun =
|
|
225
|
+
agentRuns.find((run) => run.agent.agentId === contEvalAgentId) ?? null;
|
|
226
|
+
if (!contEvalRun) {
|
|
227
|
+
return {
|
|
228
|
+
ok: true,
|
|
229
|
+
agentId: null,
|
|
230
|
+
statusCode: "pass",
|
|
231
|
+
detail: "Wave does not include cont-EVAL.",
|
|
232
|
+
logPath: null,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const summary = readRunExecutionSummary(contEvalRun, strict ? wave : null);
|
|
236
|
+
if (summary) {
|
|
237
|
+
const validation = validateContEvalSummary(contEvalRun.agent, summary, {
|
|
238
|
+
mode,
|
|
239
|
+
evalTargets: options.evalTargets || wave.evalTargets,
|
|
240
|
+
benchmarkCatalogPath: options.benchmarkCatalogPath,
|
|
241
|
+
});
|
|
242
|
+
return {
|
|
243
|
+
ok: validation.ok,
|
|
244
|
+
agentId: contEvalRun.agent.agentId,
|
|
245
|
+
statusCode: validation.statusCode,
|
|
246
|
+
detail: validation.detail,
|
|
247
|
+
logPath: summary.logPath || path.relative(REPO_ROOT, contEvalRun.logPath),
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
ok: false,
|
|
252
|
+
agentId: contEvalRun.agent.agentId,
|
|
253
|
+
statusCode: "missing-wave-eval",
|
|
254
|
+
detail: `Missing [wave-eval] marker for ${contEvalRun.agent.agentId}.`,
|
|
255
|
+
logPath: path.relative(REPO_ROOT, contEvalRun.logPath),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function readWaveEvaluatorGate(wave, agentRuns, options = {}) {
|
|
260
|
+
return readWaveContQaGate(wave, agentRuns, {
|
|
261
|
+
...options,
|
|
262
|
+
contQaAgentId: options.evaluatorAgentId || options.contQaAgentId,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function readWaveImplementationGate(wave, agentRuns) {
|
|
267
|
+
const contQaAgentId = wave.contQaAgentId || "A0";
|
|
268
|
+
const contEvalAgentId = wave.contEvalAgentId || "E0";
|
|
269
|
+
const integrationAgentId = wave.integrationAgentId || "A8";
|
|
270
|
+
const documentationAgentId = wave.documentationAgentId || "A9";
|
|
271
|
+
for (const runInfo of agentRuns) {
|
|
272
|
+
if (
|
|
273
|
+
[contQaAgentId, integrationAgentId, documentationAgentId].includes(runInfo.agent.agentId) ||
|
|
274
|
+
isContEvalReportOnlyAgent(runInfo.agent, { contEvalAgentId }) ||
|
|
275
|
+
isSecurityReviewAgent(runInfo.agent)
|
|
276
|
+
) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const summary = readRunExecutionSummary(runInfo, wave);
|
|
280
|
+
const validation = validateImplementationSummary(runInfo.agent, summary);
|
|
281
|
+
if (!validation.ok) {
|
|
282
|
+
return {
|
|
283
|
+
ok: false,
|
|
284
|
+
agentId: runInfo.agent.agentId,
|
|
285
|
+
statusCode: validation.statusCode,
|
|
286
|
+
detail: validation.detail,
|
|
287
|
+
logPath: summary?.logPath || path.relative(REPO_ROOT, runInfo.logPath),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
ok: true,
|
|
293
|
+
agentId: null,
|
|
294
|
+
statusCode: "pass",
|
|
295
|
+
detail: "All implementation exit contracts are satisfied.",
|
|
296
|
+
logPath: null,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export function analyzePromotedComponentOwners(componentId, agentRuns, summariesByAgentId) {
|
|
301
|
+
const ownerRuns = (agentRuns || []).filter((runInfo) =>
|
|
302
|
+
runInfo.agent.components?.includes(componentId),
|
|
303
|
+
);
|
|
304
|
+
const ownerAgentIds = ownerRuns.map((runInfo) => runInfo.agent.agentId);
|
|
305
|
+
const satisfiedAgentIds = [];
|
|
306
|
+
const waitingOnAgentIds = [];
|
|
307
|
+
const failedOwnContractAgentIds = [];
|
|
308
|
+
for (const runInfo of ownerRuns) {
|
|
309
|
+
const summary = summariesByAgentId?.[runInfo.agent.agentId] || null;
|
|
310
|
+
const implementationValidation = validateImplementationSummary(runInfo.agent, summary);
|
|
311
|
+
const componentMarkers = new Map(
|
|
312
|
+
Array.isArray(summary?.components)
|
|
313
|
+
? summary.components.map((component) => [component.componentId, component])
|
|
314
|
+
: [],
|
|
315
|
+
);
|
|
316
|
+
const marker = componentMarkers.get(componentId);
|
|
317
|
+
const expectedLevel = runInfo.agent.componentTargets?.[componentId] || null;
|
|
318
|
+
const componentSatisfied =
|
|
319
|
+
marker &&
|
|
320
|
+
marker.state === "met" &&
|
|
321
|
+
(!expectedLevel || marker.level === expectedLevel);
|
|
322
|
+
if (implementationValidation.ok && componentSatisfied) {
|
|
323
|
+
satisfiedAgentIds.push(runInfo.agent.agentId);
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
waitingOnAgentIds.push(runInfo.agent.agentId);
|
|
327
|
+
if (!implementationValidation.ok) {
|
|
328
|
+
failedOwnContractAgentIds.push(runInfo.agent.agentId);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
componentId,
|
|
333
|
+
ownerRuns,
|
|
334
|
+
ownerAgentIds,
|
|
335
|
+
satisfiedAgentIds,
|
|
336
|
+
waitingOnAgentIds,
|
|
337
|
+
failedOwnContractAgentIds,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export function buildSharedComponentSiblingPendingFailure(componentState) {
|
|
342
|
+
if (
|
|
343
|
+
!componentState ||
|
|
344
|
+
componentState.satisfiedAgentIds.length === 0 ||
|
|
345
|
+
componentState.waitingOnAgentIds.length === 0
|
|
346
|
+
) {
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
const landedSummary =
|
|
350
|
+
componentState.satisfiedAgentIds.length === 1
|
|
351
|
+
? `${componentState.satisfiedAgentIds[0]} desired-state slice landed`
|
|
352
|
+
: `${componentState.satisfiedAgentIds.join(", ")} desired-state slices landed`;
|
|
353
|
+
const ownerRun =
|
|
354
|
+
componentState.ownerRuns.find((runInfo) =>
|
|
355
|
+
componentState.waitingOnAgentIds.includes(runInfo.agent.agentId),
|
|
356
|
+
) ||
|
|
357
|
+
componentState.ownerRuns[0] ||
|
|
358
|
+
null;
|
|
359
|
+
return {
|
|
360
|
+
ok: false,
|
|
361
|
+
agentId: componentState.waitingOnAgentIds[0] || ownerRun?.agent?.agentId || null,
|
|
362
|
+
componentId: componentState.componentId || null,
|
|
363
|
+
statusCode: "shared-component-sibling-pending",
|
|
364
|
+
detail: `${landedSummary}; shared component closure still depends on ${componentState.waitingOnAgentIds.join("/")}.`,
|
|
365
|
+
logPath: ownerRun ? path.relative(REPO_ROOT, ownerRun.logPath) : null,
|
|
366
|
+
ownerAgentIds: componentState.ownerAgentIds,
|
|
367
|
+
satisfiedAgentIds: componentState.satisfiedAgentIds,
|
|
368
|
+
waitingOnAgentIds: componentState.waitingOnAgentIds,
|
|
369
|
+
failedOwnContractAgentIds: componentState.failedOwnContractAgentIds,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export function readWaveComponentGate(wave, agentRuns, options = {}) {
|
|
374
|
+
const summariesByAgentId = Object.fromEntries(
|
|
375
|
+
agentRuns.map((runInfo) => [runInfo.agent.agentId, readRunExecutionSummary(runInfo, wave)]),
|
|
376
|
+
);
|
|
377
|
+
const validation = validateWaveComponentPromotions(wave, summariesByAgentId, options);
|
|
378
|
+
const sharedPending = (wave.componentPromotions || [])
|
|
379
|
+
.map((promotion) =>
|
|
380
|
+
buildSharedComponentSiblingPendingFailure(
|
|
381
|
+
analyzePromotedComponentOwners(promotion.componentId, agentRuns, summariesByAgentId),
|
|
382
|
+
),
|
|
383
|
+
)
|
|
384
|
+
.find(Boolean);
|
|
385
|
+
if (sharedPending) {
|
|
386
|
+
return sharedPending;
|
|
387
|
+
}
|
|
388
|
+
if (validation.ok) {
|
|
389
|
+
return {
|
|
390
|
+
ok: true,
|
|
391
|
+
agentId: null,
|
|
392
|
+
componentId: null,
|
|
393
|
+
statusCode: validation.statusCode,
|
|
394
|
+
detail: validation.detail,
|
|
395
|
+
logPath: null,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
const componentState = analyzePromotedComponentOwners(
|
|
399
|
+
validation.componentId,
|
|
400
|
+
agentRuns,
|
|
401
|
+
summariesByAgentId,
|
|
402
|
+
);
|
|
403
|
+
const ownerRun = componentState.ownerRuns[0] ?? null;
|
|
404
|
+
return {
|
|
405
|
+
ok: false,
|
|
406
|
+
agentId: ownerRun?.agent?.agentId || null,
|
|
407
|
+
componentId: validation.componentId || null,
|
|
408
|
+
statusCode: validation.statusCode,
|
|
409
|
+
detail: validation.detail,
|
|
410
|
+
logPath: ownerRun ? path.relative(REPO_ROOT, ownerRun.logPath) : null,
|
|
411
|
+
ownerAgentIds: componentState.ownerAgentIds,
|
|
412
|
+
satisfiedAgentIds: componentState.satisfiedAgentIds,
|
|
413
|
+
waitingOnAgentIds: componentState.waitingOnAgentIds,
|
|
414
|
+
failedOwnContractAgentIds: componentState.failedOwnContractAgentIds,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function readWaveComponentMatrixGate(wave, agentRuns, options = {}) {
|
|
419
|
+
const validation = validateWaveComponentMatrixCurrentLevels(wave, options);
|
|
420
|
+
if (validation.ok) {
|
|
421
|
+
return {
|
|
422
|
+
ok: true,
|
|
423
|
+
agentId: null,
|
|
424
|
+
componentId: null,
|
|
425
|
+
statusCode: validation.statusCode,
|
|
426
|
+
detail: validation.detail,
|
|
427
|
+
logPath: null,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
const documentationAgentId =
|
|
431
|
+
options.documentationAgentId || wave.documentationAgentId || "A9";
|
|
432
|
+
const docRun =
|
|
433
|
+
agentRuns.find((runInfo) => runInfo.agent.agentId === documentationAgentId) ?? null;
|
|
434
|
+
return {
|
|
435
|
+
ok: false,
|
|
436
|
+
agentId: docRun?.agent?.agentId || null,
|
|
437
|
+
componentId: validation.componentId || null,
|
|
438
|
+
statusCode: validation.statusCode,
|
|
439
|
+
detail: validation.detail,
|
|
440
|
+
logPath: docRun ? path.relative(REPO_ROOT, docRun.logPath) : null,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export function readWaveDocumentationGate(wave, agentRuns) {
|
|
445
|
+
const documentationAgentId = wave.documentationAgentId || "A9";
|
|
446
|
+
const docRun =
|
|
447
|
+
agentRuns.find((run) => run.agent.agentId === documentationAgentId) ?? null;
|
|
448
|
+
if (!docRun) {
|
|
449
|
+
return {
|
|
450
|
+
ok: true,
|
|
451
|
+
agentId: null,
|
|
452
|
+
statusCode: "pass",
|
|
453
|
+
detail: "No documentation steward declared for this wave.",
|
|
454
|
+
logPath: null,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
const summary = readRunExecutionSummary(docRun, wave);
|
|
458
|
+
const validation = validateDocumentationClosureSummary(docRun.agent, summary);
|
|
459
|
+
return {
|
|
460
|
+
ok: validation.ok,
|
|
461
|
+
agentId: docRun.agent.agentId,
|
|
462
|
+
statusCode: validation.statusCode,
|
|
463
|
+
detail: validation.detail,
|
|
464
|
+
logPath: summary?.logPath || path.relative(REPO_ROOT, docRun.logPath),
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export function readWaveSecurityGate(wave, agentRuns) {
|
|
469
|
+
const securityRuns = (agentRuns || []).filter((run) => isSecurityReviewAgent(run.agent));
|
|
470
|
+
if (securityRuns.length === 0) {
|
|
471
|
+
return {
|
|
472
|
+
ok: true,
|
|
473
|
+
agentId: null,
|
|
474
|
+
statusCode: "pass",
|
|
475
|
+
detail: "No security reviewer declared for this wave.",
|
|
476
|
+
logPath: null,
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
const concernAgentIds = [];
|
|
480
|
+
for (const runInfo of securityRuns) {
|
|
481
|
+
const summary = readRunExecutionSummary(runInfo, wave);
|
|
482
|
+
const validation = validateSecuritySummary(runInfo.agent, summary);
|
|
483
|
+
if (!validation.ok) {
|
|
484
|
+
return {
|
|
485
|
+
ok: false,
|
|
486
|
+
agentId: runInfo.agent.agentId,
|
|
487
|
+
statusCode: validation.statusCode,
|
|
488
|
+
detail: validation.detail,
|
|
489
|
+
logPath: summary?.logPath || path.relative(REPO_ROOT, runInfo.logPath),
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
if (summary?.security?.state === "concerns") {
|
|
493
|
+
concernAgentIds.push(runInfo.agent.agentId);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
if (concernAgentIds.length > 0) {
|
|
497
|
+
return {
|
|
498
|
+
ok: true,
|
|
499
|
+
agentId: null,
|
|
500
|
+
statusCode: "security-concerns",
|
|
501
|
+
detail: `Security review reported advisory concerns (${concernAgentIds.join(", ")}).`,
|
|
502
|
+
logPath: null,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
return {
|
|
506
|
+
ok: true,
|
|
507
|
+
agentId: null,
|
|
508
|
+
statusCode: "pass",
|
|
509
|
+
detail: "Security review is clear.",
|
|
510
|
+
logPath: null,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export function readWaveIntegrationGate(wave, agentRuns, options = {}) {
|
|
515
|
+
const integrationAgentId =
|
|
516
|
+
options.integrationAgentId || wave.integrationAgentId || "A8";
|
|
517
|
+
const requireIntegration =
|
|
518
|
+
options.requireIntegrationSteward === true ||
|
|
519
|
+
(options.requireIntegrationStewardFromWave !== null &&
|
|
520
|
+
options.requireIntegrationStewardFromWave !== undefined &&
|
|
521
|
+
wave.wave >= options.requireIntegrationStewardFromWave);
|
|
522
|
+
const integrationRun =
|
|
523
|
+
agentRuns.find((run) => run.agent.agentId === integrationAgentId) ?? null;
|
|
524
|
+
if (!integrationRun) {
|
|
525
|
+
return {
|
|
526
|
+
ok: !requireIntegration,
|
|
527
|
+
agentId: requireIntegration ? integrationAgentId : null,
|
|
528
|
+
statusCode: requireIntegration ? "missing-integration" : "pass",
|
|
529
|
+
detail: requireIntegration
|
|
530
|
+
? `Agent ${integrationAgentId} is missing.`
|
|
531
|
+
: "No explicit integration steward declared for this wave.",
|
|
532
|
+
logPath: null,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
const summary = readRunExecutionSummary(integrationRun, wave);
|
|
536
|
+
const validation = validateIntegrationSummary(integrationRun.agent, summary);
|
|
537
|
+
return {
|
|
538
|
+
ok: validation.ok,
|
|
539
|
+
agentId: integrationRun.agent.agentId,
|
|
540
|
+
statusCode: validation.statusCode,
|
|
541
|
+
detail: validation.detail,
|
|
542
|
+
logPath: summary?.logPath || path.relative(REPO_ROOT, integrationRun.logPath),
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export function readWaveIntegrationBarrier(wave, agentRuns, derivedState, options = {}) {
|
|
547
|
+
const markerGate = readWaveIntegrationGate(wave, agentRuns, options);
|
|
548
|
+
if (!markerGate.ok) {
|
|
549
|
+
return markerGate;
|
|
550
|
+
}
|
|
551
|
+
const integrationSummary = derivedState?.integrationSummary || null;
|
|
552
|
+
if (!integrationSummary) {
|
|
553
|
+
return {
|
|
554
|
+
ok: false,
|
|
555
|
+
agentId: markerGate.agentId,
|
|
556
|
+
statusCode: "missing-integration-summary",
|
|
557
|
+
detail: `Missing integration summary artifact for wave ${wave.wave}.`,
|
|
558
|
+
logPath: markerGate.logPath,
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
if (integrationSummary.recommendation !== "ready-for-doc-closure") {
|
|
562
|
+
return {
|
|
563
|
+
ok: false,
|
|
564
|
+
agentId: markerGate.agentId,
|
|
565
|
+
statusCode: "integration-needs-more-work",
|
|
566
|
+
detail:
|
|
567
|
+
integrationSummary.detail ||
|
|
568
|
+
`Integration summary still reports ${integrationSummary.recommendation}.`,
|
|
569
|
+
logPath: markerGate.logPath,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
return markerGate;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
export function readClarificationBarrier(derivedState) {
|
|
576
|
+
const openClarifications = (derivedState?.coordinationState?.clarifications || []).filter(
|
|
577
|
+
(record) => isOpenCoordinationStatus(record.status),
|
|
578
|
+
);
|
|
579
|
+
if (openClarifications.length > 0) {
|
|
580
|
+
return {
|
|
581
|
+
ok: false,
|
|
582
|
+
statusCode: "clarification-open",
|
|
583
|
+
detail: `Open clarifications remain (${openClarifications.map((record) => record.id).join(", ")}).`,
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
const openClarificationRequests = openClarificationLinkedRequests(
|
|
587
|
+
derivedState?.coordinationState,
|
|
588
|
+
);
|
|
589
|
+
if (openClarificationRequests.length > 0) {
|
|
590
|
+
return {
|
|
591
|
+
ok: false,
|
|
592
|
+
statusCode: "clarification-follow-up-open",
|
|
593
|
+
detail: `Clarification follow-up requests remain open (${openClarificationRequests.map((record) => record.id).join(", ")}).`,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
const pendingHuman = [
|
|
597
|
+
...((derivedState?.coordinationState?.humanEscalations || []).filter((record) =>
|
|
598
|
+
isOpenCoordinationStatus(record.status),
|
|
599
|
+
)),
|
|
600
|
+
...((derivedState?.coordinationState?.humanFeedback || []).filter((record) =>
|
|
601
|
+
isOpenCoordinationStatus(record.status),
|
|
602
|
+
)),
|
|
603
|
+
];
|
|
604
|
+
if (pendingHuman.length > 0) {
|
|
605
|
+
return {
|
|
606
|
+
ok: false,
|
|
607
|
+
statusCode: "human-feedback-open",
|
|
608
|
+
detail: `Pending human input remains (${pendingHuman.map((record) => record.id).join(", ")}).`,
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
return {
|
|
612
|
+
ok: true,
|
|
613
|
+
statusCode: "pass",
|
|
614
|
+
detail: "",
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
export function readWaveAssignmentBarrier(derivedState) {
|
|
619
|
+
const blockingAssignments = (derivedState?.capabilityAssignments || []).filter(
|
|
620
|
+
(assignment) => assignment.blocking,
|
|
621
|
+
);
|
|
622
|
+
if (blockingAssignments.length === 0) {
|
|
623
|
+
return {
|
|
624
|
+
ok: true,
|
|
625
|
+
statusCode: "pass",
|
|
626
|
+
detail: "",
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
const unresolvedAssignments = blockingAssignments.filter((assignment) => !assignment.assignedAgentId);
|
|
630
|
+
if (unresolvedAssignments.length > 0) {
|
|
631
|
+
return {
|
|
632
|
+
ok: false,
|
|
633
|
+
statusCode: "helper-assignment-unresolved",
|
|
634
|
+
detail: `Helper assignments remain unresolved (${unresolvedAssignments.map((assignment) => assignment.requestId).join(", ")}).`,
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
return {
|
|
638
|
+
ok: false,
|
|
639
|
+
statusCode: "helper-assignment-open",
|
|
640
|
+
detail: `Helper assignments remain open (${blockingAssignments.map((assignment) => assignment.requestId).join(", ")}).`,
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
export function readWaveDependencyBarrier(derivedState) {
|
|
645
|
+
const requiredInbound = derivedState?.dependencySnapshot?.requiredInbound || [];
|
|
646
|
+
const requiredOutbound = derivedState?.dependencySnapshot?.requiredOutbound || [];
|
|
647
|
+
const unresolvedInboundAssignments =
|
|
648
|
+
derivedState?.dependencySnapshot?.unresolvedInboundAssignments || [];
|
|
649
|
+
if (unresolvedInboundAssignments.length > 0) {
|
|
650
|
+
return {
|
|
651
|
+
ok: false,
|
|
652
|
+
statusCode: "dependency-assignment-unresolved",
|
|
653
|
+
detail: `Required inbound dependencies are unassigned (${unresolvedInboundAssignments.map((record) => record.id).join(", ")}).`,
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
if (requiredInbound.length > 0 || requiredOutbound.length > 0) {
|
|
657
|
+
return {
|
|
658
|
+
ok: false,
|
|
659
|
+
statusCode: "dependency-open",
|
|
660
|
+
detail: `Open required dependencies remain (${[...requiredInbound, ...requiredOutbound].map((record) => record.id).join(", ")}).`,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
return {
|
|
664
|
+
ok: true,
|
|
665
|
+
statusCode: "pass",
|
|
666
|
+
detail: "",
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
export function buildGateSnapshot({
|
|
671
|
+
wave,
|
|
672
|
+
agentRuns,
|
|
673
|
+
derivedState,
|
|
674
|
+
lanePaths,
|
|
675
|
+
componentMatrixPayload,
|
|
676
|
+
componentMatrixJsonPath,
|
|
677
|
+
validationMode = "compat",
|
|
678
|
+
readWaveInfraGateFn,
|
|
679
|
+
}) {
|
|
680
|
+
const implementationGate = readWaveImplementationGate(wave, agentRuns);
|
|
681
|
+
const componentGate = readWaveComponentGate(wave, agentRuns, {
|
|
682
|
+
laneProfile: lanePaths?.laneProfile,
|
|
683
|
+
});
|
|
684
|
+
const integrationGate = readWaveIntegrationGate(wave, agentRuns, {
|
|
685
|
+
integrationAgentId: lanePaths?.integrationAgentId,
|
|
686
|
+
requireIntegrationStewardFromWave: lanePaths?.requireIntegrationStewardFromWave,
|
|
687
|
+
});
|
|
688
|
+
const integrationBarrier = readWaveIntegrationBarrier(wave, agentRuns, derivedState, {
|
|
689
|
+
integrationAgentId: lanePaths?.integrationAgentId,
|
|
690
|
+
requireIntegrationStewardFromWave: lanePaths?.requireIntegrationStewardFromWave,
|
|
691
|
+
});
|
|
692
|
+
const documentationGate = readWaveDocumentationGate(wave, agentRuns);
|
|
693
|
+
const componentMatrixGate = readWaveComponentMatrixGate(wave, agentRuns, {
|
|
694
|
+
laneProfile: lanePaths?.laneProfile,
|
|
695
|
+
documentationAgentId: lanePaths?.documentationAgentId,
|
|
696
|
+
componentMatrixPayload,
|
|
697
|
+
componentMatrixJsonPath,
|
|
698
|
+
});
|
|
699
|
+
const contEvalGate = readWaveContEvalGate(wave, agentRuns, {
|
|
700
|
+
contEvalAgentId: lanePaths?.contEvalAgentId,
|
|
701
|
+
mode: validationMode,
|
|
702
|
+
evalTargets: wave.evalTargets,
|
|
703
|
+
benchmarkCatalogPath: lanePaths?.laneProfile?.paths?.benchmarkCatalogPath,
|
|
704
|
+
});
|
|
705
|
+
const securityGate = readWaveSecurityGate(wave, agentRuns);
|
|
706
|
+
const contQaGate = readWaveContQaGate(wave, agentRuns, {
|
|
707
|
+
contQaAgentId: lanePaths?.contQaAgentId,
|
|
708
|
+
mode: validationMode,
|
|
709
|
+
});
|
|
710
|
+
const infraGate = readWaveInfraGateFn(agentRuns);
|
|
711
|
+
const clarificationBarrier = readClarificationBarrier(derivedState);
|
|
712
|
+
const helperAssignmentBarrier = readWaveAssignmentBarrier(derivedState);
|
|
713
|
+
const dependencyBarrier = readWaveDependencyBarrier(derivedState);
|
|
714
|
+
const orderedGates = [
|
|
715
|
+
["implementationGate", implementationGate],
|
|
716
|
+
["componentGate", componentGate],
|
|
717
|
+
["helperAssignmentBarrier", helperAssignmentBarrier],
|
|
718
|
+
["dependencyBarrier", dependencyBarrier],
|
|
719
|
+
["contEvalGate", contEvalGate],
|
|
720
|
+
["securityGate", securityGate],
|
|
721
|
+
["integrationBarrier", integrationBarrier],
|
|
722
|
+
["documentationGate", documentationGate],
|
|
723
|
+
["componentMatrixGate", componentMatrixGate],
|
|
724
|
+
["contQaGate", contQaGate],
|
|
725
|
+
["infraGate", infraGate],
|
|
726
|
+
["clarificationBarrier", clarificationBarrier],
|
|
727
|
+
];
|
|
728
|
+
const firstFailure = orderedGates.find(([, gate]) => gate?.ok === false);
|
|
729
|
+
return {
|
|
730
|
+
implementationGate,
|
|
731
|
+
componentGate,
|
|
732
|
+
integrationGate,
|
|
733
|
+
integrationBarrier,
|
|
734
|
+
documentationGate,
|
|
735
|
+
componentMatrixGate,
|
|
736
|
+
contEvalGate,
|
|
737
|
+
securityGate,
|
|
738
|
+
contQaGate,
|
|
739
|
+
infraGate,
|
|
740
|
+
clarificationBarrier,
|
|
741
|
+
helperAssignmentBarrier,
|
|
742
|
+
dependencyBarrier,
|
|
743
|
+
overall: firstFailure
|
|
744
|
+
? {
|
|
745
|
+
ok: false,
|
|
746
|
+
gate: firstFailure[0],
|
|
747
|
+
statusCode: firstFailure[1].statusCode,
|
|
748
|
+
detail: firstFailure[1].detail,
|
|
749
|
+
agentId: firstFailure[1].agentId || null,
|
|
750
|
+
}
|
|
751
|
+
: {
|
|
752
|
+
ok: true,
|
|
753
|
+
gate: "pass",
|
|
754
|
+
statusCode: "pass",
|
|
755
|
+
detail: "All replayed wave gates passed.",
|
|
756
|
+
agentId: null,
|
|
757
|
+
},
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// --- Pure gate variants (no file I/O) ---
|
|
762
|
+
// These accept agentResults map { agentId: executionSummary } instead of runInfo objects.
|
|
763
|
+
// Used by the wave-state-reducer for deterministic replay.
|
|
764
|
+
|
|
765
|
+
export function readWaveImplementationGatePure(wave, agentResults, options = {}) {
|
|
766
|
+
const contQaAgentId = options.contQaAgentId || wave.contQaAgentId || "A0";
|
|
767
|
+
const contEvalAgentId = options.contEvalAgentId || wave.contEvalAgentId || "E0";
|
|
768
|
+
const integrationAgentId = options.integrationAgentId || wave.integrationAgentId || "A8";
|
|
769
|
+
const documentationAgentId = options.documentationAgentId || wave.documentationAgentId || "A9";
|
|
770
|
+
const agents = Array.isArray(wave.agents) ? wave.agents : [];
|
|
771
|
+
for (const agent of agents) {
|
|
772
|
+
if (
|
|
773
|
+
[contQaAgentId, integrationAgentId, documentationAgentId].includes(agent.agentId) ||
|
|
774
|
+
isContEvalReportOnlyAgent(agent, { contEvalAgentId }) ||
|
|
775
|
+
isSecurityReviewAgent(agent)
|
|
776
|
+
) {
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
const summary = agentResults?.[agent.agentId] || null;
|
|
780
|
+
const validation = validateImplementationSummary(agent, summary);
|
|
781
|
+
if (!validation.ok) {
|
|
782
|
+
return {
|
|
783
|
+
ok: false,
|
|
784
|
+
agentId: agent.agentId,
|
|
785
|
+
statusCode: validation.statusCode,
|
|
786
|
+
detail: validation.detail,
|
|
787
|
+
logPath: summary?.logPath || null,
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return {
|
|
792
|
+
ok: true, agentId: null, statusCode: "pass",
|
|
793
|
+
detail: "All implementation exit contracts are satisfied.", logPath: null,
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
export function readWaveContQaGatePure(wave, agentResults, options = {}) {
|
|
798
|
+
const mode = String(options.mode || "live").trim().toLowerCase();
|
|
799
|
+
const contQaAgentId = options.contQaAgentId || wave.contQaAgentId || "A0";
|
|
800
|
+
const summary = agentResults?.[contQaAgentId] || null;
|
|
801
|
+
if (!summary) {
|
|
802
|
+
return { ok: false, agentId: contQaAgentId, statusCode: "missing-cont-qa",
|
|
803
|
+
detail: `Missing cont-QA summary for ${contQaAgentId}.`, logPath: null };
|
|
804
|
+
}
|
|
805
|
+
const agent = { agentId: contQaAgentId };
|
|
806
|
+
const validation = validateContQaSummary(agent, summary, { mode });
|
|
807
|
+
return { ok: validation.ok, agentId: contQaAgentId, statusCode: validation.statusCode,
|
|
808
|
+
detail: validation.detail, logPath: summary.logPath || null };
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
export function readWaveContEvalGatePure(wave, agentResults, options = {}) {
|
|
812
|
+
const mode = String(options.mode || "live").trim().toLowerCase();
|
|
813
|
+
const contEvalAgentId = options.contEvalAgentId || wave.contEvalAgentId || "E0";
|
|
814
|
+
const summary = agentResults?.[contEvalAgentId] || null;
|
|
815
|
+
if (!summary) {
|
|
816
|
+
return { ok: true, agentId: null, statusCode: "pass",
|
|
817
|
+
detail: "Wave does not include cont-EVAL.", logPath: null };
|
|
818
|
+
}
|
|
819
|
+
const agent = { agentId: contEvalAgentId };
|
|
820
|
+
const validation = validateContEvalSummary(agent, summary, {
|
|
821
|
+
mode, evalTargets: options.evalTargets || wave.evalTargets,
|
|
822
|
+
benchmarkCatalogPath: options.benchmarkCatalogPath,
|
|
823
|
+
});
|
|
824
|
+
return { ok: validation.ok, agentId: contEvalAgentId, statusCode: validation.statusCode,
|
|
825
|
+
detail: validation.detail, logPath: summary.logPath || null };
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
export function readWaveEvaluatorGatePure(wave, agentResults, options = {}) {
|
|
829
|
+
return readWaveContQaGatePure(wave, agentResults, {
|
|
830
|
+
...options, contQaAgentId: options.evaluatorAgentId || options.contQaAgentId,
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
function analyzePromotedComponentOwnersPure(componentId, agents, summariesByAgentId) {
|
|
835
|
+
const ownerAgents = (agents || []).filter((agent) => agent.components?.includes(componentId));
|
|
836
|
+
const ownerAgentIds = ownerAgents.map((agent) => agent.agentId);
|
|
837
|
+
const satisfiedAgentIds = [];
|
|
838
|
+
const waitingOnAgentIds = [];
|
|
839
|
+
const failedOwnContractAgentIds = [];
|
|
840
|
+
for (const agent of ownerAgents) {
|
|
841
|
+
const summary = summariesByAgentId?.[agent.agentId] || null;
|
|
842
|
+
const implementationValidation = validateImplementationSummary(agent, summary);
|
|
843
|
+
const componentMarkers = new Map(
|
|
844
|
+
Array.isArray(summary?.components)
|
|
845
|
+
? summary.components.map((c) => [c.componentId, c]) : [],
|
|
846
|
+
);
|
|
847
|
+
const marker = componentMarkers.get(componentId);
|
|
848
|
+
const expectedLevel = agent.componentTargets?.[componentId] || null;
|
|
849
|
+
const componentSatisfied = marker && marker.state === "met" &&
|
|
850
|
+
(!expectedLevel || marker.level === expectedLevel);
|
|
851
|
+
if (implementationValidation.ok && componentSatisfied) {
|
|
852
|
+
satisfiedAgentIds.push(agent.agentId);
|
|
853
|
+
} else {
|
|
854
|
+
waitingOnAgentIds.push(agent.agentId);
|
|
855
|
+
if (!implementationValidation.ok) { failedOwnContractAgentIds.push(agent.agentId); }
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return { componentId, ownerAgentIds, satisfiedAgentIds, waitingOnAgentIds, failedOwnContractAgentIds };
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
function buildSharedComponentSiblingPendingPure(componentId, agents, summariesByAgentId) {
|
|
862
|
+
const componentState = analyzePromotedComponentOwnersPure(componentId, agents, summariesByAgentId);
|
|
863
|
+
if (componentState.satisfiedAgentIds.length === 0 || componentState.waitingOnAgentIds.length === 0) {
|
|
864
|
+
return null;
|
|
865
|
+
}
|
|
866
|
+
const landedSummary = componentState.satisfiedAgentIds.length === 1
|
|
867
|
+
? `${componentState.satisfiedAgentIds[0]} desired-state slice landed`
|
|
868
|
+
: `${componentState.satisfiedAgentIds.join(", ")} desired-state slices landed`;
|
|
869
|
+
return {
|
|
870
|
+
ok: false, agentId: componentState.waitingOnAgentIds[0] || null,
|
|
871
|
+
componentId: componentState.componentId || null,
|
|
872
|
+
statusCode: "shared-component-sibling-pending",
|
|
873
|
+
detail: `${landedSummary}; shared component closure still depends on ${componentState.waitingOnAgentIds.join("/")}.`,
|
|
874
|
+
logPath: null,
|
|
875
|
+
ownerAgentIds: componentState.ownerAgentIds,
|
|
876
|
+
satisfiedAgentIds: componentState.satisfiedAgentIds,
|
|
877
|
+
waitingOnAgentIds: componentState.waitingOnAgentIds,
|
|
878
|
+
failedOwnContractAgentIds: componentState.failedOwnContractAgentIds,
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
export function readWaveComponentGatePure(wave, agentResults, options = {}) {
|
|
883
|
+
const summariesByAgentId = agentResults || {};
|
|
884
|
+
const validation = validateWaveComponentPromotions(wave, summariesByAgentId, options);
|
|
885
|
+
const agents = Array.isArray(wave.agents) ? wave.agents : [];
|
|
886
|
+
const sharedPending = (wave.componentPromotions || [])
|
|
887
|
+
.map((p) => buildSharedComponentSiblingPendingPure(p.componentId, agents, summariesByAgentId))
|
|
888
|
+
.find(Boolean);
|
|
889
|
+
if (sharedPending) { return sharedPending; }
|
|
890
|
+
if (validation.ok) {
|
|
891
|
+
return { ok: true, agentId: null, componentId: null,
|
|
892
|
+
statusCode: validation.statusCode, detail: validation.detail, logPath: null };
|
|
893
|
+
}
|
|
894
|
+
const componentState = analyzePromotedComponentOwnersPure(validation.componentId, agents, summariesByAgentId);
|
|
895
|
+
return {
|
|
896
|
+
ok: false, agentId: componentState.ownerAgentIds[0] || null,
|
|
897
|
+
componentId: validation.componentId || null,
|
|
898
|
+
statusCode: validation.statusCode, detail: validation.detail, logPath: null,
|
|
899
|
+
ownerAgentIds: componentState.ownerAgentIds,
|
|
900
|
+
satisfiedAgentIds: componentState.satisfiedAgentIds,
|
|
901
|
+
waitingOnAgentIds: componentState.waitingOnAgentIds,
|
|
902
|
+
failedOwnContractAgentIds: componentState.failedOwnContractAgentIds,
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
export function readWaveComponentMatrixGatePure(wave, agentResults, options = {}) {
|
|
907
|
+
const validation = validateWaveComponentMatrixCurrentLevels(wave, options);
|
|
908
|
+
if (validation.ok) {
|
|
909
|
+
return { ok: true, agentId: null, componentId: null,
|
|
910
|
+
statusCode: validation.statusCode, detail: validation.detail, logPath: null };
|
|
911
|
+
}
|
|
912
|
+
const documentationAgentId = options.documentationAgentId || wave.documentationAgentId || "A9";
|
|
913
|
+
return { ok: false, agentId: documentationAgentId, componentId: validation.componentId || null,
|
|
914
|
+
statusCode: validation.statusCode, detail: validation.detail, logPath: null };
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
export function readWaveDocumentationGatePure(wave, agentResults, options = {}) {
|
|
918
|
+
const documentationAgentId = options.documentationAgentId || wave.documentationAgentId || "A9";
|
|
919
|
+
const summary = agentResults?.[documentationAgentId] || null;
|
|
920
|
+
if (!summary) {
|
|
921
|
+
return { ok: true, agentId: null, statusCode: "pass",
|
|
922
|
+
detail: "No documentation steward declared for this wave.", logPath: null };
|
|
923
|
+
}
|
|
924
|
+
const agent = { agentId: documentationAgentId };
|
|
925
|
+
const validation = validateDocumentationClosureSummary(agent, summary);
|
|
926
|
+
return { ok: validation.ok, agentId: documentationAgentId, statusCode: validation.statusCode,
|
|
927
|
+
detail: validation.detail, logPath: summary.logPath || null };
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
export function readWaveSecurityGatePure(wave, agentResults, options = {}) {
|
|
931
|
+
const agents = Array.isArray(wave.agents) ? wave.agents : [];
|
|
932
|
+
const securityAgents = agents.filter((agent) => isSecurityReviewAgent(agent));
|
|
933
|
+
if (securityAgents.length === 0) {
|
|
934
|
+
return { ok: true, agentId: null, statusCode: "pass",
|
|
935
|
+
detail: "No security reviewer declared for this wave.", logPath: null };
|
|
936
|
+
}
|
|
937
|
+
const concernAgentIds = [];
|
|
938
|
+
for (const agent of securityAgents) {
|
|
939
|
+
const summary = agentResults?.[agent.agentId] || null;
|
|
940
|
+
const validation = validateSecuritySummary(agent, summary);
|
|
941
|
+
if (!validation.ok) {
|
|
942
|
+
return { ok: false, agentId: agent.agentId, statusCode: validation.statusCode,
|
|
943
|
+
detail: validation.detail, logPath: summary?.logPath || null };
|
|
944
|
+
}
|
|
945
|
+
if (summary?.security?.state === "concerns") { concernAgentIds.push(agent.agentId); }
|
|
946
|
+
}
|
|
947
|
+
if (concernAgentIds.length > 0) {
|
|
948
|
+
return { ok: true, agentId: null, statusCode: "security-concerns",
|
|
949
|
+
detail: `Security review reported advisory concerns (${concernAgentIds.join(", ")}).`, logPath: null };
|
|
950
|
+
}
|
|
951
|
+
return { ok: true, agentId: null, statusCode: "pass",
|
|
952
|
+
detail: "Security review is clear.", logPath: null };
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
export function readWaveIntegrationGatePure(wave, agentResults, options = {}) {
|
|
956
|
+
const integrationAgentId = options.integrationAgentId || wave.integrationAgentId || "A8";
|
|
957
|
+
const requireIntegration = options.requireIntegrationSteward === true ||
|
|
958
|
+
(options.requireIntegrationStewardFromWave != null && wave.wave >= options.requireIntegrationStewardFromWave);
|
|
959
|
+
const summary = agentResults?.[integrationAgentId] || null;
|
|
960
|
+
if (!summary) {
|
|
961
|
+
return {
|
|
962
|
+
ok: !requireIntegration,
|
|
963
|
+
agentId: requireIntegration ? integrationAgentId : null,
|
|
964
|
+
statusCode: requireIntegration ? "missing-integration" : "pass",
|
|
965
|
+
detail: requireIntegration ? `Agent ${integrationAgentId} is missing.`
|
|
966
|
+
: "No explicit integration steward declared for this wave.",
|
|
967
|
+
logPath: null,
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
const agent = { agentId: integrationAgentId };
|
|
971
|
+
const validation = validateIntegrationSummary(agent, summary);
|
|
972
|
+
return { ok: validation.ok, agentId: integrationAgentId, statusCode: validation.statusCode,
|
|
973
|
+
detail: validation.detail, logPath: summary.logPath || null };
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
const NON_BLOCKING_INFRA_SIGNAL_STATES = new Set([
|
|
977
|
+
"conformant", "setup-required", "setup-in-progress",
|
|
978
|
+
"action-required", "action-approved", "action-complete",
|
|
979
|
+
]);
|
|
980
|
+
|
|
981
|
+
export function readWaveInfraGatePure(wave, agentResults, options = {}) {
|
|
982
|
+
for (const [agentId, summary] of Object.entries(agentResults || {})) {
|
|
983
|
+
if (!summary?.infra) { continue; }
|
|
984
|
+
const infra = summary.infra;
|
|
985
|
+
const normalizedState = String(infra.state || "").trim().toLowerCase();
|
|
986
|
+
if (NON_BLOCKING_INFRA_SIGNAL_STATES.has(normalizedState)) { continue; }
|
|
987
|
+
return {
|
|
988
|
+
ok: false, agentId,
|
|
989
|
+
statusCode: `infra-${normalizedState || "blocked"}`,
|
|
990
|
+
detail: `Infra signal ${infra.kind || "unknown"} on ${infra.target || "unknown"} ended in state ${normalizedState || "unknown"}${infra.detail ? ` (${infra.detail})` : ""}.`,
|
|
991
|
+
logPath: summary.logPath || null,
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
return { ok: true, agentId: null, statusCode: "pass",
|
|
995
|
+
detail: "No blocking infra signals detected.", logPath: null };
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
export function buildGateSnapshotPure({ wave, agentResults, derivedState, validationMode = "live", laneConfig = {} }) {
|
|
999
|
+
const implementationGate = readWaveImplementationGatePure(wave, agentResults, {
|
|
1000
|
+
contQaAgentId: laneConfig.contQaAgentId, contEvalAgentId: laneConfig.contEvalAgentId,
|
|
1001
|
+
integrationAgentId: laneConfig.integrationAgentId, documentationAgentId: laneConfig.documentationAgentId,
|
|
1002
|
+
});
|
|
1003
|
+
const componentGate = readWaveComponentGatePure(wave, agentResults, { laneProfile: laneConfig.laneProfile });
|
|
1004
|
+
const integrationMarkerGate = readWaveIntegrationGatePure(wave, agentResults, {
|
|
1005
|
+
integrationAgentId: laneConfig.integrationAgentId,
|
|
1006
|
+
requireIntegrationStewardFromWave: laneConfig.requireIntegrationStewardFromWave,
|
|
1007
|
+
});
|
|
1008
|
+
const integrationBarrier = (() => {
|
|
1009
|
+
if (!integrationMarkerGate.ok) { return integrationMarkerGate; }
|
|
1010
|
+
const integrationSummary = derivedState?.integrationSummary || null;
|
|
1011
|
+
if (!integrationSummary) {
|
|
1012
|
+
return { ok: false, agentId: integrationMarkerGate.agentId,
|
|
1013
|
+
statusCode: "missing-integration-summary",
|
|
1014
|
+
detail: `Missing integration summary artifact for wave ${wave.wave}.`,
|
|
1015
|
+
logPath: integrationMarkerGate.logPath };
|
|
1016
|
+
}
|
|
1017
|
+
if (integrationSummary.recommendation !== "ready-for-doc-closure") {
|
|
1018
|
+
return { ok: false, agentId: integrationMarkerGate.agentId,
|
|
1019
|
+
statusCode: "integration-needs-more-work",
|
|
1020
|
+
detail: integrationSummary.detail || `Integration summary still reports ${integrationSummary.recommendation}.`,
|
|
1021
|
+
logPath: integrationMarkerGate.logPath };
|
|
1022
|
+
}
|
|
1023
|
+
return integrationMarkerGate;
|
|
1024
|
+
})();
|
|
1025
|
+
const documentationGate = readWaveDocumentationGatePure(wave, agentResults, {
|
|
1026
|
+
documentationAgentId: laneConfig.documentationAgentId });
|
|
1027
|
+
const componentMatrixGate = readWaveComponentMatrixGatePure(wave, agentResults, {
|
|
1028
|
+
laneProfile: laneConfig.laneProfile, documentationAgentId: laneConfig.documentationAgentId,
|
|
1029
|
+
componentMatrixPayload: laneConfig.componentMatrixPayload,
|
|
1030
|
+
componentMatrixJsonPath: laneConfig.componentMatrixJsonPath });
|
|
1031
|
+
const contEvalGate = readWaveContEvalGatePure(wave, agentResults, {
|
|
1032
|
+
contEvalAgentId: laneConfig.contEvalAgentId, mode: validationMode,
|
|
1033
|
+
evalTargets: wave.evalTargets, benchmarkCatalogPath: laneConfig.benchmarkCatalogPath });
|
|
1034
|
+
const securityGate = readWaveSecurityGatePure(wave, agentResults);
|
|
1035
|
+
const contQaGate = readWaveContQaGatePure(wave, agentResults, {
|
|
1036
|
+
contQaAgentId: laneConfig.contQaAgentId, mode: validationMode });
|
|
1037
|
+
const infraGate = readWaveInfraGatePure(wave, agentResults);
|
|
1038
|
+
const clarificationBarrier = derivedState?.clarificationBarrier || { ok: true, statusCode: "pass", detail: "" };
|
|
1039
|
+
const helperAssignmentBarrier = derivedState?.helperAssignmentBarrier || { ok: true, statusCode: "pass", detail: "" };
|
|
1040
|
+
const dependencyBarrier = derivedState?.dependencyBarrier || { ok: true, statusCode: "pass", detail: "" };
|
|
1041
|
+
const orderedGates = [
|
|
1042
|
+
["implementationGate", implementationGate], ["componentGate", componentGate],
|
|
1043
|
+
["helperAssignmentBarrier", helperAssignmentBarrier], ["dependencyBarrier", dependencyBarrier],
|
|
1044
|
+
["contEvalGate", contEvalGate], ["securityGate", securityGate],
|
|
1045
|
+
["integrationBarrier", integrationBarrier], ["documentationGate", documentationGate],
|
|
1046
|
+
["componentMatrixGate", componentMatrixGate], ["contQaGate", contQaGate],
|
|
1047
|
+
["infraGate", infraGate], ["clarificationBarrier", clarificationBarrier],
|
|
1048
|
+
];
|
|
1049
|
+
const firstFailure = orderedGates.find(([, gate]) => gate?.ok === false);
|
|
1050
|
+
return {
|
|
1051
|
+
implementationGate, componentGate, integrationGate: integrationMarkerGate,
|
|
1052
|
+
integrationBarrier, documentationGate, componentMatrixGate,
|
|
1053
|
+
contEvalGate, securityGate, contQaGate, infraGate,
|
|
1054
|
+
clarificationBarrier, helperAssignmentBarrier, dependencyBarrier,
|
|
1055
|
+
overall: firstFailure
|
|
1056
|
+
? { ok: false, gate: firstFailure[0], statusCode: firstFailure[1].statusCode,
|
|
1057
|
+
detail: firstFailure[1].detail, agentId: firstFailure[1].agentId || null }
|
|
1058
|
+
: { ok: true, gate: "pass", statusCode: "pass",
|
|
1059
|
+
detail: "All replayed wave gates passed.", agentId: null },
|
|
1060
|
+
};
|
|
1061
|
+
}
|