@chllming/wave-orchestration 0.7.3 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +34 -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 +39 -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/coord-cli.mjs +6 -2
  30. package/scripts/wave-orchestrator/coordination-store.mjs +3 -1
  31. package/scripts/wave-orchestrator/launcher-gates.mjs +79 -11
  32. package/scripts/wave-orchestrator/launcher-retry.mjs +36 -6
  33. package/scripts/wave-orchestrator/launcher.mjs +163 -2
  34. package/scripts/wave-orchestrator/routing-state.mjs +91 -4
  35. package/scripts/wave-orchestrator/task-entity.mjs +425 -51
  36. package/scripts/wave-orchestrator/wave-control-schema.mjs +2 -0
  37. package/scripts/wave-orchestrator/wave-state-reducer.mjs +260 -111
  38. package/skills/README.md +3 -0
  39. package/skills/repo-coding-rules/SKILL.md +1 -1
  40. package/skills/role-cont-qa/SKILL.md +2 -2
  41. package/skills/role-deploy/SKILL.md +1 -1
  42. package/skills/role-documentation/SKILL.md +1 -1
  43. package/skills/role-implementation/SKILL.md +1 -1
  44. package/skills/role-infra/SKILL.md +1 -1
  45. package/skills/role-integration/SKILL.md +2 -2
  46. package/skills/role-security/SKILL.md +1 -1
  47. package/skills/runtime-claude/SKILL.md +1 -1
  48. package/skills/runtime-codex/SKILL.md +1 -1
  49. package/skills/runtime-opencode/SKILL.md +1 -1
  50. package/skills/wave-core/SKILL.md +14 -6
  51. package/skills/wave-core/references/marker-syntax.md +1 -1
@@ -0,0 +1,487 @@
1
+ import crypto from "node:crypto";
2
+ import { toIsoTimestamp } from "./shared.mjs";
3
+
4
+ // ── Contradiction Entity (P2-13) ──
5
+ // End-state schema from docs/plans/end-state-architecture.md
6
+
7
+ export const CONTRADICTION_KINDS = new Set([
8
+ "proof_conflict",
9
+ "integration_conflict",
10
+ "claim_conflict",
11
+ "evidence_conflict",
12
+ "component_conflict",
13
+ ]);
14
+
15
+ export const CONTRADICTION_STATUSES = new Set([
16
+ "detected",
17
+ "acknowledged",
18
+ "repair_in_progress",
19
+ "resolved",
20
+ "waived",
21
+ ]);
22
+
23
+ export const CONTRADICTION_VALID_TRANSITIONS = {
24
+ detected: ["acknowledged", "resolved", "waived"],
25
+ acknowledged: ["repair_in_progress", "resolved", "waived"],
26
+ repair_in_progress: ["resolved", "waived"],
27
+ resolved: [],
28
+ waived: [],
29
+ };
30
+
31
+ export const CONTRADICTION_SEVERITIES = new Set(["blocking", "advisory"]);
32
+
33
+ export const CONTRADICTION_RESOLUTION_KINDS = new Set([
34
+ "party_accepted",
35
+ "all_revised",
36
+ "irrelevant",
37
+ "waived",
38
+ "repair_completed",
39
+ ]);
40
+
41
+ function stableId(prefix) {
42
+ return `${prefix}-${crypto.randomBytes(4).toString("hex")}`;
43
+ }
44
+
45
+ function normalizeText(value, fallback = "") {
46
+ const normalized = String(value ?? "").trim();
47
+ return normalized || fallback;
48
+ }
49
+
50
+ function normalizeStringArray(values) {
51
+ if (!Array.isArray(values)) {
52
+ return [];
53
+ }
54
+ return Array.from(
55
+ new Set(
56
+ values
57
+ .map((value) => normalizeText(value))
58
+ .filter(Boolean),
59
+ ),
60
+ );
61
+ }
62
+
63
+ function normalizeParty(raw) {
64
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
65
+ return null;
66
+ }
67
+ const agentId = normalizeText(raw.agentId);
68
+ if (!agentId) {
69
+ return null;
70
+ }
71
+ return {
72
+ agentId,
73
+ claim: normalizeText(raw.claim),
74
+ evidence: normalizeText(raw.evidence),
75
+ };
76
+ }
77
+
78
+ function normalizeParties(rawParties) {
79
+ if (!Array.isArray(rawParties)) {
80
+ return [];
81
+ }
82
+ const result = [];
83
+ for (const raw of rawParties) {
84
+ const party = normalizeParty(raw);
85
+ if (party) {
86
+ result.push(party);
87
+ }
88
+ }
89
+ return result;
90
+ }
91
+
92
+ function normalizeRepairWork(raw) {
93
+ if (!Array.isArray(raw)) {
94
+ return null;
95
+ }
96
+ const result = [];
97
+ for (const item of raw) {
98
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
99
+ continue;
100
+ }
101
+ const taskId = normalizeText(item.taskId);
102
+ if (!taskId) {
103
+ continue;
104
+ }
105
+ result.push({
106
+ taskId,
107
+ status: normalizeText(item.status, "pending"),
108
+ });
109
+ }
110
+ return result.length > 0 ? result : null;
111
+ }
112
+
113
+ function normalizeResolution(raw) {
114
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
115
+ return null;
116
+ }
117
+ const kind = normalizeText(raw.kind);
118
+ if (!kind) {
119
+ return null;
120
+ }
121
+ if (!CONTRADICTION_RESOLUTION_KINDS.has(kind)) {
122
+ throw new Error(
123
+ `resolution.kind must be one of ${[...CONTRADICTION_RESOLUTION_KINDS].join(", ")} (got: ${kind})`,
124
+ );
125
+ }
126
+ return {
127
+ kind,
128
+ detail: normalizeText(raw.detail),
129
+ evidence: normalizeText(raw.evidence),
130
+ };
131
+ }
132
+
133
+ export function normalizeContradiction(raw, defaults = {}) {
134
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
135
+ throw new Error("Contradiction must be an object");
136
+ }
137
+
138
+ const now = toIsoTimestamp();
139
+ const contradictionId =
140
+ normalizeText(raw.contradictionId) ||
141
+ normalizeText(defaults.contradictionId) ||
142
+ stableId("contra");
143
+
144
+ const kind = normalizeText(raw.kind) || normalizeText(defaults.kind) || "claim_conflict";
145
+ if (!CONTRADICTION_KINDS.has(kind)) {
146
+ throw new Error(
147
+ `kind must be one of ${[...CONTRADICTION_KINDS].join(", ")} (got: ${kind})`,
148
+ );
149
+ }
150
+
151
+ const status = normalizeText(raw.status) || normalizeText(defaults.status) || "detected";
152
+ if (!CONTRADICTION_STATUSES.has(status)) {
153
+ throw new Error(
154
+ `status must be one of ${[...CONTRADICTION_STATUSES].join(", ")} (got: ${status})`,
155
+ );
156
+ }
157
+
158
+ const severity = normalizeText(raw.severity) || normalizeText(defaults.severity) || "advisory";
159
+ if (!CONTRADICTION_SEVERITIES.has(severity)) {
160
+ throw new Error(
161
+ `severity must be one of ${[...CONTRADICTION_SEVERITIES].join(", ")} (got: ${severity})`,
162
+ );
163
+ }
164
+
165
+ const waveNumber = Number.isFinite(raw.waveNumber)
166
+ ? raw.waveNumber
167
+ : Number.isFinite(defaults.waveNumber)
168
+ ? defaults.waveNumber
169
+ : null;
170
+ const lane = normalizeText(raw.lane) || normalizeText(defaults.lane) || null;
171
+
172
+ const resolution = normalizeResolution(raw.resolution);
173
+
174
+ return {
175
+ contradictionId,
176
+ waveNumber,
177
+ lane,
178
+ kind,
179
+ status,
180
+ severity,
181
+ reportedBy: normalizeText(raw.reportedBy) || normalizeText(defaults.reportedBy) || "system",
182
+ reportedAt: normalizeText(raw.reportedAt) || normalizeText(defaults.reportedAt) || now,
183
+ resolvedBy: normalizeText(raw.resolvedBy) || null,
184
+ resolvedAt: normalizeText(raw.resolvedAt) || null,
185
+ parties: normalizeParties(raw.parties || defaults.parties),
186
+ affectedTasks: normalizeStringArray(raw.affectedTasks || defaults.affectedTasks),
187
+ affectedFacts: normalizeStringArray(raw.affectedFacts || defaults.affectedFacts),
188
+ repairWork: normalizeRepairWork(raw.repairWork),
189
+ resolution,
190
+ supersedes: normalizeText(raw.supersedes) || null,
191
+ impactedGates: normalizeStringArray(raw.impactedGates || defaults.impactedGates),
192
+ updatedAt: normalizeText(raw.updatedAt) || now,
193
+ };
194
+ }
195
+
196
+ export function transitionContradictionStatus(currentStatus, targetStatus) {
197
+ if (!CONTRADICTION_STATUSES.has(currentStatus)) {
198
+ throw new Error(`Invalid contradiction status: ${currentStatus}`);
199
+ }
200
+ if (!CONTRADICTION_STATUSES.has(targetStatus)) {
201
+ throw new Error(`Invalid target contradiction status: ${targetStatus}`);
202
+ }
203
+ const allowed = CONTRADICTION_VALID_TRANSITIONS[currentStatus];
204
+ if (!allowed || !allowed.includes(targetStatus)) {
205
+ throw new Error(
206
+ `Invalid contradiction transition from ${currentStatus} to ${targetStatus}`,
207
+ );
208
+ }
209
+ return targetStatus;
210
+ }
211
+
212
+ export function detectContradictions(coordinationState, options = {}) {
213
+ if (!coordinationState || typeof coordinationState !== "object") {
214
+ return [];
215
+ }
216
+
217
+ const records = [];
218
+ for (const category of [
219
+ "claims",
220
+ "evidence",
221
+ "decisions",
222
+ "clarifications",
223
+ "blockers",
224
+ "handoffs",
225
+ "requests",
226
+ "humanFeedback",
227
+ "humanEscalations",
228
+ ]) {
229
+ const items = coordinationState[category];
230
+ if (!Array.isArray(items)) {
231
+ continue;
232
+ }
233
+ for (const record of items) {
234
+ if (!record || typeof record !== "object") {
235
+ continue;
236
+ }
237
+ const kind = normalizeText(record.kind);
238
+ if (kind === "claim" || kind === "evidence" || kind === "decision") {
239
+ records.push(record);
240
+ }
241
+ }
242
+ }
243
+
244
+ // Also scan flat records array if present
245
+ if (Array.isArray(coordinationState.records)) {
246
+ for (const record of coordinationState.records) {
247
+ if (!record || typeof record !== "object") {
248
+ continue;
249
+ }
250
+ const kind = normalizeText(record.kind);
251
+ if (kind === "claim" || kind === "evidence" || kind === "decision") {
252
+ records.push(record);
253
+ }
254
+ }
255
+ }
256
+
257
+ if (records.length < 2) {
258
+ return [];
259
+ }
260
+
261
+ // Group by subject: component, path, or summary keyword
262
+ function subjectKey(record) {
263
+ if (record.component) {
264
+ return `component:${normalizeText(record.component)}`;
265
+ }
266
+ const refs = Array.isArray(record.artifactRefs) ? record.artifactRefs : [];
267
+ if (refs.length > 0) {
268
+ return `path:${normalizeText(refs[0])}`;
269
+ }
270
+ const targets = Array.isArray(record.targets) ? record.targets : [];
271
+ if (targets.length > 0) {
272
+ return `target:${normalizeText(targets[0])}`;
273
+ }
274
+ const summary = normalizeText(record.summary).toLowerCase();
275
+ if (summary) {
276
+ return `summary:${summary}`;
277
+ }
278
+ return null;
279
+ }
280
+
281
+ const groups = new Map();
282
+ for (const record of records) {
283
+ const key = subjectKey(record);
284
+ if (!key) {
285
+ continue;
286
+ }
287
+ if (!groups.has(key)) {
288
+ groups.set(key, []);
289
+ }
290
+ groups.get(key).push(record);
291
+ }
292
+
293
+ const now = toIsoTimestamp();
294
+ const contradictions = [];
295
+ const severityOverride = options.severity || null;
296
+ const impactedGatesDefault = options.impactedGates || [];
297
+ const defaultKind = options.kind || "claim_conflict";
298
+
299
+ for (const [, group] of groups) {
300
+ if (group.length < 2) {
301
+ continue;
302
+ }
303
+
304
+ for (let i = 0; i < group.length; i++) {
305
+ for (let j = i + 1; j < group.length; j++) {
306
+ const a = group[i];
307
+ const b = group[j];
308
+
309
+ const agentA = normalizeText(a.agentId);
310
+ const agentB = normalizeText(b.agentId);
311
+ if (!agentA || !agentB || agentA === agentB) {
312
+ continue;
313
+ }
314
+
315
+ const stateA = normalizeText(a.state || a.status);
316
+ const stateB = normalizeText(b.state || b.status);
317
+ const hasConflictingStates =
318
+ stateA && stateB && stateA !== stateB;
319
+
320
+ const contentA = normalizeText(a.detail || a.summary);
321
+ const contentB = normalizeText(b.detail || b.summary);
322
+
323
+ if (!hasConflictingStates) {
324
+ continue;
325
+ }
326
+
327
+ contradictions.push(
328
+ normalizeContradiction({
329
+ status: "detected",
330
+ kind: defaultKind,
331
+ parties: [
332
+ { agentId: agentA, claim: contentA, evidence: "" },
333
+ { agentId: agentB, claim: contentB, evidence: "" },
334
+ ],
335
+ severity: severityOverride || "advisory",
336
+ impactedGates: impactedGatesDefault,
337
+ reportedBy: "system",
338
+ reportedAt: now,
339
+ }),
340
+ );
341
+ }
342
+ }
343
+ }
344
+
345
+ return contradictions;
346
+ }
347
+
348
+ export function resolveContradiction(contradiction, resolution) {
349
+ if (!contradiction || typeof contradiction !== "object") {
350
+ throw new Error("Contradiction must be an object");
351
+ }
352
+ if (!resolution || typeof resolution !== "object") {
353
+ throw new Error("Resolution must be an object");
354
+ }
355
+
356
+ const kind = normalizeText(resolution.kind);
357
+ if (!CONTRADICTION_RESOLUTION_KINDS.has(kind)) {
358
+ throw new Error(
359
+ `resolution.kind must be one of ${[...CONTRADICTION_RESOLUTION_KINDS].join(", ")} (got: ${kind})`,
360
+ );
361
+ }
362
+
363
+ const now = toIsoTimestamp();
364
+ return {
365
+ ...contradiction,
366
+ status: "resolved",
367
+ resolution: {
368
+ kind,
369
+ detail: normalizeText(resolution.detail),
370
+ evidence: normalizeText(resolution.evidence),
371
+ },
372
+ resolvedBy: normalizeText(resolution.resolvedBy) || null,
373
+ resolvedAt: now,
374
+ updatedAt: now,
375
+ };
376
+ }
377
+
378
+ export function waiveContradiction(contradiction, reason = "") {
379
+ if (!contradiction || typeof contradiction !== "object") {
380
+ throw new Error("Contradiction must be an object");
381
+ }
382
+ const now = toIsoTimestamp();
383
+ return {
384
+ ...contradiction,
385
+ status: "waived",
386
+ resolution: {
387
+ kind: "waived",
388
+ detail: normalizeText(reason),
389
+ evidence: "",
390
+ },
391
+ updatedAt: now,
392
+ };
393
+ }
394
+
395
+ export function acknowledgeContradiction(contradiction, acknowledgedBy) {
396
+ if (!contradiction || typeof contradiction !== "object") {
397
+ throw new Error("Contradiction must be an object");
398
+ }
399
+ const now = toIsoTimestamp();
400
+ return {
401
+ ...contradiction,
402
+ status: "acknowledged",
403
+ updatedAt: now,
404
+ };
405
+ }
406
+
407
+ export function startRepair(contradiction, repairTasks) {
408
+ if (!contradiction || typeof contradiction !== "object") {
409
+ throw new Error("Contradiction must be an object");
410
+ }
411
+ const now = toIsoTimestamp();
412
+ const repairWork = normalizeRepairWork(repairTasks) || [];
413
+ return {
414
+ ...contradiction,
415
+ status: "repair_in_progress",
416
+ repairWork: repairWork.length > 0 ? repairWork : null,
417
+ updatedAt: now,
418
+ };
419
+ }
420
+
421
+ export function unresolvedContradictions(contradictions) {
422
+ if (!Array.isArray(contradictions)) {
423
+ return [];
424
+ }
425
+ return contradictions.filter(
426
+ (c) =>
427
+ c &&
428
+ typeof c === "object" &&
429
+ c.status !== "resolved" &&
430
+ c.status !== "waived",
431
+ );
432
+ }
433
+
434
+ export function contradictionsBlockingGate(contradictions, gateName) {
435
+ if (!Array.isArray(contradictions) || !gateName) {
436
+ return [];
437
+ }
438
+ return contradictions.filter(
439
+ (c) =>
440
+ c &&
441
+ typeof c === "object" &&
442
+ c.status !== "resolved" &&
443
+ c.status !== "waived" &&
444
+ c.severity === "blocking" &&
445
+ Array.isArray(c.impactedGates) &&
446
+ c.impactedGates.includes(gateName),
447
+ );
448
+ }
449
+
450
+ export function materializeContradictionsFromControlPlaneEvents(controlPlaneEvents = []) {
451
+ const contradictions = new Map();
452
+ for (const event of Array.isArray(controlPlaneEvents) ? controlPlaneEvents : []) {
453
+ if (event?.entityType !== "contradiction") {
454
+ continue;
455
+ }
456
+ const existing = contradictions.get(event.entityId) || null;
457
+ const data = event.data && typeof event.data === "object" ? event.data : {};
458
+ contradictions.set(
459
+ event.entityId,
460
+ normalizeContradiction(
461
+ {
462
+ ...(existing || {}),
463
+ ...data,
464
+ contradictionId: event.entityId,
465
+ waveNumber: event.wave ?? existing?.waveNumber ?? null,
466
+ lane: event.lane || existing?.lane || null,
467
+ reportedAt: data.reportedAt || existing?.reportedAt || event.recordedAt,
468
+ updatedAt: data.updatedAt || event.recordedAt || existing?.updatedAt || null,
469
+ reportedBy: data.reportedBy || existing?.reportedBy || "system",
470
+ severity: data.severity || existing?.severity || "blocking",
471
+ impactedGates: data.impactedGates || existing?.impactedGates || ["integrationBarrier"],
472
+ },
473
+ {
474
+ contradictionId: event.entityId,
475
+ waveNumber: event.wave ?? null,
476
+ lane: event.lane || null,
477
+ reportedAt: event.recordedAt || null,
478
+ updatedAt: event.recordedAt || null,
479
+ reportedBy: "system",
480
+ severity: "blocking",
481
+ impactedGates: ["integrationBarrier"],
482
+ },
483
+ ),
484
+ );
485
+ }
486
+ return contradictions;
487
+ }
@@ -69,7 +69,7 @@ function parseArgs(argv) {
69
69
  priority: "normal",
70
70
  dependsOn: [],
71
71
  artifactRefs: [],
72
- status: "open",
72
+ status: "",
73
73
  id: "",
74
74
  to: "",
75
75
  response: "",
@@ -309,6 +309,10 @@ function appendCoordinationStatusUpdate(logPath, record, status, options = {}) {
309
309
  });
310
310
  }
311
311
 
312
+ function defaultStatusForKind(kind) {
313
+ return String(kind || "").trim().toLowerCase() === "resolved-by-policy" ? "resolved" : "open";
314
+ }
315
+
312
316
  function appendTriageEscalationUpdateIfPresent(lanePaths, waveNumber, record) {
313
317
  const triagePath = coordinationTriagePath(lanePaths, waveNumber);
314
318
  if (!fs.existsSync(triagePath) || record?.kind !== "human-escalation") {
@@ -436,7 +440,7 @@ export async function runCoordinationCli(argv) {
436
440
  priority: options.priority,
437
441
  dependsOn: options.dependsOn,
438
442
  artifactRefs: options.artifactRefs,
439
- status: options.status,
443
+ status: options.status || defaultStatusForKind(options.kind),
440
444
  source: "agent",
441
445
  });
442
446
  console.log(JSON.stringify(record, null, 2));
@@ -88,7 +88,9 @@ export function normalizeCoordinationRecord(rawRecord, defaults = {}) {
88
88
  const lane = normalizeString(rawRecord.lane || defaults.lane);
89
89
  const wave = Number.parseInt(String(rawRecord.wave ?? defaults.wave ?? ""), 10);
90
90
  const agentId = normalizeString(rawRecord.agentId || defaults.agentId);
91
- const status = normalizeString(rawRecord.status || defaults.status || "open").toLowerCase();
91
+ const status = normalizeString(
92
+ rawRecord.status || defaults.status || (kind === "resolved-by-policy" ? "resolved" : "open"),
93
+ ).toLowerCase();
92
94
  const priority = normalizeString(rawRecord.priority || defaults.priority || "normal").toLowerCase();
93
95
  const confidence = normalizeString(rawRecord.confidence || defaults.confidence || "medium").toLowerCase();
94
96
  const createdAt = normalizeString(rawRecord.createdAt || defaults.createdAt || now);
@@ -3,7 +3,10 @@ import path from "node:path";
3
3
  import {
4
4
  agentSummaryPathFromStatusPath,
5
5
  buildAgentExecutionSummary,
6
+ buildEnvelopeFromLegacySignals,
7
+ buildExecutionSummaryFromEnvelope,
6
8
  readAgentExecutionSummary,
9
+ readAgentResultEnvelope,
7
10
  validateContQaSummary,
8
11
  validateContEvalSummary,
9
12
  validateImplementationSummary,
@@ -11,6 +14,7 @@ import {
11
14
  validateSecuritySummary,
12
15
  validateIntegrationSummary,
13
16
  writeAgentExecutionSummary,
17
+ writeAgentResultEnvelope,
14
18
  } from "./agent-state.mjs";
15
19
  import {
16
20
  REPO_ROOT,
@@ -20,32 +24,55 @@ import {
20
24
  parseVerdictFromText,
21
25
  REPORT_VERDICT_REGEX,
22
26
  WAVE_VERDICT_REGEX,
23
- toIsoTimestamp,
24
27
  writeJsonAtomic,
25
28
  } from "./shared.mjs";
26
29
  import {
27
30
  isSecurityReviewAgent,
28
31
  resolveSecurityReviewReportPath,
29
- isContEvalImplementationOwningAgent,
30
32
  isContEvalReportOnlyAgent,
31
33
  } from "./role-helpers.mjs";
32
34
  import {
33
35
  augmentSummaryWithProofRegistry,
34
36
  } from "./proof-registry.mjs";
35
37
  import {
36
- agentRequiresProofCentricValidation,
37
- waveRequiresProofCentricValidation,
38
38
  validateWaveComponentPromotions,
39
39
  validateWaveComponentMatrixCurrentLevels,
40
40
  } from "./wave-files.mjs";
41
41
  import {
42
42
  isOpenCoordinationStatus,
43
43
  openClarificationLinkedRequests,
44
- buildCoordinationResponseMetrics,
45
44
  } from "./coordination-store.mjs";
46
- import {
47
- parseStructuredSignalsFromLog,
48
- } from "./dashboard-state.mjs";
45
+ import { contradictionsBlockingGate } from "./contradiction-entity.mjs";
46
+
47
+ function contradictionList(value) {
48
+ if (value instanceof Map) {
49
+ return [...value.values()];
50
+ }
51
+ if (Array.isArray(value)) {
52
+ return value;
53
+ }
54
+ if (value && typeof value === "object") {
55
+ return Object.values(value);
56
+ }
57
+ return [];
58
+ }
59
+
60
+ function readIntegrationContradictionBarrier(derivedState, agentId, logPath) {
61
+ const blockingContradictions = contradictionsBlockingGate(
62
+ contradictionList(derivedState?.contradictions),
63
+ "integrationBarrier",
64
+ );
65
+ if (blockingContradictions.length === 0) {
66
+ return null;
67
+ }
68
+ return {
69
+ ok: false,
70
+ agentId,
71
+ statusCode: "integration-contradiction-open",
72
+ detail: `Unresolved blocking contradictions remain (${blockingContradictions.map((c) => c.contradictionId).join(", ")}).`,
73
+ logPath,
74
+ };
75
+ }
49
76
 
50
77
  function resolveRunReportPath(wave, runInfo) {
51
78
  if (!wave || !runInfo?.agent) {
@@ -77,6 +104,14 @@ export function materializeAgentExecutionSummaryForRun(wave, runInfo) {
77
104
  reportPath,
78
105
  });
79
106
  writeAgentExecutionSummary(runInfo.statusPath, summary);
107
+ writeAgentResultEnvelope(
108
+ runInfo.statusPath,
109
+ buildEnvelopeFromLegacySignals(runInfo.agent, summary, {
110
+ waveNumber: wave?.wave ?? null,
111
+ attempt: statusRecord.attempt ?? null,
112
+ exitCode: typeof statusRecord.code === "number" ? statusRecord.code : 0,
113
+ }),
114
+ );
80
115
  if (runInfo?.previewPath && fs.existsSync(runInfo.previewPath)) {
81
116
  const previewPayload = readJsonOrNull(runInfo.previewPath);
82
117
  if (previewPayload && typeof previewPayload === "object") {
@@ -110,6 +145,7 @@ export function readRunExecutionSummary(runInfo, wave = null) {
110
145
  const applyProofRegistry = (summary) =>
111
146
  runInfo?.proofRegistry ? augmentSummaryWithProofRegistry(runInfo.agent, summary, runInfo.proofRegistry) : summary;
112
147
  const statusRecord = runInfo?.statusPath ? readStatusRecordIfPresent(runInfo.statusPath) : null;
148
+ const reportPath = wave && runInfo?.logPath ? resolveRunReportPath(wave, runInfo) : null;
113
149
  const summaryReadOptions =
114
150
  wave && runInfo?.logPath
115
151
  ? {
@@ -117,11 +153,27 @@ export function readRunExecutionSummary(runInfo, wave = null) {
117
153
  statusPath: runInfo.statusPath,
118
154
  statusRecord,
119
155
  logPath: runInfo.logPath,
120
- reportPath: resolveRunReportPath(wave, runInfo),
156
+ reportPath,
121
157
  }
122
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,
165
+ };
123
166
  if (runInfo?.summary && typeof runInfo.summary === "object") {
124
- return applyProofRegistry(runInfo.summary);
167
+ const summary = runInfo.summary.schemaVersion === 2
168
+ ? buildExecutionSummaryFromEnvelope(runInfo.summary, envelopeReadOptions)
169
+ : runInfo.summary;
170
+ return applyProofRegistry(summary);
171
+ }
172
+ if (runInfo?.statusPath && fs.existsSync(runInfo.statusPath)) {
173
+ const envelope = readAgentResultEnvelope(runInfo.statusPath);
174
+ if (envelope) {
175
+ return applyProofRegistry(buildExecutionSummaryFromEnvelope(envelope, envelopeReadOptions));
176
+ }
125
177
  }
126
178
  if (runInfo?.summaryPath && fs.existsSync(runInfo.summaryPath)) {
127
179
  return applyProofRegistry(readAgentExecutionSummary(runInfo.summaryPath, summaryReadOptions));
@@ -569,6 +621,14 @@ export function readWaveIntegrationBarrier(wave, agentRuns, derivedState, option
569
621
  logPath: markerGate.logPath,
570
622
  };
571
623
  }
624
+ const contradictionBarrier = readIntegrationContradictionBarrier(
625
+ derivedState,
626
+ markerGate.agentId,
627
+ markerGate.logPath,
628
+ );
629
+ if (contradictionBarrier) {
630
+ return contradictionBarrier;
631
+ }
572
632
  return markerGate;
573
633
  }
574
634
 
@@ -893,7 +953,7 @@ export function readWaveComponentGatePure(wave, agentResults, options = {}) {
893
953
  }
894
954
  const componentState = analyzePromotedComponentOwnersPure(validation.componentId, agents, summariesByAgentId);
895
955
  return {
896
- ok: false, agentId: componentState.ownerAgentIds[0] || null,
956
+ ok: false, agentId: componentState.waitingOnAgentIds[0] || componentState.ownerAgentIds[0] || null,
897
957
  componentId: validation.componentId || null,
898
958
  statusCode: validation.statusCode, detail: validation.detail, logPath: null,
899
959
  ownerAgentIds: componentState.ownerAgentIds,
@@ -1020,6 +1080,14 @@ export function buildGateSnapshotPure({ wave, agentResults, derivedState, valida
1020
1080
  detail: integrationSummary.detail || `Integration summary still reports ${integrationSummary.recommendation}.`,
1021
1081
  logPath: integrationMarkerGate.logPath };
1022
1082
  }
1083
+ const contradictionBarrier = readIntegrationContradictionBarrier(
1084
+ derivedState,
1085
+ integrationMarkerGate.agentId,
1086
+ integrationMarkerGate.logPath,
1087
+ );
1088
+ if (contradictionBarrier) {
1089
+ return contradictionBarrier;
1090
+ }
1023
1091
  return integrationMarkerGate;
1024
1092
  })();
1025
1093
  const documentationGate = readWaveDocumentationGatePure(wave, agentResults, {