@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.
- package/CHANGELOG.md +40 -0
- package/README.md +9 -8
- package/docs/guides/planner.md +19 -0
- package/docs/guides/terminal-surfaces.md +12 -0
- package/docs/plans/component-cutover-matrix.json +50 -3
- package/docs/plans/current-state.md +1 -1
- package/docs/plans/end-state-architecture.md +927 -0
- package/docs/plans/examples/wave-example-live-proof.md +1 -1
- package/docs/plans/migration.md +26 -0
- package/docs/plans/wave-orchestrator.md +4 -7
- package/docs/plans/waves/wave-1.md +376 -0
- package/docs/plans/waves/wave-2.md +292 -0
- package/docs/plans/waves/wave-3.md +342 -0
- package/docs/plans/waves/wave-4.md +391 -0
- package/docs/plans/waves/wave-5.md +382 -0
- package/docs/plans/waves/wave-6.md +321 -0
- package/docs/reference/cli-reference.md +547 -0
- package/docs/reference/coordination-and-closure.md +1 -1
- package/docs/reference/npmjs-trusted-publishing.md +2 -2
- package/docs/reference/runtime-config/README.md +2 -2
- package/docs/reference/runtime-config/codex.md +2 -1
- package/docs/reference/sample-waves.md +4 -4
- package/package.json +1 -1
- package/releases/manifest.json +43 -2
- package/scripts/wave-orchestrator/agent-state.mjs +458 -35
- package/scripts/wave-orchestrator/artifact-schemas.mjs +81 -0
- package/scripts/wave-orchestrator/control-cli.mjs +119 -20
- package/scripts/wave-orchestrator/coordination.mjs +11 -10
- package/scripts/wave-orchestrator/dashboard-renderer.mjs +82 -2
- package/scripts/wave-orchestrator/human-input-workflow.mjs +289 -0
- package/scripts/wave-orchestrator/install.mjs +120 -3
- package/scripts/wave-orchestrator/launcher-derived-state.mjs +915 -0
- package/scripts/wave-orchestrator/launcher-gates.mjs +1061 -0
- package/scripts/wave-orchestrator/launcher-retry.mjs +873 -0
- package/scripts/wave-orchestrator/launcher-runtime.mjs +9 -9
- package/scripts/wave-orchestrator/launcher-supervisor.mjs +704 -0
- package/scripts/wave-orchestrator/launcher.mjs +317 -2999
- package/scripts/wave-orchestrator/task-entity.mjs +557 -0
- package/scripts/wave-orchestrator/terminals.mjs +1 -1
- package/scripts/wave-orchestrator/wave-files.mjs +138 -20
- package/scripts/wave-orchestrator/wave-state-reducer.mjs +566 -0
- package/wave.config.json +1 -1
|
@@ -1202,11 +1202,20 @@ function materializeLiveExecutionSummaryIfMissing({
|
|
|
1202
1202
|
contQaAgentId,
|
|
1203
1203
|
contEvalAgentId,
|
|
1204
1204
|
}) {
|
|
1205
|
-
const
|
|
1205
|
+
const logPath = logsDir ? path.join(logsDir, `wave-${wave.wave}-${agent.slug}.log`) : null;
|
|
1206
|
+
const existing = readAgentExecutionSummary(statusPath, {
|
|
1207
|
+
agent,
|
|
1208
|
+
statusPath,
|
|
1209
|
+
statusRecord,
|
|
1210
|
+
logPath,
|
|
1211
|
+
reportPath: resolveAgentSummaryReportPath(wave, agent.agentId, {
|
|
1212
|
+
contQaAgentId,
|
|
1213
|
+
contEvalAgentId,
|
|
1214
|
+
}),
|
|
1215
|
+
});
|
|
1206
1216
|
if (existing) {
|
|
1207
1217
|
return existing;
|
|
1208
1218
|
}
|
|
1209
|
-
const logPath = logsDir ? path.join(logsDir, `wave-${wave.wave}-${agent.slug}.log`) : null;
|
|
1210
1219
|
if (!statusRecord || !logPath || !fs.existsSync(logPath)) {
|
|
1211
1220
|
return null;
|
|
1212
1221
|
}
|
|
@@ -2421,6 +2430,17 @@ function relativeRepoPathOrNull(filePath) {
|
|
|
2421
2430
|
return filePath ? path.relative(REPO_ROOT, filePath) : null;
|
|
2422
2431
|
}
|
|
2423
2432
|
|
|
2433
|
+
const RUN_STATE_COMPLETED_VALUES = new Set(["completed", "completed_with_drift"]);
|
|
2434
|
+
const PROMPT_DRIFT_REASON_CODES = new Set(["prompt-hash-mismatch", "prompt-hash-missing"]);
|
|
2435
|
+
|
|
2436
|
+
function isCompletedRunStateValue(value) {
|
|
2437
|
+
return RUN_STATE_COMPLETED_VALUES.has(String(value || "").trim().toLowerCase());
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
function completedRunStateEntries(waves) {
|
|
2441
|
+
return Object.values(waves || {}).filter((entry) => isCompletedRunStateValue(entry?.currentState));
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2424
2444
|
function normalizeRunStateWaveEntry(rawEntry, waveNumber) {
|
|
2425
2445
|
const source = rawEntry && typeof rawEntry === "object" && !Array.isArray(rawEntry) ? rawEntry : {};
|
|
2426
2446
|
const normalizedWave = normalizeCompletedWaves([waveNumber])[0] ?? normalizeCompletedWaves([source.wave])[0] ?? null;
|
|
@@ -2465,9 +2485,7 @@ function normalizeRunStateHistoryEntry(rawEntry, seqFallback) {
|
|
|
2465
2485
|
|
|
2466
2486
|
function completedWavesFromStateEntries(waves) {
|
|
2467
2487
|
return normalizeCompletedWaves(
|
|
2468
|
-
|
|
2469
|
-
.filter((entry) => entry?.currentState === "completed")
|
|
2470
|
-
.map((entry) => entry.wave),
|
|
2488
|
+
completedRunStateEntries(waves).map((entry) => entry.wave),
|
|
2471
2489
|
);
|
|
2472
2490
|
}
|
|
2473
2491
|
|
|
@@ -2744,6 +2762,64 @@ function pushWaveCompletionReason(reasons, code, detail) {
|
|
|
2744
2762
|
reasons.push({ code: normalizedCode, detail: normalizedDetail });
|
|
2745
2763
|
}
|
|
2746
2764
|
|
|
2765
|
+
function promptDriftReasonForStatus(agent, statusPath, statusRecord, expectedPromptHash) {
|
|
2766
|
+
const actualPromptHash = String(statusRecord?.promptHash || "").trim();
|
|
2767
|
+
if (!actualPromptHash) {
|
|
2768
|
+
return {
|
|
2769
|
+
code: "prompt-hash-missing",
|
|
2770
|
+
detail: `${agent.agentId} status in ${path.relative(REPO_ROOT, statusPath)} is missing prompt-hash metadata required to match the current prompt fingerprint.`,
|
|
2771
|
+
};
|
|
2772
|
+
}
|
|
2773
|
+
if (actualPromptHash !== expectedPromptHash) {
|
|
2774
|
+
return {
|
|
2775
|
+
code: "prompt-hash-mismatch",
|
|
2776
|
+
detail: `${agent.agentId} status in ${path.relative(REPO_ROOT, statusPath)} does not match the current prompt fingerprint.`,
|
|
2777
|
+
};
|
|
2778
|
+
}
|
|
2779
|
+
return null;
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
function diagnosticHasOnlyPromptDriftReasons(diagnostic) {
|
|
2783
|
+
return (
|
|
2784
|
+
Array.isArray(diagnostic?.reasons) &&
|
|
2785
|
+
diagnostic.reasons.length > 0 &&
|
|
2786
|
+
diagnostic.reasons.every((reason) => PROMPT_DRIFT_REASON_CODES.has(String(reason?.code || "").trim()))
|
|
2787
|
+
);
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
function isAuthoritativeCompletedRunStateEntry(entry) {
|
|
2791
|
+
if (!isCompletedRunStateValue(entry?.currentState)) {
|
|
2792
|
+
return false;
|
|
2793
|
+
}
|
|
2794
|
+
const source = String(entry?.lastSource || "").trim().toLowerCase();
|
|
2795
|
+
return source !== "" && source !== "legacy-run-state";
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
function buildPreservedCompletionEvidence(previousEntry, diagnostic) {
|
|
2799
|
+
const baseEvidence =
|
|
2800
|
+
diagnostic?.evidence && typeof diagnostic.evidence === "object" && !Array.isArray(diagnostic.evidence)
|
|
2801
|
+
? { ...diagnostic.evidence }
|
|
2802
|
+
: {};
|
|
2803
|
+
baseEvidence.preservedCompletion = {
|
|
2804
|
+
preserved: true,
|
|
2805
|
+
preservedFromState: previousEntry?.currentState || "completed",
|
|
2806
|
+
preservedFromSource: previousEntry?.lastSource || null,
|
|
2807
|
+
preservedFromReasonCode: previousEntry?.lastReasonCode || null,
|
|
2808
|
+
driftReasons: (diagnostic?.reasons || [])
|
|
2809
|
+
.filter((reason) => PROMPT_DRIFT_REASON_CODES.has(String(reason?.code || "").trim()))
|
|
2810
|
+
.map((reason) => ({
|
|
2811
|
+
code: String(reason?.code || "").trim(),
|
|
2812
|
+
detail: String(reason?.detail || "").trim(),
|
|
2813
|
+
})),
|
|
2814
|
+
previousEvidence: previousEntry?.lastEvidence || null,
|
|
2815
|
+
};
|
|
2816
|
+
return baseEvidence;
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
function shouldPreserveCompletedWave(previousEntry, diagnostic) {
|
|
2820
|
+
return isAuthoritativeCompletedRunStateEntry(previousEntry) && diagnosticHasOnlyPromptDriftReasons(diagnostic);
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2747
2823
|
function analyzeWaveCompletionFromStatusFiles(wave, statusDir, options = {}) {
|
|
2748
2824
|
const logsDir = options.logsDir || path.join(path.resolve(statusDir, ".."), "logs");
|
|
2749
2825
|
const coordinationDir =
|
|
@@ -2770,6 +2846,7 @@ function analyzeWaveCompletionFromStatusFiles(wave, statusDir, options = {}) {
|
|
|
2770
2846
|
const statusEntries = [];
|
|
2771
2847
|
const missingStatusAgents = [];
|
|
2772
2848
|
let statusesReady = wave.agents.length > 0;
|
|
2849
|
+
let summaryValidationReady = wave.agents.length > 0;
|
|
2773
2850
|
const coordinationLogPath = path.join(coordinationDir, `wave-${wave.wave}.jsonl`);
|
|
2774
2851
|
const assignmentsPath = path.join(assignmentsDir, `wave-${wave.wave}.json`);
|
|
2775
2852
|
const dependencySnapshotPath = path.join(dependencySnapshotsDir, `wave-${wave.wave}.json`);
|
|
@@ -2780,6 +2857,7 @@ function analyzeWaveCompletionFromStatusFiles(wave, statusDir, options = {}) {
|
|
|
2780
2857
|
if (!statusRecord) {
|
|
2781
2858
|
missingStatusAgents.push(agent.agentId);
|
|
2782
2859
|
statusesReady = false;
|
|
2860
|
+
summaryValidationReady = false;
|
|
2783
2861
|
continue;
|
|
2784
2862
|
}
|
|
2785
2863
|
const summaryPath = agentSummaryPathFromStatusPath(statusPath);
|
|
@@ -2797,16 +2875,18 @@ function analyzeWaveCompletionFromStatusFiles(wave, statusDir, options = {}) {
|
|
|
2797
2875
|
`${agent.agentId} exited ${statusRecord.code} in ${path.relative(REPO_ROOT, statusPath)}.`,
|
|
2798
2876
|
);
|
|
2799
2877
|
statusesReady = false;
|
|
2878
|
+
summaryValidationReady = false;
|
|
2800
2879
|
continue;
|
|
2801
2880
|
}
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2881
|
+
const promptDriftReason = promptDriftReasonForStatus(
|
|
2882
|
+
agent,
|
|
2883
|
+
statusPath,
|
|
2884
|
+
statusRecord,
|
|
2885
|
+
expectedPromptHash,
|
|
2886
|
+
);
|
|
2887
|
+
if (promptDriftReason) {
|
|
2888
|
+
pushWaveCompletionReason(reasons, promptDriftReason.code, promptDriftReason.detail);
|
|
2808
2889
|
statusesReady = false;
|
|
2809
|
-
continue;
|
|
2810
2890
|
}
|
|
2811
2891
|
const summary = materializeLiveExecutionSummaryIfMissing({
|
|
2812
2892
|
wave,
|
|
@@ -2917,7 +2997,7 @@ function analyzeWaveCompletionFromStatusFiles(wave, statusDir, options = {}) {
|
|
|
2917
2997
|
}
|
|
2918
2998
|
|
|
2919
2999
|
if (
|
|
2920
|
-
|
|
3000
|
+
summaryValidationReady &&
|
|
2921
3001
|
componentThreshold !== null &&
|
|
2922
3002
|
wave.wave >= componentThreshold
|
|
2923
3003
|
) {
|
|
@@ -3071,32 +3151,63 @@ export function reconcileRunStateFromStatusFiles(allWaves, runStatePath, statusD
|
|
|
3071
3151
|
const before = readRunState(runStatePath);
|
|
3072
3152
|
const firstMerge = normalizeCompletedWaves(
|
|
3073
3153
|
diagnostics
|
|
3074
|
-
.filter((diagnostic) =>
|
|
3154
|
+
.filter((diagnostic) => {
|
|
3155
|
+
if (diagnostic.ok) {
|
|
3156
|
+
return true;
|
|
3157
|
+
}
|
|
3158
|
+
const previousEntry = before.waves[String(diagnostic.wave)] || null;
|
|
3159
|
+
return shouldPreserveCompletedWave(previousEntry, diagnostic);
|
|
3160
|
+
})
|
|
3075
3161
|
.map((diagnostic) => diagnostic.wave)
|
|
3076
3162
|
.concat(
|
|
3077
3163
|
before.completedWaves.filter((waveNumber) => {
|
|
3078
3164
|
const diagnostic = diagnostics.find((entry) => entry.wave === waveNumber);
|
|
3079
|
-
|
|
3165
|
+
if (!diagnostic) {
|
|
3166
|
+
return true;
|
|
3167
|
+
}
|
|
3168
|
+
const previousEntry = before.waves[String(waveNumber)] || null;
|
|
3169
|
+
return diagnostic.ok || shouldPreserveCompletedWave(previousEntry, diagnostic);
|
|
3080
3170
|
}),
|
|
3081
3171
|
),
|
|
3082
3172
|
);
|
|
3083
3173
|
const latest = readRunState(runStatePath);
|
|
3084
3174
|
let nextState = latest;
|
|
3175
|
+
const preservedWithDrift = [];
|
|
3085
3176
|
for (const diagnostic of diagnostics) {
|
|
3086
|
-
const
|
|
3177
|
+
const previousEntry = before.waves[String(diagnostic.wave)] || null;
|
|
3178
|
+
const preserveCompleted = shouldPreserveCompletedWave(previousEntry, diagnostic);
|
|
3179
|
+
const toState = diagnostic.ok
|
|
3180
|
+
? "completed"
|
|
3181
|
+
: preserveCompleted
|
|
3182
|
+
? "completed_with_drift"
|
|
3183
|
+
: "blocked";
|
|
3087
3184
|
const reasonCode = diagnostic.ok
|
|
3088
3185
|
? "status-reconcile-complete"
|
|
3089
|
-
:
|
|
3186
|
+
: preserveCompleted
|
|
3187
|
+
? "status-reconcile-completed-with-drift"
|
|
3188
|
+
: diagnostic.reasons[0]?.code || "status-reconcile-blocked";
|
|
3090
3189
|
const detail = diagnostic.ok
|
|
3091
3190
|
? `Wave ${diagnostic.wave} reconstructed as complete from status files.`
|
|
3092
|
-
:
|
|
3191
|
+
: preserveCompleted
|
|
3192
|
+
? `Wave ${diagnostic.wave} preserved as completed with prompt drift: ${diagnostic.reasons.map((reason) => reason.detail).filter(Boolean).join(" ")}`
|
|
3193
|
+
: diagnostic.reasons.map((reason) => reason.detail).filter(Boolean).join(" ");
|
|
3194
|
+
const evidence = preserveCompleted
|
|
3195
|
+
? buildPreservedCompletionEvidence(previousEntry, diagnostic)
|
|
3196
|
+
: diagnostic.evidence || null;
|
|
3197
|
+
if (preserveCompleted) {
|
|
3198
|
+
preservedWithDrift.push({
|
|
3199
|
+
wave: diagnostic.wave,
|
|
3200
|
+
reasons: diagnostic.reasons,
|
|
3201
|
+
previousState: previousEntry?.currentState || "completed",
|
|
3202
|
+
});
|
|
3203
|
+
}
|
|
3093
3204
|
nextState = appendRunStateTransition(nextState, {
|
|
3094
3205
|
waveNumber: diagnostic.wave,
|
|
3095
3206
|
toState,
|
|
3096
3207
|
source: "status-reconcile",
|
|
3097
3208
|
reasonCode,
|
|
3098
3209
|
detail,
|
|
3099
|
-
evidence
|
|
3210
|
+
evidence,
|
|
3100
3211
|
at: diagnostic.evidence?.statusFiles?.find((entry) => entry.completedAt)?.completedAt || toIsoTimestamp(),
|
|
3101
3212
|
});
|
|
3102
3213
|
}
|
|
@@ -3106,7 +3217,14 @@ export function reconcileRunStateFromStatusFiles(allWaves, runStatePath, statusD
|
|
|
3106
3217
|
completedFromStatus,
|
|
3107
3218
|
addedFromBefore: firstMerge.filter((waveNumber) => !before.completedWaves.includes(waveNumber)),
|
|
3108
3219
|
addedFromLatest: merged.filter((waveNumber) => !latest.completedWaves.includes(waveNumber)),
|
|
3109
|
-
blockedFromStatus: diagnostics.filter((diagnostic) =>
|
|
3220
|
+
blockedFromStatus: diagnostics.filter((diagnostic) => {
|
|
3221
|
+
if (diagnostic.ok) {
|
|
3222
|
+
return false;
|
|
3223
|
+
}
|
|
3224
|
+
const previousEntry = before.waves[String(diagnostic.wave)] || null;
|
|
3225
|
+
return !shouldPreserveCompletedWave(previousEntry, diagnostic);
|
|
3226
|
+
}),
|
|
3227
|
+
preservedWithDrift,
|
|
3110
3228
|
state,
|
|
3111
3229
|
};
|
|
3112
3230
|
}
|