@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.
- package/CHANGELOG.md +18 -0
- package/README.md +14 -13
- package/docs/README.md +3 -1
- package/docs/agents/wave-cont-qa-role.md +1 -0
- package/docs/agents/wave-integration-role.md +1 -0
- package/docs/agents/wave-launcher-role.md +4 -1
- package/docs/agents/wave-orchestrator-role.md +5 -3
- package/docs/concepts/operating-modes.md +1 -1
- package/docs/concepts/runtime-agnostic-orchestration.md +5 -4
- package/docs/concepts/what-is-a-wave.md +12 -10
- package/docs/guides/author-and-run-waves.md +3 -3
- package/docs/plans/architecture-hardening-migration.md +206 -0
- package/docs/plans/current-state.md +5 -3
- package/docs/plans/examples/wave-example-live-proof.md +1 -1
- package/docs/plans/master-plan.md +2 -2
- package/docs/plans/migration.md +12 -2
- package/docs/plans/wave-orchestrator.md +10 -8
- package/docs/reference/coordination-and-closure.md +8 -4
- package/docs/reference/npmjs-trusted-publishing.md +2 -2
- package/docs/reference/sample-waves.md +4 -4
- package/docs/reference/skills.md +3 -0
- package/docs/reference/wave-control.md +4 -2
- package/docs/research/coordination-failure-review.md +4 -4
- package/docs/roadmap.md +1 -1
- package/package.json +1 -1
- package/releases/manifest.json +19 -0
- package/scripts/wave-orchestrator/agent-state.mjs +405 -89
- package/scripts/wave-orchestrator/contradiction-entity.mjs +487 -0
- package/scripts/wave-orchestrator/launcher-gates.mjs +79 -11
- package/scripts/wave-orchestrator/launcher-retry.mjs +36 -6
- package/scripts/wave-orchestrator/launcher.mjs +163 -2
- package/scripts/wave-orchestrator/task-entity.mjs +425 -51
- package/scripts/wave-orchestrator/wave-control-schema.mjs +2 -0
- package/scripts/wave-orchestrator/wave-state-reducer.mjs +260 -111
- package/skills/README.md +3 -0
- package/skills/repo-coding-rules/SKILL.md +1 -1
- package/skills/role-cont-qa/SKILL.md +2 -2
- package/skills/role-deploy/SKILL.md +1 -1
- package/skills/role-documentation/SKILL.md +1 -1
- package/skills/role-implementation/SKILL.md +1 -1
- package/skills/role-infra/SKILL.md +1 -1
- package/skills/role-integration/SKILL.md +2 -2
- package/skills/role-security/SKILL.md +1 -1
- package/skills/runtime-claude/SKILL.md +1 -1
- package/skills/runtime-codex/SKILL.md +1 -1
- package/skills/runtime-opencode/SKILL.md +1 -1
- package/skills/wave-core/SKILL.md +14 -6
- package/skills/wave-core/references/marker-syntax.md +1 -1
|
@@ -226,6 +226,33 @@ export function relaunchReasonBuckets(runs, failures, derivedState) {
|
|
|
226
226
|
};
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
const HUMAN_INPUT_BLOCKER_KINDS = new Set([
|
|
230
|
+
"human-input",
|
|
231
|
+
"human-feedback",
|
|
232
|
+
"human-escalation",
|
|
233
|
+
]);
|
|
234
|
+
|
|
235
|
+
function isHumanInputBlocker(blocker) {
|
|
236
|
+
return HUMAN_INPUT_BLOCKER_KINDS.has(String(blocker?.kind || "").trim().toLowerCase());
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function normalizeRetryTargets(retryTargetSet) {
|
|
240
|
+
if (Array.isArray(retryTargetSet)) {
|
|
241
|
+
return retryTargetSet;
|
|
242
|
+
}
|
|
243
|
+
if (Array.isArray(retryTargetSet?.targets)) {
|
|
244
|
+
return retryTargetSet.targets;
|
|
245
|
+
}
|
|
246
|
+
if (Array.isArray(retryTargetSet?.agentIds)) {
|
|
247
|
+
return retryTargetSet.agentIds.map((agentId) => ({
|
|
248
|
+
agentId,
|
|
249
|
+
reason: retryTargetSet.reason || null,
|
|
250
|
+
retryOverride: retryTargetSet.retryOverride || null,
|
|
251
|
+
}));
|
|
252
|
+
}
|
|
253
|
+
return [];
|
|
254
|
+
}
|
|
255
|
+
|
|
229
256
|
export function applySharedComponentWaitStateToDashboard(componentGate, dashboardState) {
|
|
230
257
|
const waitingSummary = (componentGate?.waitingOnAgentIds || []).join("/");
|
|
231
258
|
if (!waitingSummary) {
|
|
@@ -769,7 +796,7 @@ function classifyResumeReason(waveState) {
|
|
|
769
796
|
return "all-gates-pass";
|
|
770
797
|
}
|
|
771
798
|
const humanBlockers = (waveState.openBlockers || []).filter(
|
|
772
|
-
(blocker) => blocker
|
|
799
|
+
(blocker) => isHumanInputBlocker(blocker),
|
|
773
800
|
);
|
|
774
801
|
if (humanBlockers.length > 0) {
|
|
775
802
|
return "human-request";
|
|
@@ -792,11 +819,14 @@ function classifyResumeReason(waveState) {
|
|
|
792
819
|
|
|
793
820
|
function collectResumeHumanInputBlockers(waveState) {
|
|
794
821
|
return (waveState.openBlockers || [])
|
|
795
|
-
.filter((b) => b
|
|
822
|
+
.filter((b) => isHumanInputBlocker(b))
|
|
796
823
|
.map((b) => ({
|
|
797
824
|
taskId: b.taskId || b.id || null,
|
|
798
825
|
title: b.title || b.detail || null,
|
|
799
|
-
assigneeAgentId:
|
|
826
|
+
assigneeAgentId:
|
|
827
|
+
b.agentId ||
|
|
828
|
+
b.assigneeAgentId ||
|
|
829
|
+
(Array.isArray(b.blockedAgentIds) ? b.blockedAgentIds[0] || null : null),
|
|
800
830
|
}));
|
|
801
831
|
}
|
|
802
832
|
|
|
@@ -813,13 +843,13 @@ function collectResumeGateBlockers(gateSnapshot) {
|
|
|
813
843
|
}
|
|
814
844
|
|
|
815
845
|
function collectResumeExecutorChanges(waveState) {
|
|
816
|
-
const retryTargets = waveState.retryTargetSet
|
|
817
|
-
return
|
|
846
|
+
const retryTargets = normalizeRetryTargets(waveState.retryTargetSet);
|
|
847
|
+
return retryTargets
|
|
818
848
|
.filter((t) => t.reason === "rate-limit-exhausted" || t.reason === "rate-limit" || t.retriesExhausted === true)
|
|
819
849
|
.map((t) => ({
|
|
820
850
|
agentId: t.agentId,
|
|
821
851
|
currentExecutor: t.currentExecutor || t.executor || null,
|
|
822
|
-
suggestedFallback: "claude",
|
|
852
|
+
suggestedFallback: t.retryOverride?.executorId || "claude",
|
|
823
853
|
reason: t.reason || "rate-limit-exhausted",
|
|
824
854
|
}));
|
|
825
855
|
}
|
|
@@ -145,9 +145,11 @@ import {
|
|
|
145
145
|
resolveRetryOverrideRuns,
|
|
146
146
|
waveRelaunchPlanPath,
|
|
147
147
|
} from "./retry-control.mjs";
|
|
148
|
-
import { appendWaveControlEvent } from "./control-plane.mjs";
|
|
148
|
+
import { appendWaveControlEvent, readControlPlaneEvents } from "./control-plane.mjs";
|
|
149
|
+
import { materializeContradictionsFromControlPlaneEvents } from "./contradiction-entity.mjs";
|
|
149
150
|
import { buildQualityMetrics, writeTraceBundle } from "./traces.mjs";
|
|
150
151
|
import { flushWaveControlQueue } from "./wave-control-client.mjs";
|
|
152
|
+
import { reduceWaveState } from "./wave-state-reducer.mjs";
|
|
151
153
|
import { triageClarificationRequests } from "./clarification-triage.mjs";
|
|
152
154
|
import { readProjectProfile, resolveDefaultTerminalSurface } from "./project-profile.mjs";
|
|
153
155
|
import {
|
|
@@ -167,9 +169,11 @@ import {
|
|
|
167
169
|
writeDependencySnapshotMarkdown,
|
|
168
170
|
} from "./routing-state.mjs";
|
|
169
171
|
import {
|
|
172
|
+
readWaveStateSnapshot,
|
|
170
173
|
writeAssignmentSnapshot,
|
|
171
174
|
writeDependencySnapshot,
|
|
172
175
|
writeRelaunchPlan,
|
|
176
|
+
writeWaveStateSnapshot,
|
|
173
177
|
} from "./artifact-schemas.mjs";
|
|
174
178
|
import {
|
|
175
179
|
collectUnexpectedSessionFailures as collectUnexpectedSessionFailuresImpl,
|
|
@@ -238,6 +242,7 @@ export {
|
|
|
238
242
|
|
|
239
243
|
// --- Re-exports from launcher-retry.mjs ---
|
|
240
244
|
import {
|
|
245
|
+
buildResumePlan,
|
|
241
246
|
readWaveRelaunchPlan,
|
|
242
247
|
writeWaveRelaunchPlan,
|
|
243
248
|
clearWaveRelaunchPlan,
|
|
@@ -1070,6 +1075,18 @@ export async function runLauncherCli(argv) {
|
|
|
1070
1075
|
attempt: attemptNumber,
|
|
1071
1076
|
orchestratorId: options.orchestratorId,
|
|
1072
1077
|
});
|
|
1078
|
+
const controlPlaneLogPath = path.join(
|
|
1079
|
+
lanePaths.controlPlaneDir,
|
|
1080
|
+
`wave-${wave.wave}.jsonl`,
|
|
1081
|
+
);
|
|
1082
|
+
const controlPlaneEvents = fs.existsSync(controlPlaneLogPath)
|
|
1083
|
+
? readControlPlaneEvents(controlPlaneLogPath)
|
|
1084
|
+
: [];
|
|
1085
|
+
derivedState = {
|
|
1086
|
+
...derivedState,
|
|
1087
|
+
controlPlaneEvents,
|
|
1088
|
+
contradictions: materializeContradictionsFromControlPlaneEvents(controlPlaneEvents),
|
|
1089
|
+
};
|
|
1073
1090
|
for (const run of agentRuns) {
|
|
1074
1091
|
run.messageBoardSnapshot = derivedState.messageBoardText;
|
|
1075
1092
|
run.sharedSummaryPath = derivedState.sharedSummaryPath;
|
|
@@ -1988,6 +2005,22 @@ export async function runLauncherCli(argv) {
|
|
|
1988
2005
|
validationMode: "live",
|
|
1989
2006
|
});
|
|
1990
2007
|
completionGateSnapshot = gateSnapshot;
|
|
2008
|
+
try {
|
|
2009
|
+
computeReducerSnapshot({
|
|
2010
|
+
lanePaths,
|
|
2011
|
+
wave,
|
|
2012
|
+
agentRuns,
|
|
2013
|
+
derivedState,
|
|
2014
|
+
attempt,
|
|
2015
|
+
options,
|
|
2016
|
+
});
|
|
2017
|
+
} catch (error) {
|
|
2018
|
+
recordCombinedEvent({
|
|
2019
|
+
level: "warn",
|
|
2020
|
+
agentId: lanePaths.integrationAgentId,
|
|
2021
|
+
message: `Reducer shadow snapshot failed for wave ${wave.wave}: ${error instanceof Error ? error.message : String(error)}`,
|
|
2022
|
+
});
|
|
2023
|
+
}
|
|
1991
2024
|
const traceDir = writeTraceBundle({
|
|
1992
2025
|
tracesDir: lanePaths.tracesDir,
|
|
1993
2026
|
lanePaths,
|
|
@@ -2039,7 +2072,11 @@ export async function runLauncherCli(argv) {
|
|
|
2039
2072
|
traceDir: path.relative(REPO_ROOT, traceDir),
|
|
2040
2073
|
gateSnapshot,
|
|
2041
2074
|
qualitySummary: {
|
|
2042
|
-
contradictionCount:
|
|
2075
|
+
contradictionCount: Array.isArray(derivedState?.contradictions)
|
|
2076
|
+
? derivedState.contradictions.length
|
|
2077
|
+
: derivedState?.contradictions instanceof Map
|
|
2078
|
+
? derivedState.contradictions.size
|
|
2079
|
+
: 0,
|
|
2043
2080
|
finalRecommendation: derivedState.integrationSummary?.recommendation || "unknown",
|
|
2044
2081
|
},
|
|
2045
2082
|
},
|
|
@@ -2409,3 +2446,127 @@ export async function runLauncherCli(argv) {
|
|
|
2409
2446
|
}
|
|
2410
2447
|
}
|
|
2411
2448
|
}
|
|
2449
|
+
|
|
2450
|
+
/**
|
|
2451
|
+
* Compute and persist a reducer snapshot alongside the traditional gate evaluation.
|
|
2452
|
+
* Shadow mode: the reducer runs and its output is written to disk, but decisions
|
|
2453
|
+
* still come from the traditional gate readers. This enables comparison and validation.
|
|
2454
|
+
*
|
|
2455
|
+
* @param {object} params
|
|
2456
|
+
* @param {object} params.lanePaths
|
|
2457
|
+
* @param {object} params.wave - Wave definition
|
|
2458
|
+
* @param {object} params.agentRuns - Array of run info objects
|
|
2459
|
+
* @param {object} params.derivedState - Current derived state
|
|
2460
|
+
* @param {number} params.attempt - Current attempt number
|
|
2461
|
+
* @param {object} params.options - Launcher options
|
|
2462
|
+
* @returns {object} { reducerState, resumePlan, snapshotPath }
|
|
2463
|
+
*/
|
|
2464
|
+
export function computeReducerSnapshot({
|
|
2465
|
+
lanePaths,
|
|
2466
|
+
wave,
|
|
2467
|
+
agentRuns,
|
|
2468
|
+
derivedState,
|
|
2469
|
+
attempt,
|
|
2470
|
+
options = {},
|
|
2471
|
+
}) {
|
|
2472
|
+
// Build agentResults from agentRuns
|
|
2473
|
+
const agentResults = {};
|
|
2474
|
+
for (const run of agentRuns) {
|
|
2475
|
+
const summary = readRunExecutionSummary(run, wave);
|
|
2476
|
+
if (summary) {
|
|
2477
|
+
agentResults[run.agent.agentId] = summary;
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2481
|
+
// Load canonical event sources
|
|
2482
|
+
const controlPlaneLogPath = path.join(
|
|
2483
|
+
lanePaths.controlPlaneDir,
|
|
2484
|
+
`wave-${wave.wave}.jsonl`,
|
|
2485
|
+
);
|
|
2486
|
+
const controlPlaneEvents = fs.existsSync(controlPlaneLogPath)
|
|
2487
|
+
? readControlPlaneEvents(controlPlaneLogPath)
|
|
2488
|
+
: [];
|
|
2489
|
+
|
|
2490
|
+
const coordinationLogPath = path.join(
|
|
2491
|
+
lanePaths.coordinationDir,
|
|
2492
|
+
`wave-${wave.wave}.jsonl`,
|
|
2493
|
+
);
|
|
2494
|
+
const coordinationRecords = fs.existsSync(coordinationLogPath)
|
|
2495
|
+
? readMaterializedCoordinationState(coordinationLogPath)
|
|
2496
|
+
: null;
|
|
2497
|
+
|
|
2498
|
+
const feedbackRequests = readWaveHumanFeedbackRequests({
|
|
2499
|
+
feedbackRequestsDir: lanePaths.feedbackRequestsDir,
|
|
2500
|
+
lane: lanePaths.lane,
|
|
2501
|
+
waveNumber: wave.wave,
|
|
2502
|
+
agentIds: (agentRuns || []).map((run) => run.agent.agentId),
|
|
2503
|
+
orchestratorId: options.orchestratorId,
|
|
2504
|
+
});
|
|
2505
|
+
|
|
2506
|
+
// Build dependency tickets from derivedState
|
|
2507
|
+
const dependencyTickets = derivedState?.dependencySnapshot || null;
|
|
2508
|
+
|
|
2509
|
+
// Run the reducer
|
|
2510
|
+
const reducerState = reduceWaveState({
|
|
2511
|
+
controlPlaneEvents,
|
|
2512
|
+
coordinationRecords: coordinationRecords?.latestRecords || [],
|
|
2513
|
+
agentResults,
|
|
2514
|
+
waveDefinition: wave,
|
|
2515
|
+
dependencyTickets,
|
|
2516
|
+
feedbackRequests: feedbackRequests || [],
|
|
2517
|
+
laneConfig: {
|
|
2518
|
+
lane: lanePaths.lane,
|
|
2519
|
+
contQaAgentId: lanePaths.contQaAgentId || "A0",
|
|
2520
|
+
contEvalAgentId: lanePaths.contEvalAgentId || "E0",
|
|
2521
|
+
integrationAgentId: lanePaths.integrationAgentId || "A8",
|
|
2522
|
+
documentationAgentId: lanePaths.documentationAgentId || "A9",
|
|
2523
|
+
validationMode: "live",
|
|
2524
|
+
evalTargets: wave.evalTargets,
|
|
2525
|
+
benchmarkCatalogPath: lanePaths.laneProfile?.paths?.benchmarkCatalogPath,
|
|
2526
|
+
laneProfile: lanePaths.laneProfile,
|
|
2527
|
+
requireIntegrationStewardFromWave: lanePaths.requireIntegrationStewardFromWave,
|
|
2528
|
+
capabilityRouting: lanePaths.capabilityRouting,
|
|
2529
|
+
},
|
|
2530
|
+
});
|
|
2531
|
+
|
|
2532
|
+
// Build resume plan
|
|
2533
|
+
const resumePlan = buildResumePlan(reducerState, {
|
|
2534
|
+
waveDefinition: wave,
|
|
2535
|
+
lanePaths,
|
|
2536
|
+
});
|
|
2537
|
+
|
|
2538
|
+
// Persist snapshot
|
|
2539
|
+
const stateDir = path.join(lanePaths.stateDir, "reducer");
|
|
2540
|
+
ensureDirectory(stateDir);
|
|
2541
|
+
const snapshotPath = path.join(stateDir, `wave-${wave.wave}.json`);
|
|
2542
|
+
writeWaveStateSnapshot(snapshotPath, {
|
|
2543
|
+
...reducerState,
|
|
2544
|
+
attempt,
|
|
2545
|
+
resumePlan,
|
|
2546
|
+
}, {
|
|
2547
|
+
lane: lanePaths.lane,
|
|
2548
|
+
wave: wave.wave,
|
|
2549
|
+
});
|
|
2550
|
+
|
|
2551
|
+
return {
|
|
2552
|
+
reducerState,
|
|
2553
|
+
resumePlan,
|
|
2554
|
+
snapshotPath,
|
|
2555
|
+
};
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
/**
|
|
2559
|
+
* Read a previously persisted reducer snapshot from disk.
|
|
2560
|
+
*
|
|
2561
|
+
* @param {object} lanePaths
|
|
2562
|
+
* @param {number} waveNumber
|
|
2563
|
+
* @returns {object|null} The persisted snapshot, or null if not found
|
|
2564
|
+
*/
|
|
2565
|
+
export function readPersistedReducerSnapshot(lanePaths, waveNumber) {
|
|
2566
|
+
const stateDir = path.join(lanePaths.stateDir, "reducer");
|
|
2567
|
+
const snapshotPath = path.join(stateDir, `wave-${waveNumber}.json`);
|
|
2568
|
+
return readWaveStateSnapshot(snapshotPath, {
|
|
2569
|
+
lane: lanePaths.lane,
|
|
2570
|
+
wave: waveNumber,
|
|
2571
|
+
});
|
|
2572
|
+
}
|