@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
@@ -5,6 +5,7 @@ import {
5
5
  materializeCoordinationState,
6
6
  openClarificationLinkedRequests,
7
7
  } from "./coordination-store.mjs";
8
+ import { materializeContradictionsFromControlPlaneEvents } from "./contradiction-entity.mjs";
8
9
  import {
9
10
  buildTasksFromWaveDefinition,
10
11
  buildTasksFromCoordinationState,
@@ -13,30 +14,97 @@ import {
13
14
  } from "./task-entity.mjs";
14
15
  import {
15
16
  buildGateSnapshotPure,
16
- readWaveImplementationGatePure,
17
- readWaveContQaGatePure,
18
- readWaveContEvalGatePure,
19
- readWaveComponentGatePure,
20
- readWaveComponentMatrixGatePure,
21
- readWaveDocumentationGatePure,
22
- readWaveSecurityGatePure,
23
- readWaveIntegrationGatePure,
24
- readWaveInfraGatePure,
17
+ readClarificationBarrier,
18
+ readWaveAssignmentBarrier,
25
19
  } 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";
20
+ import { buildHumanInputRequests } from "./human-input-workflow.mjs";
21
+ import { buildRequestAssignments } from "./routing-state.mjs";
22
+
23
+ const REDUCER_VERSION = 2;
24
+
25
+ /**
26
+ * Detect contradictions from control-plane events.
27
+ * Returns a Map<contradictionId, contradiction>.
28
+ */
29
+ function detectContradictions(controlPlaneState) {
30
+ return materializeContradictionsFromControlPlaneEvents(controlPlaneState?.events || []);
31
+ }
38
32
 
39
- const REDUCER_VERSION = 1;
33
+ /**
34
+ * Build fact lineage from control-plane events.
35
+ * Returns a Map<factId, fact>.
36
+ */
37
+ function buildFactLineage(controlPlaneState) {
38
+ const facts = new Map();
39
+ if (!controlPlaneState?.events) {
40
+ return facts;
41
+ }
42
+ for (const event of controlPlaneState.events) {
43
+ if (event.entityType !== "fact") {
44
+ continue;
45
+ }
46
+ const existing = facts.get(event.entityId) || {};
47
+ const data = event.data || {};
48
+ facts.set(event.entityId, {
49
+ factId: event.entityId,
50
+ contentHash: data.contentHash || existing.contentHash || null,
51
+ version: data.version || existing.version || 1,
52
+ waveNumber: event.wave ?? null,
53
+ lane: event.lane || null,
54
+ introducedBy: data.introducedBy || existing.introducedBy || null,
55
+ introducedAt: data.introducedAt || existing.introducedAt || event.recordedAt,
56
+ kind: data.kind || existing.kind || "claim",
57
+ content: data.content || existing.content || "",
58
+ sourceArtifact: data.sourceArtifact || existing.sourceArtifact || null,
59
+ citedBy: data.citedBy || existing.citedBy || [],
60
+ contradictedBy: data.contradictedBy || existing.contradictedBy || [],
61
+ supersedes: data.supersedes || existing.supersedes || null,
62
+ supersededBy: data.supersededBy || existing.supersededBy || null,
63
+ status: data.status || existing.status || "active",
64
+ });
65
+ }
66
+ return facts;
67
+ }
68
+
69
+ /**
70
+ * Build task graph DAG from task dependency edges.
71
+ * Returns { nodes: [taskId], edges: [{ from, to, kind }] }
72
+ */
73
+ function buildTaskGraph(tasks) {
74
+ const nodes = [];
75
+ const edges = [];
76
+ for (const task of tasks || []) {
77
+ nodes.push(task.taskId);
78
+ for (const edge of task.dependencyEdges || []) {
79
+ if (edge.taskId) {
80
+ edges.push({
81
+ from: task.taskId,
82
+ to: edge.taskId,
83
+ kind: edge.kind || "blocks",
84
+ });
85
+ }
86
+ }
87
+ }
88
+ return { nodes, edges };
89
+ }
90
+
91
+ /**
92
+ * Build assignments Map from coordination state.
93
+ */
94
+ function buildAssignments(coordinationState, agents, capabilityRouting = {}) {
95
+ const capabilityAssignments = buildRequestAssignments({
96
+ coordinationState,
97
+ agents,
98
+ capabilityRouting,
99
+ });
100
+ const assignments = new Map();
101
+ for (const assignment of capabilityAssignments) {
102
+ if (assignment?.id) {
103
+ assignments.set(assignment.id, assignment);
104
+ }
105
+ }
106
+ return { assignments, capabilityAssignments };
107
+ }
40
108
 
41
109
  /**
42
110
  * Derive the wave phase from the current state.
@@ -74,6 +142,10 @@ function derivePhase({
74
142
  return "blocked";
75
143
  }
76
144
 
145
+ if (gateSnapshot?.helperAssignmentBarrier && !gateSnapshot.helperAssignmentBarrier.ok) {
146
+ return "blocked";
147
+ }
148
+
77
149
  const blockingHelperTasks = (tasks || []).filter(
78
150
  (task) =>
79
151
  ["helper", "dependency"].includes(task.taskType) &&
@@ -122,6 +194,16 @@ function derivePhase({
122
194
  return "running";
123
195
  }
124
196
 
197
+ /**
198
+ * Map waveState from phase for end-state output.
199
+ */
200
+ function deriveWaveState(phase) {
201
+ if (phase === "completed") return "completed";
202
+ if (phase === "blocked") return "blocked";
203
+ if (phase === "clarifying") return "blocked";
204
+ return "running";
205
+ }
206
+
125
207
  /**
126
208
  * Build proof availability per agent from agent results and tasks.
127
209
  */
@@ -169,24 +251,19 @@ function buildProofAvailability(tasks, agentResults, controlPlaneState) {
169
251
  if (!evaluation.proven) {
170
252
  ownedSliceProven = false;
171
253
  }
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;
254
+ // End-state proofRequirements is an object: { proofLevel, proofCentric, maturityTarget }
255
+ const pr = task.proofRequirements;
256
+ if (pr && typeof pr === "object" && !Array.isArray(pr)) {
257
+ if (!evaluation.proven) {
258
+ exitContractMet = false;
259
+ if (pr.maturityTarget) {
260
+ componentsMet = false;
261
+ }
262
+ if (pr.proofCentric) {
263
+ proofArtifactsMet = false;
264
+ deliverablesMet = false;
265
+ }
266
+ }
190
267
  }
191
268
  }
192
269
 
@@ -225,7 +302,11 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
225
302
  kind: "coordination-blocker",
226
303
  id: record.id,
227
304
  detail: record.summary || record.detail || "",
228
- blockedAgentIds: record.targets || [],
305
+ agentId: record.agentId || null,
306
+ blockedAgentIds:
307
+ Array.isArray(record.targets) && record.targets.length > 0
308
+ ? record.targets
309
+ : (record.agentId ? [record.agentId] : []),
229
310
  resolutionHint: record.resolutionHint || null,
230
311
  });
231
312
  }
@@ -238,7 +319,11 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
238
319
  kind: "clarification",
239
320
  id: record.id,
240
321
  detail: record.summary || record.detail || "",
241
- blockedAgentIds: record.targets || [],
322
+ agentId: record.agentId || null,
323
+ blockedAgentIds:
324
+ Array.isArray(record.targets) && record.targets.length > 0
325
+ ? record.targets
326
+ : (record.agentId ? [record.agentId] : []),
242
327
  resolutionHint: "Resolve clarification before proceeding.",
243
328
  });
244
329
  }
@@ -251,7 +336,11 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
251
336
  kind: "human-escalation",
252
337
  id: record.id,
253
338
  detail: record.summary || record.detail || "",
254
- blockedAgentIds: record.targets || [],
339
+ agentId: record.agentId || null,
340
+ blockedAgentIds:
341
+ Array.isArray(record.targets) && record.targets.length > 0
342
+ ? record.targets
343
+ : (record.agentId ? [record.agentId] : []),
255
344
  resolutionHint: "Human intervention required.",
256
345
  });
257
346
  }
@@ -264,7 +353,11 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
264
353
  kind: "human-feedback",
265
354
  id: record.id,
266
355
  detail: record.summary || record.detail || "",
267
- blockedAgentIds: record.targets || [],
356
+ agentId: record.agentId || null,
357
+ blockedAgentIds:
358
+ Array.isArray(record.targets) && record.targets.length > 0
359
+ ? record.targets
360
+ : (record.agentId ? [record.agentId] : []),
268
361
  resolutionHint: "Awaiting human feedback.",
269
362
  });
270
363
  }
@@ -289,30 +382,78 @@ function deriveOpenBlockers(coordinationState, gateSnapshot) {
289
382
 
290
383
  /**
291
384
  * Derive retry target set from gate snapshot and proof availability.
385
+ * Includes both agents with unproven slices AND agents identified by failed gates.
292
386
  */
293
387
  function deriveRetryTargetSet(gateSnapshot, proofAvailability) {
294
- const failedAgentIds = [];
295
- let reason = "";
388
+ const retryTargetsByAgentId = new Map();
389
+ const reasons = [];
390
+
391
+ const upsertRetryTarget = (agentId, nextTarget = {}) => {
392
+ if (!agentId) {
393
+ return;
394
+ }
395
+ const existing = retryTargetsByAgentId.get(agentId) || {
396
+ agentId,
397
+ reason: null,
398
+ gate: null,
399
+ statusCode: null,
400
+ retriesExhausted: false,
401
+ };
402
+ const merged = {
403
+ ...existing,
404
+ ...nextTarget,
405
+ agentId,
406
+ reason: nextTarget.reason || existing.reason || null,
407
+ };
408
+ retryTargetsByAgentId.set(agentId, merged);
409
+ };
296
410
 
411
+ // Include agents with unproven slices
297
412
  for (const [agentId, entry] of Object.entries(proofAvailability.byAgentId || {})) {
298
413
  if (!entry.ownedSliceProven) {
299
- failedAgentIds.push(agentId);
414
+ upsertRetryTarget(agentId, {
415
+ reason: "owned-slice-not-proven",
416
+ statusCode: "owned-slice-not-proven",
417
+ });
300
418
  }
301
419
  }
302
420
 
303
- if (failedAgentIds.length > 0) {
304
- reason = `Agent(s) ${failedAgentIds.join(", ")} did not prove their owned slices.`;
421
+ if (retryTargetsByAgentId.size > 0) {
422
+ reasons.push(`Agent(s) ${[...retryTargetsByAgentId.keys()].sort().join(", ")} did not prove their owned slices.`);
423
+ }
424
+
425
+ // Include agents identified by failed gates
426
+ if (gateSnapshot) {
427
+ for (const [gateName, gate] of Object.entries(gateSnapshot)) {
428
+ if (gateName === "overall" || !gate || gate.ok !== false) {
429
+ continue;
430
+ }
431
+ if (gate.agentId) {
432
+ upsertRetryTarget(gate.agentId, {
433
+ reason: gate.statusCode || `gate-${gateName}`,
434
+ gate: gateName,
435
+ statusCode: gate.statusCode || null,
436
+ });
437
+ reasons.push(`Agent ${gate.agentId} identified by failed gate ${gateName}.`);
438
+ }
439
+ }
305
440
  }
306
441
 
442
+ const retryTargets = [...retryTargetsByAgentId.values()].sort((left, right) =>
443
+ String(left.agentId || "").localeCompare(String(right.agentId || "")),
444
+ );
445
+
307
446
  return {
308
- agentIds: failedAgentIds,
309
- reason,
447
+ agentIds: retryTargets.map((target) => target.agentId),
448
+ targets: retryTargets,
449
+ reason: reasons.join(" "),
310
450
  retryOverride: null,
311
451
  };
312
452
  }
313
453
 
314
454
  /**
315
455
  * Derive closure eligibility from gate snapshot and tasks.
456
+ * Includes proofBundles so buildResumePlan can access them.
316
457
  */
317
458
  function deriveClosureEligibility(gateSnapshot, tasks, proofAvailability) {
318
459
  const allGatesPass = gateSnapshot?.overall?.ok === true;
@@ -347,11 +488,14 @@ function deriveClosureEligibility(gateSnapshot, tasks, proofAvailability) {
347
488
  waveMayClose,
348
489
  ownedSliceProvenAgentIds,
349
490
  pendingAgentIds,
491
+ proofBundles: proofAvailability.activeProofBundles || [],
350
492
  };
351
493
  }
352
494
 
353
495
  /**
354
496
  * Mark tasks with updated closure states based on proof availability.
497
+ * Supports bidirectional: open -> owned_slice_proven when proved,
498
+ * and owned_slice_proven -> open when proof is invalidated.
355
499
  */
356
500
  function applyProofAvailabilityToTasks(tasks, proofAvailability) {
357
501
  return (tasks || []).map((task) => {
@@ -363,8 +507,13 @@ function applyProofAvailabilityToTasks(tasks, proofAvailability) {
363
507
  if (!entry) {
364
508
  return task;
365
509
  }
510
+ // Forward: open -> owned_slice_proven when proven
366
511
  if (task.closureState === "open" && entry.ownedSliceProven) {
367
- return { ...task, closureState: "owned_slice_proven" };
512
+ return { ...task, closureState: "owned_slice_proven", status: "proven" };
513
+ }
514
+ // Bidirectional: owned_slice_proven -> open when proof is invalidated
515
+ if (task.closureState === "owned_slice_proven" && !entry.ownedSliceProven) {
516
+ return { ...task, closureState: "open", status: "in_progress" };
368
517
  }
369
518
  return task;
370
519
  });
@@ -391,7 +540,11 @@ export function reduceWaveState({
391
540
  // Step 2: Materialize coordination state
392
541
  const coordinationState = materializeCoordinationState(coordinationRecords);
393
542
 
394
- // Step 3: Build tasks
543
+ // Step 3: Materialize contradictions and facts from canonical control-plane events
544
+ const contradictions = detectContradictions(controlPlaneState);
545
+ const facts = buildFactLineage(controlPlaneState);
546
+
547
+ // Step 4: Build tasks
395
548
  const seedTasks = buildTasksFromWaveDefinition(waveDefinition, laneConfig);
396
549
  const coordinationTasks = buildTasksFromCoordinationState(
397
550
  coordinationState,
@@ -399,55 +552,36 @@ export function reduceWaveState({
399
552
  );
400
553
  let tasks = mergeTaskSets(seedTasks, coordinationTasks);
401
554
 
402
- // Step 4: Evaluate proof availability per agent
555
+ // Step 5: Evaluate proof availability per agent
403
556
  const proofAvailability = buildProofAvailability(
404
557
  tasks,
405
558
  agentResults,
406
559
  controlPlaneState,
407
560
  );
408
561
 
409
- // Apply proof state to tasks (auto-transition from open -> owned_slice_proven)
562
+ // Apply proof state to tasks (bidirectional transitions)
410
563
  tasks = applyProofAvailabilityToTasks(tasks, proofAvailability);
411
564
 
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
- })();
565
+ // Step 6: Build integration summary BEFORE creating derivedState for gates
566
+ const integrationAgentId = laneConfig.integrationAgentId || "A8";
567
+ const integrationResult = agentResults?.[integrationAgentId]?.integration || null;
568
+ const integrationSummary = integrationResult
569
+ ? {
570
+ recommendation: integrationResult.state === "ready-for-doc-closure"
571
+ ? "ready-for-doc-closure"
572
+ : integrationResult.state || "needs-more-work",
573
+ detail: integrationResult.detail || null,
574
+ }
575
+ : null;
449
576
 
450
- const helperAssignmentBarrier = { ok: true, statusCode: "pass", detail: "" };
577
+ // Step 7: Build derived state for barriers (with integrationSummary already computed)
578
+ const clarificationBarrier = readClarificationBarrier({ coordinationState });
579
+ const { assignments, capabilityAssignments } = buildAssignments(
580
+ coordinationState,
581
+ Array.isArray(waveDefinition?.agents) ? waveDefinition.agents : [],
582
+ laneConfig.capabilityRouting || {},
583
+ );
584
+ const helperAssignmentBarrier = readWaveAssignmentBarrier({ capabilityAssignments });
451
585
  const dependencyBarrier = (() => {
452
586
  if (!dependencyTickets) {
453
587
  return { ok: true, statusCode: "pass", detail: "" };
@@ -477,24 +611,15 @@ export function reduceWaveState({
477
611
  clarificationBarrier,
478
612
  helperAssignmentBarrier,
479
613
  dependencyBarrier,
480
- integrationSummary: null,
614
+ integrationSummary,
481
615
  coordinationState,
616
+ capabilityAssignments,
482
617
  dependencySnapshot: dependencyTickets,
618
+ contradictions,
619
+ facts,
483
620
  };
484
621
 
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
622
+ // Step 8: Evaluate gates using pure variants (integrationSummary already in derivedState)
498
623
  const gateSnapshot = buildGateSnapshotPure({
499
624
  wave: waveDefinition || { wave: 0, agents: [] },
500
625
  agentResults,
@@ -503,20 +628,20 @@ export function reduceWaveState({
503
628
  laneConfig,
504
629
  });
505
630
 
506
- // Step 6: Derive open blockers
631
+ // Step 9: Derive open blockers
507
632
  const openBlockers = deriveOpenBlockers(coordinationState, gateSnapshot);
508
633
 
509
- // Step 7: Derive retry target set
634
+ // Step 10: Derive retry target set (includes gate-identified agents)
510
635
  const retryTargetSet = deriveRetryTargetSet(gateSnapshot, proofAvailability);
511
636
 
512
- // Step 8: Derive closure eligibility
637
+ // Step 11: Derive closure eligibility (with proofBundles for buildResumePlan)
513
638
  const closureEligibility = deriveClosureEligibility(
514
639
  gateSnapshot,
515
640
  tasks,
516
641
  proofAvailability,
517
642
  );
518
643
 
519
- // Step 9: Derive phase
644
+ // Step 12: Derive phase
520
645
  const phase = derivePhase({
521
646
  tasks,
522
647
  gateSnapshot,
@@ -524,6 +649,21 @@ export function reduceWaveState({
524
649
  dependencySnapshot: dependencyTickets,
525
650
  });
526
651
 
652
+ // Step 13: Derive waveState from phase
653
+ const waveState = deriveWaveState(phase);
654
+
655
+ // Step 14: Build human inputs from coordination state and feedback requests
656
+ const humanInputs = new Map();
657
+ const humanInputList = buildHumanInputRequests(coordinationState, feedbackRequests);
658
+ for (const input of humanInputList) {
659
+ if (input.requestId) {
660
+ humanInputs.set(input.requestId, input);
661
+ }
662
+ }
663
+
664
+ // Step 15: Build task graph DAG
665
+ const taskGraph = buildTaskGraph(tasks);
666
+
527
667
  // Build coordination metrics
528
668
  const coordinationMetrics = buildCoordinationResponseMetrics(coordinationState);
529
669
 
@@ -546,20 +686,29 @@ export function reduceWaveState({
546
686
  lane: laneConfig.lane || "main",
547
687
  attempt: controlPlaneState?.activeAttempt?.attempt ?? 0,
548
688
  phase,
689
+ waveState,
549
690
 
550
691
  tasks,
551
692
  tasksByAgentId,
693
+ taskGraph,
552
694
 
553
695
  proofAvailability,
554
696
 
555
697
  openBlockers,
556
698
 
699
+ // Renamed: gateSnapshot -> gateVerdicts (end-state P1-9), keep gateSnapshot for backward compat
557
700
  gateSnapshot,
701
+ gateVerdicts: gateSnapshot,
558
702
 
559
703
  retryTargetSet,
560
704
 
561
705
  closureEligibility,
562
706
 
707
+ contradictions,
708
+ facts,
709
+ humanInputs,
710
+ assignments,
711
+
563
712
  coordinationMetrics,
564
713
  controlPlaneState,
565
714
  };
package/skills/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  Skills are repo-owned procedural bundles that Wave attaches to agents at runtime. They capture durable operating knowledge such as coding norms, role checklists, runtime behavior, provider verification, and closure rules.
4
4
 
5
5
  Skills are not one-off prompts. They are reusable procedures with explicit routing metadata.
6
+ They are runtime inputs and overlays, not canonical runtime state.
6
7
 
7
8
  ## Bundle Layout
8
9
 
@@ -130,6 +131,8 @@ Runtime behavior:
130
131
  | OpenCode | Compact catalog injected into `opencode.json`; `skill.json`, `SKILL.md`, the selected adapter, and recursive references attached via `--file`. |
131
132
  | Local | Compact catalog only. |
132
133
 
134
+ Skills guide how agents interpret canonical state and projections. They do not become canonical runtime state, control-plane records, or replay inputs by themselves.
135
+
133
136
  ## Validation
134
137
 
135
138
  Run:
@@ -21,7 +21,7 @@ Before editing any file, confirm:
21
21
  5. If the file has a corresponding test file, you will update or extend tests to cover your change.
22
22
  6. You have checked for other files that import or depend on the symbols you are changing.
23
23
  7. If the file is a config file (JSON, YAML), you have validated the resulting structure is well-formed.
24
- 8. You are not editing generated runtime state (coordination logs, control-plane events, proof registries, trace bundles, dashboards). These are managed by the launcher and operator tooling.
24
+ 8. You are not editing generated runtime state (coordination logs, control-plane events, result artifacts, proof registries, trace bundles, dashboards). These are canonical runtime artifacts or projections managed by the orchestration runtime.
25
25
 
26
26
  ## Change Hygiene
27
27
 
@@ -10,13 +10,13 @@ Use this skill when the agent is the wave's final cont-QA closure steward.
10
10
  - Fail closed. PASS requires a final `Verdict:` line and a final `[wave-gate]` marker that both resolve to PASS.
11
11
  - Re-read the shared summary, inbox, and latest closure artifacts before the final judgment.
12
12
  - Keep verdicts consistent across the report. Do not say PASS in the verdict and CONCERNS in the gate marker.
13
- - Treat the last gate marker and last verdict line as authoritative for closure. Earlier markers are superseded.
13
+ - Treat canonical state and typed closure artifacts as authoritative for closure. The final verdict line and gate marker must match that state; earlier markers are compatibility history only.
14
14
 
15
15
  ## Workflow
16
16
 
17
17
  Execute these steps in order. Do not skip steps.
18
18
 
19
- 1. **Receive evidence** -- collect all implementation proof, coordination records, integration marker, doc closure marker, cont-EVAL marker (if present), and security marker (if present).
19
+ 1. **Receive evidence** -- collect all implementation proof, coordination records, integration output, doc closure output, cont-EVAL output (if present), and security output (if present).
20
20
  2. **Review vs exit contracts** -- walk each agent's exit contract line by line. For each line, confirm a proof artifact backs it. Record pass or gap. When the wave declares `### Proof artifacts`, verify those machine-visible artifacts are present.
21
21
  3. **Review vs promotions** -- walk each declared component promotion. Confirm evidence shows the component reached the declared target level, not just that adjacent code landed.
22
22
  4. **Verify proof registry** -- check whether operator-registered proof bundles exist. Confirm they are `active` (not `revoked` or `superseded`). Only active bundles count as evidence.
@@ -8,7 +8,7 @@
8
8
  - Name the exact service, package, or runtime surface being rolled out.
9
9
  - Record health, readiness, failure, and rollback state explicitly.
10
10
  - If rollout proof is incomplete, downgrade the wave honestly instead of implying success.
11
- - Re-read the compiled shared summary, your inbox, and the board projection before major decisions and before final output.
11
+ - Re-read the compiled shared summary, your inbox, and the board projection before major decisions and before final output. If deployment evidence disagrees with a projection, trust the deployment evidence and canonical state.
12
12
 
13
13
  ## Workflow
14
14
 
@@ -8,7 +8,7 @@
8
8
  - Update status, sequencing, ownership, and proof expectations when the wave changes them.
9
9
  - When no shared-plan delta is required, make the no-change decision explicit.
10
10
  - Keep implementation-owned docs with the implementation owner and shared-plan docs with the documentation steward.
11
- - Re-read the compiled shared summary, your inbox, and the board projection before major decisions and before final output.
11
+ - Re-read the compiled shared summary, your inbox, and the board projection before major decisions and before final output. If a projection looks stale, trust landed docs and canonical state.
12
12
 
13
13
  ## Workflow
14
14
 
@@ -15,7 +15,7 @@
15
15
  Follow this sequence for each deliverable in your exit contract:
16
16
 
17
17
  1. **Claim ownership** -- confirm the files and deliverables assigned to you in the wave definition. If anything is ambiguous, post a coordination record before starting.
18
- 2. **Read context** -- re-read the shared summary, your inbox, and the board projection. Check for coordination records from other agents that affect your scope.
18
+ 2. **Read context** -- re-read the shared summary, your inbox, and the board projection. Check for coordination records from other agents that affect your scope, but trust landed code and canonical state if a projection looks stale.
19
19
  3. **Implement** -- make the smallest change that satisfies the exit contract. Follow repo coding rules for style, tests, and change hygiene.
20
20
  4. **Proof** -- produce durable evidence that the deliverable works:
21
21
  - Tests that pass and cover the changed behavior.
@@ -8,7 +8,7 @@
8
8
  - Prefer exact dependency, identity, and admission proof over vague environment notes.
9
9
  - Do not improvise destructive infra changes. Keep actions explicit and approved.
10
10
  - Surface setup-required versus blocked states precisely so later closure decisions stay honest.
11
- - Re-read the compiled shared summary, your inbox, and the board projection before major decisions and before final output.
11
+ - Re-read the compiled shared summary, your inbox, and the board projection before major decisions and before final output. If infra evidence disagrees with a projection, trust the canonical state and direct verification.
12
12
 
13
13
  ## Workflow
14
14
 
@@ -14,7 +14,7 @@
14
14
 
15
15
  Execute these steps in order:
16
16
 
17
- 1. **Collect evidence** -- re-read the compiled shared summary, your inbox, the board projection, and all coordination records posted by implementation agents and cont-EVAL (if present). Summaries refresh during execution, so use the latest version.
17
+ 1. **Collect evidence** -- re-read the compiled shared summary, your inbox, the board projection, all coordination records posted by implementation agents and cont-EVAL (if present), and the current control-plane or result-artifact state. Summaries refresh during execution, so use the latest version, but trust canonical state over stale projections.
18
18
  2. **Check contradictions** -- identify claims from different agents that conflict (e.g., two agents claiming the same file, incompatible interface assumptions, inconsistent status claims).
19
19
  3. **Verify proof gaps** -- walk each agent's exit contract and confirm proof artifacts exist. Flag any exit contract line that lacks durable evidence. When the wave declares `### Proof artifacts`, verify those artifacts are present. Check the proof registry for any revoked or superseded bundles that no longer satisfy closure.
20
20
  4. **Check helper assignments** -- verify that every helper assignment posted during the wave has a linked resolution or explicit follow-up.
@@ -37,7 +37,7 @@ Review each item. Any failure means the wave is `needs-more-work`:
37
37
  - [ ] All blockers posted during the wave have a resolution or an explicit follow-up.
38
38
  - [ ] Helper assignments are resolved or have linked follow-up work.
39
39
  - [ ] Clarification chains are closed.
40
- - [ ] cont-EVAL marker (if present) shows `satisfied` with matching ids.
40
+ - [ ] cont-EVAL result state (if present) shows `satisfied` with matching ids.
41
41
  - [ ] Deploy-status markers (if present) show `healthy` or have explicit downgrade reasoning.
42
42
  - [ ] Cross-lane dependency tickets are resolved or explicitly deferred.
43
43
  - [ ] No active rerun request is pending (check via `wave control rerun get`).
@@ -8,7 +8,7 @@ Use this skill when the agent is the wave's dedicated security reviewer.
8
8
  - Default to report-only work. Route fixes to the owning agent unless the prompt explicitly gives you additional implementation ownership.
9
9
  - Fail closed on unresolved blocking findings. Do not mark the wave clear while findings or approvals remain open.
10
10
  - Prefer exact exploit paths, exact affected files or surfaces, and exact owners over broad warnings.
11
- - Re-read the shared summary, inbox, and board projection before final disposition.
11
+ - Re-read the shared summary, inbox, and board projection before final disposition. If the projection diverges from landed code or canonical state, trust the canonical state.
12
12
 
13
13
  ## Workflow
14
14