@chllming/wave-orchestration 0.8.3 → 0.8.5

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 (59) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/README.md +60 -11
  3. package/docs/README.md +8 -2
  4. package/docs/agents/wave-design-role.md +47 -0
  5. package/docs/concepts/what-is-a-wave.md +11 -7
  6. package/docs/guides/author-and-run-waves.md +24 -0
  7. package/docs/guides/planner.md +44 -0
  8. package/docs/plans/architecture-hardening-migration.md +8 -1
  9. package/docs/plans/current-state.md +19 -7
  10. package/docs/plans/end-state-architecture.md +88 -70
  11. package/docs/plans/examples/wave-example-design-handoff.md +262 -0
  12. package/docs/plans/examples/wave-example-live-proof.md +1 -1
  13. package/docs/plans/migration.md +370 -64
  14. package/docs/plans/wave-orchestrator.md +49 -13
  15. package/docs/reference/cli-reference.md +46 -14
  16. package/docs/reference/coordination-and-closure.md +19 -6
  17. package/docs/reference/npmjs-trusted-publishing.md +5 -4
  18. package/docs/reference/sample-waves.md +14 -7
  19. package/docs/reference/skills.md +10 -0
  20. package/package.json +1 -1
  21. package/releases/manifest.json +39 -0
  22. package/scripts/wave-orchestrator/agent-state.mjs +64 -491
  23. package/scripts/wave-orchestrator/autonomous.mjs +10 -6
  24. package/scripts/wave-orchestrator/{launcher-closure.mjs → closure-engine.mjs} +190 -74
  25. package/scripts/wave-orchestrator/config.mjs +5 -0
  26. package/scripts/wave-orchestrator/coordination.mjs +42 -1
  27. package/scripts/wave-orchestrator/{launcher-derived-state.mjs → derived-state-engine.mjs} +34 -146
  28. package/scripts/wave-orchestrator/{launcher-gates.mjs → gate-engine.mjs} +501 -141
  29. package/scripts/wave-orchestrator/human-input-resolution.mjs +14 -10
  30. package/scripts/wave-orchestrator/human-input-workflow.mjs +104 -0
  31. package/scripts/wave-orchestrator/implementation-engine.mjs +120 -0
  32. package/scripts/wave-orchestrator/install.mjs +3 -0
  33. package/scripts/wave-orchestrator/launcher-runtime.mjs +11 -6
  34. package/scripts/wave-orchestrator/launcher.mjs +324 -723
  35. package/scripts/wave-orchestrator/ledger.mjs +56 -27
  36. package/scripts/wave-orchestrator/local-executor.mjs +37 -0
  37. package/scripts/wave-orchestrator/planner.mjs +24 -4
  38. package/scripts/wave-orchestrator/projection-writer.mjs +256 -0
  39. package/scripts/wave-orchestrator/reconcile-format.mjs +32 -0
  40. package/scripts/wave-orchestrator/reducer-snapshot.mjs +297 -0
  41. package/scripts/wave-orchestrator/replay.mjs +3 -1
  42. package/scripts/wave-orchestrator/result-envelope.mjs +620 -0
  43. package/scripts/wave-orchestrator/retry-control.mjs +22 -2
  44. package/scripts/wave-orchestrator/{launcher-retry.mjs → retry-engine.mjs} +352 -18
  45. package/scripts/wave-orchestrator/role-helpers.mjs +124 -1
  46. package/scripts/wave-orchestrator/{launcher-supervisor.mjs → session-supervisor.mjs} +178 -103
  47. package/scripts/wave-orchestrator/shared.mjs +2 -0
  48. package/scripts/wave-orchestrator/skills.mjs +1 -0
  49. package/scripts/wave-orchestrator/task-entity.mjs +65 -45
  50. package/scripts/wave-orchestrator/traces.mjs +10 -1
  51. package/scripts/wave-orchestrator/wave-files.mjs +96 -10
  52. package/scripts/wave-orchestrator/wave-state-reducer.mjs +76 -12
  53. package/skills/README.md +7 -0
  54. package/skills/role-design/SKILL.md +50 -0
  55. package/skills/role-design/skill.json +36 -0
  56. package/skills/tui-design/SKILL.md +77 -0
  57. package/skills/tui-design/references/tui-design.md +259 -0
  58. package/skills/tui-design/skill.json +36 -0
  59. package/wave.config.json +15 -1
@@ -6,6 +6,7 @@ import {
6
6
  } from "./config.mjs";
7
7
  import {
8
8
  validateContEvalSummary,
9
+ validateDesignSummary,
9
10
  validateDocumentationClosureSummary,
10
11
  validateContQaSummary,
11
12
  validateImplementationSummary,
@@ -13,6 +14,8 @@ import {
13
14
  } from "./agent-state.mjs";
14
15
  import {
15
16
  isContEvalImplementationOwningAgent,
17
+ isDesignAgent,
18
+ isImplementationOwningDesignAgent,
16
19
  isSecurityReviewAgent,
17
20
  } from "./role-helpers.mjs";
18
21
  import { openClarificationLinkedRequests } from "./coordination-store.mjs";
@@ -54,6 +57,7 @@ export function buildSeedWaveLedger({
54
57
  }) {
55
58
  const tasks = [];
56
59
  for (const agent of wave.agents) {
60
+ const hybridDesignAgent = isImplementationOwningDesignAgent(agent);
57
61
  const kind =
58
62
  agent.agentId === contQaAgentId
59
63
  ? "cont-qa"
@@ -63,35 +67,46 @@ export function buildSeedWaveLedger({
63
67
  ? "integration"
64
68
  : agent.agentId === documentationAgentId
65
69
  ? "documentation"
70
+ : isDesignAgent(agent)
71
+ ? "design"
66
72
  : isSecurityReviewAgent(agent)
67
73
  ? "security"
68
- : "implementation";
69
- tasks.push({
70
- id: taskId(kind, agent.agentId),
71
- title: `${agent.agentId}: ${agent.title}`,
72
- owner: agent.agentId,
73
- kind,
74
- dependsOn: [],
75
- state: "planned",
76
- proofState: "pending",
77
- docState: "pending",
78
- infraState: "n/a",
79
- priority:
80
- kind === "implementation" ? "normal" : kind === "integration" ? "high" : "high",
81
- artifactRefs: agent.ownedPaths || [],
82
- runtime: agent.executorResolved
83
- ? {
84
- executorId: agent.executorResolved.id,
85
- role: agent.executorResolved.role,
86
- profile: agent.executorResolved.profile,
87
- selectedBy: agent.executorResolved.selectedBy,
88
- retryPolicy: agent.executorResolved.retryPolicy || null,
89
- allowFallbackOnRetry: agent.executorResolved.allowFallbackOnRetry !== false,
90
- fallbacks: agent.executorResolved.fallbacks || [],
91
- fallbackUsed: agent.executorResolved.fallbackUsed === true,
92
- }
93
- : null,
94
- });
74
+ : "implementation";
75
+ const runtime = agent.executorResolved
76
+ ? {
77
+ executorId: agent.executorResolved.id,
78
+ role: agent.executorResolved.role,
79
+ profile: agent.executorResolved.profile,
80
+ selectedBy: agent.executorResolved.selectedBy,
81
+ retryPolicy: agent.executorResolved.retryPolicy || null,
82
+ allowFallbackOnRetry: agent.executorResolved.allowFallbackOnRetry !== false,
83
+ fallbacks: agent.executorResolved.fallbacks || [],
84
+ fallbackUsed: agent.executorResolved.fallbackUsed === true,
85
+ }
86
+ : null;
87
+ const pushTask = (taskKind) => {
88
+ tasks.push({
89
+ id: taskId(taskKind, agent.agentId),
90
+ title: `${agent.agentId}: ${agent.title}`,
91
+ owner: agent.agentId,
92
+ kind: taskKind,
93
+ dependsOn: [],
94
+ state: "planned",
95
+ proofState: "pending",
96
+ docState: "pending",
97
+ infraState: "n/a",
98
+ priority:
99
+ taskKind === "implementation" ? "normal" : taskKind === "integration" ? "high" : "high",
100
+ artifactRefs: agent.ownedPaths || [],
101
+ runtime,
102
+ });
103
+ };
104
+ if (hybridDesignAgent && kind === "design") {
105
+ pushTask("design");
106
+ pushTask("implementation");
107
+ continue;
108
+ }
109
+ pushTask(kind);
95
110
  }
96
111
  for (const promotion of wave.componentPromotions || []) {
97
112
  tasks.push({
@@ -163,6 +178,11 @@ function derivePhase({
163
178
  return blockingHelperTasks.some((task) => task.state === "blocked") ? "blocked" : "running";
164
179
  }
165
180
  const implementationTasks = tasks.filter((task) => task.kind === "implementation");
181
+ const designTasks = tasks.filter((task) => task.kind === "design");
182
+ const allDesignDone = designTasks.every((task) => task.state === "done");
183
+ if (!allDesignDone && designTasks.length > 0) {
184
+ return "design";
185
+ }
166
186
  const allImplementationDone = implementationTasks.every((task) => task.state === "done");
167
187
  if (!allImplementationDone) {
168
188
  return "running";
@@ -221,6 +241,15 @@ export function deriveWaveLedger({
221
241
  docState: summary?.docDelta?.state || "pending",
222
242
  };
223
243
  }
244
+ if (task.kind === "design" && agent) {
245
+ const validation = validateDesignSummary(agent, summary);
246
+ return {
247
+ ...task,
248
+ state: taskStateFromValidation(validation),
249
+ proofState: validation.ok ? "met" : "gap",
250
+ docState: validation.ok ? "met" : "gap",
251
+ };
252
+ }
224
253
  if (task.kind === "documentation" && agent) {
225
254
  const validation = validateDocumentationClosureSummary(agent, summary);
226
255
  return {
@@ -173,6 +173,11 @@ function formatWaveEvalLine(evalMarker, detail) {
173
173
  return `[wave-eval] state=satisfied targets=${targetIds.length} benchmarks=${benchmarkIds.length} regressions=0${targetIdSegment}${benchmarkIdSegment} detail=${detail}`;
174
174
  }
175
175
 
176
+ function isDesignAgentPrompt(rawPrompt) {
177
+ const text = String(rawPrompt || "");
178
+ return /\[wave-design\]/i.test(text) || /\bwave design\b/i.test(text);
179
+ }
180
+
176
181
  export function resolveRepoOwnedDeliverablePath(relPath) {
177
182
  if (!relPath || path.isAbsolute(relPath)) {
178
183
  throw new Error(`Unsafe deliverable path: ${String(relPath || "")}`);
@@ -277,6 +282,8 @@ export function runLocalExecutorCli(argv) {
277
282
  const contQaAgent = agentId === contQaAgentId;
278
283
  const contEvalAgent = agentId === contEvalAgentId;
279
284
  const integrationAgent = agentId === integrationAgentId;
285
+ const designAgent = isDesignAgentPrompt(rawPrompt);
286
+ const implementationMarkersRequired = /\[wave-proof\]/i.test(rawPrompt);
280
287
  const ownedComponents = extractOwnedComponents(rawPrompt);
281
288
  const assignedPrompt = extractAssignedPrompt(rawPrompt);
282
289
  const ownedPaths = extractFileOwnershipPaths(assignedPrompt);
@@ -304,6 +311,21 @@ export function runLocalExecutorCli(argv) {
304
311
  console.log(
305
312
  "[wave-integration] state=ready-for-doc-closure claims=0 conflicts=0 blockers=0 detail=local-executor-no-deliverables",
306
313
  );
314
+ } else if (designAgent) {
315
+ console.log(
316
+ "[wave-design] state=ready-for-implementation decisions=1 assumptions=1 open_questions=0 detail=local-executor-no-deliverables",
317
+ );
318
+ if (implementationMarkersRequired) {
319
+ console.log(
320
+ "[wave-proof] completion=contract durability=none proof=unit state=met detail=local-executor-no-deliverables",
321
+ );
322
+ console.log("[wave-doc-delta] state=none detail=local-executor-no-deliverables");
323
+ for (const component of ownedComponents) {
324
+ console.log(
325
+ `[wave-component] component=${component.componentId} level=${component.level || "repo-landed"} state=met detail=local-executor-no-deliverables`,
326
+ );
327
+ }
328
+ }
307
329
  } else if (agentId === documentationAgentId) {
308
330
  console.log("[wave-doc-closure] state=no-change detail=local-executor-no-deliverables");
309
331
  } else if (agentId) {
@@ -348,6 +370,21 @@ export function runLocalExecutorCli(argv) {
348
370
  console.log(
349
371
  "[wave-integration] state=ready-for-doc-closure claims=0 conflicts=0 blockers=0 detail=local-executor-smoke",
350
372
  );
373
+ } else if (designAgent) {
374
+ console.log(
375
+ "[wave-design] state=ready-for-implementation decisions=2 assumptions=1 open_questions=0 detail=local-executor-smoke",
376
+ );
377
+ if (implementationMarkersRequired) {
378
+ console.log(
379
+ "[wave-proof] completion=contract durability=none proof=unit state=met detail=local-executor-smoke",
380
+ );
381
+ console.log("[wave-doc-delta] state=owned detail=local-executor-smoke");
382
+ for (const component of ownedComponents) {
383
+ console.log(
384
+ `[wave-component] component=${component.componentId} level=${component.level || "repo-landed"} state=met detail=local-executor-smoke`,
385
+ );
386
+ }
387
+ }
351
388
  } else if (agentId === documentationAgentId) {
352
389
  console.log("[wave-doc-closure] state=no-change detail=local-executor-smoke");
353
390
  } else if (agentId) {
@@ -317,6 +317,9 @@ function defaultTargetLevel(template) {
317
317
  }
318
318
 
319
319
  function defaultExecutorProfile(roleKind) {
320
+ if (roleKind === "design") {
321
+ return "design-pass";
322
+ }
320
323
  if (roleKind === "infra" || roleKind === "deploy" || roleKind === "research") {
321
324
  return "ops-triage";
322
325
  }
@@ -327,7 +330,7 @@ function defaultExecutorProfile(roleKind) {
327
330
  }
328
331
 
329
332
  function defaultExitContract(roleKind) {
330
- if (roleKind === "security") {
333
+ if (roleKind === "security" || roleKind === "design") {
331
334
  return null;
332
335
  }
333
336
  if (roleKind === "infra" || roleKind === "deploy") {
@@ -355,6 +358,9 @@ function defaultExitContract(roleKind) {
355
358
  }
356
359
 
357
360
  function buildDefaultValidationCommand(template, roleKind) {
361
+ if (roleKind === "design") {
362
+ return "Manual review of the design packet against the wave scope, constraints, and downstream ownership.";
363
+ }
358
364
  if (roleKind === "security") {
359
365
  return "Manual review of the changed security-sensitive surfaces plus required proofs.";
360
366
  }
@@ -368,6 +374,9 @@ function buildDefaultValidationCommand(template, roleKind) {
368
374
  }
369
375
 
370
376
  function buildDefaultOutputSummary(template, roleKind) {
377
+ if (roleKind === "design") {
378
+ return "Summarize the design packet, key decisions, assumptions, open questions, and exact implementation handoff.";
379
+ }
371
380
  if (roleKind === "security") {
372
381
  return "Summarize the threat model, findings, required approvals, requested fixes, and final security disposition.";
373
382
  }
@@ -381,6 +390,9 @@ function buildDefaultOutputSummary(template, roleKind) {
381
390
  }
382
391
 
383
392
  function buildDefaultPrimaryGoal(template, roleKind, title) {
393
+ if (roleKind === "design") {
394
+ return `Produce an implementation-ready design packet for the ${title.toLowerCase()} slice before coding starts.`;
395
+ }
384
396
  if (roleKind === "security") {
385
397
  return `Review the ${title.toLowerCase()} slice for security risks and route exact fixes before integration.`;
386
398
  }
@@ -1067,6 +1079,9 @@ function buildWorkerAgentSpec({
1067
1079
  if (roleKind === "research" && !capabilities.includes("research")) {
1068
1080
  capabilities.push("research");
1069
1081
  }
1082
+ if (roleKind === "design" && !capabilities.includes("design")) {
1083
+ capabilities.push("design");
1084
+ }
1070
1085
  return {
1071
1086
  agentId,
1072
1087
  title,
@@ -1075,6 +1090,8 @@ function buildWorkerAgentSpec({
1075
1090
  ? values.rolePromptPaths
1076
1091
  : roleKind === "security"
1077
1092
  ? [lanePaths.securityRolePromptPath]
1093
+ : roleKind === "design"
1094
+ ? [lanePaths.designRolePromptPath]
1078
1095
  : [],
1079
1096
  skills: values.skills || [],
1080
1097
  executor: {
@@ -1934,6 +1951,7 @@ function normalizePlannerContext7Bundle(bundle, bundleIndex) {
1934
1951
  function normalizePlannerWorkerAgent(rawAgent, context) {
1935
1952
  const agentId = cleanText(rawAgent?.agentId) || `A${context.index + 1}`;
1936
1953
  const roleKind = [
1954
+ "design",
1937
1955
  "implementation",
1938
1956
  "qa",
1939
1957
  "infra",
@@ -3067,12 +3085,12 @@ async function collectWorkerAgents({
3067
3085
  const title = cleanText(await prompt.ask(`Worker ${agentId} title`, defaults.title));
3068
3086
  const roleKind = await prompt.askChoice(
3069
3087
  `Worker ${agentId} role kind`,
3070
- ["implementation", "qa", "infra", "deploy", "research", "security"],
3088
+ ["design", "implementation", "qa", "infra", "deploy", "research", "security"],
3071
3089
  defaultRoleKind,
3072
3090
  );
3073
3091
  const executorProfile = await prompt.askChoice(
3074
3092
  `Worker ${agentId} executor profile`,
3075
- ["implement-fast", "deep-review", "eval-tuning", "docs-pass", "ops-triage", "security-review"],
3093
+ ["implement-fast", "design-pass", "deep-review", "eval-tuning", "docs-pass", "ops-triage", "security-review"],
3076
3094
  defaultExecutorProfile(roleKind),
3077
3095
  );
3078
3096
  const ownedPaths = normalizeRepoPathList(
@@ -3081,6 +3099,8 @@ async function collectWorkerAgents({
3081
3099
  `Worker ${agentId} owned paths (comma or | separated)`,
3082
3100
  roleKind === "security"
3083
3101
  ? `.tmp/${lane}-wave-launcher/security/wave-${waveNumber}-review.md`
3102
+ : roleKind === "design"
3103
+ ? `docs/plans/waves/design/wave-${waveNumber}-${agentId}.md`
3084
3104
  : template === "infra"
3085
3105
  ? "scripts/,docs/plans/"
3086
3106
  : template === "release"
@@ -3093,7 +3113,7 @@ async function collectWorkerAgents({
3093
3113
  const components = normalizeListText(
3094
3114
  await prompt.ask(
3095
3115
  `Worker ${agentId} component ids (comma or | separated)`,
3096
- roleKind === "security"
3116
+ roleKind === "security" || roleKind === "design"
3097
3117
  ? ""
3098
3118
  : componentPromotions.map((promotion) => promotion.componentId).join(", "),
3099
3119
  ),
@@ -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
+ }