@chllming/wave-orchestration 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +9 -8
  3. package/docs/guides/planner.md +19 -0
  4. package/docs/guides/terminal-surfaces.md +12 -0
  5. package/docs/plans/component-cutover-matrix.json +50 -3
  6. package/docs/plans/current-state.md +1 -1
  7. package/docs/plans/end-state-architecture.md +927 -0
  8. package/docs/plans/examples/wave-example-live-proof.md +1 -1
  9. package/docs/plans/migration.md +26 -0
  10. package/docs/plans/wave-orchestrator.md +4 -7
  11. package/docs/plans/waves/wave-1.md +376 -0
  12. package/docs/plans/waves/wave-2.md +292 -0
  13. package/docs/plans/waves/wave-3.md +342 -0
  14. package/docs/plans/waves/wave-4.md +391 -0
  15. package/docs/plans/waves/wave-5.md +382 -0
  16. package/docs/plans/waves/wave-6.md +321 -0
  17. package/docs/reference/cli-reference.md +547 -0
  18. package/docs/reference/coordination-and-closure.md +1 -1
  19. package/docs/reference/npmjs-trusted-publishing.md +2 -2
  20. package/docs/reference/runtime-config/README.md +2 -2
  21. package/docs/reference/runtime-config/codex.md +2 -1
  22. package/docs/reference/sample-waves.md +4 -4
  23. package/package.json +1 -1
  24. package/releases/manifest.json +43 -2
  25. package/scripts/wave-orchestrator/agent-state.mjs +458 -35
  26. package/scripts/wave-orchestrator/artifact-schemas.mjs +81 -0
  27. package/scripts/wave-orchestrator/control-cli.mjs +119 -20
  28. package/scripts/wave-orchestrator/coordination.mjs +11 -10
  29. package/scripts/wave-orchestrator/dashboard-renderer.mjs +82 -2
  30. package/scripts/wave-orchestrator/human-input-workflow.mjs +289 -0
  31. package/scripts/wave-orchestrator/install.mjs +120 -3
  32. package/scripts/wave-orchestrator/launcher-derived-state.mjs +915 -0
  33. package/scripts/wave-orchestrator/launcher-gates.mjs +1061 -0
  34. package/scripts/wave-orchestrator/launcher-retry.mjs +873 -0
  35. package/scripts/wave-orchestrator/launcher-runtime.mjs +9 -9
  36. package/scripts/wave-orchestrator/launcher-supervisor.mjs +704 -0
  37. package/scripts/wave-orchestrator/launcher.mjs +317 -2999
  38. package/scripts/wave-orchestrator/task-entity.mjs +557 -0
  39. package/scripts/wave-orchestrator/terminals.mjs +1 -1
  40. package/scripts/wave-orchestrator/wave-files.mjs +138 -20
  41. package/scripts/wave-orchestrator/wave-state-reducer.mjs +566 -0
  42. package/wave.config.json +1 -1
@@ -0,0 +1,566 @@
1
+ import { materializeControlPlaneState } from "./control-plane.mjs";
2
+ import {
3
+ buildCoordinationResponseMetrics,
4
+ isOpenCoordinationStatus,
5
+ materializeCoordinationState,
6
+ openClarificationLinkedRequests,
7
+ } from "./coordination-store.mjs";
8
+ import {
9
+ buildTasksFromWaveDefinition,
10
+ buildTasksFromCoordinationState,
11
+ mergeTaskSets,
12
+ evaluateOwnedSliceProven,
13
+ } from "./task-entity.mjs";
14
+ import {
15
+ buildGateSnapshotPure,
16
+ readWaveImplementationGatePure,
17
+ readWaveContQaGatePure,
18
+ readWaveContEvalGatePure,
19
+ readWaveComponentGatePure,
20
+ readWaveComponentMatrixGatePure,
21
+ readWaveDocumentationGatePure,
22
+ readWaveSecurityGatePure,
23
+ readWaveIntegrationGatePure,
24
+ readWaveInfraGatePure,
25
+ } from "./launcher-gates.mjs";
26
+ import {
27
+ validateImplementationSummary,
28
+ validateContQaSummary,
29
+ validateContEvalSummary,
30
+ validateDocumentationClosureSummary,
31
+ validateSecuritySummary,
32
+ validateIntegrationSummary,
33
+ } from "./agent-state.mjs";
34
+ import {
35
+ isSecurityReviewAgent,
36
+ isContEvalImplementationOwningAgent,
37
+ } from "./role-helpers.mjs";
38
+
39
+ const REDUCER_VERSION = 1;
40
+
41
+ /**
42
+ * Derive the wave phase from the current state.
43
+ *
44
+ * This reuses the same logic as ledger.mjs derivePhase, adapted for
45
+ * the reducer's data structures.
46
+ */
47
+ function derivePhase({
48
+ tasks,
49
+ gateSnapshot,
50
+ coordinationState,
51
+ dependencySnapshot,
52
+ }) {
53
+ const blockers = (coordinationState?.blockers || []).filter(
54
+ (record) =>
55
+ isOpenCoordinationStatus(record.status) &&
56
+ ["high", "urgent"].includes(record.priority),
57
+ );
58
+ if (blockers.length > 0) {
59
+ return "blocked";
60
+ }
61
+
62
+ const openClarifications = (coordinationState?.clarifications || []).filter(
63
+ (record) => isOpenCoordinationStatus(record.status),
64
+ );
65
+ const openClarificationRequests = openClarificationLinkedRequests(coordinationState);
66
+ if (openClarifications.length > 0 || openClarificationRequests.length > 0) {
67
+ return "clarifying";
68
+ }
69
+
70
+ const dependencyBlockers =
71
+ (dependencySnapshot?.requiredInbound || []).length +
72
+ (dependencySnapshot?.requiredOutbound || []).length;
73
+ if (dependencyBlockers > 0) {
74
+ return "blocked";
75
+ }
76
+
77
+ const blockingHelperTasks = (tasks || []).filter(
78
+ (task) =>
79
+ ["helper", "dependency"].includes(task.taskType) &&
80
+ task.closureState === "open",
81
+ );
82
+ if (blockingHelperTasks.length > 0) {
83
+ return "blocked";
84
+ }
85
+
86
+ if ((tasks || []).length === 0) {
87
+ return "running";
88
+ }
89
+
90
+ const implementationTasks = (tasks || []).filter(
91
+ (task) => task.taskType === "implementation",
92
+ );
93
+ const allImplementationProven = implementationTasks.every(
94
+ (task) =>
95
+ task.closureState === "owned_slice_proven" ||
96
+ task.closureState === "wave_closure_ready" ||
97
+ task.closureState === "closed",
98
+ );
99
+ if (!allImplementationProven && implementationTasks.length > 0) {
100
+ return "running";
101
+ }
102
+
103
+ if (gateSnapshot?.contEvalGate && !gateSnapshot.contEvalGate.ok) {
104
+ return "cont-eval";
105
+ }
106
+ if (gateSnapshot?.securityGate && !gateSnapshot.securityGate.ok) {
107
+ return "security-review";
108
+ }
109
+ if (gateSnapshot?.integrationBarrier && !gateSnapshot.integrationBarrier.ok) {
110
+ return "integrating";
111
+ }
112
+ if (gateSnapshot?.documentationGate && !gateSnapshot.documentationGate.ok) {
113
+ return "docs-closure";
114
+ }
115
+ if (gateSnapshot?.contQaGate && !gateSnapshot.contQaGate.ok) {
116
+ return "cont-qa-closure";
117
+ }
118
+ if (gateSnapshot?.overall?.ok) {
119
+ return "completed";
120
+ }
121
+
122
+ return "running";
123
+ }
124
+
125
+ /**
126
+ * Build proof availability per agent from agent results and tasks.
127
+ */
128
+ function buildProofAvailability(tasks, agentResults, controlPlaneState) {
129
+ const byAgentId = {};
130
+ const agentTasks = new Map();
131
+
132
+ for (const task of tasks || []) {
133
+ const agentId = task.assigneeAgentId || task.ownerAgentId;
134
+ if (!agentId) {
135
+ continue;
136
+ }
137
+ if (!agentTasks.has(agentId)) {
138
+ agentTasks.set(agentId, []);
139
+ }
140
+ agentTasks.get(agentId).push(task);
141
+ }
142
+
143
+ const activeProofBundles = controlPlaneState?.activeProofBundles || [];
144
+ const proofBundlesByAgentId = new Map();
145
+ for (const bundle of activeProofBundles) {
146
+ const agentId = bundle.agentId || bundle.data?.agentId;
147
+ if (!agentId) {
148
+ continue;
149
+ }
150
+ if (!proofBundlesByAgentId.has(agentId)) {
151
+ proofBundlesByAgentId.set(agentId, []);
152
+ }
153
+ proofBundlesByAgentId.get(agentId).push(bundle);
154
+ }
155
+
156
+ for (const [agentId, agentTaskList] of agentTasks) {
157
+ const result = agentResults?.[agentId] || null;
158
+ const proofBundleIds = (proofBundlesByAgentId.get(agentId) || []).map(
159
+ (bundle) => bundle.id,
160
+ );
161
+ let ownedSliceProven = true;
162
+ let exitContractMet = true;
163
+ let deliverablesMet = true;
164
+ let componentsMet = true;
165
+ let proofArtifactsMet = true;
166
+
167
+ for (const task of agentTaskList) {
168
+ const evaluation = evaluateOwnedSliceProven(task, result);
169
+ if (!evaluation.proven) {
170
+ ownedSliceProven = false;
171
+ }
172
+ if (
173
+ task.proofRequirements?.includes("implementation-exit-met") &&
174
+ !evaluation.proven
175
+ ) {
176
+ exitContractMet = false;
177
+ }
178
+ if (
179
+ task.proofRequirements?.includes("component-level-met") &&
180
+ !evaluation.proven
181
+ ) {
182
+ componentsMet = false;
183
+ }
184
+ if (
185
+ task.proofRequirements?.includes("proof-artifacts-present") &&
186
+ !evaluation.proven
187
+ ) {
188
+ proofArtifactsMet = false;
189
+ deliverablesMet = false;
190
+ }
191
+ }
192
+
193
+ byAgentId[agentId] = {
194
+ ownedSliceProven,
195
+ exitContractMet,
196
+ deliverablesMet,
197
+ componentsMet,
198
+ proofArtifactsMet,
199
+ proofBundleIds,
200
+ };
201
+ }
202
+
203
+ const allOwnedSlicesProven = Object.values(byAgentId).every(
204
+ (entry) => entry.ownedSliceProven,
205
+ );
206
+
207
+ return {
208
+ byAgentId,
209
+ allOwnedSlicesProven,
210
+ activeProofBundles,
211
+ };
212
+ }
213
+
214
+ /**
215
+ * Derive open blockers from coordination state and gate snapshot.
216
+ */
217
+ function deriveOpenBlockers(coordinationState, gateSnapshot) {
218
+ const blockers = [];
219
+
220
+ for (const record of coordinationState?.blockers || []) {
221
+ if (!isOpenCoordinationStatus(record.status)) {
222
+ continue;
223
+ }
224
+ blockers.push({
225
+ kind: "coordination-blocker",
226
+ id: record.id,
227
+ detail: record.summary || record.detail || "",
228
+ blockedAgentIds: record.targets || [],
229
+ resolutionHint: record.resolutionHint || null,
230
+ });
231
+ }
232
+
233
+ for (const record of coordinationState?.clarifications || []) {
234
+ if (!isOpenCoordinationStatus(record.status)) {
235
+ continue;
236
+ }
237
+ blockers.push({
238
+ kind: "clarification",
239
+ id: record.id,
240
+ detail: record.summary || record.detail || "",
241
+ blockedAgentIds: record.targets || [],
242
+ resolutionHint: "Resolve clarification before proceeding.",
243
+ });
244
+ }
245
+
246
+ for (const record of coordinationState?.humanEscalations || []) {
247
+ if (!isOpenCoordinationStatus(record.status)) {
248
+ continue;
249
+ }
250
+ blockers.push({
251
+ kind: "human-escalation",
252
+ id: record.id,
253
+ detail: record.summary || record.detail || "",
254
+ blockedAgentIds: record.targets || [],
255
+ resolutionHint: "Human intervention required.",
256
+ });
257
+ }
258
+
259
+ for (const record of coordinationState?.humanFeedback || []) {
260
+ if (!isOpenCoordinationStatus(record.status)) {
261
+ continue;
262
+ }
263
+ blockers.push({
264
+ kind: "human-feedback",
265
+ id: record.id,
266
+ detail: record.summary || record.detail || "",
267
+ blockedAgentIds: record.targets || [],
268
+ resolutionHint: "Awaiting human feedback.",
269
+ });
270
+ }
271
+
272
+ if (gateSnapshot) {
273
+ for (const [gateName, gate] of Object.entries(gateSnapshot)) {
274
+ if (gateName === "overall" || !gate || gate.ok !== false) {
275
+ continue;
276
+ }
277
+ blockers.push({
278
+ kind: "gate-failure",
279
+ id: gateName,
280
+ detail: gate.detail || gate.statusCode || "",
281
+ blockedAgentIds: gate.agentId ? [gate.agentId] : [],
282
+ resolutionHint: `Gate ${gateName} must pass before wave closure.`,
283
+ });
284
+ }
285
+ }
286
+
287
+ return blockers;
288
+ }
289
+
290
+ /**
291
+ * Derive retry target set from gate snapshot and proof availability.
292
+ */
293
+ function deriveRetryTargetSet(gateSnapshot, proofAvailability) {
294
+ const failedAgentIds = [];
295
+ let reason = "";
296
+
297
+ for (const [agentId, entry] of Object.entries(proofAvailability.byAgentId || {})) {
298
+ if (!entry.ownedSliceProven) {
299
+ failedAgentIds.push(agentId);
300
+ }
301
+ }
302
+
303
+ if (failedAgentIds.length > 0) {
304
+ reason = `Agent(s) ${failedAgentIds.join(", ")} did not prove their owned slices.`;
305
+ }
306
+
307
+ return {
308
+ agentIds: failedAgentIds,
309
+ reason,
310
+ retryOverride: null,
311
+ };
312
+ }
313
+
314
+ /**
315
+ * Derive closure eligibility from gate snapshot and tasks.
316
+ */
317
+ function deriveClosureEligibility(gateSnapshot, tasks, proofAvailability) {
318
+ const allGatesPass = gateSnapshot?.overall?.ok === true;
319
+ const allTasksClosed = (tasks || []).every(
320
+ (task) =>
321
+ task.closureState === "closed" ||
322
+ task.closureState === "cancelled" ||
323
+ task.closureState === "superseded",
324
+ );
325
+ const allTasksClosureReady = (tasks || []).every(
326
+ (task) =>
327
+ task.closureState === "wave_closure_ready" ||
328
+ task.closureState === "closed" ||
329
+ task.closureState === "cancelled" ||
330
+ task.closureState === "superseded",
331
+ );
332
+ const waveMayClose = allGatesPass && (allTasksClosed || allTasksClosureReady);
333
+
334
+ const ownedSliceProvenAgentIds = [];
335
+ const pendingAgentIds = [];
336
+ for (const [agentId, entry] of Object.entries(proofAvailability.byAgentId || {})) {
337
+ if (entry.ownedSliceProven) {
338
+ ownedSliceProvenAgentIds.push(agentId);
339
+ } else {
340
+ pendingAgentIds.push(agentId);
341
+ }
342
+ }
343
+
344
+ return {
345
+ allGatesPass,
346
+ allTasksClosed,
347
+ waveMayClose,
348
+ ownedSliceProvenAgentIds,
349
+ pendingAgentIds,
350
+ };
351
+ }
352
+
353
+ /**
354
+ * Mark tasks with updated closure states based on proof availability.
355
+ */
356
+ function applyProofAvailabilityToTasks(tasks, proofAvailability) {
357
+ return (tasks || []).map((task) => {
358
+ const agentId = task.assigneeAgentId || task.ownerAgentId;
359
+ if (!agentId) {
360
+ return task;
361
+ }
362
+ const entry = proofAvailability.byAgentId?.[agentId];
363
+ if (!entry) {
364
+ return task;
365
+ }
366
+ if (task.closureState === "open" && entry.ownedSliceProven) {
367
+ return { ...task, closureState: "owned_slice_proven" };
368
+ }
369
+ return task;
370
+ });
371
+ }
372
+
373
+ /**
374
+ * reduceWaveState - Pure reducer function.
375
+ *
376
+ * Takes pre-read inputs and produces a complete WaveState snapshot.
377
+ * No file I/O.
378
+ */
379
+ export function reduceWaveState({
380
+ controlPlaneEvents = [],
381
+ coordinationRecords = [],
382
+ agentResults = {},
383
+ waveDefinition = null,
384
+ dependencyTickets = null,
385
+ feedbackRequests = [],
386
+ laneConfig = {},
387
+ }) {
388
+ // Step 1: Materialize control-plane state
389
+ const controlPlaneState = materializeControlPlaneState(controlPlaneEvents);
390
+
391
+ // Step 2: Materialize coordination state
392
+ const coordinationState = materializeCoordinationState(coordinationRecords);
393
+
394
+ // Step 3: Build tasks
395
+ const seedTasks = buildTasksFromWaveDefinition(waveDefinition, laneConfig);
396
+ const coordinationTasks = buildTasksFromCoordinationState(
397
+ coordinationState,
398
+ feedbackRequests,
399
+ );
400
+ let tasks = mergeTaskSets(seedTasks, coordinationTasks);
401
+
402
+ // Step 4: Evaluate proof availability per agent
403
+ const proofAvailability = buildProofAvailability(
404
+ tasks,
405
+ agentResults,
406
+ controlPlaneState,
407
+ );
408
+
409
+ // Apply proof state to tasks (auto-transition from open -> owned_slice_proven)
410
+ tasks = applyProofAvailabilityToTasks(tasks, proofAvailability);
411
+
412
+ // Step 5: Build derived state for barriers
413
+ const clarificationBarrier = (() => {
414
+ const openClarifications = (coordinationState?.clarifications || []).filter(
415
+ (record) => isOpenCoordinationStatus(record.status),
416
+ );
417
+ if (openClarifications.length > 0) {
418
+ return {
419
+ ok: false,
420
+ statusCode: "clarification-open",
421
+ detail: `Open clarifications remain (${openClarifications.map((record) => record.id).join(", ")}).`,
422
+ };
423
+ }
424
+ const openClarificationReqs = openClarificationLinkedRequests(coordinationState);
425
+ if (openClarificationReqs.length > 0) {
426
+ return {
427
+ ok: false,
428
+ statusCode: "clarification-follow-up-open",
429
+ detail: `Clarification follow-up requests remain open (${openClarificationReqs.map((record) => record.id).join(", ")}).`,
430
+ };
431
+ }
432
+ const pendingHuman = [
433
+ ...(coordinationState?.humanEscalations || []).filter((record) =>
434
+ isOpenCoordinationStatus(record.status),
435
+ ),
436
+ ...(coordinationState?.humanFeedback || []).filter((record) =>
437
+ isOpenCoordinationStatus(record.status),
438
+ ),
439
+ ];
440
+ if (pendingHuman.length > 0) {
441
+ return {
442
+ ok: false,
443
+ statusCode: "human-feedback-open",
444
+ detail: `Pending human input remains (${pendingHuman.map((record) => record.id).join(", ")}).`,
445
+ };
446
+ }
447
+ return { ok: true, statusCode: "pass", detail: "" };
448
+ })();
449
+
450
+ const helperAssignmentBarrier = { ok: true, statusCode: "pass", detail: "" };
451
+ const dependencyBarrier = (() => {
452
+ if (!dependencyTickets) {
453
+ return { ok: true, statusCode: "pass", detail: "" };
454
+ }
455
+ const requiredInbound = dependencyTickets.requiredInbound || [];
456
+ const requiredOutbound = dependencyTickets.requiredOutbound || [];
457
+ const unresolvedInboundAssignments =
458
+ dependencyTickets.unresolvedInboundAssignments || [];
459
+ if (unresolvedInboundAssignments.length > 0) {
460
+ return {
461
+ ok: false,
462
+ statusCode: "dependency-assignment-unresolved",
463
+ detail: `Required inbound dependencies are unassigned (${unresolvedInboundAssignments.map((record) => record.id).join(", ")}).`,
464
+ };
465
+ }
466
+ if (requiredInbound.length > 0 || requiredOutbound.length > 0) {
467
+ return {
468
+ ok: false,
469
+ statusCode: "dependency-open",
470
+ detail: `Open required dependencies remain (${[...requiredInbound, ...requiredOutbound].map((record) => record.id).join(", ")}).`,
471
+ };
472
+ }
473
+ return { ok: true, statusCode: "pass", detail: "" };
474
+ })();
475
+
476
+ const derivedState = {
477
+ clarificationBarrier,
478
+ helperAssignmentBarrier,
479
+ dependencyBarrier,
480
+ integrationSummary: null,
481
+ coordinationState,
482
+ dependencySnapshot: dependencyTickets,
483
+ };
484
+
485
+ // Try to derive integration summary from agent results
486
+ const integrationAgentId = laneConfig.integrationAgentId || "A8";
487
+ const integrationSummary = agentResults?.[integrationAgentId]?.integration || null;
488
+ if (integrationSummary) {
489
+ derivedState.integrationSummary = {
490
+ recommendation: integrationSummary.state === "ready-for-doc-closure"
491
+ ? "ready-for-doc-closure"
492
+ : integrationSummary.state || "needs-more-work",
493
+ detail: integrationSummary.detail || null,
494
+ };
495
+ }
496
+
497
+ // Step 5: Evaluate gates using pure variants
498
+ const gateSnapshot = buildGateSnapshotPure({
499
+ wave: waveDefinition || { wave: 0, agents: [] },
500
+ agentResults,
501
+ derivedState,
502
+ validationMode: laneConfig.validationMode || "live",
503
+ laneConfig,
504
+ });
505
+
506
+ // Step 6: Derive open blockers
507
+ const openBlockers = deriveOpenBlockers(coordinationState, gateSnapshot);
508
+
509
+ // Step 7: Derive retry target set
510
+ const retryTargetSet = deriveRetryTargetSet(gateSnapshot, proofAvailability);
511
+
512
+ // Step 8: Derive closure eligibility
513
+ const closureEligibility = deriveClosureEligibility(
514
+ gateSnapshot,
515
+ tasks,
516
+ proofAvailability,
517
+ );
518
+
519
+ // Step 9: Derive phase
520
+ const phase = derivePhase({
521
+ tasks,
522
+ gateSnapshot,
523
+ coordinationState,
524
+ dependencySnapshot: dependencyTickets,
525
+ });
526
+
527
+ // Build coordination metrics
528
+ const coordinationMetrics = buildCoordinationResponseMetrics(coordinationState);
529
+
530
+ // Build tasksByAgentId
531
+ const tasksByAgentId = {};
532
+ for (const task of tasks) {
533
+ const agentId = task.assigneeAgentId || task.ownerAgentId;
534
+ if (!agentId) {
535
+ continue;
536
+ }
537
+ if (!tasksByAgentId[agentId]) {
538
+ tasksByAgentId[agentId] = [];
539
+ }
540
+ tasksByAgentId[agentId].push(task);
541
+ }
542
+
543
+ return {
544
+ reducerVersion: REDUCER_VERSION,
545
+ wave: waveDefinition?.wave ?? 0,
546
+ lane: laneConfig.lane || "main",
547
+ attempt: controlPlaneState?.activeAttempt?.attempt ?? 0,
548
+ phase,
549
+
550
+ tasks,
551
+ tasksByAgentId,
552
+
553
+ proofAvailability,
554
+
555
+ openBlockers,
556
+
557
+ gateSnapshot,
558
+
559
+ retryTargetSet,
560
+
561
+ closureEligibility,
562
+
563
+ coordinationMetrics,
564
+ controlPlaneState,
565
+ };
566
+ }
package/wave.config.json CHANGED
@@ -194,7 +194,7 @@
194
194
  "main": {
195
195
  "runtimePolicy": {
196
196
  "runtimeMixTargets": {
197
- "codex": 3,
197
+ "codex": 7,
198
198
  "claude": 4,
199
199
  "opencode": 2
200
200
  },