@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.
Files changed (42) hide show
  1. package/CHANGELOG.md +40 -2
  2. package/README.md +47 -11
  3. package/docs/README.md +6 -2
  4. package/docs/concepts/what-is-a-wave.md +1 -1
  5. package/docs/plans/architecture-hardening-migration.md +8 -1
  6. package/docs/plans/current-state.md +17 -7
  7. package/docs/plans/end-state-architecture.md +82 -69
  8. package/docs/plans/examples/wave-example-live-proof.md +1 -1
  9. package/docs/plans/migration.md +235 -61
  10. package/docs/plans/wave-orchestrator.md +37 -11
  11. package/docs/reference/cli-reference.md +39 -15
  12. package/docs/reference/coordination-and-closure.md +30 -6
  13. package/docs/reference/npmjs-trusted-publishing.md +5 -4
  14. package/docs/reference/sample-waves.md +4 -4
  15. package/package.json +1 -1
  16. package/releases/manifest.json +39 -0
  17. package/scripts/wave-orchestrator/agent-state.mjs +0 -491
  18. package/scripts/wave-orchestrator/autonomous.mjs +10 -6
  19. package/scripts/wave-orchestrator/{launcher-closure.mjs → closure-engine.mjs} +190 -74
  20. package/scripts/wave-orchestrator/control-cli.mjs +8 -0
  21. package/scripts/wave-orchestrator/coord-cli.mjs +8 -0
  22. package/scripts/wave-orchestrator/{launcher-derived-state.mjs → derived-state-engine.mjs} +34 -146
  23. package/scripts/wave-orchestrator/feedback.mjs +11 -1
  24. package/scripts/wave-orchestrator/{launcher-gates.mjs → gate-engine.mjs} +395 -139
  25. package/scripts/wave-orchestrator/human-input-resolution.mjs +348 -0
  26. package/scripts/wave-orchestrator/human-input-workflow.mjs +104 -0
  27. package/scripts/wave-orchestrator/implementation-engine.mjs +120 -0
  28. package/scripts/wave-orchestrator/launcher-runtime.mjs +5 -6
  29. package/scripts/wave-orchestrator/launcher.mjs +271 -724
  30. package/scripts/wave-orchestrator/projection-writer.mjs +256 -0
  31. package/scripts/wave-orchestrator/reconcile-format.mjs +32 -0
  32. package/scripts/wave-orchestrator/reducer-snapshot.mjs +297 -0
  33. package/scripts/wave-orchestrator/replay.mjs +3 -1
  34. package/scripts/wave-orchestrator/result-envelope.mjs +589 -0
  35. package/scripts/wave-orchestrator/retry-control.mjs +5 -0
  36. package/scripts/wave-orchestrator/{launcher-retry.mjs → retry-engine.mjs} +267 -18
  37. package/scripts/wave-orchestrator/role-helpers.mjs +51 -0
  38. package/scripts/wave-orchestrator/{launcher-supervisor.mjs → session-supervisor.mjs} +178 -103
  39. package/scripts/wave-orchestrator/shared.mjs +1 -0
  40. package/scripts/wave-orchestrator/traces.mjs +10 -1
  41. package/scripts/wave-orchestrator/wave-files.mjs +11 -9
  42. package/scripts/wave-orchestrator/wave-state-reducer.mjs +52 -5
@@ -3,10 +3,7 @@ import path from "node:path";
3
3
  import {
4
4
  agentSummaryPathFromStatusPath,
5
5
  buildAgentExecutionSummary,
6
- buildEnvelopeFromLegacySignals,
7
- buildExecutionSummaryFromEnvelope,
8
6
  readAgentExecutionSummary,
9
- readAgentResultEnvelope,
10
7
  validateContQaSummary,
11
8
  validateContEvalSummary,
12
9
  validateImplementationSummary,
@@ -14,8 +11,17 @@ import {
14
11
  validateSecuritySummary,
15
12
  validateIntegrationSummary,
16
13
  writeAgentExecutionSummary,
17
- writeAgentResultEnvelope,
18
14
  } from "./agent-state.mjs";
15
+ import {
16
+ projectLegacySummaryFromEnvelope,
17
+ readAgentResultEnvelope,
18
+ readAgentResultEnvelopeForRun,
19
+ resolveRunEnvelopeContext,
20
+ synthesizeLegacyEnvelope,
21
+ validateResultEnvelope,
22
+ writeAgentResultEnvelope,
23
+ writeAgentResultEnvelopeForRun,
24
+ } from "./result-envelope.mjs";
19
25
  import {
20
26
  REPO_ROOT,
21
27
  readFileTail,
@@ -91,6 +97,33 @@ function resolveRunReportPath(wave, runInfo) {
91
97
  return null;
92
98
  }
93
99
 
100
+ function normalizeReadMode(mode) {
101
+ return String(mode || "compat").trim().toLowerCase() === "live" ? "live" : "compat";
102
+ }
103
+
104
+ function buildEnvelopeReadOptions(runInfo, wave, statusRecord, reportPath) {
105
+ return {
106
+ agent: runInfo?.agent,
107
+ waveNumber: wave?.wave ?? null,
108
+ attempt: statusRecord?.attempt ?? null,
109
+ logPath: runInfo?.logPath || null,
110
+ reportPath,
111
+ };
112
+ }
113
+
114
+ function validateEnvelopeForRun(runInfo, envelope, options = {}) {
115
+ const validation = validateResultEnvelope(envelope, {
116
+ agent: runInfo?.agent,
117
+ waveNumber: options.wave?.wave ?? null,
118
+ });
119
+ return {
120
+ valid: validation.valid,
121
+ errors: validation.errors || [],
122
+ detail: validation.valid ? null : validation.errors.join(" "),
123
+ envelope: validation.valid ? envelope : null,
124
+ };
125
+ }
126
+
94
127
  export function materializeAgentExecutionSummaryForRun(wave, runInfo) {
95
128
  const statusRecord = readStatusRecordIfPresent(runInfo.statusPath);
96
129
  if (!statusRecord) {
@@ -104,13 +137,17 @@ export function materializeAgentExecutionSummaryForRun(wave, runInfo) {
104
137
  reportPath,
105
138
  });
106
139
  writeAgentExecutionSummary(runInfo.statusPath, summary);
107
- writeAgentResultEnvelope(
108
- runInfo.statusPath,
109
- buildEnvelopeFromLegacySignals(runInfo.agent, summary, {
140
+ writeAgentResultEnvelopeForRun(
141
+ runInfo,
142
+ wave,
143
+ synthesizeLegacyEnvelope(runInfo.agent, summary, {
110
144
  waveNumber: wave?.wave ?? null,
111
145
  attempt: statusRecord.attempt ?? null,
112
146
  exitCode: typeof statusRecord.code === "number" ? statusRecord.code : 0,
113
147
  }),
148
+ {
149
+ statusRecord,
150
+ },
114
151
  );
115
152
  if (runInfo?.previewPath && fs.existsSync(runInfo.previewPath)) {
116
153
  const previewPayload = readJsonOrNull(runInfo.previewPath);
@@ -141,48 +178,164 @@ export function materializeAgentExecutionSummaryForRun(wave, runInfo) {
141
178
  return summary;
142
179
  }
143
180
 
144
- export function readRunExecutionSummary(runInfo, wave = null) {
145
- const applyProofRegistry = (summary) =>
146
- runInfo?.proofRegistry ? augmentSummaryWithProofRegistry(runInfo.agent, summary, runInfo.proofRegistry) : summary;
181
+ export function readRunResultEnvelope(runInfo, wave = null, options = {}) {
182
+ const mode = normalizeReadMode(options.mode);
147
183
  const statusRecord = runInfo?.statusPath ? readStatusRecordIfPresent(runInfo.statusPath) : null;
148
- const reportPath = wave && runInfo?.logPath ? resolveRunReportPath(wave, runInfo) : null;
149
- const summaryReadOptions =
150
- wave && runInfo?.logPath
151
- ? {
152
- agent: runInfo.agent,
153
- statusPath: runInfo.statusPath,
154
- statusRecord,
155
- logPath: runInfo.logPath,
156
- reportPath,
157
- }
158
- : {};
159
- const envelopeReadOptions = {
160
- agent: runInfo?.agent,
161
- waveNumber: wave?.wave ?? null,
162
- attempt: statusRecord?.attempt ?? null,
163
- logPath: runInfo?.logPath || null,
164
- reportPath,
184
+ const reportPath = wave ? resolveRunReportPath(wave, runInfo) : null;
185
+ const runEnvelopeContext = resolveRunEnvelopeContext(runInfo, wave, { statusRecord });
186
+ const envelopeReadOptions = buildEnvelopeReadOptions(runInfo, wave, statusRecord, reportPath);
187
+ const synthesizeFromSummary = (summary, source) => {
188
+ if (!summary || mode === "live") {
189
+ return null;
190
+ }
191
+ const envelope = synthesizeLegacyEnvelope(runInfo?.agent, summary, {
192
+ waveNumber: wave?.wave ?? null,
193
+ attempt: statusRecord?.attempt ?? null,
194
+ exitCode:
195
+ typeof statusRecord?.code === "number"
196
+ ? statusRecord.code
197
+ : typeof summary?.exitCode === "number"
198
+ ? summary.exitCode
199
+ : 0,
200
+ });
201
+ return {
202
+ source,
203
+ ...validateEnvelopeForRun(runInfo, envelope, { wave }),
204
+ };
165
205
  };
206
+
166
207
  if (runInfo?.summary && typeof runInfo.summary === "object") {
167
- const summary = runInfo.summary.schemaVersion === 2
168
- ? buildExecutionSummaryFromEnvelope(runInfo.summary, envelopeReadOptions)
169
- : runInfo.summary;
170
- return applyProofRegistry(summary);
208
+ if (runInfo.summary.schemaVersion === 2) {
209
+ return {
210
+ source: "inline-envelope",
211
+ ...validateEnvelopeForRun(runInfo, runInfo.summary, { wave }),
212
+ };
213
+ }
214
+ const synthesized = synthesizeFromSummary(runInfo.summary, "inline-legacy-summary");
215
+ if (synthesized) {
216
+ return synthesized;
217
+ }
171
218
  }
172
219
  if (runInfo?.statusPath && fs.existsSync(runInfo.statusPath)) {
220
+ const envelope = readAgentResultEnvelopeForRun(runInfo, wave, { statusRecord });
221
+ if (envelope) {
222
+ const validation = {
223
+ source: "run-envelope",
224
+ ...validateEnvelopeForRun(runInfo, envelope, { wave }),
225
+ };
226
+ if (validation.valid || mode === "live") {
227
+ return validation;
228
+ }
229
+ }
230
+ }
231
+ if (mode !== "live" && runInfo?.statusPath && fs.existsSync(runInfo.statusPath)) {
173
232
  const envelope = readAgentResultEnvelope(runInfo.statusPath);
174
233
  if (envelope) {
175
- return applyProofRegistry(buildExecutionSummaryFromEnvelope(envelope, envelopeReadOptions));
234
+ const validation = {
235
+ source: "legacy-status-envelope",
236
+ ...validateEnvelopeForRun(runInfo, envelope, { wave }),
237
+ };
238
+ if (validation.valid) {
239
+ return validation;
240
+ }
241
+ }
242
+ }
243
+ if (mode !== "live" && runInfo?.summaryPath && fs.existsSync(runInfo.summaryPath)) {
244
+ const summary = readAgentExecutionSummary(runInfo.summaryPath, {
245
+ agent: runInfo.agent,
246
+ statusPath: runInfo.summaryPath,
247
+ statusRecord,
248
+ logPath: runInfo.logPath,
249
+ reportPath,
250
+ });
251
+ const synthesized = synthesizeFromSummary(summary, "summary-file-legacy");
252
+ if (synthesized) {
253
+ return synthesized;
254
+ }
255
+ }
256
+ if (
257
+ mode !== "live" &&
258
+ runInfo?.statusPath &&
259
+ fs.existsSync(agentSummaryPathFromStatusPath(runInfo.statusPath))
260
+ ) {
261
+ const summary = readAgentExecutionSummary(runInfo.statusPath, {
262
+ agent: runInfo.agent,
263
+ statusPath: runInfo.statusPath,
264
+ statusRecord,
265
+ logPath: runInfo.logPath,
266
+ reportPath,
267
+ });
268
+ const synthesized = synthesizeFromSummary(summary, "status-summary-legacy");
269
+ if (synthesized) {
270
+ return synthesized;
271
+ }
272
+ }
273
+ if (
274
+ mode !== "live" &&
275
+ wave &&
276
+ runInfo?.statusPath &&
277
+ runInfo?.logPath &&
278
+ fs.existsSync(runInfo.statusPath)
279
+ ) {
280
+ materializeAgentExecutionSummaryForRun(wave, runInfo);
281
+ const envelope =
282
+ readAgentResultEnvelopeForRun(runInfo, wave, { statusRecord }) ||
283
+ readAgentResultEnvelope(runInfo.statusPath);
284
+ if (envelope) {
285
+ return {
286
+ source: "materialized-legacy-envelope",
287
+ ...validateEnvelopeForRun(runInfo, envelope, { wave }),
288
+ };
176
289
  }
177
290
  }
291
+ return {
292
+ source: "missing-envelope",
293
+ valid: false,
294
+ errors: [
295
+ `Missing result envelope for ${runInfo?.agent?.agentId || "unknown-agent"} at ${path.relative(REPO_ROOT, runEnvelopeContext.envelopePath)}.`,
296
+ ],
297
+ detail: `Missing result envelope for ${runInfo?.agent?.agentId || "unknown-agent"} at ${path.relative(REPO_ROOT, runEnvelopeContext.envelopePath)}.`,
298
+ envelope: null,
299
+ envelopeReadOptions,
300
+ };
301
+ }
302
+
303
+ export function readRunExecutionSummary(runInfo, wave = null, options = {}) {
304
+ const mode = normalizeReadMode(options.mode);
305
+ const applyProofRegistry = (summary) =>
306
+ runInfo?.proofRegistry ? augmentSummaryWithProofRegistry(runInfo.agent, summary, runInfo.proofRegistry) : summary;
307
+ const statusRecord = runInfo?.statusPath ? readStatusRecordIfPresent(runInfo.statusPath) : null;
308
+ const reportPath = wave ? resolveRunReportPath(wave, runInfo) : null;
309
+ const envelopeReadOptions = buildEnvelopeReadOptions(runInfo, wave, statusRecord, reportPath);
310
+ const envelopeResult = readRunResultEnvelope(runInfo, wave, { mode });
311
+ if (envelopeResult?.valid && envelopeResult.envelope) {
312
+ return applyProofRegistry(
313
+ projectLegacySummaryFromEnvelope(envelopeResult.envelope, envelopeReadOptions),
314
+ );
315
+ }
316
+ if (mode === "live") {
317
+ return null;
318
+ }
319
+ if (runInfo?.summary && typeof runInfo.summary === "object") {
320
+ return applyProofRegistry(runInfo.summary);
321
+ }
178
322
  if (runInfo?.summaryPath && fs.existsSync(runInfo.summaryPath)) {
179
- return applyProofRegistry(readAgentExecutionSummary(runInfo.summaryPath, summaryReadOptions));
323
+ return applyProofRegistry(readAgentExecutionSummary(runInfo.summaryPath, {
324
+ agent: runInfo.agent,
325
+ statusPath: runInfo.summaryPath,
326
+ statusRecord,
327
+ logPath: runInfo.logPath,
328
+ reportPath,
329
+ }));
180
330
  }
181
331
  if (runInfo?.statusPath && fs.existsSync(agentSummaryPathFromStatusPath(runInfo.statusPath))) {
182
- return applyProofRegistry(readAgentExecutionSummary(runInfo.statusPath, summaryReadOptions));
183
- }
184
- if (wave && runInfo?.statusPath && runInfo?.logPath && fs.existsSync(runInfo.statusPath)) {
185
- return applyProofRegistry(materializeAgentExecutionSummaryForRun(wave, runInfo));
332
+ return applyProofRegistry(readAgentExecutionSummary(runInfo.statusPath, {
333
+ agent: runInfo.agent,
334
+ statusPath: runInfo.statusPath,
335
+ statusRecord,
336
+ logPath: runInfo.logPath,
337
+ reportPath,
338
+ }));
186
339
  }
187
340
  return null;
188
341
  }
@@ -208,7 +361,18 @@ export function readWaveContQaGate(wave, agentRuns, options = {}) {
208
361
  logPath: null,
209
362
  };
210
363
  }
211
- const summary = readRunExecutionSummary(contQaRun, strict ? wave : null);
364
+ const envelopeResult = readRunResultEnvelope(contQaRun, wave, { mode });
365
+ const summary = envelopeResult.valid
366
+ ? projectLegacySummaryFromEnvelope(
367
+ envelopeResult.envelope,
368
+ buildEnvelopeReadOptions(
369
+ contQaRun,
370
+ wave,
371
+ contQaRun?.statusPath ? readStatusRecordIfPresent(contQaRun.statusPath) : null,
372
+ resolveRunReportPath(wave, contQaRun),
373
+ ),
374
+ )
375
+ : readRunExecutionSummary(contQaRun, wave, { mode });
212
376
  if (summary) {
213
377
  const validation = validateContQaSummary(contQaRun.agent, summary, { mode });
214
378
  return {
@@ -223,8 +387,13 @@ export function readWaveContQaGate(wave, agentRuns, options = {}) {
223
387
  return {
224
388
  ok: false,
225
389
  agentId: contQaRun.agent.agentId,
226
- statusCode: "missing-wave-gate",
227
- detail: `Missing structured cont-QA summary for ${contQaRun.agent.agentId}.`,
390
+ statusCode:
391
+ envelopeResult.source === "missing-envelope"
392
+ ? "missing-result-envelope"
393
+ : "invalid-result-envelope",
394
+ detail:
395
+ envelopeResult.detail ||
396
+ `Missing structured cont-QA result envelope for ${contQaRun.agent.agentId}.`,
228
397
  logPath: path.relative(REPO_ROOT, contQaRun.logPath),
229
398
  };
230
399
  }
@@ -284,7 +453,18 @@ export function readWaveContEvalGate(wave, agentRuns, options = {}) {
284
453
  logPath: null,
285
454
  };
286
455
  }
287
- const summary = readRunExecutionSummary(contEvalRun, strict ? wave : null);
456
+ const envelopeResult = readRunResultEnvelope(contEvalRun, wave, { mode });
457
+ const summary = envelopeResult.valid
458
+ ? projectLegacySummaryFromEnvelope(
459
+ envelopeResult.envelope,
460
+ buildEnvelopeReadOptions(
461
+ contEvalRun,
462
+ wave,
463
+ contEvalRun?.statusPath ? readStatusRecordIfPresent(contEvalRun.statusPath) : null,
464
+ resolveRunReportPath(wave, contEvalRun),
465
+ ),
466
+ )
467
+ : readRunExecutionSummary(contEvalRun, wave, { mode });
288
468
  if (summary) {
289
469
  const validation = validateContEvalSummary(contEvalRun.agent, summary, {
290
470
  mode,
@@ -302,8 +482,16 @@ export function readWaveContEvalGate(wave, agentRuns, options = {}) {
302
482
  return {
303
483
  ok: false,
304
484
  agentId: contEvalRun.agent.agentId,
305
- statusCode: "missing-wave-eval",
306
- detail: `Missing [wave-eval] marker for ${contEvalRun.agent.agentId}.`,
485
+ statusCode:
486
+ strict && envelopeResult.source !== "missing-envelope"
487
+ ? "invalid-result-envelope"
488
+ : strict
489
+ ? "missing-result-envelope"
490
+ : "missing-wave-eval",
491
+ detail:
492
+ strict && envelopeResult.detail
493
+ ? envelopeResult.detail
494
+ : `Missing [wave-eval] marker for ${contEvalRun.agent.agentId}.`,
307
495
  logPath: path.relative(REPO_ROOT, contEvalRun.logPath),
308
496
  };
309
497
  }
@@ -315,7 +503,8 @@ export function readWaveEvaluatorGate(wave, agentRuns, options = {}) {
315
503
  });
316
504
  }
317
505
 
318
- export function readWaveImplementationGate(wave, agentRuns) {
506
+ export function readWaveImplementationGate(wave, agentRuns, options = {}) {
507
+ const mode = normalizeReadMode(options.mode || "live");
319
508
  const contQaAgentId = wave.contQaAgentId || "A0";
320
509
  const contEvalAgentId = wave.contEvalAgentId || "E0";
321
510
  const integrationAgentId = wave.integrationAgentId || "A8";
@@ -328,7 +517,32 @@ export function readWaveImplementationGate(wave, agentRuns) {
328
517
  ) {
329
518
  continue;
330
519
  }
331
- const summary = readRunExecutionSummary(runInfo, wave);
520
+ const envelopeResult = readRunResultEnvelope(runInfo, wave, { mode });
521
+ if (mode === "live" && !envelopeResult.valid) {
522
+ return {
523
+ ok: false,
524
+ agentId: runInfo.agent.agentId,
525
+ statusCode:
526
+ envelopeResult.source === "missing-envelope"
527
+ ? "missing-result-envelope"
528
+ : "invalid-result-envelope",
529
+ detail:
530
+ envelopeResult.detail ||
531
+ `Missing structured implementation result envelope for ${runInfo.agent.agentId}.`,
532
+ logPath: path.relative(REPO_ROOT, runInfo.logPath),
533
+ };
534
+ }
535
+ const summary = envelopeResult.valid
536
+ ? projectLegacySummaryFromEnvelope(
537
+ envelopeResult.envelope,
538
+ buildEnvelopeReadOptions(
539
+ runInfo,
540
+ wave,
541
+ runInfo?.statusPath ? readStatusRecordIfPresent(runInfo.statusPath) : null,
542
+ resolveRunReportPath(wave, runInfo),
543
+ ),
544
+ )
545
+ : readRunExecutionSummary(runInfo, wave, { mode });
332
546
  const validation = validateImplementationSummary(runInfo.agent, summary);
333
547
  if (!validation.ok) {
334
548
  return {
@@ -423,8 +637,12 @@ export function buildSharedComponentSiblingPendingFailure(componentState) {
423
637
  }
424
638
 
425
639
  export function readWaveComponentGate(wave, agentRuns, options = {}) {
640
+ const mode = normalizeReadMode(options.mode);
426
641
  const summariesByAgentId = Object.fromEntries(
427
- agentRuns.map((runInfo) => [runInfo.agent.agentId, readRunExecutionSummary(runInfo, wave)]),
642
+ agentRuns.map((runInfo) => [
643
+ runInfo.agent.agentId,
644
+ readRunExecutionSummary(runInfo, wave, { mode }),
645
+ ]),
428
646
  );
429
647
  const validation = validateWaveComponentPromotions(wave, summariesByAgentId, options);
430
648
  const sharedPending = (wave.componentPromotions || [])
@@ -493,7 +711,8 @@ export function readWaveComponentMatrixGate(wave, agentRuns, options = {}) {
493
711
  };
494
712
  }
495
713
 
496
- export function readWaveDocumentationGate(wave, agentRuns) {
714
+ export function readWaveDocumentationGate(wave, agentRuns, options = {}) {
715
+ const mode = normalizeReadMode(options.mode || "live");
497
716
  const documentationAgentId = wave.documentationAgentId || "A9";
498
717
  const docRun =
499
718
  agentRuns.find((run) => run.agent.agentId === documentationAgentId) ?? null;
@@ -506,7 +725,32 @@ export function readWaveDocumentationGate(wave, agentRuns) {
506
725
  logPath: null,
507
726
  };
508
727
  }
509
- const summary = readRunExecutionSummary(docRun, wave);
728
+ const envelopeResult = readRunResultEnvelope(docRun, wave, { mode });
729
+ if (mode === "live" && !envelopeResult.valid) {
730
+ return {
731
+ ok: false,
732
+ agentId: docRun.agent.agentId,
733
+ statusCode:
734
+ envelopeResult.source === "missing-envelope"
735
+ ? "missing-result-envelope"
736
+ : "invalid-result-envelope",
737
+ detail:
738
+ envelopeResult.detail ||
739
+ `Missing structured documentation result envelope for ${docRun.agent.agentId}.`,
740
+ logPath: path.relative(REPO_ROOT, docRun.logPath),
741
+ };
742
+ }
743
+ const summary = envelopeResult.valid
744
+ ? projectLegacySummaryFromEnvelope(
745
+ envelopeResult.envelope,
746
+ buildEnvelopeReadOptions(
747
+ docRun,
748
+ wave,
749
+ docRun?.statusPath ? readStatusRecordIfPresent(docRun.statusPath) : null,
750
+ resolveRunReportPath(wave, docRun),
751
+ ),
752
+ )
753
+ : readRunExecutionSummary(docRun, wave, { mode });
510
754
  const validation = validateDocumentationClosureSummary(docRun.agent, summary);
511
755
  return {
512
756
  ok: validation.ok,
@@ -517,7 +761,8 @@ export function readWaveDocumentationGate(wave, agentRuns) {
517
761
  };
518
762
  }
519
763
 
520
- export function readWaveSecurityGate(wave, agentRuns) {
764
+ export function readWaveSecurityGate(wave, agentRuns, options = {}) {
765
+ const mode = normalizeReadMode(options.mode || "live");
521
766
  const securityRuns = (agentRuns || []).filter((run) => isSecurityReviewAgent(run.agent));
522
767
  if (securityRuns.length === 0) {
523
768
  return {
@@ -530,7 +775,32 @@ export function readWaveSecurityGate(wave, agentRuns) {
530
775
  }
531
776
  const concernAgentIds = [];
532
777
  for (const runInfo of securityRuns) {
533
- const summary = readRunExecutionSummary(runInfo, wave);
778
+ const envelopeResult = readRunResultEnvelope(runInfo, wave, { mode });
779
+ if (mode === "live" && !envelopeResult.valid) {
780
+ return {
781
+ ok: false,
782
+ agentId: runInfo.agent.agentId,
783
+ statusCode:
784
+ envelopeResult.source === "missing-envelope"
785
+ ? "missing-result-envelope"
786
+ : "invalid-result-envelope",
787
+ detail:
788
+ envelopeResult.detail ||
789
+ `Missing structured security result envelope for ${runInfo.agent.agentId}.`,
790
+ logPath: path.relative(REPO_ROOT, runInfo.logPath),
791
+ };
792
+ }
793
+ const summary = envelopeResult.valid
794
+ ? projectLegacySummaryFromEnvelope(
795
+ envelopeResult.envelope,
796
+ buildEnvelopeReadOptions(
797
+ runInfo,
798
+ wave,
799
+ runInfo?.statusPath ? readStatusRecordIfPresent(runInfo.statusPath) : null,
800
+ resolveRunReportPath(wave, runInfo),
801
+ ),
802
+ )
803
+ : readRunExecutionSummary(runInfo, wave, { mode });
534
804
  const validation = validateSecuritySummary(runInfo.agent, summary);
535
805
  if (!validation.ok) {
536
806
  return {
@@ -564,6 +834,7 @@ export function readWaveSecurityGate(wave, agentRuns) {
564
834
  }
565
835
 
566
836
  export function readWaveIntegrationGate(wave, agentRuns, options = {}) {
837
+ const mode = normalizeReadMode(options.mode || "live");
567
838
  const integrationAgentId =
568
839
  options.integrationAgentId || wave.integrationAgentId || "A8";
569
840
  const requireIntegration =
@@ -584,7 +855,32 @@ export function readWaveIntegrationGate(wave, agentRuns, options = {}) {
584
855
  logPath: null,
585
856
  };
586
857
  }
587
- const summary = readRunExecutionSummary(integrationRun, wave);
858
+ const envelopeResult = readRunResultEnvelope(integrationRun, wave, { mode });
859
+ if (mode === "live" && !envelopeResult.valid) {
860
+ return {
861
+ ok: false,
862
+ agentId: integrationRun.agent.agentId,
863
+ statusCode:
864
+ envelopeResult.source === "missing-envelope"
865
+ ? "missing-result-envelope"
866
+ : "invalid-result-envelope",
867
+ detail:
868
+ envelopeResult.detail ||
869
+ `Missing structured integration result envelope for ${integrationRun.agent.agentId}.`,
870
+ logPath: path.relative(REPO_ROOT, integrationRun.logPath),
871
+ };
872
+ }
873
+ const summary = envelopeResult.valid
874
+ ? projectLegacySummaryFromEnvelope(
875
+ envelopeResult.envelope,
876
+ buildEnvelopeReadOptions(
877
+ integrationRun,
878
+ wave,
879
+ integrationRun?.statusPath ? readStatusRecordIfPresent(integrationRun.statusPath) : null,
880
+ resolveRunReportPath(wave, integrationRun),
881
+ ),
882
+ )
883
+ : readRunExecutionSummary(integrationRun, wave, { mode });
588
884
  const validation = validateIntegrationSummary(integrationRun.agent, summary);
589
885
  return {
590
886
  ok: validation.ok,
@@ -737,91 +1033,51 @@ export function buildGateSnapshot({
737
1033
  validationMode = "compat",
738
1034
  readWaveInfraGateFn,
739
1035
  }) {
740
- const implementationGate = readWaveImplementationGate(wave, agentRuns);
741
- const componentGate = readWaveComponentGate(wave, agentRuns, {
742
- laneProfile: lanePaths?.laneProfile,
743
- });
744
- const integrationGate = readWaveIntegrationGate(wave, agentRuns, {
745
- integrationAgentId: lanePaths?.integrationAgentId,
746
- requireIntegrationStewardFromWave: lanePaths?.requireIntegrationStewardFromWave,
747
- });
748
- const integrationBarrier = readWaveIntegrationBarrier(wave, agentRuns, derivedState, {
749
- integrationAgentId: lanePaths?.integrationAgentId,
750
- requireIntegrationStewardFromWave: lanePaths?.requireIntegrationStewardFromWave,
751
- });
752
- const documentationGate = readWaveDocumentationGate(wave, agentRuns);
753
- const componentMatrixGate = readWaveComponentMatrixGate(wave, agentRuns, {
754
- laneProfile: lanePaths?.laneProfile,
755
- documentationAgentId: lanePaths?.documentationAgentId,
756
- componentMatrixPayload,
757
- componentMatrixJsonPath,
758
- });
759
- const contEvalGate = readWaveContEvalGate(wave, agentRuns, {
760
- contEvalAgentId: lanePaths?.contEvalAgentId,
761
- mode: validationMode,
762
- evalTargets: wave.evalTargets,
763
- benchmarkCatalogPath: lanePaths?.laneProfile?.paths?.benchmarkCatalogPath,
764
- });
765
- const securityGate = readWaveSecurityGate(wave, agentRuns);
766
- const contQaGate = readWaveContQaGate(wave, agentRuns, {
767
- contQaAgentId: lanePaths?.contQaAgentId,
768
- mode: validationMode,
1036
+ const agentResults = Object.fromEntries(
1037
+ (agentRuns || [])
1038
+ .map((runInfo) => [
1039
+ runInfo.agent.agentId,
1040
+ readRunExecutionSummary(runInfo, wave, { mode: validationMode }),
1041
+ ])
1042
+ .filter(([, summary]) => Boolean(summary)),
1043
+ );
1044
+ return buildGateSnapshotPure({
1045
+ wave,
1046
+ agentResults,
1047
+ derivedState: {
1048
+ ...derivedState,
1049
+ clarificationBarrier:
1050
+ derivedState?.clarificationBarrier || readClarificationBarrier(derivedState),
1051
+ helperAssignmentBarrier:
1052
+ derivedState?.helperAssignmentBarrier || readWaveAssignmentBarrier(derivedState),
1053
+ dependencyBarrier:
1054
+ derivedState?.dependencyBarrier || readWaveDependencyBarrier(derivedState),
1055
+ },
1056
+ validationMode,
1057
+ laneConfig: {
1058
+ contQaAgentId: lanePaths?.contQaAgentId,
1059
+ contEvalAgentId: lanePaths?.contEvalAgentId,
1060
+ integrationAgentId: lanePaths?.integrationAgentId,
1061
+ documentationAgentId: lanePaths?.documentationAgentId,
1062
+ requireIntegrationStewardFromWave: lanePaths?.requireIntegrationStewardFromWave,
1063
+ laneProfile: lanePaths?.laneProfile,
1064
+ benchmarkCatalogPath: lanePaths?.laneProfile?.paths?.benchmarkCatalogPath,
1065
+ componentMatrixPayload,
1066
+ componentMatrixJsonPath,
1067
+ },
769
1068
  });
770
- const infraGate = readWaveInfraGateFn(agentRuns);
771
- const clarificationBarrier = readClarificationBarrier(derivedState);
772
- const helperAssignmentBarrier = readWaveAssignmentBarrier(derivedState);
773
- const dependencyBarrier = readWaveDependencyBarrier(derivedState);
774
- const orderedGates = [
775
- ["implementationGate", implementationGate],
776
- ["componentGate", componentGate],
777
- ["helperAssignmentBarrier", helperAssignmentBarrier],
778
- ["dependencyBarrier", dependencyBarrier],
779
- ["contEvalGate", contEvalGate],
780
- ["securityGate", securityGate],
781
- ["integrationBarrier", integrationBarrier],
782
- ["documentationGate", documentationGate],
783
- ["componentMatrixGate", componentMatrixGate],
784
- ["contQaGate", contQaGate],
785
- ["infraGate", infraGate],
786
- ["clarificationBarrier", clarificationBarrier],
787
- ];
788
- const firstFailure = orderedGates.find(([, gate]) => gate?.ok === false);
789
- return {
790
- implementationGate,
791
- componentGate,
792
- integrationGate,
793
- integrationBarrier,
794
- documentationGate,
795
- componentMatrixGate,
796
- contEvalGate,
797
- securityGate,
798
- contQaGate,
799
- infraGate,
800
- clarificationBarrier,
801
- helperAssignmentBarrier,
802
- dependencyBarrier,
803
- overall: firstFailure
804
- ? {
805
- ok: false,
806
- gate: firstFailure[0],
807
- statusCode: firstFailure[1].statusCode,
808
- detail: firstFailure[1].detail,
809
- agentId: firstFailure[1].agentId || null,
810
- }
811
- : {
812
- ok: true,
813
- gate: "pass",
814
- statusCode: "pass",
815
- detail: "All replayed wave gates passed.",
816
- agentId: null,
817
- },
818
- };
819
1069
  }
820
1070
 
821
1071
  // --- Pure gate variants (no file I/O) ---
822
1072
  // These accept agentResults map { agentId: executionSummary } instead of runInfo objects.
823
1073
  // Used by the wave-state-reducer for deterministic replay.
824
1074
 
1075
+ function waveDeclaresAgent(wave, agentId) {
1076
+ return (Array.isArray(wave?.agents) ? wave.agents : []).some(
1077
+ (agent) => agent?.agentId === agentId,
1078
+ );
1079
+ }
1080
+
825
1081
  export function readWaveImplementationGatePure(wave, agentResults, options = {}) {
826
1082
  const contQaAgentId = options.contQaAgentId || wave.contQaAgentId || "A0";
827
1083
  const contEvalAgentId = options.contEvalAgentId || wave.contEvalAgentId || "E0";
@@ -865,24 +1121,24 @@ export function readWaveContQaGatePure(wave, agentResults, options = {}) {
865
1121
  const agent = { agentId: contQaAgentId };
866
1122
  const validation = validateContQaSummary(agent, summary, { mode });
867
1123
  return { ok: validation.ok, agentId: contQaAgentId, statusCode: validation.statusCode,
868
- detail: validation.detail, logPath: summary.logPath || null };
1124
+ detail: validation.detail, logPath: summary?.logPath || null };
869
1125
  }
870
1126
 
871
1127
  export function readWaveContEvalGatePure(wave, agentResults, options = {}) {
872
1128
  const mode = String(options.mode || "live").trim().toLowerCase();
873
1129
  const contEvalAgentId = options.contEvalAgentId || wave.contEvalAgentId || "E0";
874
- const summary = agentResults?.[contEvalAgentId] || null;
875
- if (!summary) {
1130
+ if (!waveDeclaresAgent(wave, contEvalAgentId)) {
876
1131
  return { ok: true, agentId: null, statusCode: "pass",
877
1132
  detail: "Wave does not include cont-EVAL.", logPath: null };
878
1133
  }
1134
+ const summary = agentResults?.[contEvalAgentId] || null;
879
1135
  const agent = { agentId: contEvalAgentId };
880
1136
  const validation = validateContEvalSummary(agent, summary, {
881
1137
  mode, evalTargets: options.evalTargets || wave.evalTargets,
882
1138
  benchmarkCatalogPath: options.benchmarkCatalogPath,
883
1139
  });
884
1140
  return { ok: validation.ok, agentId: contEvalAgentId, statusCode: validation.statusCode,
885
- detail: validation.detail, logPath: summary.logPath || null };
1141
+ detail: validation.detail, logPath: summary?.logPath || null };
886
1142
  }
887
1143
 
888
1144
  export function readWaveEvaluatorGatePure(wave, agentResults, options = {}) {
@@ -976,15 +1232,15 @@ export function readWaveComponentMatrixGatePure(wave, agentResults, options = {}
976
1232
 
977
1233
  export function readWaveDocumentationGatePure(wave, agentResults, options = {}) {
978
1234
  const documentationAgentId = options.documentationAgentId || wave.documentationAgentId || "A9";
979
- const summary = agentResults?.[documentationAgentId] || null;
980
- if (!summary) {
1235
+ if (!waveDeclaresAgent(wave, documentationAgentId)) {
981
1236
  return { ok: true, agentId: null, statusCode: "pass",
982
1237
  detail: "No documentation steward declared for this wave.", logPath: null };
983
1238
  }
1239
+ const summary = agentResults?.[documentationAgentId] || null;
984
1240
  const agent = { agentId: documentationAgentId };
985
1241
  const validation = validateDocumentationClosureSummary(agent, summary);
986
1242
  return { ok: validation.ok, agentId: documentationAgentId, statusCode: validation.statusCode,
987
- detail: validation.detail, logPath: summary.logPath || null };
1243
+ detail: validation.detail, logPath: summary?.logPath || null };
988
1244
  }
989
1245
 
990
1246
  export function readWaveSecurityGatePure(wave, agentResults, options = {}) {
@@ -1016,8 +1272,7 @@ export function readWaveIntegrationGatePure(wave, agentResults, options = {}) {
1016
1272
  const integrationAgentId = options.integrationAgentId || wave.integrationAgentId || "A8";
1017
1273
  const requireIntegration = options.requireIntegrationSteward === true ||
1018
1274
  (options.requireIntegrationStewardFromWave != null && wave.wave >= options.requireIntegrationStewardFromWave);
1019
- const summary = agentResults?.[integrationAgentId] || null;
1020
- if (!summary) {
1275
+ if (!waveDeclaresAgent(wave, integrationAgentId)) {
1021
1276
  return {
1022
1277
  ok: !requireIntegration,
1023
1278
  agentId: requireIntegration ? integrationAgentId : null,
@@ -1027,10 +1282,11 @@ export function readWaveIntegrationGatePure(wave, agentResults, options = {}) {
1027
1282
  logPath: null,
1028
1283
  };
1029
1284
  }
1285
+ const summary = agentResults?.[integrationAgentId] || null;
1030
1286
  const agent = { agentId: integrationAgentId };
1031
1287
  const validation = validateIntegrationSummary(agent, summary);
1032
1288
  return { ok: validation.ok, agentId: integrationAgentId, statusCode: validation.statusCode,
1033
- detail: validation.detail, logPath: summary.logPath || null };
1289
+ detail: validation.detail, logPath: summary?.logPath || null };
1034
1290
  }
1035
1291
 
1036
1292
  const NON_BLOCKING_INFRA_SIGNAL_STATES = new Set([