@chllming/wave-orchestration 0.8.3 → 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 (39) hide show
  1. package/CHANGELOG.md +19 -0
  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 +15 -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 -62
  10. package/docs/plans/wave-orchestrator.md +37 -11
  11. package/docs/reference/cli-reference.md +34 -14
  12. package/docs/reference/coordination-and-closure.md +19 -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 +20 -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/{launcher-derived-state.mjs → derived-state-engine.mjs} +34 -146
  21. package/scripts/wave-orchestrator/{launcher-gates.mjs → gate-engine.mjs} +395 -139
  22. package/scripts/wave-orchestrator/human-input-resolution.mjs +14 -10
  23. package/scripts/wave-orchestrator/human-input-workflow.mjs +104 -0
  24. package/scripts/wave-orchestrator/implementation-engine.mjs +120 -0
  25. package/scripts/wave-orchestrator/launcher-runtime.mjs +5 -6
  26. package/scripts/wave-orchestrator/launcher.mjs +271 -724
  27. package/scripts/wave-orchestrator/projection-writer.mjs +256 -0
  28. package/scripts/wave-orchestrator/reconcile-format.mjs +32 -0
  29. package/scripts/wave-orchestrator/reducer-snapshot.mjs +297 -0
  30. package/scripts/wave-orchestrator/replay.mjs +3 -1
  31. package/scripts/wave-orchestrator/result-envelope.mjs +589 -0
  32. package/scripts/wave-orchestrator/retry-control.mjs +5 -0
  33. package/scripts/wave-orchestrator/{launcher-retry.mjs → retry-engine.mjs} +267 -18
  34. package/scripts/wave-orchestrator/role-helpers.mjs +51 -0
  35. package/scripts/wave-orchestrator/{launcher-supervisor.mjs → session-supervisor.mjs} +178 -103
  36. package/scripts/wave-orchestrator/shared.mjs +1 -0
  37. package/scripts/wave-orchestrator/traces.mjs +10 -1
  38. package/scripts/wave-orchestrator/wave-files.mjs +11 -9
  39. package/scripts/wave-orchestrator/wave-state-reducer.mjs +52 -5
@@ -0,0 +1,256 @@
1
+ import path from "node:path";
2
+ import {
3
+ writeAssignmentSnapshot,
4
+ writeDependencySnapshot,
5
+ } from "./artifact-schemas.mjs";
6
+ import {
7
+ syncGlobalWaveFromWaveDashboard,
8
+ writeGlobalDashboard,
9
+ writeWaveDashboard,
10
+ } from "./dashboard-state.mjs";
11
+ import { writeDocsQueue } from "./docs-queue.mjs";
12
+ import { writeWaveLedger } from "./ledger.mjs";
13
+ import { writeDependencySnapshotMarkdown } from "./routing-state.mjs";
14
+ import {
15
+ writeCompiledInbox,
16
+ writeCoordinationBoardProjection,
17
+ writeJsonArtifact,
18
+ } from "./coordination-store.mjs";
19
+ import { parseStructuredSignalsFromLog } from "./dashboard-state.mjs";
20
+ import { readRunExecutionSummary } from "./gate-engine.mjs";
21
+ import { waveProofRegistryPath } from "./proof-registry.mjs";
22
+ import { relaunchReasonBuckets, writeWaveRelaunchPlan } from "./retry-engine.mjs";
23
+ import { toIsoTimestamp, writeTextAtomic } from "./shared.mjs";
24
+ import { buildQualityMetrics, writeTraceBundle } from "./traces.mjs";
25
+
26
+ export function writeWaveDerivedProjections({ lanePaths, wave, derivedState }) {
27
+ if (!derivedState) {
28
+ return null;
29
+ }
30
+ writeAssignmentSnapshot(derivedState.assignmentSnapshotPath, derivedState.capabilityAssignments, {
31
+ lane: lanePaths.lane,
32
+ wave: wave.wave,
33
+ });
34
+ writeDependencySnapshot(
35
+ derivedState.dependencySnapshotPath,
36
+ derivedState.dependencySnapshot,
37
+ {
38
+ lane: lanePaths.lane,
39
+ wave: wave.wave,
40
+ },
41
+ );
42
+ writeDependencySnapshotMarkdown(
43
+ derivedState.dependencySnapshotMarkdownPath,
44
+ derivedState.dependencySnapshot,
45
+ );
46
+ writeDocsQueue(derivedState.docsQueuePath, derivedState.docsQueue);
47
+ writeJsonArtifact(derivedState.securitySummaryPath, derivedState.securitySummary);
48
+ writeTextAtomic(
49
+ derivedState.securityMarkdownPath,
50
+ `${derivedState.securitySummary ? renderWaveSecuritySummaryMarkdown(derivedState.securitySummary) : ""}\n`,
51
+ );
52
+ writeJsonArtifact(derivedState.integrationSummaryPath, derivedState.integrationSummary);
53
+ writeTextAtomic(
54
+ derivedState.integrationMarkdownPath,
55
+ `${derivedState.integrationSummary ? renderIntegrationSummaryMarkdown(derivedState.integrationSummary) : ""}\n`,
56
+ );
57
+ writeWaveLedger(derivedState.ledgerPath, derivedState.ledger);
58
+ writeCompiledInbox(derivedState.sharedSummaryPath, derivedState.sharedSummaryText);
59
+ for (const inbox of Object.values(derivedState.inboxesByAgentId || {})) {
60
+ writeCompiledInbox(inbox.path, inbox.text);
61
+ }
62
+ writeCoordinationBoardProjection(derivedState.messageBoardPath, {
63
+ wave: wave.wave,
64
+ waveFile: wave.file,
65
+ agents: wave.agents,
66
+ state: derivedState.coordinationState,
67
+ capabilityAssignments: derivedState.capabilityAssignments,
68
+ dependencySnapshot: derivedState.dependencySnapshot,
69
+ });
70
+ return derivedState;
71
+ }
72
+
73
+ export function writeDashboardProjections({
74
+ lanePaths,
75
+ globalDashboard = null,
76
+ dashboardState = null,
77
+ dashboardPath = null,
78
+ }) {
79
+ if (dashboardState && dashboardPath) {
80
+ writeWaveDashboard(dashboardPath, dashboardState);
81
+ }
82
+ if (globalDashboard && dashboardState) {
83
+ syncGlobalWaveFromWaveDashboard(globalDashboard, dashboardState);
84
+ }
85
+ if (globalDashboard) {
86
+ writeGlobalDashboard(lanePaths.globalDashboardPath, globalDashboard);
87
+ }
88
+ }
89
+
90
+ export function writeWaveAttemptTraceProjection({
91
+ lanePaths,
92
+ wave,
93
+ attempt,
94
+ launcherOptions,
95
+ derivedState,
96
+ manifest,
97
+ agentRuns,
98
+ gateSnapshot,
99
+ tracesDir,
100
+ }) {
101
+ const structuredSignals = Object.fromEntries(
102
+ agentRuns.map((run) => [run.agent.agentId, parseStructuredSignalsFromLog(run.logPath)]),
103
+ );
104
+ const summariesByAgentId = Object.fromEntries(
105
+ agentRuns
106
+ .map((run) => [run.agent.agentId, readRunExecutionSummary(run, wave, { mode: "compat" })])
107
+ .filter(([, summary]) => summary),
108
+ );
109
+ const traceDir = writeTraceBundle({
110
+ tracesDir,
111
+ lanePaths,
112
+ launcherOptions,
113
+ wave,
114
+ attempt,
115
+ manifest,
116
+ coordinationLogPath: derivedState.coordinationLogPath,
117
+ coordinationState: derivedState.coordinationState,
118
+ ledger: derivedState.ledger,
119
+ docsQueue: derivedState.docsQueue,
120
+ capabilityAssignments: derivedState.capabilityAssignments,
121
+ dependencySnapshot: derivedState.dependencySnapshot,
122
+ securitySummary: derivedState.securitySummary,
123
+ integrationSummary: derivedState.integrationSummary,
124
+ integrationMarkdownPath: derivedState.integrationMarkdownPath,
125
+ proofRegistryPath: lanePaths.proofDir ? waveProofRegistryPath(lanePaths, wave.wave) : null,
126
+ controlPlanePath: path.join(lanePaths.controlPlaneDir, `wave-${wave.wave}.jsonl`),
127
+ clarificationTriage: derivedState.clarificationTriage,
128
+ agentRuns,
129
+ structuredSignals,
130
+ gateSnapshot,
131
+ quality: buildQualityMetrics({
132
+ tracesDir,
133
+ wave,
134
+ coordinationState: derivedState.coordinationState,
135
+ integrationSummary: derivedState.integrationSummary,
136
+ ledger: derivedState.ledger,
137
+ docsQueue: derivedState.docsQueue,
138
+ capabilityAssignments: derivedState.capabilityAssignments,
139
+ dependencySnapshot: derivedState.dependencySnapshot,
140
+ summariesByAgentId,
141
+ agentRuns,
142
+ gateSnapshot,
143
+ attempt,
144
+ coordinationLogPath: derivedState.coordinationLogPath,
145
+ }),
146
+ });
147
+ return {
148
+ traceDir,
149
+ structuredSignals,
150
+ summariesByAgentId,
151
+ };
152
+ }
153
+
154
+ export function writeWaveRelaunchProjection({
155
+ lanePaths,
156
+ wave,
157
+ attempt,
158
+ runs,
159
+ failures,
160
+ derivedState,
161
+ }) {
162
+ writeWaveRelaunchPlan(lanePaths, wave.wave, {
163
+ wave: wave.wave,
164
+ attempt,
165
+ phase: derivedState?.ledger?.phase || null,
166
+ selectedAgentIds: runs.map((run) => run.agent.agentId),
167
+ reasonBuckets: relaunchReasonBuckets(runs, failures, derivedState),
168
+ executorStates: Object.fromEntries(
169
+ runs.map((run) => [run.agent.agentId, run.agent.executorResolved || null]),
170
+ ),
171
+ fallbackHistory: Object.fromEntries(
172
+ runs.map((run) => [
173
+ run.agent.agentId,
174
+ run.agent.executorResolved?.executorHistory || [],
175
+ ]),
176
+ ),
177
+ createdAt: toIsoTimestamp(),
178
+ });
179
+ }
180
+
181
+ function renderWaveSecuritySummaryMarkdown(securitySummary) {
182
+ return [
183
+ `# Wave ${securitySummary.wave} Security Summary`,
184
+ "",
185
+ `- State: ${securitySummary.overallState || "unknown"}`,
186
+ `- Detail: ${securitySummary.detail || "n/a"}`,
187
+ `- Total findings: ${securitySummary.totalFindings || 0}`,
188
+ `- Total approvals: ${securitySummary.totalApprovals || 0}`,
189
+ `- Reviewers: ${(securitySummary.agents || []).length}`,
190
+ "",
191
+ "## Reviews",
192
+ ...((securitySummary.agents || []).length > 0
193
+ ? securitySummary.agents.map(
194
+ (entry) =>
195
+ `- ${entry.agentId}: state=${entry.state || "unknown"} findings=${entry.findings || 0} approvals=${entry.approvals || 0}${entry.reportPath ? ` report=${entry.reportPath}` : ""}${entry.detail ? ` detail=${entry.detail}` : ""}`,
196
+ )
197
+ : ["- None."]),
198
+ "",
199
+ ].join("\n");
200
+ }
201
+
202
+ function renderIntegrationSection(title, items) {
203
+ return [
204
+ title,
205
+ ...((items || []).length > 0 ? items.map((item) => `- ${item}`) : ["- None."]),
206
+ "",
207
+ ];
208
+ }
209
+
210
+ function renderIntegrationSummaryMarkdown(integrationSummary) {
211
+ return [
212
+ `# Wave ${integrationSummary.wave} Integration Summary`,
213
+ "",
214
+ `- Recommendation: ${integrationSummary.recommendation || "unknown"}`,
215
+ `- Detail: ${integrationSummary.detail || "n/a"}`,
216
+ `- Open claims: ${(integrationSummary.openClaims || []).length}`,
217
+ `- Conflicting claims: ${(integrationSummary.conflictingClaims || []).length}`,
218
+ `- Unresolved blockers: ${(integrationSummary.unresolvedBlockers || []).length}`,
219
+ `- Changed interfaces: ${(integrationSummary.changedInterfaces || []).length}`,
220
+ `- Cross-component impacts: ${(integrationSummary.crossComponentImpacts || []).length}`,
221
+ `- Proof gaps: ${(integrationSummary.proofGaps || []).length}`,
222
+ `- Deploy risks: ${(integrationSummary.deployRisks || []).length}`,
223
+ `- Documentation gaps: ${(integrationSummary.docGaps || []).length}`,
224
+ `- Security review: ${integrationSummary.securityState || "not-applicable"}`,
225
+ `- Security findings: ${(integrationSummary.securityFindings || []).length}`,
226
+ `- Security approvals: ${(integrationSummary.securityApprovals || []).length}`,
227
+ `- Inbound dependencies: ${(integrationSummary.inboundDependencies || []).length}`,
228
+ `- Outbound dependencies: ${(integrationSummary.outboundDependencies || []).length}`,
229
+ `- Helper assignments: ${(integrationSummary.helperAssignments || []).length}`,
230
+ "",
231
+ ...renderIntegrationSection("## Open Claims", integrationSummary.openClaims),
232
+ ...renderIntegrationSection("## Conflicting Claims", integrationSummary.conflictingClaims),
233
+ ...renderIntegrationSection("## Unresolved Blockers", integrationSummary.unresolvedBlockers),
234
+ ...renderIntegrationSection("## Changed Interfaces", integrationSummary.changedInterfaces),
235
+ ...renderIntegrationSection(
236
+ "## Cross-Component Impacts",
237
+ integrationSummary.crossComponentImpacts,
238
+ ),
239
+ ...renderIntegrationSection("## Proof Gaps", integrationSummary.proofGaps),
240
+ ...renderIntegrationSection("## Deploy Risks", integrationSummary.deployRisks),
241
+ ...renderIntegrationSection("## Security Findings", integrationSummary.securityFindings),
242
+ ...renderIntegrationSection("## Security Approvals", integrationSummary.securityApprovals),
243
+ ...renderIntegrationSection("## Inbound Dependencies", integrationSummary.inboundDependencies),
244
+ ...renderIntegrationSection("## Outbound Dependencies", integrationSummary.outboundDependencies),
245
+ ...renderIntegrationSection("## Helper Assignments", integrationSummary.helperAssignments),
246
+ "## Runtime Assignments",
247
+ ...((integrationSummary.runtimeAssignments || []).length > 0
248
+ ? integrationSummary.runtimeAssignments.map(
249
+ (assignment) =>
250
+ `- ${assignment.agentId}: executor=${assignment.executorId || "n/a"} role=${assignment.role || "n/a"} profile=${assignment.profile || "none"} fallback_used=${assignment.fallbackUsed ? "yes" : "no"}`,
251
+ )
252
+ : ["- None."]),
253
+ "",
254
+ ...renderIntegrationSection("## Documentation Gaps", integrationSummary.docGaps),
255
+ ].join("\n");
256
+ }
@@ -0,0 +1,32 @@
1
+ import { compactSingleLine } from "./shared.mjs";
2
+
3
+ export function formatReconcileBlockedWaveLine(blockedWave) {
4
+ const parts = Array.isArray(blockedWave?.reasons)
5
+ ? blockedWave.reasons
6
+ .map((reason) => {
7
+ const code = compactSingleLine(reason?.code || "", 80);
8
+ const detail = compactSingleLine(reason?.detail || "", 240);
9
+ return code && detail ? `${code}=${detail}` : "";
10
+ })
11
+ .filter(Boolean)
12
+ : [];
13
+ return `[reconcile] wave ${blockedWave?.wave ?? "unknown"} not reconstructable: ${
14
+ parts.join("; ") || "unknown reason"
15
+ }`;
16
+ }
17
+
18
+ export function formatReconcilePreservedWaveLine(preservedWave) {
19
+ const parts = Array.isArray(preservedWave?.reasons)
20
+ ? preservedWave.reasons
21
+ .map((reason) => {
22
+ const code = compactSingleLine(reason?.code || "", 80);
23
+ const detail = compactSingleLine(reason?.detail || "", 240);
24
+ return code && detail ? `${code}=${detail}` : "";
25
+ })
26
+ .filter(Boolean)
27
+ : [];
28
+ const previousState = compactSingleLine(preservedWave?.previousState || "completed", 80);
29
+ return `[reconcile] wave ${preservedWave?.wave ?? "unknown"} preserved as ${previousState}: ${
30
+ parts.join("; ") || "unknown reason"
31
+ }`;
32
+ }
@@ -0,0 +1,297 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import {
4
+ isOpenCoordinationStatus,
5
+ openClarificationLinkedRequests,
6
+ readMaterializedCoordinationState,
7
+ } from "./coordination-store.mjs";
8
+ import { readWaveHumanFeedbackRequests } from "./coordination.mjs";
9
+ import { readControlPlaneEvents } from "./control-plane.mjs";
10
+ import {
11
+ readWaveAssignmentBarrier,
12
+ readWaveDependencyBarrier,
13
+ readRunResultEnvelope,
14
+ } from "./gate-engine.mjs";
15
+ import { buildResumePlan } from "./retry-engine.mjs";
16
+ import { reduceWaveState } from "./wave-state-reducer.mjs";
17
+ import { resolveWaveRoleBindings } from "./role-helpers.mjs";
18
+ import {
19
+ readWaveStateSnapshot,
20
+ writeWaveStateSnapshot,
21
+ } from "./artifact-schemas.mjs";
22
+ import { ensureDirectory } from "./shared.mjs";
23
+
24
+ function normalizeShadowGate(gate) {
25
+ if (!gate || typeof gate !== "object") {
26
+ return null;
27
+ }
28
+ return {
29
+ ok: gate.ok === true,
30
+ agentId: gate.agentId || null,
31
+ componentId: gate.componentId || null,
32
+ statusCode: gate.statusCode || null,
33
+ detail: gate.detail || null,
34
+ waitingOnAgentIds: Array.isArray(gate.waitingOnAgentIds)
35
+ ? [...new Set(gate.waitingOnAgentIds.filter(Boolean))].sort()
36
+ : [],
37
+ };
38
+ }
39
+
40
+ function normalizeShadowIdList(values) {
41
+ return [...new Set((values || []).filter(Boolean))].sort();
42
+ }
43
+
44
+ function shadowSlice(compatibility, reducer) {
45
+ return {
46
+ matches: JSON.stringify(compatibility) === JSON.stringify(reducer),
47
+ compatibility,
48
+ reducer,
49
+ };
50
+ }
51
+
52
+ function contradictionIds(value) {
53
+ if (value instanceof Map) {
54
+ return [...value.keys()];
55
+ }
56
+ if (Array.isArray(value)) {
57
+ return value.map((entry) => entry?.contradictionId || entry?.id).filter(Boolean);
58
+ }
59
+ if (value && typeof value === "object") {
60
+ return Object.keys(value);
61
+ }
62
+ return [];
63
+ }
64
+
65
+ function compatibilityBlockerIds(derivedState) {
66
+ const coordinationState = derivedState?.coordinationState || {};
67
+ return normalizeShadowIdList([
68
+ ...(coordinationState.blockers || [])
69
+ .filter((record) => isOpenCoordinationStatus(record.status))
70
+ .map((record) => record.id),
71
+ ...(coordinationState.clarifications || [])
72
+ .filter((record) => isOpenCoordinationStatus(record.status))
73
+ .map((record) => record.id),
74
+ ...openClarificationLinkedRequests(coordinationState).map((record) => record.id),
75
+ ...(coordinationState.humanFeedback || [])
76
+ .filter((record) => isOpenCoordinationStatus(record.status))
77
+ .map((record) => record.id),
78
+ ...(coordinationState.humanEscalations || [])
79
+ .filter((record) => isOpenCoordinationStatus(record.status))
80
+ .map((record) => record.id),
81
+ ...((derivedState?.capabilityAssignments || [])
82
+ .filter((assignment) => assignment.blocking)
83
+ .map((assignment) => assignment.requestId || assignment.id)),
84
+ ...((derivedState?.dependencySnapshot?.requiredInbound || []).map((record) => record.id)),
85
+ ...((derivedState?.dependencySnapshot?.requiredOutbound || []).map((record) => record.id)),
86
+ ...((derivedState?.dependencySnapshot?.unresolvedInboundAssignments || []).map((record) => record.id)),
87
+ ]);
88
+ }
89
+
90
+ function buildReducerShadowDiff({
91
+ derivedState,
92
+ compatibilityGateSnapshot = null,
93
+ compatibilityRelaunchResolution = null,
94
+ reducerState,
95
+ resumePlan,
96
+ }) {
97
+ const helperCompatibility = compatibilityGateSnapshot
98
+ ? normalizeShadowGate(compatibilityGateSnapshot.helperAssignmentBarrier)
99
+ : normalizeShadowGate(readWaveAssignmentBarrier(derivedState));
100
+ const dependencyCompatibility = compatibilityGateSnapshot
101
+ ? normalizeShadowGate(compatibilityGateSnapshot.dependencyBarrier)
102
+ : normalizeShadowGate(readWaveDependencyBarrier(derivedState));
103
+ const overallCompatibility = compatibilityGateSnapshot
104
+ ? normalizeShadowGate(compatibilityGateSnapshot.overall)
105
+ : null;
106
+ const retryCompatibility = compatibilityRelaunchResolution
107
+ ? {
108
+ selectedAgentIds: normalizeShadowIdList(
109
+ (compatibilityRelaunchResolution.runs || []).map((run) => run.agent.agentId),
110
+ ),
111
+ barrier: compatibilityRelaunchResolution.barrier
112
+ ? {
113
+ statusCode: compatibilityRelaunchResolution.barrier.statusCode || null,
114
+ detail: compatibilityRelaunchResolution.barrier.detail || null,
115
+ }
116
+ : null,
117
+ }
118
+ : null;
119
+ const blockerCompatibility = compatibilityBlockerIds(derivedState);
120
+ const contradictionCompatibility = normalizeShadowIdList(
121
+ contradictionIds(derivedState?.contradictions),
122
+ );
123
+ const blockerReducer = normalizeShadowIdList(
124
+ (reducerState?.openBlockers || []).map(
125
+ (blocker) => blocker.id || blocker.taskId || blocker.title || blocker.detail,
126
+ ),
127
+ );
128
+ const contradictionReducer = normalizeShadowIdList(
129
+ contradictionIds(reducerState?.contradictions),
130
+ );
131
+ const retryReducer = {
132
+ selectedAgentIds: normalizeShadowIdList(
133
+ reducerState?.retryTargetSet?.agentIds || resumePlan?.invalidatedAgentIds || [],
134
+ ),
135
+ barrier:
136
+ reducerState?.gateSnapshot?.helperAssignmentBarrier?.ok === false
137
+ ? {
138
+ statusCode: reducerState.gateSnapshot.helperAssignmentBarrier.statusCode || null,
139
+ detail: reducerState.gateSnapshot.helperAssignmentBarrier.detail || null,
140
+ }
141
+ : reducerState?.gateSnapshot?.dependencyBarrier?.ok === false
142
+ ? {
143
+ statusCode: reducerState.gateSnapshot.dependencyBarrier.statusCode || null,
144
+ detail: reducerState.gateSnapshot.dependencyBarrier.detail || null,
145
+ }
146
+ : null,
147
+ resumeFromPhase: resumePlan?.resumeFromPhase || null,
148
+ };
149
+ const shadowDiff = {
150
+ helperAssignmentBarrier: shadowSlice(
151
+ helperCompatibility,
152
+ normalizeShadowGate(reducerState?.gateSnapshot?.helperAssignmentBarrier),
153
+ ),
154
+ dependencyBarrier: shadowSlice(
155
+ dependencyCompatibility,
156
+ normalizeShadowGate(reducerState?.gateSnapshot?.dependencyBarrier),
157
+ ),
158
+ overallGate: shadowSlice(
159
+ overallCompatibility,
160
+ normalizeShadowGate(reducerState?.gateSnapshot?.overall),
161
+ ),
162
+ blockers: shadowSlice(blockerCompatibility, blockerReducer),
163
+ contradictions: shadowSlice(contradictionCompatibility, contradictionReducer),
164
+ closureReadiness: shadowSlice(
165
+ overallCompatibility
166
+ ? { allGatesPass: overallCompatibility.ok === true }
167
+ : null,
168
+ {
169
+ allGatesPass: reducerState?.closureEligibility?.allGatesPass === true,
170
+ waveMayClose: reducerState?.closureEligibility?.waveMayClose === true,
171
+ pendingAgentIds: normalizeShadowIdList(
172
+ reducerState?.closureEligibility?.pendingAgentIds || [],
173
+ ),
174
+ },
175
+ ),
176
+ retryPlan: shadowSlice(retryCompatibility, retryReducer),
177
+ };
178
+ const comparedSlices = Object.values(shadowDiff).filter(
179
+ (slice) => slice.compatibility !== null && slice.reducer !== null,
180
+ );
181
+ return {
182
+ comparedSliceCount: comparedSlices.length,
183
+ matches: comparedSlices.every((slice) => slice.matches),
184
+ slices: shadowDiff,
185
+ };
186
+ }
187
+
188
+ export function computeReducerSnapshot({
189
+ lanePaths,
190
+ wave,
191
+ agentRuns,
192
+ derivedState,
193
+ attempt,
194
+ options = {},
195
+ compatibilityGateSnapshot = null,
196
+ compatibilityRelaunchResolution = null,
197
+ }) {
198
+ const agentEnvelopes = {};
199
+ for (const run of agentRuns) {
200
+ const envelopeResult = readRunResultEnvelope(run, wave, { mode: "live" });
201
+ if (envelopeResult?.valid && envelopeResult.envelope) {
202
+ agentEnvelopes[run.agent.agentId] = envelopeResult.envelope;
203
+ }
204
+ }
205
+
206
+ const controlPlaneLogPath = path.join(
207
+ lanePaths.controlPlaneDir,
208
+ `wave-${wave.wave}.jsonl`,
209
+ );
210
+ const controlPlaneEvents = fs.existsSync(controlPlaneLogPath)
211
+ ? readControlPlaneEvents(controlPlaneLogPath)
212
+ : [];
213
+
214
+ const coordinationLogPath = path.join(
215
+ lanePaths.coordinationDir,
216
+ `wave-${wave.wave}.jsonl`,
217
+ );
218
+ const coordinationRecords = fs.existsSync(coordinationLogPath)
219
+ ? readMaterializedCoordinationState(coordinationLogPath)
220
+ : null;
221
+
222
+ const feedbackRequests = readWaveHumanFeedbackRequests({
223
+ feedbackRequestsDir: lanePaths.feedbackRequestsDir,
224
+ lane: lanePaths.lane,
225
+ waveNumber: wave.wave,
226
+ agentIds: (agentRuns || []).map((run) => run.agent.agentId),
227
+ orchestratorId: options.orchestratorId,
228
+ });
229
+ const roleBindings = resolveWaveRoleBindings(wave, lanePaths, wave.agents);
230
+
231
+ const reducerState = reduceWaveState({
232
+ controlPlaneEvents,
233
+ coordinationRecords: coordinationRecords?.latestRecords || [],
234
+ agentEnvelopes,
235
+ waveDefinition: wave,
236
+ dependencyTickets: derivedState?.dependencySnapshot || null,
237
+ feedbackRequests: feedbackRequests || [],
238
+ laneConfig: {
239
+ lane: lanePaths.lane,
240
+ contQaAgentId: roleBindings.contQaAgentId,
241
+ contEvalAgentId: roleBindings.contEvalAgentId,
242
+ integrationAgentId: roleBindings.integrationAgentId,
243
+ documentationAgentId: roleBindings.documentationAgentId,
244
+ validationMode: "live",
245
+ evalTargets: wave.evalTargets,
246
+ benchmarkCatalogPath: lanePaths.laneProfile?.paths?.benchmarkCatalogPath,
247
+ laneProfile: lanePaths.laneProfile,
248
+ requireIntegrationStewardFromWave: lanePaths.requireIntegrationStewardFromWave,
249
+ capabilityRouting: lanePaths.capabilityRouting,
250
+ },
251
+ });
252
+
253
+ const resumePlan = buildResumePlan(reducerState, {
254
+ waveDefinition: wave,
255
+ lanePaths,
256
+ });
257
+ const shadowDiff = buildReducerShadowDiff({
258
+ derivedState,
259
+ compatibilityGateSnapshot,
260
+ compatibilityRelaunchResolution,
261
+ reducerState,
262
+ resumePlan,
263
+ });
264
+
265
+ const stateDir = path.join(lanePaths.stateDir, "reducer");
266
+ ensureDirectory(stateDir);
267
+ const snapshotPath = path.join(stateDir, `wave-${wave.wave}.json`);
268
+ writeWaveStateSnapshot(
269
+ snapshotPath,
270
+ {
271
+ ...reducerState,
272
+ attempt,
273
+ resumePlan,
274
+ shadowDiff,
275
+ },
276
+ {
277
+ lane: lanePaths.lane,
278
+ wave: wave.wave,
279
+ },
280
+ );
281
+
282
+ return {
283
+ reducerState,
284
+ resumePlan,
285
+ shadowDiff,
286
+ snapshotPath,
287
+ };
288
+ }
289
+
290
+ export function readPersistedReducerSnapshot(lanePaths, waveNumber) {
291
+ const stateDir = path.join(lanePaths.stateDir, "reducer");
292
+ const snapshotPath = path.join(stateDir, `wave-${waveNumber}.json`);
293
+ return readWaveStateSnapshot(snapshotPath, {
294
+ lane: lanePaths.lane,
295
+ wave: waveNumber,
296
+ });
297
+ }
@@ -1,7 +1,8 @@
1
1
  import path from "node:path";
2
2
  import { augmentSummaryWithProofRegistry } from "./proof-registry.mjs";
3
3
  import { readJsonOrNull } from "./shared.mjs";
4
- import { buildGateSnapshot } from "./launcher.mjs";
4
+ import { buildGateSnapshot } from "./gate-engine.mjs";
5
+ import { materializeContradictionsFromControlPlaneEvents } from "./contradiction-entity.mjs";
5
6
  import {
6
7
  buildQualityMetrics,
7
8
  loadTraceBundle,
@@ -188,6 +189,7 @@ export function replayTraceBundle(dir) {
188
189
  capabilityAssignments: bundle.capabilityAssignments || [],
189
190
  dependencySnapshot: bundle.dependencySnapshot || null,
190
191
  integrationSummary: bundle.integrationSummary,
192
+ contradictions: materializeContradictionsFromControlPlaneEvents(bundle.controlPlaneEvents),
191
193
  };
192
194
  const gateSnapshot = normalizeGateSnapshotForBundle(
193
195
  buildGateSnapshot({