@chllming/wave-orchestration 0.7.3 → 0.8.0

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 (48) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +14 -13
  3. package/docs/README.md +3 -1
  4. package/docs/agents/wave-cont-qa-role.md +1 -0
  5. package/docs/agents/wave-integration-role.md +1 -0
  6. package/docs/agents/wave-launcher-role.md +4 -1
  7. package/docs/agents/wave-orchestrator-role.md +5 -3
  8. package/docs/concepts/operating-modes.md +1 -1
  9. package/docs/concepts/runtime-agnostic-orchestration.md +5 -4
  10. package/docs/concepts/what-is-a-wave.md +12 -10
  11. package/docs/guides/author-and-run-waves.md +3 -3
  12. package/docs/plans/architecture-hardening-migration.md +206 -0
  13. package/docs/plans/current-state.md +5 -3
  14. package/docs/plans/examples/wave-example-live-proof.md +1 -1
  15. package/docs/plans/master-plan.md +2 -2
  16. package/docs/plans/migration.md +12 -2
  17. package/docs/plans/wave-orchestrator.md +10 -8
  18. package/docs/reference/coordination-and-closure.md +8 -4
  19. package/docs/reference/npmjs-trusted-publishing.md +2 -2
  20. package/docs/reference/sample-waves.md +4 -4
  21. package/docs/reference/skills.md +3 -0
  22. package/docs/reference/wave-control.md +4 -2
  23. package/docs/research/coordination-failure-review.md +4 -4
  24. package/docs/roadmap.md +1 -1
  25. package/package.json +1 -1
  26. package/releases/manifest.json +19 -0
  27. package/scripts/wave-orchestrator/agent-state.mjs +405 -89
  28. package/scripts/wave-orchestrator/contradiction-entity.mjs +487 -0
  29. package/scripts/wave-orchestrator/launcher-gates.mjs +79 -11
  30. package/scripts/wave-orchestrator/launcher-retry.mjs +36 -6
  31. package/scripts/wave-orchestrator/launcher.mjs +163 -2
  32. package/scripts/wave-orchestrator/task-entity.mjs +425 -51
  33. package/scripts/wave-orchestrator/wave-control-schema.mjs +2 -0
  34. package/scripts/wave-orchestrator/wave-state-reducer.mjs +260 -111
  35. package/skills/README.md +3 -0
  36. package/skills/repo-coding-rules/SKILL.md +1 -1
  37. package/skills/role-cont-qa/SKILL.md +2 -2
  38. package/skills/role-deploy/SKILL.md +1 -1
  39. package/skills/role-documentation/SKILL.md +1 -1
  40. package/skills/role-implementation/SKILL.md +1 -1
  41. package/skills/role-infra/SKILL.md +1 -1
  42. package/skills/role-integration/SKILL.md +2 -2
  43. package/skills/role-security/SKILL.md +1 -1
  44. package/skills/runtime-claude/SKILL.md +1 -1
  45. package/skills/runtime-codex/SKILL.md +1 -1
  46. package/skills/runtime-opencode/SKILL.md +1 -1
  47. package/skills/wave-core/SKILL.md +14 -6
  48. package/skills/wave-core/references/marker-syntax.md +1 -1
@@ -1182,13 +1182,93 @@ export function validateContQaSummary(agent, summary, options = {}) {
1182
1182
  }
1183
1183
 
1184
1184
  // ---------------------------------------------------------------------------
1185
- // Agent Result Envelope — Wave 3
1185
+ // Agent Result Envelope — P1-6 End-State (schemaVersion 2)
1186
1186
  // ---------------------------------------------------------------------------
1187
1187
 
1188
1188
  import { toIsoTimestamp } from "./shared.mjs";
1189
1189
 
1190
1190
  /**
1191
- * Path to the envelope file derived from the status path.
1191
+ * Valid roles for the agent result envelope.
1192
+ */
1193
+ export const ENVELOPE_VALID_ROLES = [
1194
+ "implementation",
1195
+ "integration",
1196
+ "documentation",
1197
+ "cont-qa",
1198
+ "cont-eval",
1199
+ "security",
1200
+ "deploy",
1201
+ ];
1202
+
1203
+ function inferEnvelopeRole(agent, summary) {
1204
+ const candidate = String(agent?.role || summary?.role || "").trim().toLowerCase();
1205
+ if (candidate && ENVELOPE_VALID_ROLES.includes(candidate)) {
1206
+ return candidate;
1207
+ }
1208
+ if (summary?.integration) {
1209
+ return "integration";
1210
+ }
1211
+ if (summary?.docClosure) {
1212
+ return "documentation";
1213
+ }
1214
+ if (summary?.eval) {
1215
+ return "cont-eval";
1216
+ }
1217
+ if (summary?.security) {
1218
+ return "security";
1219
+ }
1220
+ if (summary?.gate || summary?.verdict) {
1221
+ return "cont-qa";
1222
+ }
1223
+ if (summary?.deploy) {
1224
+ return "deploy";
1225
+ }
1226
+ if (summary?.proof || summary?.docDelta || Array.isArray(summary?.components)) {
1227
+ return "implementation";
1228
+ }
1229
+ return null;
1230
+ }
1231
+
1232
+ function toSummaryRelativePath(filePath) {
1233
+ const normalized = String(filePath || "").trim();
1234
+ if (!normalized) {
1235
+ return null;
1236
+ }
1237
+ return path.isAbsolute(normalized) ? path.relative(REPO_ROOT, normalized) : normalized;
1238
+ }
1239
+
1240
+ function toLegacyProofState(state) {
1241
+ if (state === "satisfied") {
1242
+ return "met";
1243
+ }
1244
+ if (state === "partial") {
1245
+ return "gap";
1246
+ }
1247
+ if (state === "failed") {
1248
+ return "failed";
1249
+ }
1250
+ return "not_applicable";
1251
+ }
1252
+
1253
+ /**
1254
+ * Compute the attempt-scoped canonical envelope path.
1255
+ *
1256
+ * End-state path: .tmp/<lane>/results/wave-<N>/attempt-<A>/<agentId>.json
1257
+ *
1258
+ * @param {object} options - { lane, waveNumber, attempt, agentId }
1259
+ * @returns {string} The envelope file path
1260
+ */
1261
+ export function agentEnvelopePath({ lane, waveNumber, attempt, agentId }) {
1262
+ const safeLane = lane || "main";
1263
+ const safeWave = waveNumber ?? 0;
1264
+ const safeAttempt = attempt ?? 1;
1265
+ const safeAgent = agentId || "unknown";
1266
+ return `.tmp/${safeLane}-wave-launcher/results/wave-${safeWave}/attempt-${safeAttempt}/${safeAgent}.json`;
1267
+ }
1268
+
1269
+ /**
1270
+ * Legacy path derivation from status path.
1271
+ * Retained for backward compatibility during migration.
1192
1272
  *
1193
1273
  * @param {string} statusPath - Path to the .status or .summary file
1194
1274
  * @returns {string} The envelope file path
@@ -1207,129 +1287,365 @@ export function agentEnvelopePathFromStatusPath(statusPath) {
1207
1287
  * Build a structured result envelope from an already-parsed execution summary.
1208
1288
  * Pure function — the envelope is a normalized projection of the summary.
1209
1289
  *
1210
- * @param {object} agent - Agent definition from wave
1290
+ * End-state P1-6: common header + role-specific typed optional payloads.
1291
+ * Absent role sections are NOT included (not null, not empty object).
1292
+ *
1293
+ * @param {object} agent - Agent definition from wave (must include .role)
1211
1294
  * @param {object} summary - Execution summary from buildAgentExecutionSummary
1212
- * @returns {object} AgentResultEnvelope
1295
+ * @param {object} [options] - { waveNumber, attempt, exitCode }
1296
+ * @returns {object} AgentResultEnvelope (schemaVersion 2)
1213
1297
  */
1214
- export function buildAgentResultEnvelope(agent, summary) {
1298
+ export function buildAgentResultEnvelope(agent, summary, options = {}) {
1215
1299
  const safeAgent = agent || {};
1216
1300
  const safeSummary = summary || {};
1301
+ const safeOptions = options || {};
1217
1302
 
1218
- // Exit contract from proof dimensions + doc delta
1303
+ const agentId = safeAgent.agentId || safeSummary.agentId || null;
1304
+ const role = inferEnvelopeRole(safeAgent, safeSummary);
1305
+
1306
+ // --- Common header ---
1219
1307
  const proof = safeSummary.proof || {};
1220
- const docDelta = safeSummary.docDelta || {};
1221
- const exitContract = {
1308
+ const proofSection = {
1309
+ state: proof.state === "met" ? "satisfied"
1310
+ : proof.state === "gap" ? "partial"
1311
+ : proof.state === "failed" ? "failed"
1312
+ : "not_applicable",
1222
1313
  completion: proof.completion || null,
1223
1314
  durability: proof.durability || null,
1224
- proof: proof.proof || null,
1225
- docImpact: docDelta.state || null,
1315
+ proofLevel: proof.proof || null,
1316
+ detail: proof.detail || null,
1226
1317
  };
1227
1318
 
1228
- // Proof artifacts
1319
+ // Deliverables — with sha256
1320
+ const deliverables = Array.isArray(safeSummary.deliverables)
1321
+ ? safeSummary.deliverables.map((d) => ({
1322
+ path: d.path || null,
1323
+ exists: d.exists === true,
1324
+ sha256: d.sha256 || null,
1325
+ }))
1326
+ : [];
1327
+
1328
+ // Proof artifacts — with sha256 and requiredFor
1229
1329
  const proofArtifacts = Array.isArray(safeSummary.proofArtifacts)
1230
1330
  ? safeSummary.proofArtifacts.map((artifact) => ({
1231
1331
  path: artifact.path || null,
1232
1332
  kind: artifact.kind || null,
1233
- sha256: artifact.sha256 || null,
1234
1333
  exists: artifact.exists === true,
1334
+ sha256: artifact.sha256 || null,
1335
+ requiredFor: artifact.requiredFor || null,
1235
1336
  }))
1236
1337
  : [];
1237
1338
 
1238
- // Deliverables
1239
- const deliverables = Array.isArray(safeSummary.deliverables)
1240
- ? safeSummary.deliverables.map((d) => ({
1241
- path: d.path || null,
1242
- exists: d.exists === true,
1339
+ // Gaps
1340
+ const gaps = Array.isArray(safeSummary.gaps)
1341
+ ? safeSummary.gaps.map((g) => ({
1342
+ kind: g.kind || null,
1343
+ detail: g.detail || null,
1243
1344
  }))
1244
1345
  : [];
1245
1346
 
1246
- // Components
1247
- const components = Array.isArray(safeSummary.components)
1248
- ? safeSummary.components.map((c) => ({
1249
- componentId: c.componentId || null,
1250
- level: c.level || null,
1251
- state: c.state || null,
1347
+ // Unresolved blockers
1348
+ const unresolvedBlockers = Array.isArray(safeSummary.unresolvedBlockers)
1349
+ ? safeSummary.unresolvedBlockers.map((b) =>
1350
+ typeof b === "object" ? { kind: b.kind || null, detail: b.detail || null, blocking: b.blocking || null } : { kind: null, detail: String(b), blocking: null },
1351
+ )
1352
+ : [];
1353
+
1354
+ // Risk notes
1355
+ const riskNotes = Array.isArray(safeSummary.riskNotes) ? safeSummary.riskNotes : [];
1356
+
1357
+ // Facts
1358
+ const facts = Array.isArray(safeSummary.facts)
1359
+ ? safeSummary.facts.map((f) => ({
1360
+ factId: f.factId || null,
1361
+ kind: f.kind || null,
1362
+ content: f.content || null,
1252
1363
  }))
1253
1364
  : [];
1254
1365
 
1255
- // Gate claims from the gate marker
1256
- const gateClaims = [];
1257
- if (safeSummary.gate) {
1258
- const gateKeys = ["architecture", "integration", "durability", "live", "docs"];
1259
- for (const key of gateKeys) {
1260
- if (safeSummary.gate[key]) {
1261
- gateClaims.push({
1262
- gateId: key,
1263
- claim: safeSummary.gate[key],
1264
- detail: safeSummary.gate.detail || null,
1265
- });
1266
- }
1267
- }
1366
+ const envelope = {
1367
+ schemaVersion: 2,
1368
+ agentId,
1369
+ waveNumber: safeOptions.waveNumber ?? safeSummary.waveNumber ?? null,
1370
+ attempt: safeOptions.attempt ?? safeSummary.attempt ?? null,
1371
+ completedAt: safeOptions.completedAt || toIsoTimestamp(),
1372
+ exitCode: typeof safeOptions.exitCode === "number" ? safeOptions.exitCode : (typeof safeSummary.exitCode === "number" ? safeSummary.exitCode : 0),
1373
+ role,
1374
+ proof: proofSection,
1375
+ deliverables,
1376
+ proofArtifacts,
1377
+ gaps,
1378
+ unresolvedBlockers,
1379
+ riskNotes,
1380
+ facts,
1381
+ };
1382
+
1383
+ // --- Role-specific typed payloads (absent when not applicable) ---
1384
+
1385
+ if (role === "implementation") {
1386
+ const docDelta = safeSummary.docDelta || {};
1387
+ const components = Array.isArray(safeSummary.components)
1388
+ ? safeSummary.components.map((c) => ({
1389
+ componentId: c.componentId || null,
1390
+ level: c.level || null,
1391
+ state: c.state || null,
1392
+ detail: c.detail || null,
1393
+ }))
1394
+ : [];
1395
+ envelope.implementation = {
1396
+ docDelta: {
1397
+ state: docDelta.state || "none",
1398
+ paths: Array.isArray(docDelta.paths) ? docDelta.paths : [],
1399
+ detail: docDelta.detail || null,
1400
+ },
1401
+ components,
1402
+ };
1268
1403
  }
1269
1404
 
1270
- // Validation outputs from proof state
1271
- const validationOutputs = {
1272
- testsPassed: proof.state === "met" && proof.proof != null,
1273
- buildPassed: proof.state === "met",
1274
- };
1405
+ if (role === "integration") {
1406
+ const integ = safeSummary.integration || {};
1407
+ envelope.integration = {
1408
+ state: integ.state || null,
1409
+ claims: integ.claims || 0,
1410
+ conflicts: integ.conflicts || 0,
1411
+ blockers: integ.blockers || 0,
1412
+ detail: integ.detail || null,
1413
+ };
1414
+ }
1275
1415
 
1276
- // Risk notes
1277
- const riskNotes = Array.isArray(safeSummary.riskNotes) ? safeSummary.riskNotes : [];
1416
+ if (role === "documentation") {
1417
+ const docDelta = safeSummary.docDelta || safeSummary.docClosure || {};
1418
+ envelope.documentation = {
1419
+ docClosure: {
1420
+ state: docDelta.state || "no-change",
1421
+ paths: Array.isArray(docDelta.paths) ? docDelta.paths : [],
1422
+ detail: docDelta.detail || null,
1423
+ },
1424
+ };
1425
+ }
1278
1426
 
1279
- // Unresolved blockers
1280
- const unresolvedBlockers = Array.isArray(safeSummary.unresolvedBlockers)
1281
- ? safeSummary.unresolvedBlockers
1282
- : [];
1427
+ if (role === "cont-qa") {
1428
+ const verdict = safeSummary.verdict || {};
1429
+ const gate = safeSummary.gate || {};
1430
+ envelope.contQa = {
1431
+ verdict: {
1432
+ verdict: verdict.verdict || null,
1433
+ detail: verdict.detail || null,
1434
+ },
1435
+ gateClaims: {
1436
+ architecture: gate.architecture || null,
1437
+ integration: gate.integration || null,
1438
+ durability: gate.durability || null,
1439
+ live: gate.live || null,
1440
+ docs: gate.docs || null,
1441
+ },
1442
+ };
1443
+ }
1283
1444
 
1284
- // Docs deltas
1285
- const docsDeltas = [];
1286
- if (docDelta.state) {
1287
- docsDeltas.push({
1288
- state: docDelta.state,
1289
- paths: Array.isArray(docDelta.paths) ? docDelta.paths : [],
1290
- detail: docDelta.detail || null,
1291
- });
1445
+ if (role === "cont-eval") {
1446
+ const evalSection = safeSummary.eval || {};
1447
+ envelope.contEval = {
1448
+ state: evalSection.state || null,
1449
+ targets: evalSection.targets || 0,
1450
+ benchmarks: evalSection.benchmarks || 0,
1451
+ regressions: evalSection.regressions || 0,
1452
+ targetIds: Array.isArray(evalSection.targetIds) ? evalSection.targetIds : [],
1453
+ benchmarkIds: Array.isArray(evalSection.benchmarkIds) ? evalSection.benchmarkIds : [],
1454
+ detail: evalSection.detail || null,
1455
+ };
1292
1456
  }
1293
1457
 
1294
- // Security findings
1295
- const securityFindings = [];
1296
- if (safeSummary.security) {
1297
- securityFindings.push({
1298
- state: safeSummary.security.state || null,
1299
- findings: safeSummary.security.findings || 0,
1300
- approvals: safeSummary.security.approvals || 0,
1301
- detail: safeSummary.security.detail || null,
1302
- });
1458
+ if (role === "security") {
1459
+ const sec = safeSummary.security || {};
1460
+ envelope.security = {
1461
+ state: sec.state || null,
1462
+ findings: sec.findings || 0,
1463
+ approvals: sec.approvals || 0,
1464
+ detail: sec.detail || null,
1465
+ };
1303
1466
  }
1304
1467
 
1305
- // Integration claims
1306
- const integrationClaims = [];
1307
- if (safeSummary.integration) {
1308
- integrationClaims.push({
1309
- state: safeSummary.integration.state || null,
1310
- claims: safeSummary.integration.claims || 0,
1311
- conflicts: safeSummary.integration.conflicts || 0,
1312
- blockers: safeSummary.integration.blockers || 0,
1313
- detail: safeSummary.integration.detail || null,
1314
- });
1468
+ if (role === "deploy") {
1469
+ const dep = safeSummary.deploy || {};
1470
+ envelope.deploy = {
1471
+ state: dep.state || "not_applicable",
1472
+ environment: dep.environment || null,
1473
+ healthCheck: dep.healthCheck || null,
1474
+ rolloutArtifact: dep.rolloutArtifact || null,
1475
+ detail: dep.detail || null,
1476
+ };
1315
1477
  }
1316
1478
 
1317
- return {
1318
- envelopeVersion: 1,
1319
- agentId: safeAgent.agentId || safeSummary.agentId || null,
1320
- exitContract,
1321
- proofArtifacts,
1322
- deliverables,
1323
- components,
1324
- gateClaims,
1325
- validationOutputs,
1326
- riskNotes,
1327
- unresolvedBlockers,
1328
- docsDeltas,
1329
- securityFindings,
1330
- integrationClaims,
1331
- createdAt: toIsoTimestamp(),
1479
+ return envelope;
1480
+ }
1481
+
1482
+ /**
1483
+ * Build a v2 envelope from legacy parsed log markers.
1484
+ * Migration adapter: synthesizes the end-state envelope shape from the
1485
+ * output of buildAgentExecutionSummary (which parses log markers).
1486
+ *
1487
+ * This exists so the gate engine always sees a consistent v2 shape,
1488
+ * even when the agent emitted legacy log markers rather than a structured envelope.
1489
+ *
1490
+ * @param {object} agent - Agent definition (must include .agentId, .role)
1491
+ * @param {object} legacySummary - Result from buildAgentExecutionSummary
1492
+ * @param {object} [options] - { waveNumber, attempt, exitCode }
1493
+ * @returns {object} AgentResultEnvelope (schemaVersion 2)
1494
+ */
1495
+ export function buildEnvelopeFromLegacySignals(agent, legacySummary, options = {}) {
1496
+ const envelope = buildAgentResultEnvelope(agent, legacySummary, options);
1497
+ envelope._synthesizedFromLegacy = true;
1498
+ return envelope;
1499
+ }
1500
+
1501
+ export function buildExecutionSummaryFromEnvelope(envelope, options = {}) {
1502
+ if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
1503
+ return null;
1504
+ }
1505
+ const role = inferEnvelopeRole(options.agent || {}, envelope);
1506
+ const proof = envelope.proof && typeof envelope.proof === "object"
1507
+ ? {
1508
+ state: toLegacyProofState(envelope.proof.state),
1509
+ completion: envelope.proof.completion || null,
1510
+ durability: envelope.proof.durability || null,
1511
+ proof: envelope.proof.proofLevel || null,
1512
+ detail: envelope.proof.detail || null,
1513
+ }
1514
+ : null;
1515
+ const summary = {
1516
+ agentId: envelope.agentId || options.agent?.agentId || null,
1517
+ role,
1518
+ waveNumber: envelope.waveNumber ?? options.waveNumber ?? null,
1519
+ attempt: envelope.attempt ?? options.attempt ?? null,
1520
+ completedAt: envelope.completedAt || null,
1521
+ exitCode: typeof envelope.exitCode === "number" ? envelope.exitCode : 0,
1522
+ logPath: toSummaryRelativePath(options.logPath),
1523
+ reportPath: toSummaryRelativePath(options.reportPath),
1524
+ proof,
1525
+ deliverables: Array.isArray(envelope.deliverables)
1526
+ ? envelope.deliverables.map((deliverable) => ({
1527
+ path: deliverable.path || null,
1528
+ exists: deliverable.exists === true,
1529
+ sha256: deliverable.sha256 || null,
1530
+ }))
1531
+ : [],
1532
+ proofArtifacts: Array.isArray(envelope.proofArtifacts)
1533
+ ? envelope.proofArtifacts.map((artifact) => ({
1534
+ path: artifact.path || null,
1535
+ kind: artifact.kind || null,
1536
+ exists: artifact.exists === true,
1537
+ sha256: artifact.sha256 || null,
1538
+ requiredFor: artifact.requiredFor || null,
1539
+ }))
1540
+ : [],
1541
+ gaps: Array.isArray(envelope.gaps)
1542
+ ? envelope.gaps.map((gap) => ({
1543
+ kind: gap.kind || null,
1544
+ detail: gap.detail || null,
1545
+ }))
1546
+ : [],
1547
+ unresolvedBlockers: Array.isArray(envelope.unresolvedBlockers)
1548
+ ? envelope.unresolvedBlockers.map((blocker) => ({
1549
+ kind: blocker.kind || null,
1550
+ detail: blocker.detail || null,
1551
+ blocking: blocker.blocking ?? null,
1552
+ }))
1553
+ : [],
1554
+ riskNotes: Array.isArray(envelope.riskNotes) ? envelope.riskNotes.slice() : [],
1555
+ facts: Array.isArray(envelope.facts)
1556
+ ? envelope.facts.map((fact) => ({
1557
+ factId: fact.factId || null,
1558
+ kind: fact.kind || null,
1559
+ content: fact.content || null,
1560
+ }))
1561
+ : [],
1332
1562
  };
1563
+
1564
+ if (envelope.implementation) {
1565
+ summary.docDelta = {
1566
+ state: envelope.implementation.docDelta?.state || "none",
1567
+ paths: Array.isArray(envelope.implementation.docDelta?.paths)
1568
+ ? envelope.implementation.docDelta.paths
1569
+ : [],
1570
+ detail: envelope.implementation.docDelta?.detail || null,
1571
+ };
1572
+ summary.components = Array.isArray(envelope.implementation.components)
1573
+ ? envelope.implementation.components.map((component) => ({
1574
+ componentId: component.componentId || null,
1575
+ level: component.level || null,
1576
+ state: component.state || null,
1577
+ detail: component.detail || null,
1578
+ }))
1579
+ : [];
1580
+ }
1581
+
1582
+ if (envelope.documentation?.docClosure) {
1583
+ summary.docClosure = {
1584
+ state: envelope.documentation.docClosure.state || "no-change",
1585
+ paths: Array.isArray(envelope.documentation.docClosure.paths)
1586
+ ? envelope.documentation.docClosure.paths
1587
+ : [],
1588
+ detail: envelope.documentation.docClosure.detail || null,
1589
+ };
1590
+ }
1591
+
1592
+ if (envelope.integration) {
1593
+ summary.integration = {
1594
+ state: envelope.integration.state || null,
1595
+ claims: envelope.integration.claims || 0,
1596
+ conflicts: envelope.integration.conflicts || 0,
1597
+ blockers: envelope.integration.blockers || 0,
1598
+ detail: envelope.integration.detail || null,
1599
+ };
1600
+ }
1601
+
1602
+ if (envelope.contEval) {
1603
+ summary.eval = {
1604
+ state: envelope.contEval.state || null,
1605
+ targets: envelope.contEval.targets || 0,
1606
+ benchmarks: envelope.contEval.benchmarks || 0,
1607
+ regressions: envelope.contEval.regressions || 0,
1608
+ targetIds: Array.isArray(envelope.contEval.targetIds) ? envelope.contEval.targetIds : [],
1609
+ benchmarkIds: Array.isArray(envelope.contEval.benchmarkIds) ? envelope.contEval.benchmarkIds : [],
1610
+ detail: envelope.contEval.detail || null,
1611
+ };
1612
+ }
1613
+
1614
+ if (envelope.contQa) {
1615
+ summary.gate = {
1616
+ architecture: envelope.contQa.gateClaims?.architecture || null,
1617
+ integration: envelope.contQa.gateClaims?.integration || null,
1618
+ durability: envelope.contQa.gateClaims?.durability || null,
1619
+ live: envelope.contQa.gateClaims?.live || null,
1620
+ docs: envelope.contQa.gateClaims?.docs || null,
1621
+ detail: envelope.contQa.gateClaims?.detail || null,
1622
+ };
1623
+ summary.verdict = {
1624
+ verdict: envelope.contQa.verdict?.verdict || null,
1625
+ detail: envelope.contQa.verdict?.detail || null,
1626
+ };
1627
+ }
1628
+
1629
+ if (envelope.security) {
1630
+ summary.security = {
1631
+ state: envelope.security.state || null,
1632
+ findings: envelope.security.findings || 0,
1633
+ approvals: envelope.security.approvals || 0,
1634
+ detail: envelope.security.detail || null,
1635
+ };
1636
+ }
1637
+
1638
+ if (envelope.deploy) {
1639
+ summary.deploy = {
1640
+ state: envelope.deploy.state || "not_applicable",
1641
+ environment: envelope.deploy.environment || null,
1642
+ healthCheck: envelope.deploy.healthCheck || null,
1643
+ rolloutArtifact: envelope.deploy.rolloutArtifact || null,
1644
+ detail: envelope.deploy.detail || null,
1645
+ };
1646
+ }
1647
+
1648
+ return summary;
1333
1649
  }
1334
1650
 
1335
1651
  /**