@chllming/wave-orchestration 0.9.11 → 0.9.13

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 (55) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +7 -8
  3. package/docs/README.md +4 -10
  4. package/docs/architecture/README.md +12 -4
  5. package/docs/concepts/operating-modes.md +1 -1
  6. package/docs/guides/author-and-run-waves.md +3 -2
  7. package/docs/guides/planner.md +3 -3
  8. package/docs/guides/recommendations-0.9.13.md +49 -0
  9. package/docs/guides/sandboxed-environments.md +2 -2
  10. package/docs/guides/terminal-surfaces.md +1 -1
  11. package/docs/plans/current-state.md +3 -3
  12. package/docs/plans/end-state-architecture.md +1 -1
  13. package/docs/plans/examples/wave-example-design-handoff.md +1 -1
  14. package/docs/plans/examples/wave-example-live-proof.md +1 -1
  15. package/docs/plans/migration.md +31 -28
  16. package/docs/plans/wave-orchestrator.md +1 -1
  17. package/docs/reference/cli-reference.md +2 -2
  18. package/docs/reference/coordination-and-closure.md +1 -1
  19. package/docs/reference/npmjs-token-publishing.md +3 -3
  20. package/docs/reference/package-publishing-flow.md +12 -12
  21. package/docs/reference/runtime-config/README.md +2 -2
  22. package/docs/reference/sample-waves.md +5 -5
  23. package/docs/reference/skills.md +1 -1
  24. package/docs/reference/wave-control.md +3 -1
  25. package/docs/roadmap.md +3 -3
  26. package/package.json +8 -9
  27. package/releases/manifest.json +50 -0
  28. package/scripts/context7-api-check.sh +0 -0
  29. package/scripts/context7-export-env.sh +0 -0
  30. package/scripts/wave-autonomous.mjs +0 -0
  31. package/scripts/wave-dashboard.mjs +0 -0
  32. package/scripts/wave-human-feedback.mjs +0 -0
  33. package/scripts/wave-launcher.mjs +0 -0
  34. package/scripts/wave-local-executor.mjs +0 -0
  35. package/scripts/wave-orchestrator/agent-process-runner.mjs +24 -0
  36. package/scripts/wave-orchestrator/agent-state.mjs +4 -4
  37. package/scripts/wave-orchestrator/autonomous.mjs +2 -2
  38. package/scripts/wave-orchestrator/closure-engine.mjs +103 -6
  39. package/scripts/wave-orchestrator/closure-policy.mjs +319 -0
  40. package/scripts/wave-orchestrator/config.mjs +15 -0
  41. package/scripts/wave-orchestrator/coordination.mjs +2 -2
  42. package/scripts/wave-orchestrator/derived-state-engine.mjs +45 -2
  43. package/scripts/wave-orchestrator/executors.mjs +2 -1
  44. package/scripts/wave-orchestrator/gate-engine.mjs +72 -4
  45. package/scripts/wave-orchestrator/install.mjs +1 -1
  46. package/scripts/wave-orchestrator/launcher.mjs +44 -7
  47. package/scripts/wave-orchestrator/planner.mjs +4 -3
  48. package/scripts/wave-orchestrator/shared.mjs +2 -3
  49. package/scripts/wave-orchestrator/swe-bench-pro-task.mjs +1 -1
  50. package/scripts/wave-orchestrator/traces.mjs +22 -1
  51. package/scripts/wave-orchestrator/wave-files.mjs +14 -3
  52. package/scripts/wave-status.sh +0 -0
  53. package/scripts/wave-watch.sh +0 -0
  54. package/scripts/wave.mjs +0 -0
  55. package/wave.config.json +13 -2
@@ -97,6 +97,7 @@ import {
97
97
  markWaveCompleted,
98
98
  parseWaveFiles,
99
99
  reconcileRunStateFromStatusFiles,
100
+ resolveCompletedWavesForValidation,
100
101
  resolveAutoNextWaveStart,
101
102
  validateWaveRuntimeMixAssignments,
102
103
  validateWaveComponentMatrixCurrentLevels,
@@ -265,9 +266,9 @@ Options:
265
266
  --dry-run Parse waves and update manifest only
266
267
  --terminal-surface <mode>
267
268
  Terminal surface: ${TERMINAL_SURFACES.join(" | ")} (default: ${terminalSurface})
268
- --no-dashboard Disable per-wave tmux dashboard session
269
- --cleanup-sessions Kill lane tmux sessions after each wave (default: on)
270
- --keep-sessions Keep lane tmux sessions after each wave
269
+ --no-dashboard Disable the per-wave dashboard projection session
270
+ --cleanup-sessions Clean up lane tmux dashboard/projection sessions after each wave (default: on)
271
+ --keep-sessions Keep lane tmux dashboard/projection sessions after each wave
271
272
  --keep-terminals Do not remove temporary terminal entries after each wave
272
273
  --orchestrator-id <id> Stable orchestrator identity for cross-lane coordination
273
274
  --orchestrator-board <path>
@@ -477,6 +478,15 @@ function parseArgs(argv) {
477
478
  return { help: false, lanePaths, options, config };
478
479
  }
479
480
 
481
+ function maybePrintOptionalTmuxNote(options) {
482
+ if (options.dryRun || options.terminalSurface !== "tmux" || options.dashboard) {
483
+ return;
484
+ }
485
+ console.log(
486
+ "[terminal-surface] tmux is optional here: live agents still run as detached processes, and tmux only affects dashboard/projection attach when dashboards are enabled.",
487
+ );
488
+ }
489
+
480
490
  // --- Local wrappers that bind engine calls to launcher scope ---
481
491
 
482
492
  async function runClosureSweepPhase({
@@ -872,7 +882,7 @@ export async function runLauncherCli(argv) {
872
882
  terminalSurface: options.terminalSurface,
873
883
  });
874
884
  const context7BundleIndex = loadContext7BundleIndex(lanePaths.context7BundleIndexPath);
875
- const allWaves = parseWaveFiles(lanePaths.wavesDir, { laneProfile: lanePaths.laneProfile })
885
+ const parsedWaves = parseWaveFiles(lanePaths.wavesDir, { laneProfile: lanePaths.laneProfile })
876
886
  .map((wave) =>
877
887
  applyExecutorSelectionsToWave(wave, {
878
888
  laneProfile: lanePaths.laneProfile,
@@ -892,8 +902,34 @@ export async function runLauncherCli(argv) {
892
902
  ...resolveWaveRoleBindings(waveWithContext7, lanePaths, waveWithContext7.agents),
893
903
  };
894
904
  },
895
- )
896
- .map((wave) => validateWaveDefinition(wave, { laneProfile: lanePaths.laneProfile }));
905
+ );
906
+ // Read completed waves before validation so we can skip stale-promotion
907
+ // checks for waves that already ran, including waves recoverable from
908
+ // status files when run-state lags behind after a restart.
909
+ const preValidationCompletedWaves = new Set(
910
+ resolveCompletedWavesForValidation(
911
+ parsedWaves,
912
+ options.runStatePath,
913
+ lanePaths.statusDir,
914
+ {
915
+ logsDir: lanePaths.logsDir,
916
+ coordinationDir: lanePaths.coordinationDir,
917
+ contQaAgentId: lanePaths.contQaAgentId,
918
+ contEvalAgentId: lanePaths.contEvalAgentId,
919
+ integrationAgentId: lanePaths.integrationAgentId,
920
+ documentationAgentId: lanePaths.documentationAgentId,
921
+ requireExitContractsFromWave: lanePaths.requireExitContractsFromWave,
922
+ requireIntegrationStewardFromWave: lanePaths.requireIntegrationStewardFromWave,
923
+ requireComponentPromotionsFromWave: lanePaths.requireComponentPromotionsFromWave,
924
+ laneProfile: lanePaths.laneProfile,
925
+ },
926
+ ),
927
+ );
928
+ const allWaves = parsedWaves
929
+ .map((wave) => validateWaveDefinition(wave, {
930
+ laneProfile: lanePaths.laneProfile,
931
+ completedWaves: preValidationCompletedWaves,
932
+ }));
897
933
  const reconciliation = reconcileRunStateFromStatusFiles(
898
934
  allWaves,
899
935
  options.runStatePath,
@@ -1062,11 +1098,12 @@ export async function runLauncherCli(argv) {
1062
1098
  console.log(
1063
1099
  `[dry-run] prompts and executor overlays written: ${path.relative(REPO_ROOT, lanePaths.executorOverlaysDir)}`,
1064
1100
  );
1065
- console.log("Dry run enabled, skipping tmux and executor launch.");
1101
+ console.log("Dry run enabled, skipping live execution and optional dashboard/projection launch.");
1066
1102
  return;
1067
1103
  }
1068
1104
 
1069
1105
  preflightWavesForExecutorAvailability(filteredWaves, lanePaths);
1106
+ maybePrintOptionalTmuxNote(options);
1070
1107
  const terminalRegistryEnabled = terminalSurfaceUsesTerminalRegistry(
1071
1108
  options.terminalSurface,
1072
1109
  );
@@ -2944,9 +2944,10 @@ async function runProjectSetupFlow(options = {}) {
2944
2944
  ),
2945
2945
  );
2946
2946
 
2947
- prompt.describe("\nHow do you want to watch agent sessions?");
2948
- prompt.describe(" vscode agent sessions appear as VS Code terminal tabs");
2949
- prompt.describe(" tmux agent sessions run in tmux panes (terminal-native)");
2947
+ prompt.describe("\nHow do you want to follow live runs?");
2948
+ prompt.describe("Live agent execution stays process-backed either way; this only changes the operator surface.");
2949
+ prompt.describe(" vscode VS Code gets temporary terminal entries for agent logs and dashboards");
2950
+ prompt.describe(" tmux — terminal-native dashboard and projection surface with no VS Code integration");
2950
2951
  const defaultTerminalSurface = normalizeTerminalSurface(
2951
2952
  await prompt.askChoice(
2952
2953
  "Default terminal surface",
@@ -277,6 +277,8 @@ export function buildLanePaths(laneInput = DEFAULT_WAVE_LANE, options = {}) {
277
277
  laneProfile.validation.requireComponentPromotionsFromWave,
278
278
  requireAgentComponentsFromWave: laneProfile.validation.requireAgentComponentsFromWave,
279
279
  gateModeThresholds: laneProfile.validation.gateModeThresholds,
280
+ closureModeThresholds: laneProfile.validation.closureModeThresholds,
281
+ autoClosure: laneProfile.validation.autoClosure,
280
282
  executors: laneProfile.executors,
281
283
  skills: laneProfile.skills,
282
284
  capabilityRouting: laneProfile.capabilityRouting,
@@ -428,9 +430,6 @@ export function normalizeWaveVerdict(verdict) {
428
430
  const normalized = String(verdict || "")
429
431
  .trim()
430
432
  .toLowerCase();
431
- if (normalized === "hold") {
432
- return "concerns";
433
- }
434
433
  if (normalized === "fail") {
435
434
  return "blocked";
436
435
  }
@@ -796,7 +796,7 @@ function buildFullWaveSolve(row, taskWorkspace, options) {
796
796
  });
797
797
  assertSuccess(doctor, "wave doctor");
798
798
  const launch = runShellCommand(
799
- `node ${shellQuote(WAVE_ENTRY)} launch --lane main --start-wave 1 --end-wave 1 --no-dashboard --terminal-surface tmux`,
799
+ `node ${shellQuote(WAVE_ENTRY)} launch --lane main --start-wave 1 --end-wave 1 --no-dashboard`,
800
800
  {
801
801
  cwd: taskWorkspace.repoDir,
802
802
  timeoutMs: options.maxWallClockMinutes * 60 * 1000,
@@ -623,6 +623,7 @@ export function normalizeGateSnapshotForBundle(gateSnapshot, agentArtifacts) {
623
623
  return gateSnapshot;
624
624
  }
625
625
  const normalized = { ...gateSnapshot };
626
+ const overallGate = String(gateSnapshot.overall?.gate || "").trim();
626
627
  for (const key of [
627
628
  "implementationGate",
628
629
  "componentGate",
@@ -638,7 +639,27 @@ export function normalizeGateSnapshotForBundle(gateSnapshot, agentArtifacts) {
638
639
  "evaluatorGate",
639
640
  "infraGate",
640
641
  ]) {
641
- normalized[key] = normalizeGateLogPath(gateSnapshot[key], agentArtifacts);
642
+ const nextValue = normalizeGateLogPath(gateSnapshot[key], agentArtifacts);
643
+ if (key === "documentationGate") {
644
+ normalized[key] =
645
+ overallGate === "documentationGate" && nextValue
646
+ ? {
647
+ ok: Boolean(nextValue.ok),
648
+ statusCode: nextValue.statusCode || null,
649
+ }
650
+ : null;
651
+ continue;
652
+ }
653
+ if (key === "integrationGate" || key === "integrationBarrier") {
654
+ normalized[key] = nextValue
655
+ ? {
656
+ ok: Boolean(nextValue.ok),
657
+ statusCode: nextValue.statusCode || null,
658
+ }
659
+ : null;
660
+ continue;
661
+ }
662
+ normalized[key] = nextValue;
642
663
  }
643
664
  return normalized;
644
665
  }
@@ -1534,9 +1534,13 @@ export function validateWaveDefinition(wave, options = {}) {
1534
1534
  `Wave ${wave.wave} promotes ${componentId} to ${targetLevel}, but ${componentMatrix.jsonPath} declares ${matrixPromotion.target}`,
1535
1535
  );
1536
1536
  } else if (componentMatrix.levelOrder[targetLevel] < componentMatrix.levelOrder[component.currentLevel]) {
1537
- errors.push(
1538
- `Wave ${wave.wave} promotes ${componentId} to ${targetLevel}, but ${componentMatrix.jsonPath} already records currentLevel ${component.currentLevel}`,
1539
- );
1537
+ // Skip this error for already-completed waves — their promotions may have
1538
+ // been superseded by later waves that advanced the component further.
1539
+ if (!options?.completedWaves?.has(wave.wave)) {
1540
+ errors.push(
1541
+ `Wave ${wave.wave} promotes ${componentId} to ${targetLevel}, but ${componentMatrix.jsonPath} already records currentLevel ${component.currentLevel}`,
1542
+ );
1543
+ }
1540
1544
  }
1541
1545
  }
1542
1546
  const matrixWavePromotions = Object.entries(componentMatrix.components)
@@ -3294,6 +3298,13 @@ export function completedWavesFromStatusFiles(allWaves, statusDir, options = {})
3294
3298
  return normalizeCompletedWaves(completed);
3295
3299
  }
3296
3300
 
3301
+ export function resolveCompletedWavesForValidation(allWaves, runStatePath, statusDir, options = {}) {
3302
+ return normalizeCompletedWaves([
3303
+ ...readRunState(runStatePath).completedWaves,
3304
+ ...completedWavesFromStatusFiles(allWaves, statusDir, options),
3305
+ ]);
3306
+ }
3307
+
3297
3308
  export function reconcileRunStateFromStatusFiles(allWaves, runStatePath, statusDir, options = {}) {
3298
3309
  const diagnostics = allWaves.map((wave) => ({
3299
3310
  wave: wave.wave,
File without changes
File without changes
package/scripts/wave.mjs CHANGED
File without changes
package/wave.config.json CHANGED
@@ -276,7 +276,18 @@
276
276
  "requireExitContractsFromWave": 6,
277
277
  "requireIntegrationStewardFromWave": 0,
278
278
  "requireComponentPromotionsFromWave": 0,
279
- "requireAgentComponentsFromWave": 0
279
+ "requireAgentComponentsFromWave": 0,
280
+ "closureModeThresholds": {
281
+ "bootstrap": 0,
282
+ "standard": 4,
283
+ "strict": 10
284
+ },
285
+ "autoClosure": {
286
+ "allowInferredIntegration": true,
287
+ "allowAutoDocNoChange": true,
288
+ "allowAutoDocProjection": false,
289
+ "allowSkipContQaInBootstrap": true
290
+ }
280
291
  },
281
292
  "capabilityRouting": {
282
293
  "preferredAgents": {}
@@ -309,4 +320,4 @@
309
320
  }
310
321
  }
311
322
  }
312
- }
323
+ }