@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
|
@@ -181,6 +181,118 @@ import {
|
|
|
181
181
|
readWaveInfraGate as readWaveInfraGateImpl,
|
|
182
182
|
runClosureSweepPhase as runClosureSweepPhaseImpl,
|
|
183
183
|
} from "./launcher-closure.mjs";
|
|
184
|
+
|
|
185
|
+
// --- Re-exports from launcher-gates.mjs ---
|
|
186
|
+
import {
|
|
187
|
+
materializeAgentExecutionSummaryForRun,
|
|
188
|
+
readRunExecutionSummary,
|
|
189
|
+
materializeAgentExecutionSummaries,
|
|
190
|
+
readWaveContQaGate,
|
|
191
|
+
readWaveContEvalGate,
|
|
192
|
+
readWaveEvaluatorGate,
|
|
193
|
+
readWaveImplementationGate,
|
|
194
|
+
analyzePromotedComponentOwners,
|
|
195
|
+
buildSharedComponentSiblingPendingFailure,
|
|
196
|
+
readWaveComponentGate,
|
|
197
|
+
readWaveComponentMatrixGate,
|
|
198
|
+
readWaveDocumentationGate,
|
|
199
|
+
readWaveSecurityGate,
|
|
200
|
+
readWaveIntegrationGate,
|
|
201
|
+
readWaveIntegrationBarrier,
|
|
202
|
+
readClarificationBarrier,
|
|
203
|
+
readWaveAssignmentBarrier,
|
|
204
|
+
readWaveDependencyBarrier,
|
|
205
|
+
buildGateSnapshot as buildGateSnapshotImpl,
|
|
206
|
+
} from "./launcher-gates.mjs";
|
|
207
|
+
|
|
208
|
+
export {
|
|
209
|
+
readWaveContQaGate,
|
|
210
|
+
readWaveContEvalGate,
|
|
211
|
+
readWaveEvaluatorGate,
|
|
212
|
+
readWaveImplementationGate,
|
|
213
|
+
readWaveComponentGate,
|
|
214
|
+
readWaveComponentMatrixGate,
|
|
215
|
+
readWaveDocumentationGate,
|
|
216
|
+
readWaveSecurityGate,
|
|
217
|
+
readWaveIntegrationGate,
|
|
218
|
+
readWaveIntegrationBarrier,
|
|
219
|
+
readClarificationBarrier,
|
|
220
|
+
readWaveAssignmentBarrier,
|
|
221
|
+
readWaveDependencyBarrier,
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// --- Re-exports from launcher-derived-state.mjs ---
|
|
225
|
+
import {
|
|
226
|
+
waveAssignmentsPath,
|
|
227
|
+
waveDependencySnapshotPath,
|
|
228
|
+
writeWaveDerivedState,
|
|
229
|
+
applyDerivedStateToDashboard,
|
|
230
|
+
buildWaveSecuritySummary,
|
|
231
|
+
buildWaveIntegrationSummary,
|
|
232
|
+
} from "./launcher-derived-state.mjs";
|
|
233
|
+
|
|
234
|
+
export {
|
|
235
|
+
buildWaveSecuritySummary,
|
|
236
|
+
buildWaveIntegrationSummary,
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// --- Re-exports from launcher-retry.mjs ---
|
|
240
|
+
import {
|
|
241
|
+
readWaveRelaunchPlan,
|
|
242
|
+
writeWaveRelaunchPlan,
|
|
243
|
+
clearWaveRelaunchPlan,
|
|
244
|
+
resetPersistedWaveLaunchState,
|
|
245
|
+
persistedRelaunchPlanMatchesCurrentState,
|
|
246
|
+
resolveSharedComponentContinuationRuns,
|
|
247
|
+
relaunchReasonBuckets,
|
|
248
|
+
applySharedComponentWaitStateToDashboard,
|
|
249
|
+
reconcileFailuresAgainstSharedComponentState,
|
|
250
|
+
hasReusableSuccessStatus,
|
|
251
|
+
selectReusablePreCompletedAgentIds,
|
|
252
|
+
selectInitialWaveRuns,
|
|
253
|
+
resolveRelaunchRuns,
|
|
254
|
+
applyPersistedRelaunchPlan,
|
|
255
|
+
executorFallbackChain,
|
|
256
|
+
preflightWavesForExecutorAvailability,
|
|
257
|
+
} from "./launcher-retry.mjs";
|
|
258
|
+
|
|
259
|
+
export {
|
|
260
|
+
resetPersistedWaveLaunchState,
|
|
261
|
+
persistedRelaunchPlanMatchesCurrentState,
|
|
262
|
+
resolveSharedComponentContinuationRuns,
|
|
263
|
+
hasReusableSuccessStatus,
|
|
264
|
+
selectReusablePreCompletedAgentIds,
|
|
265
|
+
selectInitialWaveRuns,
|
|
266
|
+
resolveRelaunchRuns,
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// --- Re-exports from launcher-supervisor.mjs ---
|
|
270
|
+
import {
|
|
271
|
+
markLauncherFailed,
|
|
272
|
+
acquireLauncherLock,
|
|
273
|
+
releaseLauncherLock,
|
|
274
|
+
reconcileStaleLauncherArtifacts,
|
|
275
|
+
collectUnexpectedSessionFailures,
|
|
276
|
+
launchAgentSession,
|
|
277
|
+
waitForWaveCompletion,
|
|
278
|
+
monitorWaveHumanFeedback,
|
|
279
|
+
buildResidentOrchestratorRun,
|
|
280
|
+
monitorResidentOrchestratorSession,
|
|
281
|
+
launchWaveDashboardSession,
|
|
282
|
+
cleanupLaneTmuxSessions,
|
|
283
|
+
pruneDryRunExecutorPreviewDirs,
|
|
284
|
+
runTmux,
|
|
285
|
+
} from "./launcher-supervisor.mjs";
|
|
286
|
+
|
|
287
|
+
export {
|
|
288
|
+
markLauncherFailed,
|
|
289
|
+
acquireLauncherLock,
|
|
290
|
+
releaseLauncherLock,
|
|
291
|
+
reconcileStaleLauncherArtifacts,
|
|
292
|
+
collectUnexpectedSessionFailures,
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
// --- Original re-exports that stay ---
|
|
184
296
|
export { CODEX_SANDBOX_MODES, DEFAULT_CODEX_SANDBOX_MODE, normalizeCodexSandboxMode, buildCodexExecInvocation };
|
|
185
297
|
|
|
186
298
|
export function formatReconcileBlockedWaveLine(blockedWave) {
|
|
@@ -198,6 +310,22 @@ export function formatReconcileBlockedWaveLine(blockedWave) {
|
|
|
198
310
|
}`;
|
|
199
311
|
}
|
|
200
312
|
|
|
313
|
+
export function formatReconcilePreservedWaveLine(preservedWave) {
|
|
314
|
+
const parts = Array.isArray(preservedWave?.reasons)
|
|
315
|
+
? preservedWave.reasons
|
|
316
|
+
.map((reason) => {
|
|
317
|
+
const code = compactSingleLine(reason?.code || "", 80);
|
|
318
|
+
const detail = compactSingleLine(reason?.detail || "", 240);
|
|
319
|
+
return code && detail ? `${code}=${detail}` : "";
|
|
320
|
+
})
|
|
321
|
+
.filter(Boolean)
|
|
322
|
+
: [];
|
|
323
|
+
const previousState = compactSingleLine(preservedWave?.previousState || "completed", 80);
|
|
324
|
+
return `[reconcile] wave ${preservedWave?.wave ?? "unknown"} preserved as ${previousState}: ${
|
|
325
|
+
parts.join("; ") || "unknown reason"
|
|
326
|
+
}`;
|
|
327
|
+
}
|
|
328
|
+
|
|
201
329
|
function printUsage(lanePaths, terminalSurface) {
|
|
202
330
|
console.log(`Usage: pnpm exec wave launch [options]
|
|
203
331
|
|
|
@@ -206,6 +334,7 @@ Options:
|
|
|
206
334
|
--start-wave <n> Start from wave number (default: 0)
|
|
207
335
|
--end-wave <n> End at wave number (default: last available)
|
|
208
336
|
--auto-next Start from the next unfinished wave and continue forward
|
|
337
|
+
--resume-control-state Preserve the prior auto-generated relaunch plan for this wave
|
|
209
338
|
--reconcile-status Reconcile run-state from agent status files and exit
|
|
210
339
|
--state-file <path> Path to run-state JSON (default: ${path.relative(REPO_ROOT, lanePaths.defaultRunStatePath)})
|
|
211
340
|
--timeout-minutes <n> Max minutes to wait per wave (default: ${DEFAULT_TIMEOUT_MINUTES})
|
|
@@ -252,6 +381,7 @@ function parseArgs(argv) {
|
|
|
252
381
|
startWave: 0,
|
|
253
382
|
endWave: null,
|
|
254
383
|
autoNext: false,
|
|
384
|
+
resumeControlState: false,
|
|
255
385
|
reconcileStatus: false,
|
|
256
386
|
runStatePath: lanePaths.defaultRunStatePath,
|
|
257
387
|
timeoutMinutes: DEFAULT_TIMEOUT_MINUTES,
|
|
@@ -272,3036 +402,202 @@ function parseArgs(argv) {
|
|
|
272
402
|
telemetryEnabled: true,
|
|
273
403
|
residentOrchestrator: false,
|
|
274
404
|
orchestratorId: null,
|
|
275
|
-
orchestratorBoardPath: null,
|
|
276
|
-
coordinationNote: "",
|
|
277
|
-
adhocRunId: null,
|
|
278
|
-
};
|
|
279
|
-
let stateFileProvided = false;
|
|
280
|
-
let manifestOutProvided = false;
|
|
281
|
-
let orchestratorBoardProvided = false;
|
|
282
|
-
let executorProvided = false;
|
|
283
|
-
|
|
284
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
285
|
-
const arg = argv[i];
|
|
286
|
-
if (arg === "--") {
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
if (arg === "--help" || arg === "-h") {
|
|
290
|
-
return { help: true, lanePaths, options };
|
|
291
|
-
}
|
|
292
|
-
if (arg === "--dry-run") {
|
|
293
|
-
options.dryRun = true;
|
|
294
|
-
} else if (arg === "--terminal-surface") {
|
|
295
|
-
options.terminalSurface = normalizeTerminalSurface(argv[++i], "--terminal-surface");
|
|
296
|
-
} else if (arg === "--no-dashboard") {
|
|
297
|
-
options.dashboard = false;
|
|
298
|
-
} else if (arg === "--cleanup-sessions") {
|
|
299
|
-
options.cleanupSessions = true;
|
|
300
|
-
} else if (arg === "--keep-sessions") {
|
|
301
|
-
options.cleanupSessions = false;
|
|
302
|
-
} else if (arg === "--auto-next") {
|
|
303
|
-
options.autoNext = true;
|
|
304
|
-
} else if (arg === "--reconcile-status") {
|
|
305
|
-
options.reconcileStatus = true;
|
|
306
|
-
} else if (arg === "--keep-terminals") {
|
|
307
|
-
options.keepTerminals = true;
|
|
308
|
-
} else if (arg === "--no-context7") {
|
|
309
|
-
options.context7Enabled = false;
|
|
310
|
-
} else if (arg === "--no-telemetry") {
|
|
311
|
-
options.telemetryEnabled = false;
|
|
312
|
-
} else if (arg === "--no-orchestrator-board") {
|
|
313
|
-
options.orchestratorBoardPath = null;
|
|
314
|
-
orchestratorBoardProvided = true;
|
|
315
|
-
} else if (arg === "--lane") {
|
|
316
|
-
options.lane = String(argv[++i] || "").trim();
|
|
317
|
-
lanePaths = buildLanePaths(options.lane, {
|
|
318
|
-
adhocRunId: options.adhocRunId,
|
|
319
|
-
});
|
|
320
|
-
} else if (arg === "--adhoc-run") {
|
|
321
|
-
options.adhocRunId = sanitizeAdhocRunId(argv[++i]);
|
|
322
|
-
lanePaths = buildLanePaths(options.lane, {
|
|
323
|
-
adhocRunId: options.adhocRunId,
|
|
324
|
-
});
|
|
325
|
-
} else if (arg === "--orchestrator-id") {
|
|
326
|
-
options.orchestratorId = sanitizeOrchestratorId(argv[++i]);
|
|
327
|
-
} else if (arg === "--orchestrator-board") {
|
|
328
|
-
options.orchestratorBoardPath = path.resolve(REPO_ROOT, argv[++i] || "");
|
|
329
|
-
orchestratorBoardProvided = true;
|
|
330
|
-
} else if (arg === "--coordination-note") {
|
|
331
|
-
options.coordinationNote = String(argv[++i] || "").trim();
|
|
332
|
-
} else if (arg === "--resident-orchestrator") {
|
|
333
|
-
options.residentOrchestrator = true;
|
|
334
|
-
} else if (arg === "--state-file") {
|
|
335
|
-
options.runStatePath = path.resolve(REPO_ROOT, argv[++i] || "");
|
|
336
|
-
stateFileProvided = true;
|
|
337
|
-
} else if (arg === "--start-wave") {
|
|
338
|
-
options.startWave = parseNonNegativeInt(argv[++i], "--start-wave");
|
|
339
|
-
} else if (arg === "--end-wave") {
|
|
340
|
-
options.endWave = parseNonNegativeInt(argv[++i], "--end-wave");
|
|
341
|
-
} else if (arg === "--timeout-minutes") {
|
|
342
|
-
options.timeoutMinutes = parsePositiveInt(argv[++i], "--timeout-minutes");
|
|
343
|
-
} else if (arg === "--max-retries-per-wave") {
|
|
344
|
-
options.maxRetriesPerWave = parseNonNegativeInt(argv[++i], "--max-retries-per-wave");
|
|
345
|
-
} else if (arg === "--agent-rate-limit-retries") {
|
|
346
|
-
options.agentRateLimitRetries = parseNonNegativeInt(argv[++i], "--agent-rate-limit-retries");
|
|
347
|
-
} else if (arg === "--agent-rate-limit-base-delay-seconds") {
|
|
348
|
-
options.agentRateLimitBaseDelaySeconds = parsePositiveInt(
|
|
349
|
-
argv[++i],
|
|
350
|
-
"--agent-rate-limit-base-delay-seconds",
|
|
351
|
-
);
|
|
352
|
-
} else if (arg === "--agent-rate-limit-max-delay-seconds") {
|
|
353
|
-
options.agentRateLimitMaxDelaySeconds = parsePositiveInt(
|
|
354
|
-
argv[++i],
|
|
355
|
-
"--agent-rate-limit-max-delay-seconds",
|
|
356
|
-
);
|
|
357
|
-
} else if (arg === "--agent-launch-stagger-ms") {
|
|
358
|
-
options.agentLaunchStaggerMs = parseNonNegativeInt(argv[++i], "--agent-launch-stagger-ms");
|
|
359
|
-
} else if (arg === "--executor") {
|
|
360
|
-
options.executorMode = normalizeExecutorMode(argv[++i], "--executor");
|
|
361
|
-
executorProvided = true;
|
|
362
|
-
} else if (arg === "--codex-sandbox") {
|
|
363
|
-
options.codexSandboxMode = normalizeCodexSandboxMode(argv[++i], "--codex-sandbox");
|
|
364
|
-
} else if (arg === "--manifest-out") {
|
|
365
|
-
options.manifestOut = path.resolve(REPO_ROOT, argv[++i] || "");
|
|
366
|
-
manifestOutProvided = true;
|
|
367
|
-
} else {
|
|
368
|
-
throw new Error(`Unknown argument: ${arg}`);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
lanePaths = buildLanePaths(options.lane, {
|
|
373
|
-
runVariant: options.dryRun ? "dry-run" : undefined,
|
|
374
|
-
adhocRunId: options.adhocRunId,
|
|
375
|
-
});
|
|
376
|
-
if (!stateFileProvided) {
|
|
377
|
-
options.runStatePath = lanePaths.defaultRunStatePath;
|
|
378
|
-
}
|
|
379
|
-
if (!manifestOutProvided) {
|
|
380
|
-
options.manifestOut = lanePaths.defaultManifestPath;
|
|
381
|
-
}
|
|
382
|
-
if (!orchestratorBoardProvided) {
|
|
383
|
-
options.orchestratorBoardPath = lanePaths.defaultOrchestratorBoardPath;
|
|
384
|
-
}
|
|
385
|
-
if (!executorProvided) {
|
|
386
|
-
options.executorMode = lanePaths.executors.default;
|
|
387
|
-
}
|
|
388
|
-
if (!options.telemetryEnabled) {
|
|
389
|
-
lanePaths.waveControl = {
|
|
390
|
-
...(lanePaths.waveControl || {}),
|
|
391
|
-
enabled: false,
|
|
392
|
-
};
|
|
393
|
-
lanePaths.laneProfile = {
|
|
394
|
-
...(lanePaths.laneProfile || {}),
|
|
395
|
-
waveControl: lanePaths.waveControl,
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
options.orchestratorId ||= sanitizeOrchestratorId(`${lanePaths.lane}-orch-${process.pid}`);
|
|
399
|
-
lanePaths.orchestratorId = options.orchestratorId;
|
|
400
|
-
if (options.agentRateLimitMaxDelaySeconds < options.agentRateLimitBaseDelaySeconds) {
|
|
401
|
-
throw new Error(
|
|
402
|
-
"--agent-rate-limit-max-delay-seconds must be >= --agent-rate-limit-base-delay-seconds",
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
if (!options.autoNext && options.endWave !== null && options.endWave < options.startWave) {
|
|
406
|
-
throw new Error("--end-wave must be >= --start-wave");
|
|
407
|
-
}
|
|
408
|
-
if (!options.dryRun && options.terminalSurface === "none") {
|
|
409
|
-
throw new Error("--terminal-surface none is only supported with --dry-run");
|
|
410
|
-
}
|
|
411
|
-
return { help: false, lanePaths, options };
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
function isProcessAlive(pid) {
|
|
415
|
-
if (!Number.isInteger(pid) || pid <= 0) {
|
|
416
|
-
return false;
|
|
417
|
-
}
|
|
418
|
-
try {
|
|
419
|
-
process.kill(pid, 0);
|
|
420
|
-
return true;
|
|
421
|
-
} catch {
|
|
422
|
-
return false;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
export function readWaveContQaGate(wave, agentRuns, options = {}) {
|
|
427
|
-
const mode = String(options.mode || "compat").trim().toLowerCase();
|
|
428
|
-
const strict = mode === "live";
|
|
429
|
-
const contQaAgentId = options.contQaAgentId || wave.contQaAgentId || "A0";
|
|
430
|
-
const contQaRun =
|
|
431
|
-
agentRuns.find((run) => run.agent.agentId === contQaAgentId) ?? null;
|
|
432
|
-
if (!contQaRun) {
|
|
433
|
-
return {
|
|
434
|
-
ok: false,
|
|
435
|
-
agentId: contQaAgentId,
|
|
436
|
-
statusCode: "missing-cont-qa",
|
|
437
|
-
detail: `Agent ${contQaAgentId} is missing.`,
|
|
438
|
-
logPath: null,
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
const summary = readRunExecutionSummary(contQaRun, strict ? wave : null);
|
|
442
|
-
if (summary) {
|
|
443
|
-
const validation = validateContQaSummary(contQaRun.agent, summary, { mode });
|
|
444
|
-
return {
|
|
445
|
-
ok: validation.ok,
|
|
446
|
-
agentId: contQaRun.agent.agentId,
|
|
447
|
-
statusCode: validation.statusCode,
|
|
448
|
-
detail: validation.detail,
|
|
449
|
-
logPath: summary.logPath || path.relative(REPO_ROOT, contQaRun.logPath),
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
if (strict) {
|
|
453
|
-
return {
|
|
454
|
-
ok: false,
|
|
455
|
-
agentId: contQaRun.agent.agentId,
|
|
456
|
-
statusCode: "missing-wave-gate",
|
|
457
|
-
detail: `Missing structured cont-QA summary for ${contQaRun.agent.agentId}.`,
|
|
458
|
-
logPath: path.relative(REPO_ROOT, contQaRun.logPath),
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
const contQaReportPath = wave.contQaReportPath
|
|
462
|
-
? path.resolve(REPO_ROOT, wave.contQaReportPath)
|
|
463
|
-
: null;
|
|
464
|
-
const reportText =
|
|
465
|
-
contQaReportPath && fs.existsSync(contQaReportPath)
|
|
466
|
-
? fs.readFileSync(contQaReportPath, "utf8")
|
|
467
|
-
: "";
|
|
468
|
-
const reportVerdict = parseVerdictFromText(reportText, REPORT_VERDICT_REGEX);
|
|
469
|
-
if (reportVerdict.verdict) {
|
|
470
|
-
return {
|
|
471
|
-
ok: reportVerdict.verdict === "pass",
|
|
472
|
-
agentId: contQaRun.agent.agentId,
|
|
473
|
-
statusCode: reportVerdict.verdict === "pass" ? "pass" : `cont-qa-${reportVerdict.verdict}`,
|
|
474
|
-
detail: reportVerdict.detail || "Verdict read from cont-QA report.",
|
|
475
|
-
logPath: path.relative(REPO_ROOT, contQaRun.logPath),
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
const logVerdict = parseVerdictFromText(
|
|
479
|
-
readFileTail(contQaRun.logPath, 30000),
|
|
480
|
-
WAVE_VERDICT_REGEX,
|
|
481
|
-
);
|
|
482
|
-
if (logVerdict.verdict) {
|
|
483
|
-
return {
|
|
484
|
-
ok: logVerdict.verdict === "pass",
|
|
485
|
-
agentId: contQaRun.agent.agentId,
|
|
486
|
-
statusCode: logVerdict.verdict === "pass" ? "pass" : `cont-qa-${logVerdict.verdict}`,
|
|
487
|
-
detail: logVerdict.detail || "Verdict read from cont-QA log marker.",
|
|
488
|
-
logPath: path.relative(REPO_ROOT, contQaRun.logPath),
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
return {
|
|
492
|
-
ok: false,
|
|
493
|
-
agentId: contQaRun.agent.agentId,
|
|
494
|
-
statusCode: "missing-cont-qa-verdict",
|
|
495
|
-
detail: contQaReportPath
|
|
496
|
-
? `Missing Verdict line in ${path.relative(REPO_ROOT, contQaReportPath)} and no [wave-verdict] marker in ${path.relative(REPO_ROOT, contQaRun.logPath)}.`
|
|
497
|
-
: `Missing cont-QA report path and no [wave-verdict] marker in ${path.relative(REPO_ROOT, contQaRun.logPath)}.`,
|
|
498
|
-
logPath: path.relative(REPO_ROOT, contQaRun.logPath),
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
export function readWaveContEvalGate(wave, agentRuns, options = {}) {
|
|
503
|
-
const mode = String(options.mode || "compat").trim().toLowerCase();
|
|
504
|
-
const strict = mode === "live";
|
|
505
|
-
const contEvalAgentId = options.contEvalAgentId || wave.contEvalAgentId || "E0";
|
|
506
|
-
const contEvalRun =
|
|
507
|
-
agentRuns.find((run) => run.agent.agentId === contEvalAgentId) ?? null;
|
|
508
|
-
if (!contEvalRun) {
|
|
509
|
-
return {
|
|
510
|
-
ok: true,
|
|
511
|
-
agentId: null,
|
|
512
|
-
statusCode: "pass",
|
|
513
|
-
detail: "Wave does not include cont-EVAL.",
|
|
514
|
-
logPath: null,
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
const summary = readRunExecutionSummary(contEvalRun, strict ? wave : null);
|
|
518
|
-
if (summary) {
|
|
519
|
-
const validation = validateContEvalSummary(contEvalRun.agent, summary, {
|
|
520
|
-
mode,
|
|
521
|
-
evalTargets: options.evalTargets || wave.evalTargets,
|
|
522
|
-
benchmarkCatalogPath: options.benchmarkCatalogPath,
|
|
523
|
-
});
|
|
524
|
-
return {
|
|
525
|
-
ok: validation.ok,
|
|
526
|
-
agentId: contEvalRun.agent.agentId,
|
|
527
|
-
statusCode: validation.statusCode,
|
|
528
|
-
detail: validation.detail,
|
|
529
|
-
logPath: summary.logPath || path.relative(REPO_ROOT, contEvalRun.logPath),
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
return {
|
|
533
|
-
ok: false,
|
|
534
|
-
agentId: contEvalRun.agent.agentId,
|
|
535
|
-
statusCode: "missing-wave-eval",
|
|
536
|
-
detail: `Missing [wave-eval] marker for ${contEvalRun.agent.agentId}.`,
|
|
537
|
-
logPath: path.relative(REPO_ROOT, contEvalRun.logPath),
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
function materializeAgentExecutionSummaryForRun(wave, runInfo) {
|
|
542
|
-
const statusRecord = readStatusRecordIfPresent(runInfo.statusPath);
|
|
543
|
-
if (!statusRecord) {
|
|
544
|
-
return null;
|
|
545
|
-
}
|
|
546
|
-
const reportPath = (() => {
|
|
547
|
-
if (runInfo.agent.agentId === (wave.contQaAgentId || "A0") && wave.contQaReportPath) {
|
|
548
|
-
return path.resolve(REPO_ROOT, wave.contQaReportPath);
|
|
549
|
-
}
|
|
550
|
-
if (runInfo.agent.agentId === (wave.contEvalAgentId || "E0") && wave.contEvalReportPath) {
|
|
551
|
-
return path.resolve(REPO_ROOT, wave.contEvalReportPath);
|
|
552
|
-
}
|
|
553
|
-
if (isSecurityReviewAgent(runInfo.agent)) {
|
|
554
|
-
const securityReportPath = resolveSecurityReviewReportPath(runInfo.agent);
|
|
555
|
-
return securityReportPath ? path.resolve(REPO_ROOT, securityReportPath) : null;
|
|
556
|
-
}
|
|
557
|
-
return null;
|
|
558
|
-
})();
|
|
559
|
-
const summary = buildAgentExecutionSummary({
|
|
560
|
-
agent: runInfo.agent,
|
|
561
|
-
statusRecord,
|
|
562
|
-
logPath: runInfo.logPath,
|
|
563
|
-
reportPath,
|
|
564
|
-
});
|
|
565
|
-
writeAgentExecutionSummary(runInfo.statusPath, summary);
|
|
566
|
-
return summary;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
function readRunExecutionSummary(runInfo, wave = null) {
|
|
570
|
-
const applyProofRegistry = (summary) =>
|
|
571
|
-
runInfo?.proofRegistry ? augmentSummaryWithProofRegistry(runInfo.agent, summary, runInfo.proofRegistry) : summary;
|
|
572
|
-
if (runInfo?.summary && typeof runInfo.summary === "object") {
|
|
573
|
-
return applyProofRegistry(runInfo.summary);
|
|
574
|
-
}
|
|
575
|
-
if (runInfo?.summaryPath && fs.existsSync(runInfo.summaryPath)) {
|
|
576
|
-
return applyProofRegistry(readAgentExecutionSummary(runInfo.summaryPath));
|
|
577
|
-
}
|
|
578
|
-
if (runInfo?.statusPath && fs.existsSync(agentSummaryPathFromStatusPath(runInfo.statusPath))) {
|
|
579
|
-
return applyProofRegistry(readAgentExecutionSummary(runInfo.statusPath));
|
|
580
|
-
}
|
|
581
|
-
if (wave && runInfo?.statusPath && runInfo?.logPath && fs.existsSync(runInfo.statusPath)) {
|
|
582
|
-
return applyProofRegistry(materializeAgentExecutionSummaryForRun(wave, runInfo));
|
|
583
|
-
}
|
|
584
|
-
return null;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
export function readWaveEvaluatorGate(wave, agentRuns, options = {}) {
|
|
588
|
-
return readWaveContQaGate(wave, agentRuns, {
|
|
589
|
-
...options,
|
|
590
|
-
contQaAgentId: options.evaluatorAgentId || options.contQaAgentId,
|
|
591
|
-
});
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
function materializeAgentExecutionSummaries(wave, agentRuns) {
|
|
595
|
-
return Object.fromEntries(
|
|
596
|
-
agentRuns.map((runInfo) => [runInfo.agent.agentId, materializeAgentExecutionSummaryForRun(wave, runInfo)]),
|
|
597
|
-
);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
function waveCoordinationLogPath(lanePaths, waveNumber) {
|
|
601
|
-
return path.join(lanePaths.coordinationDir, `wave-${waveNumber}.jsonl`);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
function waveInboxDir(lanePaths, waveNumber) {
|
|
605
|
-
return path.join(lanePaths.inboxesDir, `wave-${waveNumber}`);
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
function waveAssignmentsPath(lanePaths, waveNumber) {
|
|
609
|
-
return path.join(lanePaths.assignmentsDir, `wave-${waveNumber}.json`);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
function waveLedgerPath(lanePaths, waveNumber) {
|
|
613
|
-
return path.join(lanePaths.ledgerDir, `wave-${waveNumber}.json`);
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
function waveDependencySnapshotPath(lanePaths, waveNumber) {
|
|
617
|
-
return path.join(lanePaths.dependencySnapshotsDir, `wave-${waveNumber}.json`);
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
function waveDependencySnapshotMarkdownPath(lanePaths, waveNumber) {
|
|
621
|
-
return path.join(lanePaths.dependencySnapshotsDir, `wave-${waveNumber}.md`);
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
function waveDocsQueuePath(lanePaths, waveNumber) {
|
|
625
|
-
return path.join(lanePaths.docsQueueDir, `wave-${waveNumber}.json`);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
function waveIntegrationPath(lanePaths, waveNumber) {
|
|
629
|
-
return path.join(lanePaths.integrationDir, `wave-${waveNumber}.json`);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
function waveIntegrationMarkdownPath(lanePaths, waveNumber) {
|
|
633
|
-
return path.join(lanePaths.integrationDir, `wave-${waveNumber}.md`);
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
function readWaveRelaunchPlan(lanePaths, waveNumber) {
|
|
637
|
-
return readWaveRelaunchPlanSnapshot(lanePaths, waveNumber);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
function writeWaveRelaunchPlan(lanePaths, waveNumber, payload) {
|
|
641
|
-
const filePath = waveRelaunchPlanPath(lanePaths, waveNumber);
|
|
642
|
-
writeRelaunchPlan(filePath, payload, { wave: waveNumber });
|
|
643
|
-
return filePath;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
function clearWaveRelaunchPlan(lanePaths, waveNumber) {
|
|
647
|
-
const filePath = waveRelaunchPlanPath(lanePaths, waveNumber);
|
|
648
|
-
try {
|
|
649
|
-
fs.rmSync(filePath, { force: true });
|
|
650
|
-
} catch {
|
|
651
|
-
// no-op
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
function waveSecurityPath(lanePaths, waveNumber) {
|
|
656
|
-
return path.join(lanePaths.securityDir, `wave-${waveNumber}.json`);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
function waveSecurityMarkdownPath(lanePaths, waveNumber) {
|
|
660
|
-
return path.join(lanePaths.securityDir, `wave-${waveNumber}.md`);
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
function uniqueStringEntries(values) {
|
|
664
|
-
return Array.from(
|
|
665
|
-
new Set(
|
|
666
|
-
(Array.isArray(values) ? values : [])
|
|
667
|
-
.map((value) => String(value || "").trim())
|
|
668
|
-
.filter(Boolean),
|
|
669
|
-
),
|
|
670
|
-
);
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
function summarizeIntegrationRecord(record, options = {}) {
|
|
674
|
-
const summary = compactSingleLine(
|
|
675
|
-
record?.summary || record?.detail || record?.kind || "coordination item",
|
|
676
|
-
options.maxChars || 180,
|
|
677
|
-
);
|
|
678
|
-
return `${record.id}: ${summary}`;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
function summarizeDocsQueueItem(item) {
|
|
682
|
-
return `${item.id}: ${compactSingleLine(item.summary || item.path || item.detail || "documentation update required", 180)}`;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
function summarizeGap(agentId, detail, fallback) {
|
|
686
|
-
return `${agentId}: ${compactSingleLine(detail || fallback, 180)}`;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
function textMentionsAnyKeyword(value, keywords) {
|
|
690
|
-
const text = String(value || "").trim().toLowerCase();
|
|
691
|
-
if (!text) {
|
|
692
|
-
return false;
|
|
693
|
-
}
|
|
694
|
-
return keywords.some((keyword) => text.includes(String(keyword || "").trim().toLowerCase()));
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
function actionableIntegrationRecords(coordinationState) {
|
|
698
|
-
return (coordinationState?.latestRecords || []).filter(
|
|
699
|
-
(record) =>
|
|
700
|
-
!["cancelled", "superseded"].includes(String(record?.status || "").trim().toLowerCase()) &&
|
|
701
|
-
![
|
|
702
|
-
"human-feedback",
|
|
703
|
-
"human-escalation",
|
|
704
|
-
"orchestrator-guidance",
|
|
705
|
-
"resolved-by-policy",
|
|
706
|
-
"integration-summary",
|
|
707
|
-
].includes(record?.kind),
|
|
708
|
-
);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
function normalizeOwnedReference(value) {
|
|
712
|
-
return String(value || "").trim().replace(/\/+$/, "");
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
function matchesOwnedPathArtifact(artifactRef, ownedPath) {
|
|
716
|
-
const normalizedArtifact = normalizeOwnedReference(artifactRef);
|
|
717
|
-
const normalizedOwnedPath = normalizeOwnedReference(ownedPath);
|
|
718
|
-
if (!normalizedArtifact || !normalizedOwnedPath) {
|
|
719
|
-
return false;
|
|
720
|
-
}
|
|
721
|
-
return (
|
|
722
|
-
normalizedArtifact === normalizedOwnedPath ||
|
|
723
|
-
normalizedArtifact.startsWith(`${normalizedOwnedPath}/`)
|
|
724
|
-
);
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
function resolveArtifactOwners(artifactRef, agents) {
|
|
728
|
-
const owners = [];
|
|
729
|
-
const normalizedArtifact = normalizeOwnedReference(artifactRef);
|
|
730
|
-
if (!normalizedArtifact) {
|
|
731
|
-
return owners;
|
|
732
|
-
}
|
|
733
|
-
for (const agent of agents || []) {
|
|
734
|
-
const ownedComponents = Array.isArray(agent?.components) ? agent.components : [];
|
|
735
|
-
const ownedPaths = Array.isArray(agent?.ownedPaths) ? agent.ownedPaths : [];
|
|
736
|
-
if (
|
|
737
|
-
ownedComponents.some((componentId) => normalizeOwnedReference(componentId) === normalizedArtifact) ||
|
|
738
|
-
ownedPaths.some((ownedPath) => matchesOwnedPathArtifact(normalizedArtifact, ownedPath))
|
|
739
|
-
) {
|
|
740
|
-
owners.push(agent.agentId);
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
return owners;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
function inferIntegrationRecommendation(evidence) {
|
|
747
|
-
if ((evidence.unresolvedBlockers || []).length > 0) {
|
|
748
|
-
return {
|
|
749
|
-
recommendation: "needs-more-work",
|
|
750
|
-
detail: `${evidence.unresolvedBlockers.length} unresolved blocker(s) remain.`,
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
if ((evidence.conflictingClaims || []).length > 0) {
|
|
754
|
-
return {
|
|
755
|
-
recommendation: "needs-more-work",
|
|
756
|
-
detail: `${evidence.conflictingClaims.length} conflicting claim(s) remain.`,
|
|
757
|
-
};
|
|
758
|
-
}
|
|
759
|
-
if ((evidence.proofGaps || []).length > 0) {
|
|
760
|
-
return {
|
|
761
|
-
recommendation: "needs-more-work",
|
|
762
|
-
detail: `${evidence.proofGaps.length} proof gap(s) remain.`,
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
if ((evidence.deployRisks || []).length > 0) {
|
|
766
|
-
return {
|
|
767
|
-
recommendation: "needs-more-work",
|
|
768
|
-
detail: `${evidence.deployRisks.length} deploy or ops risk(s) remain.`,
|
|
769
|
-
};
|
|
770
|
-
}
|
|
771
|
-
return {
|
|
772
|
-
recommendation: "ready-for-doc-closure",
|
|
773
|
-
detail:
|
|
774
|
-
"No unresolved blockers, contradictions, proof gaps, or deploy risks remain in integration state.",
|
|
775
|
-
};
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
export function buildWaveSecuritySummary({
|
|
779
|
-
lanePaths,
|
|
780
|
-
wave,
|
|
781
|
-
attempt,
|
|
782
|
-
summariesByAgentId = {},
|
|
783
|
-
}) {
|
|
784
|
-
const createdAt = toIsoTimestamp();
|
|
785
|
-
const securityAgents = (wave.agents || []).filter((agent) => isSecurityReviewAgent(agent));
|
|
786
|
-
if (securityAgents.length === 0) {
|
|
787
|
-
return {
|
|
788
|
-
wave: wave.wave,
|
|
789
|
-
lane: lanePaths.lane,
|
|
790
|
-
attempt,
|
|
791
|
-
overallState: "not-applicable",
|
|
792
|
-
totalFindings: 0,
|
|
793
|
-
totalApprovals: 0,
|
|
794
|
-
concernAgentIds: [],
|
|
795
|
-
blockedAgentIds: [],
|
|
796
|
-
detail: "No security reviewer declared for this wave.",
|
|
797
|
-
agents: [],
|
|
798
|
-
createdAt,
|
|
799
|
-
updatedAt: createdAt,
|
|
800
|
-
};
|
|
801
|
-
}
|
|
802
|
-
const agents = securityAgents.map((agent) => {
|
|
803
|
-
const summary = summariesByAgentId?.[agent.agentId] || null;
|
|
804
|
-
const validation = validateSecuritySummary(agent, summary);
|
|
805
|
-
const explicitState = summary?.security?.state || null;
|
|
806
|
-
return {
|
|
807
|
-
agentId: agent.agentId,
|
|
808
|
-
title: agent.title || agent.agentId,
|
|
809
|
-
state: validation.ok
|
|
810
|
-
? explicitState || "clear"
|
|
811
|
-
: explicitState === "blocked"
|
|
812
|
-
? "blocked"
|
|
813
|
-
: "pending",
|
|
814
|
-
findings: summary?.security?.findings || 0,
|
|
815
|
-
approvals: summary?.security?.approvals || 0,
|
|
816
|
-
detail: validation.ok
|
|
817
|
-
? summary?.security?.detail || validation.detail || ""
|
|
818
|
-
: validation.detail,
|
|
819
|
-
reportPath: summary?.reportPath || resolveSecurityReviewReportPath(agent) || null,
|
|
820
|
-
statusCode: validation.statusCode,
|
|
821
|
-
ok: validation.ok,
|
|
822
|
-
};
|
|
823
|
-
});
|
|
824
|
-
const blockedAgentIds = agents
|
|
825
|
-
.filter((entry) => entry.state === "blocked")
|
|
826
|
-
.map((entry) => entry.agentId);
|
|
827
|
-
const concernAgentIds = agents
|
|
828
|
-
.filter((entry) => entry.state === "concerns")
|
|
829
|
-
.map((entry) => entry.agentId);
|
|
830
|
-
const pendingAgentIds = agents
|
|
831
|
-
.filter((entry) => entry.state === "pending")
|
|
832
|
-
.map((entry) => entry.agentId);
|
|
833
|
-
const overallState =
|
|
834
|
-
blockedAgentIds.length > 0
|
|
835
|
-
? "blocked"
|
|
836
|
-
: pendingAgentIds.length > 0
|
|
837
|
-
? "pending"
|
|
838
|
-
: concernAgentIds.length > 0
|
|
839
|
-
? "concerns"
|
|
840
|
-
: "clear";
|
|
841
|
-
const totalFindings = agents.reduce((sum, entry) => sum + (entry.findings || 0), 0);
|
|
842
|
-
const totalApprovals = agents.reduce((sum, entry) => sum + (entry.approvals || 0), 0);
|
|
843
|
-
const detail =
|
|
844
|
-
overallState === "blocked"
|
|
845
|
-
? `Security review blocked by ${blockedAgentIds.join(", ")}.`
|
|
846
|
-
: overallState === "pending"
|
|
847
|
-
? `Security review output is incomplete for ${pendingAgentIds.join(", ")}.`
|
|
848
|
-
: overallState === "concerns"
|
|
849
|
-
? `Security review reported advisory concerns from ${concernAgentIds.join(", ")}.`
|
|
850
|
-
: "Security review is clear.";
|
|
851
|
-
return {
|
|
852
|
-
wave: wave.wave,
|
|
853
|
-
lane: lanePaths.lane,
|
|
854
|
-
attempt,
|
|
855
|
-
overallState,
|
|
856
|
-
totalFindings,
|
|
857
|
-
totalApprovals,
|
|
858
|
-
concernAgentIds,
|
|
859
|
-
blockedAgentIds,
|
|
860
|
-
detail,
|
|
861
|
-
agents,
|
|
862
|
-
createdAt,
|
|
863
|
-
updatedAt: createdAt,
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
function renderWaveSecuritySummaryMarkdown(securitySummary) {
|
|
868
|
-
return [
|
|
869
|
-
`# Wave ${securitySummary.wave} Security Summary`,
|
|
870
|
-
"",
|
|
871
|
-
`- State: ${securitySummary.overallState || "unknown"}`,
|
|
872
|
-
`- Detail: ${securitySummary.detail || "n/a"}`,
|
|
873
|
-
`- Total findings: ${securitySummary.totalFindings || 0}`,
|
|
874
|
-
`- Total approvals: ${securitySummary.totalApprovals || 0}`,
|
|
875
|
-
`- Reviewers: ${(securitySummary.agents || []).length}`,
|
|
876
|
-
"",
|
|
877
|
-
"## Reviews",
|
|
878
|
-
...((securitySummary.agents || []).length > 0
|
|
879
|
-
? securitySummary.agents.map(
|
|
880
|
-
(entry) =>
|
|
881
|
-
`- ${entry.agentId}: state=${entry.state || "unknown"} findings=${entry.findings || 0} approvals=${entry.approvals || 0}${entry.reportPath ? ` report=${entry.reportPath}` : ""}${entry.detail ? ` detail=${entry.detail}` : ""}`,
|
|
882
|
-
)
|
|
883
|
-
: ["- None."]),
|
|
884
|
-
"",
|
|
885
|
-
].join("\n");
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
function padReportedEntries(entries, minimumCount, label) {
|
|
889
|
-
const padded = [...entries];
|
|
890
|
-
for (let index = padded.length + 1; index <= minimumCount; index += 1) {
|
|
891
|
-
padded.push(`${label} #${index}`);
|
|
892
|
-
}
|
|
893
|
-
return padded;
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
function buildIntegrationEvidence({
|
|
897
|
-
lanePaths,
|
|
898
|
-
wave,
|
|
899
|
-
coordinationState,
|
|
900
|
-
summariesByAgentId,
|
|
901
|
-
docsQueue,
|
|
902
|
-
agentRuns,
|
|
903
|
-
dependencySnapshot = null,
|
|
904
|
-
capabilityAssignments = [],
|
|
905
|
-
securitySummary = null,
|
|
906
|
-
}) {
|
|
907
|
-
const openClaims = (coordinationState?.claims || [])
|
|
908
|
-
.filter((record) => isOpenCoordinationStatus(record.status))
|
|
909
|
-
.map((record) => summarizeIntegrationRecord(record));
|
|
910
|
-
const conflictingClaims = (coordinationState?.claims || [])
|
|
911
|
-
.filter(
|
|
912
|
-
(record) =>
|
|
913
|
-
isOpenCoordinationStatus(record.status) &&
|
|
914
|
-
/conflict|contradict/i.test(`${record.summary || ""}\n${record.detail || ""}`),
|
|
915
|
-
)
|
|
916
|
-
.map((record) => summarizeIntegrationRecord(record));
|
|
917
|
-
const unresolvedBlockers = (coordinationState?.blockers || [])
|
|
918
|
-
.filter((record) => isOpenCoordinationStatus(record.status))
|
|
919
|
-
.map((record) => summarizeIntegrationRecord(record));
|
|
920
|
-
|
|
921
|
-
const interfaceKeywords = ["interface", "contract", "api", "schema", "migration", "signature"];
|
|
922
|
-
const changedInterfaces = actionableIntegrationRecords(coordinationState)
|
|
923
|
-
.filter((record) =>
|
|
924
|
-
textMentionsAnyKeyword(
|
|
925
|
-
[record.summary, record.detail, ...(record.artifactRefs || [])].join("\n"),
|
|
926
|
-
interfaceKeywords,
|
|
927
|
-
),
|
|
928
|
-
)
|
|
929
|
-
.map((record) => summarizeIntegrationRecord(record));
|
|
930
|
-
|
|
931
|
-
const crossComponentImpacts = actionableIntegrationRecords(coordinationState)
|
|
932
|
-
.flatMap((record) => {
|
|
933
|
-
const owners = new Set();
|
|
934
|
-
for (const artifactRef of record.artifactRefs || []) {
|
|
935
|
-
for (const owner of resolveArtifactOwners(artifactRef, wave.agents)) {
|
|
936
|
-
owners.add(owner);
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
for (const target of record.targets || []) {
|
|
940
|
-
if (String(target).startsWith("agent:")) {
|
|
941
|
-
owners.add(String(target).slice("agent:".length));
|
|
942
|
-
} else if ((wave.agents || []).some((agent) => agent.agentId === target)) {
|
|
943
|
-
owners.add(String(target));
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
if (owners.size <= 1) {
|
|
947
|
-
return [];
|
|
948
|
-
}
|
|
949
|
-
return [
|
|
950
|
-
`${summarizeIntegrationRecord(record)} [owners: ${Array.from(owners).toSorted().join(", ")}]`,
|
|
951
|
-
];
|
|
952
|
-
});
|
|
953
|
-
|
|
954
|
-
const proofGapEntries = [];
|
|
955
|
-
const docGapEntries = Array.isArray(docsQueue?.items)
|
|
956
|
-
? docsQueue.items.map((item) => summarizeDocsQueueItem(item))
|
|
957
|
-
: [];
|
|
958
|
-
const deployRiskEntries = [];
|
|
959
|
-
const securityFindingEntries = [];
|
|
960
|
-
const securityApprovalEntries = [];
|
|
961
|
-
for (const agent of wave.agents || []) {
|
|
962
|
-
const summary = summariesByAgentId?.[agent.agentId] || null;
|
|
963
|
-
const contEvalImplementationOwning =
|
|
964
|
-
agent.agentId === lanePaths.contEvalAgentId &&
|
|
965
|
-
isContEvalImplementationOwningAgent(agent, {
|
|
966
|
-
contEvalAgentId: lanePaths.contEvalAgentId,
|
|
967
|
-
});
|
|
968
|
-
if (isSecurityReviewAgent(agent)) {
|
|
969
|
-
continue;
|
|
970
|
-
}
|
|
971
|
-
if (agent.agentId === lanePaths.contEvalAgentId) {
|
|
972
|
-
const validation = validateContEvalSummary(agent, summary, {
|
|
973
|
-
mode: "live",
|
|
974
|
-
evalTargets: wave.evalTargets,
|
|
975
|
-
benchmarkCatalogPath: lanePaths.laneProfile?.paths?.benchmarkCatalogPath,
|
|
976
|
-
});
|
|
977
|
-
if (!validation.ok) {
|
|
978
|
-
proofGapEntries.push(
|
|
979
|
-
summarizeGap(agent.agentId, validation.detail, "cont-EVAL target is not yet satisfied."),
|
|
980
|
-
);
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
if (
|
|
984
|
-
![
|
|
985
|
-
lanePaths.contQaAgentId,
|
|
986
|
-
lanePaths.integrationAgentId,
|
|
987
|
-
lanePaths.documentationAgentId,
|
|
988
|
-
].includes(agent.agentId) &&
|
|
989
|
-
(agent.agentId !== lanePaths.contEvalAgentId || contEvalImplementationOwning)
|
|
990
|
-
) {
|
|
991
|
-
const validation = validateImplementationSummary(agent, summary);
|
|
992
|
-
if (!validation.ok) {
|
|
993
|
-
const entry = summarizeGap(agent.agentId, validation.detail, "Implementation validation failed.");
|
|
994
|
-
if (["missing-doc-delta", "doc-impact-gap"].includes(validation.statusCode)) {
|
|
995
|
-
docGapEntries.push(entry);
|
|
996
|
-
} else {
|
|
997
|
-
proofGapEntries.push(entry);
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
for (const gap of summary?.gaps || []) {
|
|
1002
|
-
const entry = summarizeGap(
|
|
1003
|
-
agent.agentId,
|
|
1004
|
-
gap.detail,
|
|
1005
|
-
`${gap.kind || "unknown"} gap reported.`,
|
|
1006
|
-
);
|
|
1007
|
-
if (gap.kind === "docs") {
|
|
1008
|
-
docGapEntries.push(entry);
|
|
1009
|
-
} else if (gap.kind === "ops") {
|
|
1010
|
-
deployRiskEntries.push(entry);
|
|
1011
|
-
} else {
|
|
1012
|
-
proofGapEntries.push(entry);
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
for (const run of agentRuns || []) {
|
|
1018
|
-
const signals = parseStructuredSignalsFromLog(run.logPath);
|
|
1019
|
-
if (signals?.deployment && signals.deployment.state !== "healthy") {
|
|
1020
|
-
deployRiskEntries.push(
|
|
1021
|
-
summarizeGap(
|
|
1022
|
-
run.agent.agentId,
|
|
1023
|
-
`Deployment ${signals.deployment.service} ended in state ${signals.deployment.state}${signals.deployment.detail ? ` (${signals.deployment.detail})` : ""}.`,
|
|
1024
|
-
"Deployment did not finish healthy.",
|
|
1025
|
-
),
|
|
1026
|
-
);
|
|
1027
|
-
}
|
|
1028
|
-
if (
|
|
1029
|
-
signals?.infra &&
|
|
1030
|
-
!["conformant", "action-complete"].includes(
|
|
1031
|
-
String(signals.infra.state || "").trim().toLowerCase(),
|
|
1032
|
-
)
|
|
1033
|
-
) {
|
|
1034
|
-
deployRiskEntries.push(
|
|
1035
|
-
summarizeGap(
|
|
1036
|
-
run.agent.agentId,
|
|
1037
|
-
`Infra ${signals.infra.kind || "unknown"} on ${signals.infra.target || "unknown"} ended in state ${signals.infra.state || "unknown"}${signals.infra.detail ? ` (${signals.infra.detail})` : ""}.`,
|
|
1038
|
-
"Infra risk remains open.",
|
|
1039
|
-
),
|
|
1040
|
-
);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
const inboundDependencies = (dependencySnapshot?.openInbound || []).map(
|
|
1045
|
-
(record) =>
|
|
1046
|
-
`${record.id}: ${compactSingleLine(record.summary || record.detail || "inbound dependency", 180)}${record.assignedAgentId ? ` -> ${record.assignedAgentId}` : ""}`,
|
|
1047
|
-
);
|
|
1048
|
-
const outboundDependencies = (dependencySnapshot?.openOutbound || []).map(
|
|
1049
|
-
(record) =>
|
|
1050
|
-
`${record.id}: ${compactSingleLine(record.summary || record.detail || "outbound dependency", 180)}`,
|
|
1051
|
-
);
|
|
1052
|
-
const helperAssignments = (capabilityAssignments || [])
|
|
1053
|
-
.filter((assignment) => assignment.blocking)
|
|
1054
|
-
.map(
|
|
1055
|
-
(assignment) =>
|
|
1056
|
-
`${assignment.requestId}: ${assignment.target}${assignment.assignedAgentId ? ` -> ${assignment.assignedAgentId}` : " -> unresolved"} (${assignment.assignmentReason || "n/a"})`,
|
|
1057
|
-
);
|
|
1058
|
-
|
|
1059
|
-
for (const review of securitySummary?.agents || []) {
|
|
1060
|
-
if (review.state === "blocked" || review.state === "concerns") {
|
|
1061
|
-
securityFindingEntries.push(
|
|
1062
|
-
summarizeGap(
|
|
1063
|
-
review.agentId,
|
|
1064
|
-
review.detail,
|
|
1065
|
-
review.state === "blocked"
|
|
1066
|
-
? "Security review blocked the wave."
|
|
1067
|
-
: "Security review reported advisory concerns.",
|
|
1068
|
-
),
|
|
1069
|
-
);
|
|
1070
|
-
}
|
|
1071
|
-
if ((review.approvals || 0) > 0) {
|
|
1072
|
-
securityApprovalEntries.push(
|
|
1073
|
-
summarizeGap(
|
|
1074
|
-
review.agentId,
|
|
1075
|
-
review.detail,
|
|
1076
|
-
`${review.approvals} security approval(s) remain open.`,
|
|
1077
|
-
),
|
|
1078
|
-
);
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
return {
|
|
1083
|
-
openClaims: uniqueStringEntries(openClaims),
|
|
1084
|
-
conflictingClaims: uniqueStringEntries(conflictingClaims),
|
|
1085
|
-
unresolvedBlockers: uniqueStringEntries(unresolvedBlockers),
|
|
1086
|
-
changedInterfaces: uniqueStringEntries(changedInterfaces),
|
|
1087
|
-
crossComponentImpacts: uniqueStringEntries(crossComponentImpacts),
|
|
1088
|
-
proofGaps: uniqueStringEntries(proofGapEntries),
|
|
1089
|
-
docGaps: uniqueStringEntries(docGapEntries),
|
|
1090
|
-
deployRisks: uniqueStringEntries(deployRiskEntries),
|
|
1091
|
-
inboundDependencies: uniqueStringEntries(inboundDependencies),
|
|
1092
|
-
outboundDependencies: uniqueStringEntries(outboundDependencies),
|
|
1093
|
-
helperAssignments: uniqueStringEntries(helperAssignments),
|
|
1094
|
-
securityState: securitySummary?.overallState || "not-applicable",
|
|
1095
|
-
securityFindings: uniqueStringEntries(securityFindingEntries),
|
|
1096
|
-
securityApprovals: uniqueStringEntries(securityApprovalEntries),
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
export function buildWaveIntegrationSummary({
|
|
1101
|
-
lanePaths,
|
|
1102
|
-
wave,
|
|
1103
|
-
attempt,
|
|
1104
|
-
coordinationState,
|
|
1105
|
-
summariesByAgentId,
|
|
1106
|
-
docsQueue,
|
|
1107
|
-
runtimeAssignments,
|
|
1108
|
-
agentRuns,
|
|
1109
|
-
capabilityAssignments = [],
|
|
1110
|
-
dependencySnapshot = null,
|
|
1111
|
-
securitySummary = null,
|
|
1112
|
-
}) {
|
|
1113
|
-
const explicitIntegration = summariesByAgentId[lanePaths.integrationAgentId]?.integration || null;
|
|
1114
|
-
const evidence = buildIntegrationEvidence({
|
|
1115
|
-
lanePaths,
|
|
1116
|
-
wave,
|
|
1117
|
-
coordinationState,
|
|
1118
|
-
summariesByAgentId,
|
|
1119
|
-
docsQueue,
|
|
1120
|
-
agentRuns,
|
|
1121
|
-
capabilityAssignments,
|
|
1122
|
-
dependencySnapshot,
|
|
1123
|
-
securitySummary,
|
|
1124
|
-
});
|
|
1125
|
-
if (explicitIntegration) {
|
|
1126
|
-
return {
|
|
1127
|
-
wave: wave.wave,
|
|
1128
|
-
lane: lanePaths.lane,
|
|
1129
|
-
agentId: lanePaths.integrationAgentId,
|
|
1130
|
-
attempt,
|
|
1131
|
-
openClaims: padReportedEntries(
|
|
1132
|
-
evidence.openClaims,
|
|
1133
|
-
explicitIntegration.claims || 0,
|
|
1134
|
-
"Integration steward reported unresolved claim",
|
|
1135
|
-
),
|
|
1136
|
-
conflictingClaims: padReportedEntries(
|
|
1137
|
-
evidence.conflictingClaims,
|
|
1138
|
-
explicitIntegration.conflicts || 0,
|
|
1139
|
-
"Integration steward reported unresolved conflict",
|
|
1140
|
-
),
|
|
1141
|
-
unresolvedBlockers: padReportedEntries(
|
|
1142
|
-
evidence.unresolvedBlockers,
|
|
1143
|
-
explicitIntegration.blockers || 0,
|
|
1144
|
-
"Integration steward reported unresolved blocker",
|
|
1145
|
-
),
|
|
1146
|
-
changedInterfaces: evidence.changedInterfaces,
|
|
1147
|
-
crossComponentImpacts: evidence.crossComponentImpacts,
|
|
1148
|
-
proofGaps: evidence.proofGaps,
|
|
1149
|
-
docGaps: evidence.docGaps,
|
|
1150
|
-
deployRisks: evidence.deployRisks,
|
|
1151
|
-
securityState: evidence.securityState,
|
|
1152
|
-
securityFindings: evidence.securityFindings,
|
|
1153
|
-
securityApprovals: evidence.securityApprovals,
|
|
1154
|
-
inboundDependencies: evidence.inboundDependencies,
|
|
1155
|
-
outboundDependencies: evidence.outboundDependencies,
|
|
1156
|
-
helperAssignments: evidence.helperAssignments,
|
|
1157
|
-
runtimeAssignments,
|
|
1158
|
-
recommendation: explicitIntegration.state,
|
|
1159
|
-
detail: explicitIntegration.detail || "",
|
|
1160
|
-
createdAt: toIsoTimestamp(),
|
|
1161
|
-
updatedAt: toIsoTimestamp(),
|
|
1162
|
-
};
|
|
1163
|
-
}
|
|
1164
|
-
const inferred = inferIntegrationRecommendation(evidence);
|
|
1165
|
-
return {
|
|
1166
|
-
wave: wave.wave,
|
|
1167
|
-
lane: lanePaths.lane,
|
|
1168
|
-
agentId: "launcher",
|
|
1169
|
-
attempt,
|
|
1170
|
-
...evidence,
|
|
1171
|
-
runtimeAssignments,
|
|
1172
|
-
recommendation: inferred.recommendation,
|
|
1173
|
-
detail: inferred.detail,
|
|
1174
|
-
createdAt: toIsoTimestamp(),
|
|
1175
|
-
updatedAt: toIsoTimestamp(),
|
|
1176
|
-
};
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
function renderIntegrationSection(title, items) {
|
|
1180
|
-
return [
|
|
1181
|
-
title,
|
|
1182
|
-
...((items || []).length > 0 ? items.map((item) => `- ${item}`) : ["- None."]),
|
|
1183
|
-
"",
|
|
1184
|
-
];
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
function renderIntegrationSummaryMarkdown(integrationSummary) {
|
|
1188
|
-
return [
|
|
1189
|
-
`# Wave ${integrationSummary.wave} Integration Summary`,
|
|
1190
|
-
"",
|
|
1191
|
-
`- Recommendation: ${integrationSummary.recommendation || "unknown"}`,
|
|
1192
|
-
`- Detail: ${integrationSummary.detail || "n/a"}`,
|
|
1193
|
-
`- Open claims: ${(integrationSummary.openClaims || []).length}`,
|
|
1194
|
-
`- Conflicting claims: ${(integrationSummary.conflictingClaims || []).length}`,
|
|
1195
|
-
`- Unresolved blockers: ${(integrationSummary.unresolvedBlockers || []).length}`,
|
|
1196
|
-
`- Changed interfaces: ${(integrationSummary.changedInterfaces || []).length}`,
|
|
1197
|
-
`- Cross-component impacts: ${(integrationSummary.crossComponentImpacts || []).length}`,
|
|
1198
|
-
`- Proof gaps: ${(integrationSummary.proofGaps || []).length}`,
|
|
1199
|
-
`- Deploy risks: ${(integrationSummary.deployRisks || []).length}`,
|
|
1200
|
-
`- Documentation gaps: ${(integrationSummary.docGaps || []).length}`,
|
|
1201
|
-
`- Security review: ${integrationSummary.securityState || "not-applicable"}`,
|
|
1202
|
-
`- Security findings: ${(integrationSummary.securityFindings || []).length}`,
|
|
1203
|
-
`- Security approvals: ${(integrationSummary.securityApprovals || []).length}`,
|
|
1204
|
-
`- Inbound dependencies: ${(integrationSummary.inboundDependencies || []).length}`,
|
|
1205
|
-
`- Outbound dependencies: ${(integrationSummary.outboundDependencies || []).length}`,
|
|
1206
|
-
`- Helper assignments: ${(integrationSummary.helperAssignments || []).length}`,
|
|
1207
|
-
"",
|
|
1208
|
-
...renderIntegrationSection("## Open Claims", integrationSummary.openClaims),
|
|
1209
|
-
...renderIntegrationSection("## Conflicting Claims", integrationSummary.conflictingClaims),
|
|
1210
|
-
...renderIntegrationSection("## Unresolved Blockers", integrationSummary.unresolvedBlockers),
|
|
1211
|
-
...renderIntegrationSection("## Changed Interfaces", integrationSummary.changedInterfaces),
|
|
1212
|
-
...renderIntegrationSection(
|
|
1213
|
-
"## Cross-Component Impacts",
|
|
1214
|
-
integrationSummary.crossComponentImpacts,
|
|
1215
|
-
),
|
|
1216
|
-
...renderIntegrationSection("## Proof Gaps", integrationSummary.proofGaps),
|
|
1217
|
-
...renderIntegrationSection("## Deploy Risks", integrationSummary.deployRisks),
|
|
1218
|
-
...renderIntegrationSection("## Security Findings", integrationSummary.securityFindings),
|
|
1219
|
-
...renderIntegrationSection("## Security Approvals", integrationSummary.securityApprovals),
|
|
1220
|
-
...renderIntegrationSection("## Inbound Dependencies", integrationSummary.inboundDependencies),
|
|
1221
|
-
...renderIntegrationSection("## Outbound Dependencies", integrationSummary.outboundDependencies),
|
|
1222
|
-
...renderIntegrationSection("## Helper Assignments", integrationSummary.helperAssignments),
|
|
1223
|
-
"## Runtime Assignments",
|
|
1224
|
-
...((integrationSummary.runtimeAssignments || []).length > 0
|
|
1225
|
-
? integrationSummary.runtimeAssignments.map(
|
|
1226
|
-
(assignment) =>
|
|
1227
|
-
`- ${assignment.agentId}: executor=${assignment.executorId || "n/a"} role=${assignment.role || "n/a"} profile=${assignment.profile || "none"} fallback_used=${assignment.fallbackUsed ? "yes" : "no"}`,
|
|
1228
|
-
)
|
|
1229
|
-
: ["- None."]),
|
|
1230
|
-
"",
|
|
1231
|
-
...renderIntegrationSection("## Documentation Gaps", integrationSummary.docGaps),
|
|
1232
|
-
].join("\n");
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
function writeWaveDerivedState({
|
|
1236
|
-
lanePaths,
|
|
1237
|
-
wave,
|
|
1238
|
-
agentRuns = [],
|
|
1239
|
-
summariesByAgentId = {},
|
|
1240
|
-
feedbackRequests = [],
|
|
1241
|
-
attempt = 0,
|
|
1242
|
-
orchestratorId = null,
|
|
1243
|
-
}) {
|
|
1244
|
-
const coordinationLogPath = waveCoordinationLogPath(lanePaths, wave.wave);
|
|
1245
|
-
const existingDocsQueue = readDocsQueue(waveDocsQueuePath(lanePaths, wave.wave));
|
|
1246
|
-
const existingIntegrationSummary = readJsonOrNull(waveIntegrationPath(lanePaths, wave.wave));
|
|
1247
|
-
const existingLedger = readWaveLedger(waveLedgerPath(lanePaths, wave.wave));
|
|
1248
|
-
updateSeedRecords(coordinationLogPath, {
|
|
1249
|
-
lane: lanePaths.lane,
|
|
1250
|
-
wave: wave.wave,
|
|
1251
|
-
agents: wave.agents,
|
|
1252
|
-
componentPromotions: wave.componentPromotions,
|
|
1253
|
-
sharedPlanDocs: lanePaths.sharedPlanDocs,
|
|
1254
|
-
contQaAgentId: lanePaths.contQaAgentId,
|
|
1255
|
-
contEvalAgentId: lanePaths.contEvalAgentId,
|
|
1256
|
-
integrationAgentId: lanePaths.integrationAgentId,
|
|
1257
|
-
documentationAgentId: lanePaths.documentationAgentId,
|
|
1258
|
-
feedbackRequests,
|
|
1259
|
-
});
|
|
1260
|
-
let coordinationState = readMaterializedCoordinationState(coordinationLogPath);
|
|
1261
|
-
const clarificationTriage = triageClarificationRequests({
|
|
1262
|
-
lanePaths,
|
|
1263
|
-
wave,
|
|
1264
|
-
coordinationLogPath,
|
|
1265
|
-
coordinationState,
|
|
1266
|
-
orchestratorId,
|
|
1267
|
-
attempt,
|
|
1268
|
-
resolutionContext: {
|
|
1269
|
-
docsQueue: existingDocsQueue,
|
|
1270
|
-
integrationSummary: existingIntegrationSummary,
|
|
1271
|
-
ledger: existingLedger,
|
|
1272
|
-
summariesByAgentId,
|
|
1273
|
-
},
|
|
1274
|
-
});
|
|
1275
|
-
if (clarificationTriage.changed) {
|
|
1276
|
-
coordinationState = readMaterializedCoordinationState(coordinationLogPath);
|
|
1277
|
-
}
|
|
1278
|
-
const capabilityAssignments = buildRequestAssignments({
|
|
1279
|
-
coordinationState,
|
|
1280
|
-
agents: wave.agents,
|
|
1281
|
-
ledger: existingLedger,
|
|
1282
|
-
capabilityRouting: lanePaths.capabilityRouting,
|
|
1283
|
-
});
|
|
1284
|
-
syncAssignmentRecords(coordinationLogPath, {
|
|
1285
|
-
lane: lanePaths.lane,
|
|
1286
|
-
wave: wave.wave,
|
|
1287
|
-
assignments: capabilityAssignments,
|
|
1288
|
-
});
|
|
1289
|
-
coordinationState = readMaterializedCoordinationState(coordinationLogPath);
|
|
1290
|
-
const dependencySnapshot = buildDependencySnapshot({
|
|
1291
|
-
dirPath: lanePaths.crossLaneDependenciesDir,
|
|
1292
|
-
lane: lanePaths.lane,
|
|
1293
|
-
waveNumber: wave.wave,
|
|
1294
|
-
agents: wave.agents,
|
|
1295
|
-
ledger: existingLedger,
|
|
1296
|
-
capabilityRouting: lanePaths.capabilityRouting,
|
|
1297
|
-
});
|
|
1298
|
-
writeAssignmentSnapshot(waveAssignmentsPath(lanePaths, wave.wave), capabilityAssignments, {
|
|
1299
|
-
lane: lanePaths.lane,
|
|
1300
|
-
wave: wave.wave,
|
|
1301
|
-
});
|
|
1302
|
-
writeDependencySnapshot(waveDependencySnapshotPath(lanePaths, wave.wave), dependencySnapshot, {
|
|
1303
|
-
lane: lanePaths.lane,
|
|
1304
|
-
wave: wave.wave,
|
|
1305
|
-
});
|
|
1306
|
-
writeDependencySnapshotMarkdown(
|
|
1307
|
-
waveDependencySnapshotMarkdownPath(lanePaths, wave.wave),
|
|
1308
|
-
dependencySnapshot,
|
|
1309
|
-
);
|
|
1310
|
-
const runtimeAssignments = wave.agents.map((agent) => ({
|
|
1311
|
-
agentId: agent.agentId,
|
|
1312
|
-
role: agent.executorResolved?.role || null,
|
|
1313
|
-
initialExecutorId: agent.executorResolved?.initialExecutorId || null,
|
|
1314
|
-
executorId: agent.executorResolved?.id || null,
|
|
1315
|
-
profile: agent.executorResolved?.profile || null,
|
|
1316
|
-
selectedBy: agent.executorResolved?.selectedBy || null,
|
|
1317
|
-
retryPolicy: agent.executorResolved?.retryPolicy || null,
|
|
1318
|
-
allowFallbackOnRetry: agent.executorResolved?.allowFallbackOnRetry !== false,
|
|
1319
|
-
fallbacks: agent.executorResolved?.fallbacks || [],
|
|
1320
|
-
fallbackUsed: agent.executorResolved?.fallbackUsed === true,
|
|
1321
|
-
fallbackReason: agent.executorResolved?.fallbackReason || null,
|
|
1322
|
-
executorHistory: agent.executorResolved?.executorHistory || [],
|
|
1323
|
-
}));
|
|
1324
|
-
const docsQueue = buildDocsQueue({
|
|
1325
|
-
lane: lanePaths.lane,
|
|
1326
|
-
wave,
|
|
1327
|
-
summariesByAgentId,
|
|
1328
|
-
sharedPlanDocs: lanePaths.sharedPlanDocs,
|
|
1329
|
-
componentPromotions: wave.componentPromotions,
|
|
1330
|
-
runtimeAssignments,
|
|
1331
|
-
});
|
|
1332
|
-
writeDocsQueue(waveDocsQueuePath(lanePaths, wave.wave), docsQueue);
|
|
1333
|
-
const securitySummary = buildWaveSecuritySummary({
|
|
1334
|
-
lanePaths,
|
|
1335
|
-
wave,
|
|
1336
|
-
attempt,
|
|
1337
|
-
summariesByAgentId,
|
|
1338
|
-
});
|
|
1339
|
-
writeJsonArtifact(waveSecurityPath(lanePaths, wave.wave), securitySummary);
|
|
1340
|
-
writeTextAtomic(
|
|
1341
|
-
waveSecurityMarkdownPath(lanePaths, wave.wave),
|
|
1342
|
-
`${renderWaveSecuritySummaryMarkdown(securitySummary)}\n`,
|
|
1343
|
-
);
|
|
1344
|
-
const integrationSummary = buildWaveIntegrationSummary({
|
|
1345
|
-
lanePaths,
|
|
1346
|
-
wave,
|
|
1347
|
-
attempt,
|
|
1348
|
-
coordinationState,
|
|
1349
|
-
summariesByAgentId,
|
|
1350
|
-
docsQueue,
|
|
1351
|
-
runtimeAssignments,
|
|
1352
|
-
agentRuns,
|
|
1353
|
-
capabilityAssignments,
|
|
1354
|
-
dependencySnapshot,
|
|
1355
|
-
securitySummary,
|
|
1356
|
-
});
|
|
1357
|
-
writeJsonArtifact(waveIntegrationPath(lanePaths, wave.wave), integrationSummary);
|
|
1358
|
-
writeTextAtomic(
|
|
1359
|
-
waveIntegrationMarkdownPath(lanePaths, wave.wave),
|
|
1360
|
-
`${renderIntegrationSummaryMarkdown(integrationSummary)}\n`,
|
|
1361
|
-
);
|
|
1362
|
-
const ledger = deriveWaveLedger({
|
|
1363
|
-
lane: lanePaths.lane,
|
|
1364
|
-
wave,
|
|
1365
|
-
summariesByAgentId,
|
|
1366
|
-
coordinationState,
|
|
1367
|
-
integrationSummary,
|
|
1368
|
-
docsQueue,
|
|
1369
|
-
attempt,
|
|
1370
|
-
contQaAgentId: lanePaths.contQaAgentId,
|
|
1371
|
-
contEvalAgentId: lanePaths.contEvalAgentId,
|
|
1372
|
-
integrationAgentId: lanePaths.integrationAgentId,
|
|
1373
|
-
documentationAgentId: lanePaths.documentationAgentId,
|
|
1374
|
-
benchmarkCatalogPath: lanePaths.laneProfile?.paths?.benchmarkCatalogPath,
|
|
1375
|
-
capabilityAssignments,
|
|
1376
|
-
dependencySnapshot,
|
|
1377
|
-
});
|
|
1378
|
-
writeWaveLedger(waveLedgerPath(lanePaths, wave.wave), ledger);
|
|
1379
|
-
const inboxDir = waveInboxDir(lanePaths, wave.wave);
|
|
1380
|
-
ensureDirectory(inboxDir);
|
|
1381
|
-
const sharedSummary = compileSharedSummary({
|
|
1382
|
-
wave,
|
|
1383
|
-
state: coordinationState,
|
|
1384
|
-
ledger,
|
|
1385
|
-
integrationSummary,
|
|
1386
|
-
capabilityAssignments,
|
|
1387
|
-
dependencySnapshot,
|
|
1388
|
-
});
|
|
1389
|
-
const sharedSummaryPath = path.join(inboxDir, "shared-summary.md");
|
|
1390
|
-
writeCompiledInbox(sharedSummaryPath, sharedSummary.text);
|
|
1391
|
-
const inboxesByAgentId = {};
|
|
1392
|
-
for (const agent of wave.agents) {
|
|
1393
|
-
const inbox = compileAgentInbox({
|
|
1394
|
-
wave,
|
|
1395
|
-
agent,
|
|
1396
|
-
state: coordinationState,
|
|
1397
|
-
ledger,
|
|
1398
|
-
docsQueue,
|
|
1399
|
-
integrationSummary,
|
|
1400
|
-
capabilityAssignments,
|
|
1401
|
-
dependencySnapshot,
|
|
1402
|
-
});
|
|
1403
|
-
const inboxPath = path.join(inboxDir, `${agent.agentId}.md`);
|
|
1404
|
-
writeCompiledInbox(inboxPath, inbox.text);
|
|
1405
|
-
inboxesByAgentId[agent.agentId] = { path: inboxPath, text: inbox.text, truncated: inbox.truncated };
|
|
1406
|
-
}
|
|
1407
|
-
const boardText = renderCoordinationBoardProjection({
|
|
1408
|
-
wave: wave.wave,
|
|
1409
|
-
waveFile: wave.file,
|
|
1410
|
-
agents: wave.agents,
|
|
1411
|
-
state: coordinationState,
|
|
1412
|
-
capabilityAssignments,
|
|
1413
|
-
dependencySnapshot,
|
|
1414
|
-
});
|
|
1415
|
-
const responseMetrics = buildCoordinationResponseMetrics(coordinationState);
|
|
1416
|
-
const messageBoardPath = path.join(lanePaths.messageboardsDir, `wave-${wave.wave}.md`);
|
|
1417
|
-
writeCoordinationBoardProjection(messageBoardPath, {
|
|
1418
|
-
wave: wave.wave,
|
|
1419
|
-
waveFile: wave.file,
|
|
1420
|
-
agents: wave.agents,
|
|
1421
|
-
state: coordinationState,
|
|
1422
|
-
capabilityAssignments,
|
|
1423
|
-
dependencySnapshot,
|
|
1424
|
-
});
|
|
1425
|
-
return {
|
|
1426
|
-
coordinationLogPath,
|
|
1427
|
-
coordinationState,
|
|
1428
|
-
clarificationTriage,
|
|
1429
|
-
docsQueue,
|
|
1430
|
-
capabilityAssignments,
|
|
1431
|
-
dependencySnapshot,
|
|
1432
|
-
securitySummary,
|
|
1433
|
-
integrationSummary,
|
|
1434
|
-
integrationMarkdownPath: waveIntegrationMarkdownPath(lanePaths, wave.wave),
|
|
1435
|
-
securityMarkdownPath: waveSecurityMarkdownPath(lanePaths, wave.wave),
|
|
1436
|
-
ledger,
|
|
1437
|
-
responseMetrics,
|
|
1438
|
-
sharedSummaryPath,
|
|
1439
|
-
sharedSummaryText: sharedSummary.text,
|
|
1440
|
-
inboxesByAgentId,
|
|
1441
|
-
messageBoardPath,
|
|
1442
|
-
messageBoardText: boardText,
|
|
1443
|
-
};
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
function applyDerivedStateToDashboard(dashboardState, derivedState) {
|
|
1447
|
-
if (!dashboardState || !derivedState) {
|
|
1448
|
-
return;
|
|
1449
|
-
}
|
|
1450
|
-
dashboardState.helperAssignmentsOpen = (derivedState.capabilityAssignments || []).filter(
|
|
1451
|
-
(assignment) => assignment.blocking,
|
|
1452
|
-
).length;
|
|
1453
|
-
dashboardState.inboundDependenciesOpen = (derivedState.dependencySnapshot?.openInbound || []).length;
|
|
1454
|
-
dashboardState.outboundDependenciesOpen = (derivedState.dependencySnapshot?.openOutbound || []).length;
|
|
1455
|
-
dashboardState.coordinationOpen = derivedState.coordinationState?.openRecords?.length || 0;
|
|
1456
|
-
dashboardState.openClarifications =
|
|
1457
|
-
(derivedState.coordinationState?.clarifications || []).filter((record) =>
|
|
1458
|
-
isOpenCoordinationStatus(record.status),
|
|
1459
|
-
).length;
|
|
1460
|
-
dashboardState.openHumanEscalations =
|
|
1461
|
-
derivedState.responseMetrics?.openHumanEscalationCount ||
|
|
1462
|
-
(derivedState.coordinationState?.humanEscalations || []).filter((record) =>
|
|
1463
|
-
isOpenCoordinationStatus(record.status),
|
|
1464
|
-
).length;
|
|
1465
|
-
dashboardState.oldestOpenCoordinationAgeMs =
|
|
1466
|
-
derivedState.responseMetrics?.oldestOpenCoordinationAgeMs ?? null;
|
|
1467
|
-
dashboardState.oldestUnackedRequestAgeMs =
|
|
1468
|
-
derivedState.responseMetrics?.oldestUnackedRequestAgeMs ?? null;
|
|
1469
|
-
dashboardState.overdueAckCount = derivedState.responseMetrics?.overdueAckCount || 0;
|
|
1470
|
-
dashboardState.overdueClarificationCount =
|
|
1471
|
-
derivedState.responseMetrics?.overdueClarificationCount || 0;
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
|
-
export function readWaveImplementationGate(wave, agentRuns) {
|
|
1475
|
-
const contQaAgentId = wave.contQaAgentId || "A0";
|
|
1476
|
-
const contEvalAgentId = wave.contEvalAgentId || "E0";
|
|
1477
|
-
const integrationAgentId = wave.integrationAgentId || "A8";
|
|
1478
|
-
const documentationAgentId = wave.documentationAgentId || "A9";
|
|
1479
|
-
for (const runInfo of agentRuns) {
|
|
1480
|
-
if (
|
|
1481
|
-
[contQaAgentId, integrationAgentId, documentationAgentId].includes(runInfo.agent.agentId) ||
|
|
1482
|
-
isContEvalReportOnlyAgent(runInfo.agent, { contEvalAgentId }) ||
|
|
1483
|
-
isSecurityReviewAgent(runInfo.agent)
|
|
1484
|
-
) {
|
|
1485
|
-
continue;
|
|
1486
|
-
}
|
|
1487
|
-
const summary = readRunExecutionSummary(runInfo, wave);
|
|
1488
|
-
const validation = validateImplementationSummary(runInfo.agent, summary);
|
|
1489
|
-
if (!validation.ok) {
|
|
1490
|
-
return {
|
|
1491
|
-
ok: false,
|
|
1492
|
-
agentId: runInfo.agent.agentId,
|
|
1493
|
-
statusCode: validation.statusCode,
|
|
1494
|
-
detail: validation.detail,
|
|
1495
|
-
logPath: summary?.logPath || path.relative(REPO_ROOT, runInfo.logPath),
|
|
1496
|
-
};
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
return {
|
|
1500
|
-
ok: true,
|
|
1501
|
-
agentId: null,
|
|
1502
|
-
statusCode: "pass",
|
|
1503
|
-
detail: "All implementation exit contracts are satisfied.",
|
|
1504
|
-
logPath: null,
|
|
1505
|
-
};
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
|
-
function analyzePromotedComponentOwners(componentId, agentRuns, summariesByAgentId) {
|
|
1509
|
-
const ownerRuns = (agentRuns || []).filter((runInfo) =>
|
|
1510
|
-
runInfo.agent.components?.includes(componentId),
|
|
1511
|
-
);
|
|
1512
|
-
const ownerAgentIds = ownerRuns.map((runInfo) => runInfo.agent.agentId);
|
|
1513
|
-
const satisfiedAgentIds = [];
|
|
1514
|
-
const waitingOnAgentIds = [];
|
|
1515
|
-
const failedOwnContractAgentIds = [];
|
|
1516
|
-
for (const runInfo of ownerRuns) {
|
|
1517
|
-
const summary = summariesByAgentId?.[runInfo.agent.agentId] || null;
|
|
1518
|
-
const implementationValidation = validateImplementationSummary(runInfo.agent, summary);
|
|
1519
|
-
const componentMarkers = new Map(
|
|
1520
|
-
Array.isArray(summary?.components)
|
|
1521
|
-
? summary.components.map((component) => [component.componentId, component])
|
|
1522
|
-
: [],
|
|
1523
|
-
);
|
|
1524
|
-
const marker = componentMarkers.get(componentId);
|
|
1525
|
-
const expectedLevel = runInfo.agent.componentTargets?.[componentId] || null;
|
|
1526
|
-
const componentSatisfied =
|
|
1527
|
-
marker &&
|
|
1528
|
-
marker.state === "met" &&
|
|
1529
|
-
(!expectedLevel || marker.level === expectedLevel);
|
|
1530
|
-
if (implementationValidation.ok && componentSatisfied) {
|
|
1531
|
-
satisfiedAgentIds.push(runInfo.agent.agentId);
|
|
1532
|
-
continue;
|
|
1533
|
-
}
|
|
1534
|
-
waitingOnAgentIds.push(runInfo.agent.agentId);
|
|
1535
|
-
if (!implementationValidation.ok) {
|
|
1536
|
-
failedOwnContractAgentIds.push(runInfo.agent.agentId);
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
return {
|
|
1540
|
-
componentId,
|
|
1541
|
-
ownerRuns,
|
|
1542
|
-
ownerAgentIds,
|
|
1543
|
-
satisfiedAgentIds,
|
|
1544
|
-
waitingOnAgentIds,
|
|
1545
|
-
failedOwnContractAgentIds,
|
|
1546
|
-
};
|
|
1547
|
-
}
|
|
1548
|
-
|
|
1549
|
-
function buildSharedComponentSiblingPendingFailure(componentState) {
|
|
1550
|
-
if (
|
|
1551
|
-
!componentState ||
|
|
1552
|
-
componentState.satisfiedAgentIds.length === 0 ||
|
|
1553
|
-
componentState.waitingOnAgentIds.length === 0
|
|
1554
|
-
) {
|
|
1555
|
-
return null;
|
|
1556
|
-
}
|
|
1557
|
-
const landedSummary =
|
|
1558
|
-
componentState.satisfiedAgentIds.length === 1
|
|
1559
|
-
? `${componentState.satisfiedAgentIds[0]} desired-state slice landed`
|
|
1560
|
-
: `${componentState.satisfiedAgentIds.join(", ")} desired-state slices landed`;
|
|
1561
|
-
const ownerRun =
|
|
1562
|
-
componentState.ownerRuns.find((runInfo) =>
|
|
1563
|
-
componentState.waitingOnAgentIds.includes(runInfo.agent.agentId),
|
|
1564
|
-
) ||
|
|
1565
|
-
componentState.ownerRuns[0] ||
|
|
1566
|
-
null;
|
|
1567
|
-
return {
|
|
1568
|
-
ok: false,
|
|
1569
|
-
agentId: componentState.waitingOnAgentIds[0] || ownerRun?.agent?.agentId || null,
|
|
1570
|
-
componentId: componentState.componentId || null,
|
|
1571
|
-
statusCode: "shared-component-sibling-pending",
|
|
1572
|
-
detail: `${landedSummary}; shared component closure still depends on ${componentState.waitingOnAgentIds.join("/")}.`,
|
|
1573
|
-
logPath: ownerRun ? path.relative(REPO_ROOT, ownerRun.logPath) : null,
|
|
1574
|
-
ownerAgentIds: componentState.ownerAgentIds,
|
|
1575
|
-
satisfiedAgentIds: componentState.satisfiedAgentIds,
|
|
1576
|
-
waitingOnAgentIds: componentState.waitingOnAgentIds,
|
|
1577
|
-
failedOwnContractAgentIds: componentState.failedOwnContractAgentIds,
|
|
1578
|
-
};
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
export function readWaveComponentGate(wave, agentRuns, options = {}) {
|
|
1582
|
-
const summariesByAgentId = Object.fromEntries(
|
|
1583
|
-
agentRuns.map((runInfo) => [runInfo.agent.agentId, readRunExecutionSummary(runInfo, wave)]),
|
|
1584
|
-
);
|
|
1585
|
-
const validation = validateWaveComponentPromotions(wave, summariesByAgentId, options);
|
|
1586
|
-
const sharedPending = (wave.componentPromotions || [])
|
|
1587
|
-
.map((promotion) =>
|
|
1588
|
-
buildSharedComponentSiblingPendingFailure(
|
|
1589
|
-
analyzePromotedComponentOwners(promotion.componentId, agentRuns, summariesByAgentId),
|
|
1590
|
-
),
|
|
1591
|
-
)
|
|
1592
|
-
.find(Boolean);
|
|
1593
|
-
if (sharedPending) {
|
|
1594
|
-
return sharedPending;
|
|
1595
|
-
}
|
|
1596
|
-
if (validation.ok) {
|
|
1597
|
-
return {
|
|
1598
|
-
ok: true,
|
|
1599
|
-
agentId: null,
|
|
1600
|
-
componentId: null,
|
|
1601
|
-
statusCode: validation.statusCode,
|
|
1602
|
-
detail: validation.detail,
|
|
1603
|
-
logPath: null,
|
|
1604
|
-
};
|
|
1605
|
-
}
|
|
1606
|
-
const componentState = analyzePromotedComponentOwners(
|
|
1607
|
-
validation.componentId,
|
|
1608
|
-
agentRuns,
|
|
1609
|
-
summariesByAgentId,
|
|
1610
|
-
);
|
|
1611
|
-
const ownerRun = componentState.ownerRuns[0] ?? null;
|
|
1612
|
-
return {
|
|
1613
|
-
ok: false,
|
|
1614
|
-
agentId: ownerRun?.agent?.agentId || null,
|
|
1615
|
-
componentId: validation.componentId || null,
|
|
1616
|
-
statusCode: validation.statusCode,
|
|
1617
|
-
detail: validation.detail,
|
|
1618
|
-
logPath: ownerRun ? path.relative(REPO_ROOT, ownerRun.logPath) : null,
|
|
1619
|
-
ownerAgentIds: componentState.ownerAgentIds,
|
|
1620
|
-
satisfiedAgentIds: componentState.satisfiedAgentIds,
|
|
1621
|
-
waitingOnAgentIds: componentState.waitingOnAgentIds,
|
|
1622
|
-
failedOwnContractAgentIds: componentState.failedOwnContractAgentIds,
|
|
1623
|
-
};
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
export function readWaveComponentMatrixGate(wave, agentRuns, options = {}) {
|
|
1627
|
-
const validation = validateWaveComponentMatrixCurrentLevels(wave, options);
|
|
1628
|
-
if (validation.ok) {
|
|
1629
|
-
return {
|
|
1630
|
-
ok: true,
|
|
1631
|
-
agentId: null,
|
|
1632
|
-
componentId: null,
|
|
1633
|
-
statusCode: validation.statusCode,
|
|
1634
|
-
detail: validation.detail,
|
|
1635
|
-
logPath: null,
|
|
1636
|
-
};
|
|
1637
|
-
}
|
|
1638
|
-
const documentationAgentId =
|
|
1639
|
-
options.documentationAgentId || wave.documentationAgentId || "A9";
|
|
1640
|
-
const docRun =
|
|
1641
|
-
agentRuns.find((runInfo) => runInfo.agent.agentId === documentationAgentId) ?? null;
|
|
1642
|
-
return {
|
|
1643
|
-
ok: false,
|
|
1644
|
-
agentId: docRun?.agent?.agentId || null,
|
|
1645
|
-
componentId: validation.componentId || null,
|
|
1646
|
-
statusCode: validation.statusCode,
|
|
1647
|
-
detail: validation.detail,
|
|
1648
|
-
logPath: docRun ? path.relative(REPO_ROOT, docRun.logPath) : null,
|
|
1649
|
-
};
|
|
1650
|
-
}
|
|
1651
|
-
|
|
1652
|
-
export function readWaveDocumentationGate(wave, agentRuns) {
|
|
1653
|
-
const documentationAgentId = wave.documentationAgentId || "A9";
|
|
1654
|
-
const docRun =
|
|
1655
|
-
agentRuns.find((run) => run.agent.agentId === documentationAgentId) ?? null;
|
|
1656
|
-
if (!docRun) {
|
|
1657
|
-
return {
|
|
1658
|
-
ok: true,
|
|
1659
|
-
agentId: null,
|
|
1660
|
-
statusCode: "pass",
|
|
1661
|
-
detail: "No documentation steward declared for this wave.",
|
|
1662
|
-
logPath: null,
|
|
1663
|
-
};
|
|
1664
|
-
}
|
|
1665
|
-
const summary = readRunExecutionSummary(docRun, wave);
|
|
1666
|
-
const validation = validateDocumentationClosureSummary(docRun.agent, summary);
|
|
1667
|
-
return {
|
|
1668
|
-
ok: validation.ok,
|
|
1669
|
-
agentId: docRun.agent.agentId,
|
|
1670
|
-
statusCode: validation.statusCode,
|
|
1671
|
-
detail: validation.detail,
|
|
1672
|
-
logPath: summary?.logPath || path.relative(REPO_ROOT, docRun.logPath),
|
|
1673
|
-
};
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
|
-
export function readWaveSecurityGate(wave, agentRuns) {
|
|
1677
|
-
const securityRuns = (agentRuns || []).filter((run) => isSecurityReviewAgent(run.agent));
|
|
1678
|
-
if (securityRuns.length === 0) {
|
|
1679
|
-
return {
|
|
1680
|
-
ok: true,
|
|
1681
|
-
agentId: null,
|
|
1682
|
-
statusCode: "pass",
|
|
1683
|
-
detail: "No security reviewer declared for this wave.",
|
|
1684
|
-
logPath: null,
|
|
1685
|
-
};
|
|
1686
|
-
}
|
|
1687
|
-
const concernAgentIds = [];
|
|
1688
|
-
for (const runInfo of securityRuns) {
|
|
1689
|
-
const summary = readRunExecutionSummary(runInfo, wave);
|
|
1690
|
-
const validation = validateSecuritySummary(runInfo.agent, summary);
|
|
1691
|
-
if (!validation.ok) {
|
|
1692
|
-
return {
|
|
1693
|
-
ok: false,
|
|
1694
|
-
agentId: runInfo.agent.agentId,
|
|
1695
|
-
statusCode: validation.statusCode,
|
|
1696
|
-
detail: validation.detail,
|
|
1697
|
-
logPath: summary?.logPath || path.relative(REPO_ROOT, runInfo.logPath),
|
|
1698
|
-
};
|
|
1699
|
-
}
|
|
1700
|
-
if (summary?.security?.state === "concerns") {
|
|
1701
|
-
concernAgentIds.push(runInfo.agent.agentId);
|
|
1702
|
-
}
|
|
1703
|
-
}
|
|
1704
|
-
if (concernAgentIds.length > 0) {
|
|
1705
|
-
return {
|
|
1706
|
-
ok: true,
|
|
1707
|
-
agentId: null,
|
|
1708
|
-
statusCode: "security-concerns",
|
|
1709
|
-
detail: `Security review reported advisory concerns (${concernAgentIds.join(", ")}).`,
|
|
1710
|
-
logPath: null,
|
|
1711
|
-
};
|
|
1712
|
-
}
|
|
1713
|
-
return {
|
|
1714
|
-
ok: true,
|
|
1715
|
-
agentId: null,
|
|
1716
|
-
statusCode: "pass",
|
|
1717
|
-
detail: "Security review is clear.",
|
|
1718
|
-
logPath: null,
|
|
1719
|
-
};
|
|
1720
|
-
}
|
|
1721
|
-
|
|
1722
|
-
export function readWaveIntegrationGate(wave, agentRuns, options = {}) {
|
|
1723
|
-
const integrationAgentId =
|
|
1724
|
-
options.integrationAgentId || wave.integrationAgentId || "A8";
|
|
1725
|
-
const requireIntegration =
|
|
1726
|
-
options.requireIntegrationSteward === true ||
|
|
1727
|
-
(options.requireIntegrationStewardFromWave !== null &&
|
|
1728
|
-
options.requireIntegrationStewardFromWave !== undefined &&
|
|
1729
|
-
wave.wave >= options.requireIntegrationStewardFromWave);
|
|
1730
|
-
const integrationRun =
|
|
1731
|
-
agentRuns.find((run) => run.agent.agentId === integrationAgentId) ?? null;
|
|
1732
|
-
if (!integrationRun) {
|
|
1733
|
-
return {
|
|
1734
|
-
ok: !requireIntegration,
|
|
1735
|
-
agentId: requireIntegration ? integrationAgentId : null,
|
|
1736
|
-
statusCode: requireIntegration ? "missing-integration" : "pass",
|
|
1737
|
-
detail: requireIntegration
|
|
1738
|
-
? `Agent ${integrationAgentId} is missing.`
|
|
1739
|
-
: "No explicit integration steward declared for this wave.",
|
|
1740
|
-
logPath: null,
|
|
1741
|
-
};
|
|
1742
|
-
}
|
|
1743
|
-
const summary = readRunExecutionSummary(integrationRun, wave);
|
|
1744
|
-
const validation = validateIntegrationSummary(integrationRun.agent, summary);
|
|
1745
|
-
return {
|
|
1746
|
-
ok: validation.ok,
|
|
1747
|
-
agentId: integrationRun.agent.agentId,
|
|
1748
|
-
statusCode: validation.statusCode,
|
|
1749
|
-
detail: validation.detail,
|
|
1750
|
-
logPath: summary?.logPath || path.relative(REPO_ROOT, integrationRun.logPath),
|
|
1751
|
-
};
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
|
-
export function readWaveIntegrationBarrier(wave, agentRuns, derivedState, options = {}) {
|
|
1755
|
-
const markerGate = readWaveIntegrationGate(wave, agentRuns, options);
|
|
1756
|
-
if (!markerGate.ok) {
|
|
1757
|
-
return markerGate;
|
|
1758
|
-
}
|
|
1759
|
-
const integrationSummary = derivedState?.integrationSummary || null;
|
|
1760
|
-
if (!integrationSummary) {
|
|
1761
|
-
return {
|
|
1762
|
-
ok: false,
|
|
1763
|
-
agentId: markerGate.agentId,
|
|
1764
|
-
statusCode: "missing-integration-summary",
|
|
1765
|
-
detail: `Missing integration summary artifact for wave ${wave.wave}.`,
|
|
1766
|
-
logPath: markerGate.logPath,
|
|
1767
|
-
};
|
|
1768
|
-
}
|
|
1769
|
-
if (integrationSummary.recommendation !== "ready-for-doc-closure") {
|
|
1770
|
-
return {
|
|
1771
|
-
ok: false,
|
|
1772
|
-
agentId: markerGate.agentId,
|
|
1773
|
-
statusCode: "integration-needs-more-work",
|
|
1774
|
-
detail:
|
|
1775
|
-
integrationSummary.detail ||
|
|
1776
|
-
`Integration summary still reports ${integrationSummary.recommendation}.`,
|
|
1777
|
-
logPath: markerGate.logPath,
|
|
1778
|
-
};
|
|
1779
|
-
}
|
|
1780
|
-
return markerGate;
|
|
1781
|
-
}
|
|
1782
|
-
|
|
1783
|
-
export async function runClosureSweepPhase({
|
|
1784
|
-
lanePaths,
|
|
1785
|
-
wave,
|
|
1786
|
-
closureRuns,
|
|
1787
|
-
coordinationLogPath,
|
|
1788
|
-
refreshDerivedState,
|
|
1789
|
-
dashboardState,
|
|
1790
|
-
recordCombinedEvent,
|
|
1791
|
-
flushDashboards,
|
|
1792
|
-
options,
|
|
1793
|
-
feedbackStateByRequestId,
|
|
1794
|
-
appendCoordination,
|
|
1795
|
-
launchAgentSessionFn = launchAgentSession,
|
|
1796
|
-
waitForWaveCompletionFn = waitForWaveCompletion,
|
|
1797
|
-
}) {
|
|
1798
|
-
return runClosureSweepPhaseImpl({
|
|
1799
|
-
lanePaths,
|
|
1800
|
-
wave,
|
|
1801
|
-
closureRuns,
|
|
1802
|
-
coordinationLogPath,
|
|
1803
|
-
refreshDerivedState,
|
|
1804
|
-
dashboardState,
|
|
1805
|
-
recordCombinedEvent,
|
|
1806
|
-
flushDashboards,
|
|
1807
|
-
options,
|
|
1808
|
-
feedbackStateByRequestId,
|
|
1809
|
-
appendCoordination,
|
|
1810
|
-
launchAgentSessionFn,
|
|
1811
|
-
waitForWaveCompletionFn,
|
|
1812
|
-
readWaveContEvalGateFn: readWaveContEvalGate,
|
|
1813
|
-
readWaveSecurityGateFn: readWaveSecurityGate,
|
|
1814
|
-
readWaveIntegrationBarrierFn: readWaveIntegrationBarrier,
|
|
1815
|
-
readWaveDocumentationGateFn: readWaveDocumentationGate,
|
|
1816
|
-
readWaveComponentMatrixGateFn: readWaveComponentMatrixGate,
|
|
1817
|
-
readWaveContQaGateFn: readWaveContQaGate,
|
|
1818
|
-
materializeAgentExecutionSummaryForRunFn: materializeAgentExecutionSummaryForRun,
|
|
1819
|
-
monitorWaveHumanFeedbackFn: monitorWaveHumanFeedback,
|
|
1820
|
-
});
|
|
1821
|
-
}
|
|
1822
|
-
|
|
1823
|
-
export function readWaveInfraGate(agentRuns) {
|
|
1824
|
-
return readWaveInfraGateImpl(agentRuns);
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1827
|
-
export function markLauncherFailed(
|
|
1828
|
-
globalDashboard,
|
|
1829
|
-
lanePaths,
|
|
1830
|
-
selectedWaves,
|
|
1831
|
-
appendCoordination,
|
|
1832
|
-
error,
|
|
1833
|
-
) {
|
|
1834
|
-
if (globalDashboard) {
|
|
1835
|
-
globalDashboard.status = "failed";
|
|
1836
|
-
recordGlobalDashboardEvent(globalDashboard, {
|
|
1837
|
-
level: "error",
|
|
1838
|
-
message: error instanceof Error ? error.message : String(error),
|
|
1839
|
-
});
|
|
1840
|
-
writeGlobalDashboard(lanePaths.globalDashboardPath, globalDashboard);
|
|
1841
|
-
}
|
|
1842
|
-
appendCoordination({
|
|
1843
|
-
event: "launcher_finish",
|
|
1844
|
-
waves: selectedWaves,
|
|
1845
|
-
status: "failed",
|
|
1846
|
-
details: error instanceof Error ? error.message : String(error),
|
|
1847
|
-
actionRequested: `Lane ${lanePaths.lane} owners should inspect the failing wave logs and dashboards before retrying.`,
|
|
1848
|
-
});
|
|
1849
|
-
}
|
|
1850
|
-
|
|
1851
|
-
export function acquireLauncherLock(lockPath, options) {
|
|
1852
|
-
ensureDirectory(path.dirname(lockPath));
|
|
1853
|
-
const payload = {
|
|
1854
|
-
lane: options.lane,
|
|
1855
|
-
pid: process.pid,
|
|
1856
|
-
startedAt: toIsoTimestamp(),
|
|
1857
|
-
argv: process.argv.slice(2),
|
|
1858
|
-
cwd: REPO_ROOT,
|
|
1859
|
-
mode: options.reconcileStatus ? "reconcile" : "launch",
|
|
1860
|
-
};
|
|
1861
|
-
try {
|
|
1862
|
-
const fd = fs.openSync(lockPath, "wx");
|
|
1863
|
-
fs.writeFileSync(fd, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
1864
|
-
fs.closeSync(fd);
|
|
1865
|
-
return payload;
|
|
1866
|
-
} catch (error) {
|
|
1867
|
-
if (error?.code !== "EEXIST") {
|
|
1868
|
-
throw error;
|
|
1869
|
-
}
|
|
1870
|
-
const existing = readJsonOrNull(lockPath);
|
|
1871
|
-
const existingPid = Number.parseInt(String(existing?.pid ?? ""), 10);
|
|
1872
|
-
if (isProcessAlive(existingPid)) {
|
|
1873
|
-
const lockError = new Error(
|
|
1874
|
-
`Another launcher is active (pid ${existingPid}, started ${existing?.startedAt || "unknown"}). Lock: ${path.relative(REPO_ROOT, lockPath)}`,
|
|
1875
|
-
{ cause: error },
|
|
1876
|
-
);
|
|
1877
|
-
lockError.exitCode = 32;
|
|
1878
|
-
throw lockError;
|
|
1879
|
-
}
|
|
1880
|
-
fs.rmSync(lockPath, { force: true });
|
|
1881
|
-
const retryFd = fs.openSync(lockPath, "wx");
|
|
1882
|
-
fs.writeFileSync(retryFd, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
1883
|
-
fs.closeSync(retryFd);
|
|
1884
|
-
return payload;
|
|
1885
|
-
}
|
|
1886
|
-
}
|
|
1887
|
-
|
|
1888
|
-
export function releaseLauncherLock(lockPath) {
|
|
1889
|
-
fs.rmSync(lockPath, { force: true });
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
|
-
function isLaneSessionName(lanePaths, sessionName) {
|
|
1893
|
-
return (
|
|
1894
|
-
sessionName.startsWith(lanePaths.tmuxSessionPrefix) ||
|
|
1895
|
-
sessionName.startsWith(lanePaths.tmuxDashboardSessionPrefix) ||
|
|
1896
|
-
sessionName.startsWith(lanePaths.tmuxGlobalDashboardSessionPrefix)
|
|
1897
|
-
);
|
|
1898
|
-
}
|
|
1899
|
-
|
|
1900
|
-
function listLaneTmuxSessionNames(lanePaths) {
|
|
1901
|
-
return listTmuxSessionNames(lanePaths).filter((sessionName) =>
|
|
1902
|
-
isLaneSessionName(lanePaths, sessionName),
|
|
1903
|
-
);
|
|
1904
|
-
}
|
|
1905
|
-
|
|
1906
|
-
function residentOrchestratorRolePromptPath() {
|
|
1907
|
-
return path.join(REPO_ROOT, "docs", "agents", "wave-orchestrator-role.md");
|
|
1908
|
-
}
|
|
1909
|
-
|
|
1910
|
-
function loadResidentOrchestratorRolePrompt() {
|
|
1911
|
-
const filePath = residentOrchestratorRolePromptPath();
|
|
1912
|
-
if (!fs.existsSync(filePath)) {
|
|
1913
|
-
return "Monitor the wave, triage clarification timing, and intervene through coordination records only.";
|
|
1914
|
-
}
|
|
1915
|
-
return fs.readFileSync(filePath, "utf8");
|
|
1916
|
-
}
|
|
1917
|
-
|
|
1918
|
-
function defaultResidentExecutorState(options) {
|
|
1919
|
-
if (options.executorMode === "claude") {
|
|
1920
|
-
return {
|
|
1921
|
-
id: "claude",
|
|
1922
|
-
role: "orchestrator",
|
|
1923
|
-
selectedBy: "resident-orchestrator",
|
|
1924
|
-
budget: { minutes: options.timeoutMinutes },
|
|
1925
|
-
claude: {
|
|
1926
|
-
command: "claude",
|
|
1927
|
-
},
|
|
1928
|
-
};
|
|
1929
|
-
}
|
|
1930
|
-
if (options.executorMode === "opencode") {
|
|
1931
|
-
return {
|
|
1932
|
-
id: "opencode",
|
|
1933
|
-
role: "orchestrator",
|
|
1934
|
-
selectedBy: "resident-orchestrator",
|
|
1935
|
-
budget: { minutes: options.timeoutMinutes },
|
|
1936
|
-
opencode: {
|
|
1937
|
-
command: "opencode",
|
|
1938
|
-
},
|
|
1939
|
-
};
|
|
1940
|
-
}
|
|
1941
|
-
return {
|
|
1942
|
-
id: "codex",
|
|
1943
|
-
role: "orchestrator",
|
|
1944
|
-
selectedBy: "resident-orchestrator",
|
|
1945
|
-
budget: { minutes: options.timeoutMinutes },
|
|
1946
|
-
codex: {
|
|
1947
|
-
command: "codex",
|
|
1948
|
-
sandbox: options.codexSandboxMode,
|
|
1949
|
-
},
|
|
1950
|
-
};
|
|
1951
|
-
}
|
|
1952
|
-
|
|
1953
|
-
function buildResidentExecutorState(executorTemplate, options) {
|
|
1954
|
-
const source = executorTemplate
|
|
1955
|
-
? JSON.parse(JSON.stringify(executorTemplate))
|
|
1956
|
-
: defaultResidentExecutorState(options);
|
|
1957
|
-
source.role = "orchestrator";
|
|
1958
|
-
source.selectedBy = "resident-orchestrator";
|
|
1959
|
-
source.budget = {
|
|
1960
|
-
...(source.budget || {}),
|
|
1961
|
-
minutes: Math.max(
|
|
1962
|
-
Number.parseInt(String(source?.budget?.minutes || 0), 10) || 0,
|
|
1963
|
-
options.timeoutMinutes,
|
|
1964
|
-
),
|
|
1965
|
-
};
|
|
1966
|
-
if (source.id === "codex") {
|
|
1967
|
-
source.codex = {
|
|
1968
|
-
...(source.codex || {}),
|
|
1969
|
-
command: source?.codex?.command || "codex",
|
|
1970
|
-
sandbox: source?.codex?.sandbox || options.codexSandboxMode,
|
|
1971
|
-
};
|
|
1972
|
-
} else if (source.id === "claude") {
|
|
1973
|
-
source.claude = {
|
|
1974
|
-
...(source.claude || {}),
|
|
1975
|
-
command: source?.claude?.command || "claude",
|
|
1976
|
-
};
|
|
1977
|
-
} else if (source.id === "opencode") {
|
|
1978
|
-
source.opencode = {
|
|
1979
|
-
...(source.opencode || {}),
|
|
1980
|
-
command: source?.opencode?.command || "opencode",
|
|
1981
|
-
};
|
|
1982
|
-
}
|
|
1983
|
-
return source;
|
|
1984
|
-
}
|
|
1985
|
-
|
|
1986
|
-
function buildResidentOrchestratorRun({
|
|
1987
|
-
lanePaths,
|
|
1988
|
-
wave,
|
|
1989
|
-
agentRuns,
|
|
1990
|
-
derivedState,
|
|
1991
|
-
dashboardPath,
|
|
1992
|
-
runTag,
|
|
1993
|
-
options,
|
|
1994
|
-
}) {
|
|
1995
|
-
const executorTemplate =
|
|
1996
|
-
agentRuns.find((run) => run.agent.executorResolved?.id === options.executorMode)?.agent
|
|
1997
|
-
?.executorResolved ||
|
|
1998
|
-
agentRuns.find((run) => run.agent.executorResolved)?.agent?.executorResolved ||
|
|
1999
|
-
null;
|
|
2000
|
-
const executorResolved = buildResidentExecutorState(executorTemplate, options);
|
|
2001
|
-
if (executorResolved.id === "local") {
|
|
2002
|
-
return {
|
|
2003
|
-
run: null,
|
|
2004
|
-
skipReason: "Resident orchestrator requires codex, claude, or opencode; local executor is not suitable.",
|
|
2005
|
-
};
|
|
2006
|
-
}
|
|
2007
|
-
const agent = {
|
|
2008
|
-
agentId: "ORCH",
|
|
2009
|
-
title: "Resident Orchestrator",
|
|
2010
|
-
slug: `${wave.wave}-resident-orchestrator`,
|
|
2011
|
-
prompt: loadResidentOrchestratorRolePrompt(),
|
|
2012
|
-
executorResolved,
|
|
2013
|
-
};
|
|
2014
|
-
const baseName = `wave-${wave.wave}-resident-orchestrator`;
|
|
2015
|
-
const sessionName = `${lanePaths.tmuxSessionPrefix}${wave.wave}_resident_orchestrator_${runTag}`.replace(
|
|
2016
|
-
/[^a-zA-Z0-9_-]/g,
|
|
2017
|
-
"_",
|
|
2018
|
-
);
|
|
2019
|
-
return {
|
|
2020
|
-
run: {
|
|
2021
|
-
agent,
|
|
2022
|
-
sessionName,
|
|
2023
|
-
promptPath: path.join(lanePaths.promptsDir, `${baseName}.prompt.md`),
|
|
2024
|
-
logPath: path.join(lanePaths.logsDir, `${baseName}.log`),
|
|
2025
|
-
statusPath: path.join(lanePaths.statusDir, `${baseName}.status`),
|
|
2026
|
-
promptOverride: buildResidentOrchestratorPrompt({
|
|
2027
|
-
lane: lanePaths.lane,
|
|
2028
|
-
wave: wave.wave,
|
|
2029
|
-
waveFile: wave.file,
|
|
2030
|
-
orchestratorId: options.orchestratorId,
|
|
2031
|
-
coordinationLogPath: derivedState.coordinationLogPath,
|
|
2032
|
-
messageBoardPath: derivedState.messageBoardPath,
|
|
2033
|
-
sharedSummaryPath: derivedState.sharedSummaryPath,
|
|
2034
|
-
dashboardPath,
|
|
2035
|
-
triagePath: derivedState.clarificationTriage?.triagePath || null,
|
|
2036
|
-
rolePrompt: agent.prompt,
|
|
2037
|
-
}),
|
|
2038
|
-
},
|
|
2039
|
-
skipReason: "",
|
|
2040
|
-
};
|
|
2041
|
-
}
|
|
2042
|
-
|
|
2043
|
-
function monitorResidentOrchestratorSession({
|
|
2044
|
-
lanePaths,
|
|
2045
|
-
run,
|
|
2046
|
-
waveNumber,
|
|
2047
|
-
recordCombinedEvent,
|
|
2048
|
-
appendCoordination,
|
|
2049
|
-
sessionState,
|
|
2050
|
-
}) {
|
|
2051
|
-
if (!run || sessionState?.closed === true) {
|
|
2052
|
-
return false;
|
|
2053
|
-
}
|
|
2054
|
-
if (fs.existsSync(run.statusPath)) {
|
|
2055
|
-
sessionState.closed = true;
|
|
2056
|
-
const exitCode = readStatusCodeIfPresent(run.statusPath);
|
|
2057
|
-
recordCombinedEvent({
|
|
2058
|
-
level: exitCode === 0 ? "info" : "warn",
|
|
2059
|
-
agentId: run.agent.agentId,
|
|
2060
|
-
message:
|
|
2061
|
-
exitCode === 0
|
|
2062
|
-
? "Resident orchestrator exited; launcher continues as the control plane."
|
|
2063
|
-
: `Resident orchestrator exited with code ${exitCode}; launcher continues as the control plane.`,
|
|
2064
|
-
});
|
|
2065
|
-
appendCoordination({
|
|
2066
|
-
event: "resident_orchestrator_exit",
|
|
2067
|
-
waves: [waveNumber],
|
|
2068
|
-
status: exitCode === 0 ? "resolved" : "warn",
|
|
2069
|
-
details:
|
|
2070
|
-
exitCode === 0
|
|
2071
|
-
? "Resident orchestrator session ended before wave completion."
|
|
2072
|
-
: `Resident orchestrator session ended with code ${exitCode} before wave completion.`,
|
|
2073
|
-
actionRequested: "None",
|
|
2074
|
-
});
|
|
2075
|
-
return true;
|
|
2076
|
-
}
|
|
2077
|
-
const activeSessions = new Set(listLaneTmuxSessionNames(lanePaths));
|
|
2078
|
-
if (!activeSessions.has(run.sessionName)) {
|
|
2079
|
-
sessionState.closed = true;
|
|
2080
|
-
recordCombinedEvent({
|
|
2081
|
-
level: "warn",
|
|
2082
|
-
agentId: run.agent.agentId,
|
|
2083
|
-
message:
|
|
2084
|
-
"Resident orchestrator session disappeared before writing a status file; launcher continues as the control plane.",
|
|
2085
|
-
});
|
|
2086
|
-
appendCoordination({
|
|
2087
|
-
event: "resident_orchestrator_missing",
|
|
2088
|
-
waves: [waveNumber],
|
|
2089
|
-
status: "warn",
|
|
2090
|
-
details: `tmux session ${run.sessionName} disappeared before ${path.relative(REPO_ROOT, run.statusPath)} was written.`,
|
|
2091
|
-
actionRequested: "None",
|
|
2092
|
-
});
|
|
2093
|
-
return true;
|
|
2094
|
-
}
|
|
2095
|
-
return false;
|
|
2096
|
-
}
|
|
2097
|
-
|
|
2098
|
-
function isWaveDashboardBackedByLiveSession(lanePaths, dashboardPath, activeSessionNames) {
|
|
2099
|
-
const waveMatch = path.basename(dashboardPath).match(/^wave-(\d+)\.json$/);
|
|
2100
|
-
if (!waveMatch) {
|
|
2101
|
-
return false;
|
|
2102
|
-
}
|
|
2103
|
-
const waveNumber = Number.parseInt(waveMatch[1], 10);
|
|
2104
|
-
if (!Number.isFinite(waveNumber)) {
|
|
2105
|
-
return false;
|
|
2106
|
-
}
|
|
2107
|
-
const dashboardState = readJsonOrNull(dashboardPath);
|
|
2108
|
-
const runTag = String(dashboardState?.runTag || "").trim();
|
|
2109
|
-
const agentPrefix = `${lanePaths.tmuxSessionPrefix}${waveNumber}_`;
|
|
2110
|
-
const dashboardPrefix = `${lanePaths.tmuxDashboardSessionPrefix}${waveNumber}_`;
|
|
2111
|
-
for (const sessionName of activeSessionNames) {
|
|
2112
|
-
if (!(sessionName.startsWith(agentPrefix) || sessionName.startsWith(dashboardPrefix))) {
|
|
2113
|
-
continue;
|
|
2114
|
-
}
|
|
2115
|
-
if (!runTag || sessionName.endsWith(`_${runTag}`)) {
|
|
2116
|
-
return true;
|
|
2117
|
-
}
|
|
2118
|
-
}
|
|
2119
|
-
return false;
|
|
2120
|
-
}
|
|
2121
|
-
|
|
2122
|
-
function removeOrphanWaveDashboards(lanePaths, activeSessionNames) {
|
|
2123
|
-
if (!fs.existsSync(lanePaths.dashboardsDir)) {
|
|
2124
|
-
return [];
|
|
2125
|
-
}
|
|
2126
|
-
const removedDashboardPaths = [];
|
|
2127
|
-
for (const fileName of fs.readdirSync(lanePaths.dashboardsDir)) {
|
|
2128
|
-
if (!/^wave-\d+\.json$/.test(fileName)) {
|
|
2129
|
-
continue;
|
|
2130
|
-
}
|
|
2131
|
-
const dashboardPath = path.join(lanePaths.dashboardsDir, fileName);
|
|
2132
|
-
if (isWaveDashboardBackedByLiveSession(lanePaths, dashboardPath, activeSessionNames)) {
|
|
2133
|
-
continue;
|
|
2134
|
-
}
|
|
2135
|
-
fs.rmSync(dashboardPath, { force: true });
|
|
2136
|
-
removedDashboardPaths.push(path.relative(REPO_ROOT, dashboardPath));
|
|
2137
|
-
}
|
|
2138
|
-
return removedDashboardPaths;
|
|
2139
|
-
}
|
|
2140
|
-
|
|
2141
|
-
function pruneDryRunExecutorPreviewDirs(lanePaths, waves) {
|
|
2142
|
-
if (!fs.existsSync(lanePaths.executorOverlaysDir)) {
|
|
2143
|
-
return [];
|
|
2144
|
-
}
|
|
2145
|
-
const expectedSlugsByWave = new Map(
|
|
2146
|
-
(waves || []).map((wave) => [wave.wave, new Set((wave.agents || []).map((agent) => agent.slug))]),
|
|
2147
|
-
);
|
|
2148
|
-
const removedPaths = [];
|
|
2149
|
-
for (const entry of fs.readdirSync(lanePaths.executorOverlaysDir, { withFileTypes: true })) {
|
|
2150
|
-
if (!entry.isDirectory() || !/^wave-\d+$/.test(entry.name)) {
|
|
2151
|
-
continue;
|
|
2152
|
-
}
|
|
2153
|
-
const waveNumber = Number.parseInt(entry.name.slice("wave-".length), 10);
|
|
2154
|
-
const waveDir = path.join(lanePaths.executorOverlaysDir, entry.name);
|
|
2155
|
-
const expectedSlugs = expectedSlugsByWave.get(waveNumber);
|
|
2156
|
-
if (!expectedSlugs) {
|
|
2157
|
-
fs.rmSync(waveDir, { recursive: true, force: true });
|
|
2158
|
-
removedPaths.push(path.relative(REPO_ROOT, waveDir));
|
|
2159
|
-
continue;
|
|
2160
|
-
}
|
|
2161
|
-
for (const child of fs.readdirSync(waveDir, { withFileTypes: true })) {
|
|
2162
|
-
if (!child.isDirectory() || expectedSlugs.has(child.name)) {
|
|
2163
|
-
continue;
|
|
2164
|
-
}
|
|
2165
|
-
const childPath = path.join(waveDir, child.name);
|
|
2166
|
-
fs.rmSync(childPath, { recursive: true, force: true });
|
|
2167
|
-
removedPaths.push(path.relative(REPO_ROOT, childPath));
|
|
2168
|
-
}
|
|
2169
|
-
}
|
|
2170
|
-
return removedPaths.toSorted();
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
export function reconcileStaleLauncherArtifacts(lanePaths, options = {}) {
|
|
2174
|
-
const outcome = {
|
|
2175
|
-
removedLock: false,
|
|
2176
|
-
removedSessions: [],
|
|
2177
|
-
removedTerminalNames: [],
|
|
2178
|
-
clearedDashboards: false,
|
|
2179
|
-
removedDashboardPaths: [],
|
|
2180
|
-
staleWaves: [],
|
|
2181
|
-
activeLockPid: null,
|
|
2182
|
-
};
|
|
2183
|
-
|
|
2184
|
-
if (fs.existsSync(lanePaths.launcherLockPath)) {
|
|
2185
|
-
const existing = readJsonOrNull(lanePaths.launcherLockPath);
|
|
2186
|
-
const existingPid = Number.parseInt(String(existing?.pid ?? ""), 10);
|
|
2187
|
-
if (isProcessAlive(existingPid)) {
|
|
2188
|
-
outcome.activeLockPid = existingPid;
|
|
2189
|
-
return outcome;
|
|
2190
|
-
}
|
|
2191
|
-
fs.rmSync(lanePaths.launcherLockPath, { force: true });
|
|
2192
|
-
outcome.removedLock = true;
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
outcome.removedSessions = cleanupLaneTmuxSessions(lanePaths);
|
|
2196
|
-
const activeSessionNames = new Set(listLaneTmuxSessionNames(lanePaths));
|
|
2197
|
-
if (terminalSurfaceUsesTerminalRegistry(options.terminalSurface || "vscode")) {
|
|
2198
|
-
const terminalCleanup = pruneOrphanLaneTemporaryTerminalEntries(
|
|
2199
|
-
lanePaths.terminalsPath,
|
|
2200
|
-
lanePaths,
|
|
2201
|
-
activeSessionNames,
|
|
2202
|
-
);
|
|
2203
|
-
outcome.removedTerminalNames = terminalCleanup.removedNames;
|
|
2204
|
-
}
|
|
2205
|
-
|
|
2206
|
-
const globalDashboard = readJsonOrNull(lanePaths.globalDashboardPath);
|
|
2207
|
-
if (globalDashboard && typeof globalDashboard === "object" && Array.isArray(globalDashboard.waves)) {
|
|
2208
|
-
const staleWaves = new Set();
|
|
2209
|
-
for (const waveEntry of globalDashboard.waves) {
|
|
2210
|
-
const waveNumber = Number.parseInt(String(waveEntry?.wave ?? ""), 10);
|
|
2211
|
-
if (Number.isFinite(waveNumber)) {
|
|
2212
|
-
staleWaves.add(waveNumber);
|
|
2213
|
-
}
|
|
2214
|
-
}
|
|
2215
|
-
outcome.staleWaves = Array.from(staleWaves).toSorted((a, b) => a - b);
|
|
2216
|
-
}
|
|
2217
|
-
|
|
2218
|
-
if (fs.existsSync(lanePaths.globalDashboardPath)) {
|
|
2219
|
-
fs.rmSync(lanePaths.globalDashboardPath, { force: true });
|
|
2220
|
-
outcome.removedDashboardPaths.push(path.relative(REPO_ROOT, lanePaths.globalDashboardPath));
|
|
2221
|
-
}
|
|
2222
|
-
outcome.removedDashboardPaths.push(
|
|
2223
|
-
...removeOrphanWaveDashboards(lanePaths, activeSessionNames),
|
|
2224
|
-
);
|
|
2225
|
-
outcome.clearedDashboards = outcome.removedDashboardPaths.length > 0;
|
|
2226
|
-
return outcome;
|
|
2227
|
-
}
|
|
2228
|
-
|
|
2229
|
-
function runTmux(lanePaths, args, description) {
|
|
2230
|
-
const result = spawnSync("tmux", ["-L", lanePaths.tmuxSocketName, ...args], {
|
|
2231
|
-
cwd: REPO_ROOT,
|
|
2232
|
-
encoding: "utf8",
|
|
2233
|
-
env: { ...process.env, TMUX: "" },
|
|
2234
|
-
timeout: TMUX_COMMAND_TIMEOUT_MS,
|
|
2235
|
-
});
|
|
2236
|
-
if (result.error) {
|
|
2237
|
-
if (result.error.code === "ETIMEDOUT") {
|
|
2238
|
-
throw new Error(
|
|
2239
|
-
`${description} failed: tmux command timed out after ${TMUX_COMMAND_TIMEOUT_MS}ms`,
|
|
2240
|
-
);
|
|
2241
|
-
}
|
|
2242
|
-
throw new Error(`${description} failed: ${result.error.message}`);
|
|
2243
|
-
}
|
|
2244
|
-
if (result.status !== 0) {
|
|
2245
|
-
throw new Error(
|
|
2246
|
-
`${description} failed: ${(result.stderr || "").trim() || "tmux command failed"}`,
|
|
2247
|
-
);
|
|
2248
|
-
}
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
|
-
function listTmuxSessionNames(lanePaths) {
|
|
2252
|
-
const result = spawnSync(
|
|
2253
|
-
"tmux",
|
|
2254
|
-
["-L", lanePaths.tmuxSocketName, "list-sessions", "-F", "#{session_name}"],
|
|
2255
|
-
{
|
|
2256
|
-
cwd: REPO_ROOT,
|
|
2257
|
-
encoding: "utf8",
|
|
2258
|
-
env: { ...process.env, TMUX: "" },
|
|
2259
|
-
timeout: TMUX_COMMAND_TIMEOUT_MS,
|
|
2260
|
-
},
|
|
2261
|
-
);
|
|
2262
|
-
if (result.error) {
|
|
2263
|
-
if (result.error.code === "ENOENT") {
|
|
2264
|
-
return [];
|
|
2265
|
-
}
|
|
2266
|
-
if (result.error.code === "ETIMEDOUT") {
|
|
2267
|
-
throw new Error(`list tmux sessions failed: timed out after ${TMUX_COMMAND_TIMEOUT_MS}ms`);
|
|
2268
|
-
}
|
|
2269
|
-
throw new Error(`list tmux sessions failed: ${result.error.message}`);
|
|
2270
|
-
}
|
|
2271
|
-
if (result.status !== 0) {
|
|
2272
|
-
const combined = `${String(result.stderr || "").toLowerCase()}\n${String(result.stdout || "").toLowerCase()}`;
|
|
2273
|
-
if (
|
|
2274
|
-
combined.includes("no server running") ||
|
|
2275
|
-
combined.includes("failed to connect") ||
|
|
2276
|
-
combined.includes("error connecting")
|
|
2277
|
-
) {
|
|
2278
|
-
return [];
|
|
2279
|
-
}
|
|
2280
|
-
throw new Error(
|
|
2281
|
-
`list tmux sessions failed: ${(result.stderr || "").trim() || "unknown error"}`,
|
|
2282
|
-
);
|
|
2283
|
-
}
|
|
2284
|
-
return String(result.stdout || "")
|
|
2285
|
-
.split(/\r?\n/)
|
|
2286
|
-
.map((line) => line.trim())
|
|
2287
|
-
.filter(Boolean);
|
|
2288
|
-
}
|
|
2289
|
-
|
|
2290
|
-
function cleanupLaneTmuxSessions(lanePaths, { excludeSessionNames = new Set() } = {}) {
|
|
2291
|
-
const sessionNames = listTmuxSessionNames(lanePaths);
|
|
2292
|
-
const killed = [];
|
|
2293
|
-
for (const sessionName of sessionNames) {
|
|
2294
|
-
if (excludeSessionNames.has(sessionName) || !isLaneSessionName(lanePaths, sessionName)) {
|
|
2295
|
-
continue;
|
|
2296
|
-
}
|
|
2297
|
-
killTmuxSessionIfExists(lanePaths.tmuxSocketName, sessionName);
|
|
2298
|
-
killed.push(sessionName);
|
|
2299
|
-
}
|
|
2300
|
-
return killed;
|
|
2301
|
-
}
|
|
2302
|
-
|
|
2303
|
-
export function collectUnexpectedSessionFailures(lanePaths, agentRuns, pendingAgentIds) {
|
|
2304
|
-
return collectUnexpectedSessionFailuresImpl(lanePaths, agentRuns, pendingAgentIds, {
|
|
2305
|
-
listLaneTmuxSessionNamesFn: listLaneTmuxSessionNames,
|
|
2306
|
-
});
|
|
2307
|
-
}
|
|
2308
|
-
|
|
2309
|
-
function launchWaveDashboardSession(lanePaths, { sessionName, dashboardPath, messageBoardPath }) {
|
|
2310
|
-
killTmuxSessionIfExists(lanePaths.tmuxSocketName, sessionName);
|
|
2311
|
-
const messageBoardArg = messageBoardPath
|
|
2312
|
-
? ` --message-board ${shellQuote(messageBoardPath)}`
|
|
2313
|
-
: "";
|
|
2314
|
-
const command = [
|
|
2315
|
-
`cd ${shellQuote(REPO_ROOT)}`,
|
|
2316
|
-
`node ${shellQuote(path.join(PACKAGE_ROOT, "scripts", "wave-dashboard.mjs"))} --dashboard-file ${shellQuote(
|
|
2317
|
-
dashboardPath,
|
|
2318
|
-
)}${messageBoardArg} --lane ${shellQuote(lanePaths.lane)} --watch`,
|
|
2319
|
-
"exec bash -l",
|
|
2320
|
-
].join("; ");
|
|
2321
|
-
runTmux(
|
|
2322
|
-
lanePaths,
|
|
2323
|
-
["new-session", "-d", "-s", sessionName, `bash -lc ${shellQuote(command)}`],
|
|
2324
|
-
`launch dashboard session ${sessionName}`,
|
|
2325
|
-
);
|
|
2326
|
-
}
|
|
2327
|
-
|
|
2328
|
-
async function launchAgentSession(lanePaths, params) {
|
|
2329
|
-
return launchAgentSessionImpl(lanePaths, params, { runTmuxFn: runTmux });
|
|
2330
|
-
}
|
|
2331
|
-
|
|
2332
|
-
async function waitForWaveCompletion(lanePaths, agentRuns, timeoutMinutes, onProgress = null) {
|
|
2333
|
-
return waitForWaveCompletionImpl(lanePaths, agentRuns, timeoutMinutes, onProgress, {
|
|
2334
|
-
collectUnexpectedSessionFailuresFn: collectUnexpectedSessionFailures,
|
|
2335
|
-
});
|
|
2336
|
-
}
|
|
2337
|
-
|
|
2338
|
-
function monitorWaveHumanFeedback({
|
|
2339
|
-
lanePaths,
|
|
2340
|
-
waveNumber,
|
|
2341
|
-
agentRuns,
|
|
2342
|
-
orchestratorId,
|
|
2343
|
-
coordinationLogPath,
|
|
2344
|
-
feedbackStateByRequestId,
|
|
2345
|
-
recordCombinedEvent,
|
|
2346
|
-
appendCoordination,
|
|
2347
|
-
}) {
|
|
2348
|
-
const triageLogPath = path.join(lanePaths.feedbackTriageDir, `wave-${waveNumber}.jsonl`);
|
|
2349
|
-
const requests = readWaveHumanFeedbackRequests({
|
|
2350
|
-
feedbackRequestsDir: lanePaths.feedbackRequestsDir,
|
|
2351
|
-
lane: lanePaths.lane,
|
|
2352
|
-
waveNumber,
|
|
2353
|
-
agentIds: agentRuns.map((run) => run.agent.agentId),
|
|
2354
|
-
orchestratorId,
|
|
2355
|
-
});
|
|
2356
|
-
let changed = false;
|
|
2357
|
-
for (const request of requests) {
|
|
2358
|
-
const signature = feedbackStateSignature(request);
|
|
2359
|
-
if (feedbackStateByRequestId.get(request.id) === signature) {
|
|
2360
|
-
continue;
|
|
2361
|
-
}
|
|
2362
|
-
feedbackStateByRequestId.set(request.id, signature);
|
|
2363
|
-
changed = true;
|
|
2364
|
-
const question = request.question || "n/a";
|
|
2365
|
-
const context = request.context ? `; context=${request.context}` : "";
|
|
2366
|
-
const responseOperator = request.responseOperator || "human-operator";
|
|
2367
|
-
const responseText = request.responseText || "(empty response)";
|
|
2368
|
-
if (request.status === "pending") {
|
|
2369
|
-
recordCombinedEvent({
|
|
2370
|
-
level: "warn",
|
|
2371
|
-
agentId: request.agentId,
|
|
2372
|
-
message: `Human feedback requested (${request.id}): ${question}`,
|
|
2373
|
-
});
|
|
2374
|
-
console.warn(
|
|
2375
|
-
`[human-feedback] wave=${waveNumber} agent=${request.agentId} request=${request.id} pending: ${question}`,
|
|
2376
|
-
);
|
|
2377
|
-
console.warn(
|
|
2378
|
-
`[human-feedback] respond with: pnpm exec wave control task act answer --lane ${lanePaths.lane} --wave ${waveNumber} --id ${request.id} --response "<answer>" --operator "<name>"`,
|
|
2379
|
-
);
|
|
2380
|
-
appendCoordination({
|
|
2381
|
-
event: "human_feedback_requested",
|
|
2382
|
-
waves: [waveNumber],
|
|
2383
|
-
status: "waiting-human",
|
|
2384
|
-
details: `request_id=${request.id}; agent=${request.agentId}; question=${question}${context}`,
|
|
2385
|
-
actionRequested: `Launcher operator should ask or answer in the parent session, then run: pnpm exec wave control task act answer --lane ${lanePaths.lane} --wave ${waveNumber} --id ${request.id} --response "<answer>" --operator "<name>"`,
|
|
2386
|
-
});
|
|
2387
|
-
if (coordinationLogPath) {
|
|
2388
|
-
appendCoordinationRecord(coordinationLogPath, {
|
|
2389
|
-
id: request.id,
|
|
2390
|
-
lane: lanePaths.lane,
|
|
2391
|
-
wave: waveNumber,
|
|
2392
|
-
agentId: request.agentId || "human",
|
|
2393
|
-
kind: "human-feedback",
|
|
2394
|
-
targets: request.agentId ? [`agent:${request.agentId}`] : [],
|
|
2395
|
-
priority: "high",
|
|
2396
|
-
summary: question,
|
|
2397
|
-
detail: request.context || "",
|
|
2398
|
-
status: "open",
|
|
2399
|
-
source: "feedback",
|
|
2400
|
-
});
|
|
2401
|
-
}
|
|
2402
|
-
} else if (request.status === "answered") {
|
|
2403
|
-
recordCombinedEvent({
|
|
2404
|
-
level: "info",
|
|
2405
|
-
agentId: request.agentId,
|
|
2406
|
-
message: `Human feedback answered (${request.id}) by ${responseOperator}: ${responseText}`,
|
|
2407
|
-
});
|
|
2408
|
-
appendCoordination({
|
|
2409
|
-
event: "human_feedback_answered",
|
|
2410
|
-
waves: [waveNumber],
|
|
2411
|
-
status: "resolved",
|
|
2412
|
-
details: `request_id=${request.id}; agent=${request.agentId}; operator=${responseOperator}; response=${responseText}`,
|
|
2413
|
-
});
|
|
2414
|
-
if (coordinationLogPath) {
|
|
2415
|
-
const escalationId = `escalation-${request.id}`;
|
|
2416
|
-
const existingEscalation =
|
|
2417
|
-
(fs.existsSync(triageLogPath)
|
|
2418
|
-
? readMaterializedCoordinationState(triageLogPath).byId.get(escalationId)
|
|
2419
|
-
: null) ||
|
|
2420
|
-
readMaterializedCoordinationState(coordinationLogPath).byId.get(escalationId) ||
|
|
2421
|
-
null;
|
|
2422
|
-
if (fs.existsSync(triageLogPath)) {
|
|
2423
|
-
appendCoordinationRecord(triageLogPath, {
|
|
2424
|
-
id: escalationId,
|
|
2425
|
-
lane: lanePaths.lane,
|
|
2426
|
-
wave: waveNumber,
|
|
2427
|
-
agentId: request.agentId || "human",
|
|
2428
|
-
kind: "human-escalation",
|
|
2429
|
-
targets:
|
|
2430
|
-
existingEscalation?.targets ||
|
|
2431
|
-
(request.agentId ? [`agent:${request.agentId}`] : []),
|
|
2432
|
-
dependsOn: existingEscalation?.dependsOn || [],
|
|
2433
|
-
closureCondition: existingEscalation?.closureCondition || "",
|
|
2434
|
-
priority: "high",
|
|
2435
|
-
summary: question,
|
|
2436
|
-
detail: responseText,
|
|
2437
|
-
artifactRefs: [request.id],
|
|
2438
|
-
status: "resolved",
|
|
2439
|
-
source: "feedback",
|
|
2440
|
-
});
|
|
2441
|
-
}
|
|
2442
|
-
appendCoordinationRecord(coordinationLogPath, {
|
|
2443
|
-
id: escalationId,
|
|
2444
|
-
lane: lanePaths.lane,
|
|
2445
|
-
wave: waveNumber,
|
|
2446
|
-
agentId: request.agentId || "human",
|
|
2447
|
-
kind: "human-escalation",
|
|
2448
|
-
targets:
|
|
2449
|
-
existingEscalation?.targets ||
|
|
2450
|
-
(request.agentId ? [`agent:${request.agentId}`] : []),
|
|
2451
|
-
dependsOn: existingEscalation?.dependsOn || [],
|
|
2452
|
-
closureCondition: existingEscalation?.closureCondition || "",
|
|
2453
|
-
priority: "high",
|
|
2454
|
-
summary: question,
|
|
2455
|
-
detail: responseText,
|
|
2456
|
-
artifactRefs: [request.id],
|
|
2457
|
-
status: "resolved",
|
|
2458
|
-
source: "feedback",
|
|
2459
|
-
});
|
|
2460
|
-
appendCoordinationRecord(coordinationLogPath, {
|
|
2461
|
-
id: request.id,
|
|
2462
|
-
lane: lanePaths.lane,
|
|
2463
|
-
wave: waveNumber,
|
|
2464
|
-
agentId: request.agentId || "human",
|
|
2465
|
-
kind: "human-feedback",
|
|
2466
|
-
targets: request.agentId ? [`agent:${request.agentId}`] : [],
|
|
2467
|
-
priority: "high",
|
|
2468
|
-
summary: question,
|
|
2469
|
-
detail: responseText,
|
|
2470
|
-
status: "resolved",
|
|
2471
|
-
source: "feedback",
|
|
2472
|
-
});
|
|
2473
|
-
}
|
|
2474
|
-
}
|
|
2475
|
-
}
|
|
2476
|
-
return changed;
|
|
2477
|
-
}
|
|
2478
|
-
|
|
2479
|
-
function proofCentricReuseBlocked(derivedState) {
|
|
2480
|
-
if (!derivedState) {
|
|
2481
|
-
return false;
|
|
2482
|
-
}
|
|
2483
|
-
return (
|
|
2484
|
-
readClarificationBarrier(derivedState).ok === false ||
|
|
2485
|
-
readWaveAssignmentBarrier(derivedState).ok === false ||
|
|
2486
|
-
readWaveDependencyBarrier(derivedState).ok === false
|
|
2487
|
-
);
|
|
2488
|
-
}
|
|
2489
|
-
|
|
2490
|
-
function sameAgentIdSet(left = [], right = []) {
|
|
2491
|
-
const leftIds = Array.from(new Set((left || []).filter(Boolean))).toSorted();
|
|
2492
|
-
const rightIds = Array.from(new Set((right || []).filter(Boolean))).toSorted();
|
|
2493
|
-
return leftIds.length === rightIds.length && leftIds.every((agentId, index) => agentId === rightIds[index]);
|
|
2494
|
-
}
|
|
2495
|
-
|
|
2496
|
-
export function persistedRelaunchPlanMatchesCurrentState(
|
|
2497
|
-
agentRuns,
|
|
2498
|
-
persistedPlan,
|
|
2499
|
-
lanePaths,
|
|
2500
|
-
waveDefinition,
|
|
2501
|
-
) {
|
|
2502
|
-
if (!persistedPlan || !Array.isArray(persistedPlan.selectedAgentIds)) {
|
|
2503
|
-
return false;
|
|
2504
|
-
}
|
|
2505
|
-
const componentGate = readWaveComponentGate(waveDefinition, agentRuns, {
|
|
2506
|
-
laneProfile: lanePaths?.laneProfile,
|
|
2507
|
-
});
|
|
2508
|
-
if (componentGate?.statusCode !== "shared-component-sibling-pending") {
|
|
2509
|
-
return true;
|
|
2510
|
-
}
|
|
2511
|
-
return sameAgentIdSet(
|
|
2512
|
-
persistedPlan.selectedAgentIds,
|
|
2513
|
-
componentGate.waitingOnAgentIds || [],
|
|
2514
|
-
);
|
|
2515
|
-
}
|
|
2516
|
-
|
|
2517
|
-
function applyPersistedRelaunchPlan(agentRuns, persistedPlan, lanePaths, waveDefinition) {
|
|
2518
|
-
if (!persistedPlan || !Array.isArray(persistedPlan.selectedAgentIds)) {
|
|
2519
|
-
return [];
|
|
2520
|
-
}
|
|
2521
|
-
const runsByAgentId = new Map(agentRuns.map((run) => [run.agent.agentId, run]));
|
|
2522
|
-
for (const [agentId, executorState] of Object.entries(persistedPlan.executorStates || {})) {
|
|
2523
|
-
const run = runsByAgentId.get(agentId);
|
|
2524
|
-
if (!run || !executorState || typeof executorState !== "object") {
|
|
2525
|
-
continue;
|
|
2526
|
-
}
|
|
2527
|
-
run.agent.executorResolved = executorState;
|
|
2528
|
-
refreshResolvedSkillsForRun(run, waveDefinition, lanePaths);
|
|
2529
|
-
}
|
|
2530
|
-
return persistedPlan.selectedAgentIds
|
|
2531
|
-
.map((agentId) => runsByAgentId.get(agentId))
|
|
2532
|
-
.filter(Boolean);
|
|
2533
|
-
}
|
|
2534
|
-
|
|
2535
|
-
export function resolveSharedComponentContinuationRuns(
|
|
2536
|
-
currentRuns,
|
|
2537
|
-
agentRuns,
|
|
2538
|
-
failures,
|
|
2539
|
-
derivedState,
|
|
2540
|
-
lanePaths,
|
|
2541
|
-
waveDefinition = null,
|
|
2542
|
-
) {
|
|
2543
|
-
if (!Array.isArray(currentRuns) || currentRuns.length === 0 || !Array.isArray(failures) || failures.length === 0) {
|
|
2544
|
-
return [];
|
|
2545
|
-
}
|
|
2546
|
-
if (!failures.every((failure) => failure.statusCode === "shared-component-sibling-pending")) {
|
|
2547
|
-
return [];
|
|
2548
|
-
}
|
|
2549
|
-
const currentRunIds = new Set(currentRuns.map((run) => run.agent.agentId));
|
|
2550
|
-
const waitingAgentIds = new Set(
|
|
2551
|
-
failures.flatMap((failure) => failure.waitingOnAgentIds || []).filter(Boolean),
|
|
2552
|
-
);
|
|
2553
|
-
if (Array.from(currentRunIds).some((agentId) => waitingAgentIds.has(agentId))) {
|
|
2554
|
-
return [];
|
|
2555
|
-
}
|
|
2556
|
-
const relaunchResolution = resolveRelaunchRuns(
|
|
2557
|
-
agentRuns,
|
|
2558
|
-
failures,
|
|
2559
|
-
derivedState,
|
|
2560
|
-
lanePaths,
|
|
2561
|
-
waveDefinition,
|
|
2562
|
-
);
|
|
2563
|
-
if (relaunchResolution.barrier || relaunchResolution.runs.length === 0) {
|
|
2564
|
-
return [];
|
|
2565
|
-
}
|
|
2566
|
-
return relaunchResolution.runs.some((run) => !currentRunIds.has(run.agent.agentId))
|
|
2567
|
-
? relaunchResolution.runs
|
|
2568
|
-
: [];
|
|
2569
|
-
}
|
|
2570
|
-
|
|
2571
|
-
function relaunchReasonBuckets(runs, failures, derivedState) {
|
|
2572
|
-
const selectedAgentIds = new Set((runs || []).map((run) => run.agent.agentId));
|
|
2573
|
-
return {
|
|
2574
|
-
clarification: openClarificationLinkedRequests(derivedState?.coordinationState)
|
|
2575
|
-
.flatMap((record) => record.targets || [])
|
|
2576
|
-
.some((target) => {
|
|
2577
|
-
const agentId = String(target || "").startsWith("agent:")
|
|
2578
|
-
? String(target).slice("agent:".length)
|
|
2579
|
-
: String(target || "");
|
|
2580
|
-
return selectedAgentIds.has(agentId);
|
|
2581
|
-
}),
|
|
2582
|
-
helperAssignment: (derivedState?.capabilityAssignments || []).some(
|
|
2583
|
-
(assignment) => assignment.blocking && selectedAgentIds.has(assignment.assignedAgentId),
|
|
2584
|
-
),
|
|
2585
|
-
dependency: ((derivedState?.dependencySnapshot?.openInbound || []).some((record) =>
|
|
2586
|
-
selectedAgentIds.has(record.assignedAgentId),
|
|
2587
|
-
)),
|
|
2588
|
-
blocker: (derivedState?.coordinationState?.blockers || []).some(
|
|
2589
|
-
(record) =>
|
|
2590
|
-
isOpenCoordinationStatus(record.status) &&
|
|
2591
|
-
(selectedAgentIds.has(record.agentId) ||
|
|
2592
|
-
(record.targets || []).some((target) => {
|
|
2593
|
-
const agentId = String(target || "").startsWith("agent:")
|
|
2594
|
-
? String(target).slice("agent:".length)
|
|
2595
|
-
: String(target || "");
|
|
2596
|
-
return selectedAgentIds.has(agentId);
|
|
2597
|
-
})),
|
|
2598
|
-
),
|
|
2599
|
-
closureGate: (failures || []).some(
|
|
2600
|
-
(failure) => failure.agentId && selectedAgentIds.has(failure.agentId),
|
|
2601
|
-
),
|
|
2602
|
-
sharedComponentSiblingWait: (failures || []).some(
|
|
2603
|
-
(failure) =>
|
|
2604
|
-
failure.statusCode === "shared-component-sibling-pending" &&
|
|
2605
|
-
(failure.waitingOnAgentIds || []).some((agentId) => selectedAgentIds.has(agentId)),
|
|
2606
|
-
),
|
|
2607
|
-
};
|
|
2608
|
-
}
|
|
2609
|
-
|
|
2610
|
-
function applySharedComponentWaitStateToDashboard(componentGate, dashboardState) {
|
|
2611
|
-
const waitingSummary = (componentGate?.waitingOnAgentIds || []).join("/");
|
|
2612
|
-
if (!waitingSummary) {
|
|
2613
|
-
return;
|
|
2614
|
-
}
|
|
2615
|
-
for (const agentId of componentGate?.satisfiedAgentIds || []) {
|
|
2616
|
-
setWaveDashboardAgent(dashboardState, agentId, {
|
|
2617
|
-
state: "completed",
|
|
2618
|
-
detail: `Desired-state slice landed; waiting on ${waitingSummary} for shared component closure`,
|
|
2619
|
-
});
|
|
2620
|
-
}
|
|
2621
|
-
}
|
|
2622
|
-
|
|
2623
|
-
function reconcileFailuresAgainstSharedComponentState(wave, agentRuns, failures) {
|
|
2624
|
-
if (!Array.isArray(failures) || failures.length === 0) {
|
|
2625
|
-
return failures;
|
|
2626
|
-
}
|
|
2627
|
-
const summariesByAgentId = Object.fromEntries(
|
|
2628
|
-
(agentRuns || []).map((runInfo) => [runInfo.agent.agentId, readRunExecutionSummary(runInfo, wave)]),
|
|
2629
|
-
);
|
|
2630
|
-
const failureAgentIds = new Set(failures.map((failure) => failure.agentId).filter(Boolean));
|
|
2631
|
-
const consumedSatisfiedAgentIds = new Set();
|
|
2632
|
-
const synthesizedFailures = [];
|
|
2633
|
-
for (const promotion of wave?.componentPromotions || []) {
|
|
2634
|
-
const componentState = analyzePromotedComponentOwners(
|
|
2635
|
-
promotion.componentId,
|
|
2636
|
-
agentRuns,
|
|
2637
|
-
summariesByAgentId,
|
|
2638
|
-
);
|
|
2639
|
-
if (
|
|
2640
|
-
componentState.satisfiedAgentIds.length === 0 ||
|
|
2641
|
-
componentState.waitingOnAgentIds.length === 0 ||
|
|
2642
|
-
!componentState.satisfiedAgentIds.some((agentId) => failureAgentIds.has(agentId))
|
|
2643
|
-
) {
|
|
2644
|
-
continue;
|
|
2645
|
-
}
|
|
2646
|
-
for (const agentId of componentState.satisfiedAgentIds) {
|
|
2647
|
-
if (failureAgentIds.has(agentId)) {
|
|
2648
|
-
consumedSatisfiedAgentIds.add(agentId);
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
synthesizedFailures.push(buildSharedComponentSiblingPendingFailure(componentState));
|
|
2652
|
-
}
|
|
2653
|
-
return [
|
|
2654
|
-
...synthesizedFailures.filter(Boolean),
|
|
2655
|
-
...failures.filter((failure) => !consumedSatisfiedAgentIds.has(failure.agentId)),
|
|
2656
|
-
];
|
|
2657
|
-
}
|
|
2658
|
-
|
|
2659
|
-
export function hasReusableSuccessStatus(agent, statusPath, options = {}) {
|
|
2660
|
-
const statusRecord = readStatusRecordIfPresent(statusPath);
|
|
2661
|
-
const basicReuseOk = Boolean(
|
|
2662
|
-
statusRecord && statusRecord.code === 0 && statusRecord.promptHash === hashAgentPromptFingerprint(agent),
|
|
2663
|
-
);
|
|
2664
|
-
if (!basicReuseOk) {
|
|
2665
|
-
return false;
|
|
2666
|
-
}
|
|
2667
|
-
const proofCentric =
|
|
2668
|
-
agentRequiresProofCentricValidation(agent) || waveRequiresProofCentricValidation(options.wave);
|
|
2669
|
-
if (!proofCentric) {
|
|
2670
|
-
return true;
|
|
2671
|
-
}
|
|
2672
|
-
const summary = readAgentExecutionSummary(statusPath);
|
|
2673
|
-
if (!summary) {
|
|
2674
|
-
return false;
|
|
2675
|
-
}
|
|
2676
|
-
const effectiveSummary = options.proofRegistry
|
|
2677
|
-
? augmentSummaryWithProofRegistry(agent, summary, options.proofRegistry)
|
|
2678
|
-
: summary;
|
|
2679
|
-
if (!validateImplementationSummary(agent, effectiveSummary).ok) {
|
|
2680
|
-
return false;
|
|
2681
|
-
}
|
|
2682
|
-
if (proofCentricReuseBlocked(options.derivedState)) {
|
|
2683
|
-
return false;
|
|
2684
|
-
}
|
|
2685
|
-
return true;
|
|
2686
|
-
}
|
|
2687
|
-
|
|
2688
|
-
function isClosureAgentId(agent, lanePaths) {
|
|
2689
|
-
return [
|
|
2690
|
-
lanePaths.contEvalAgentId || "E0",
|
|
2691
|
-
lanePaths.integrationAgentId || "A8",
|
|
2692
|
-
lanePaths.documentationAgentId || "A9",
|
|
2693
|
-
lanePaths.contQaAgentId || "A0",
|
|
2694
|
-
].includes(agent?.agentId) || isSecurityReviewAgent(agent);
|
|
2695
|
-
}
|
|
2696
|
-
|
|
2697
|
-
export function selectReusablePreCompletedAgentIds(
|
|
2698
|
-
agentRuns,
|
|
2699
|
-
lanePaths,
|
|
2700
|
-
{ retryOverride = null, wave = null, derivedState = null, proofRegistry = null } = {},
|
|
2701
|
-
) {
|
|
2702
|
-
const retryOverrideClearedAgentIds = new Set(retryOverride?.clearReusableAgentIds || []);
|
|
2703
|
-
return new Set(
|
|
2704
|
-
(agentRuns || [])
|
|
2705
|
-
.filter(
|
|
2706
|
-
(run) =>
|
|
2707
|
-
!retryOverrideClearedAgentIds.has(run.agent.agentId) &&
|
|
2708
|
-
!isClosureAgentId(run.agent, lanePaths) &&
|
|
2709
|
-
hasReusableSuccessStatus(run.agent, run.statusPath, {
|
|
2710
|
-
wave,
|
|
2711
|
-
derivedState,
|
|
2712
|
-
proofRegistry,
|
|
2713
|
-
}),
|
|
2714
|
-
)
|
|
2715
|
-
.map((run) => run.agent.agentId),
|
|
2716
|
-
);
|
|
2717
|
-
}
|
|
2718
|
-
|
|
2719
|
-
export function selectInitialWaveRuns(agentRuns, lanePaths) {
|
|
2720
|
-
const implementationRuns = (agentRuns || []).filter(
|
|
2721
|
-
(run) => !isClosureAgentId(run?.agent, lanePaths),
|
|
2722
|
-
);
|
|
2723
|
-
return implementationRuns.length > 0 ? implementationRuns : agentRuns;
|
|
2724
|
-
}
|
|
2725
|
-
|
|
2726
|
-
function isLauncherSeedRequest(record) {
|
|
2727
|
-
return (
|
|
2728
|
-
record?.source === "launcher" &&
|
|
2729
|
-
/^wave-\d+-agent-[^-]+-request$/.test(String(record.id || "")) &&
|
|
2730
|
-
!String(record.closureCondition || "").trim() &&
|
|
2731
|
-
(!Array.isArray(record.dependsOn) || record.dependsOn.length === 0)
|
|
2732
|
-
);
|
|
2733
|
-
}
|
|
2734
|
-
|
|
2735
|
-
function runtimeMixValidationForRuns(agentRuns, lanePaths) {
|
|
2736
|
-
return validateWaveRuntimeMixAssignments(
|
|
2737
|
-
{
|
|
2738
|
-
wave: 0,
|
|
2739
|
-
agents: agentRuns.map((run) => run.agent),
|
|
2740
|
-
},
|
|
2741
|
-
{ laneProfile: lanePaths.laneProfile },
|
|
2742
|
-
);
|
|
2743
|
-
}
|
|
2744
|
-
|
|
2745
|
-
function nextExecutorModel(executorState, executorId) {
|
|
2746
|
-
if (executorId === "claude") {
|
|
2747
|
-
return executorState?.claude?.model || null;
|
|
2748
|
-
}
|
|
2749
|
-
if (executorId === "opencode") {
|
|
2750
|
-
return executorState?.opencode?.model || null;
|
|
2751
|
-
}
|
|
2752
|
-
return null;
|
|
2753
|
-
}
|
|
2754
|
-
|
|
2755
|
-
function executorFallbackChain(executorState) {
|
|
2756
|
-
if (
|
|
2757
|
-
executorState?.retryPolicy === "sticky" ||
|
|
2758
|
-
executorState?.allowFallbackOnRetry === false
|
|
2759
|
-
) {
|
|
2760
|
-
return [];
|
|
2761
|
-
}
|
|
2762
|
-
return Array.isArray(executorState?.fallbacks)
|
|
2763
|
-
? executorState.fallbacks.filter(Boolean)
|
|
2764
|
-
: [];
|
|
2765
|
-
}
|
|
2766
|
-
|
|
2767
|
-
function buildFallbackExecutorState(executorState, executorId, attempt, reason) {
|
|
2768
|
-
const history = Array.isArray(executorState?.executorHistory)
|
|
2769
|
-
? executorState.executorHistory
|
|
2770
|
-
: [];
|
|
2771
|
-
return {
|
|
2772
|
-
...executorState,
|
|
2773
|
-
id: executorId,
|
|
2774
|
-
model: nextExecutorModel(executorState, executorId),
|
|
2775
|
-
selectedBy: "retry-fallback",
|
|
2776
|
-
fallbackUsed: true,
|
|
2777
|
-
fallbackReason: reason,
|
|
2778
|
-
initialExecutorId: executorState?.initialExecutorId || executorState?.id || executorId,
|
|
2779
|
-
executorHistory: [
|
|
2780
|
-
...history,
|
|
2781
|
-
{
|
|
2782
|
-
attempt,
|
|
2783
|
-
executorId,
|
|
2784
|
-
reason,
|
|
2785
|
-
},
|
|
2786
|
-
],
|
|
405
|
+
orchestratorBoardPath: null,
|
|
406
|
+
coordinationNote: "",
|
|
407
|
+
adhocRunId: null,
|
|
2787
408
|
};
|
|
2788
|
-
|
|
409
|
+
let stateFileProvided = false;
|
|
410
|
+
let manifestOutProvided = false;
|
|
411
|
+
let orchestratorBoardProvided = false;
|
|
412
|
+
let executorProvided = false;
|
|
2789
413
|
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
.filter((failure) => failure.statusCode !== "shared-component-sibling-pending")
|
|
2794
|
-
.map((failure) => failure.agentId),
|
|
2795
|
-
);
|
|
2796
|
-
let changed = false;
|
|
2797
|
-
const outcomes = new Map();
|
|
2798
|
-
for (const run of agentRuns) {
|
|
2799
|
-
if (!failedAgentIds.has(run.agent.agentId)) {
|
|
414
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
415
|
+
const arg = argv[i];
|
|
416
|
+
if (arg === "--") {
|
|
2800
417
|
continue;
|
|
2801
418
|
}
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
outcomes.set(run.agent.agentId, {
|
|
2805
|
-
applied: false,
|
|
2806
|
-
blocking: false,
|
|
2807
|
-
statusCode: "no-executor-state",
|
|
2808
|
-
detail: `Agent ${run.agent.agentId} has no resolved executor state.`,
|
|
2809
|
-
});
|
|
2810
|
-
continue;
|
|
419
|
+
if (arg === "--help" || arg === "-h") {
|
|
420
|
+
return { help: true, lanePaths, options };
|
|
2811
421
|
}
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
422
|
+
if (arg === "--dry-run") {
|
|
423
|
+
options.dryRun = true;
|
|
424
|
+
} else if (arg === "--terminal-surface") {
|
|
425
|
+
options.terminalSurface = normalizeTerminalSurface(argv[++i], "--terminal-surface");
|
|
426
|
+
} else if (arg === "--no-dashboard") {
|
|
427
|
+
options.dashboard = false;
|
|
428
|
+
} else if (arg === "--cleanup-sessions") {
|
|
429
|
+
options.cleanupSessions = true;
|
|
430
|
+
} else if (arg === "--keep-sessions") {
|
|
431
|
+
options.cleanupSessions = false;
|
|
432
|
+
} else if (arg === "--auto-next") {
|
|
433
|
+
options.autoNext = true;
|
|
434
|
+
} else if (arg === "--resume-control-state") {
|
|
435
|
+
options.resumeControlState = true;
|
|
436
|
+
} else if (arg === "--reconcile-status") {
|
|
437
|
+
options.reconcileStatus = true;
|
|
438
|
+
} else if (arg === "--keep-terminals") {
|
|
439
|
+
options.keepTerminals = true;
|
|
440
|
+
} else if (arg === "--no-context7") {
|
|
441
|
+
options.context7Enabled = false;
|
|
442
|
+
} else if (arg === "--no-telemetry") {
|
|
443
|
+
options.telemetryEnabled = false;
|
|
444
|
+
} else if (arg === "--no-orchestrator-board") {
|
|
445
|
+
options.orchestratorBoardPath = null;
|
|
446
|
+
orchestratorBoardProvided = true;
|
|
447
|
+
} else if (arg === "--lane") {
|
|
448
|
+
options.lane = String(argv[++i] || "").trim();
|
|
449
|
+
lanePaths = buildLanePaths(options.lane, {
|
|
450
|
+
adhocRunId: options.adhocRunId,
|
|
2819
451
|
});
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
452
|
+
} else if (arg === "--adhoc-run") {
|
|
453
|
+
options.adhocRunId = sanitizeAdhocRunId(argv[++i]);
|
|
454
|
+
lanePaths = buildLanePaths(options.lane, {
|
|
455
|
+
adhocRunId: options.adhocRunId,
|
|
456
|
+
});
|
|
457
|
+
} else if (arg === "--orchestrator-id") {
|
|
458
|
+
options.orchestratorId = sanitizeOrchestratorId(argv[++i]);
|
|
459
|
+
} else if (arg === "--orchestrator-board") {
|
|
460
|
+
options.orchestratorBoardPath = path.resolve(REPO_ROOT, argv[++i] || "");
|
|
461
|
+
orchestratorBoardProvided = true;
|
|
462
|
+
} else if (arg === "--coordination-note") {
|
|
463
|
+
options.coordinationNote = String(argv[++i] || "").trim();
|
|
464
|
+
} else if (arg === "--resident-orchestrator") {
|
|
465
|
+
options.residentOrchestrator = true;
|
|
466
|
+
} else if (arg === "--state-file") {
|
|
467
|
+
options.runStatePath = path.resolve(REPO_ROOT, argv[++i] || "");
|
|
468
|
+
stateFileProvided = true;
|
|
469
|
+
} else if (arg === "--start-wave") {
|
|
470
|
+
options.startWave = parseNonNegativeInt(argv[++i], "--start-wave");
|
|
471
|
+
} else if (arg === "--end-wave") {
|
|
472
|
+
options.endWave = parseNonNegativeInt(argv[++i], "--end-wave");
|
|
473
|
+
} else if (arg === "--timeout-minutes") {
|
|
474
|
+
options.timeoutMinutes = parsePositiveInt(argv[++i], "--timeout-minutes");
|
|
475
|
+
} else if (arg === "--max-retries-per-wave") {
|
|
476
|
+
options.maxRetriesPerWave = parseNonNegativeInt(argv[++i], "--max-retries-per-wave");
|
|
477
|
+
} else if (arg === "--agent-rate-limit-retries") {
|
|
478
|
+
options.agentRateLimitRetries = parseNonNegativeInt(argv[++i], "--agent-rate-limit-retries");
|
|
479
|
+
} else if (arg === "--agent-rate-limit-base-delay-seconds") {
|
|
480
|
+
options.agentRateLimitBaseDelaySeconds = parsePositiveInt(
|
|
481
|
+
argv[++i],
|
|
482
|
+
"--agent-rate-limit-base-delay-seconds",
|
|
2846
483
|
);
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
: entry,
|
|
2852
|
-
),
|
|
2853
|
-
lanePaths,
|
|
484
|
+
} else if (arg === "--agent-rate-limit-max-delay-seconds") {
|
|
485
|
+
options.agentRateLimitMaxDelaySeconds = parsePositiveInt(
|
|
486
|
+
argv[++i],
|
|
487
|
+
"--agent-rate-limit-max-delay-seconds",
|
|
2854
488
|
);
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
executorId: candidate,
|
|
2868
|
-
});
|
|
2869
|
-
break;
|
|
2870
|
-
}
|
|
2871
|
-
if (!outcomes.has(run.agent.agentId)) {
|
|
2872
|
-
outcomes.set(run.agent.agentId, {
|
|
2873
|
-
applied: false,
|
|
2874
|
-
blocking: true,
|
|
2875
|
-
statusCode: "retry-fallback-blocked",
|
|
2876
|
-
detail: `Agent ${run.agent.agentId} cannot retry safely on a configured fallback (${blockedCandidates.join("; ") || "no safe fallback remained"}).`,
|
|
2877
|
-
});
|
|
489
|
+
} else if (arg === "--agent-launch-stagger-ms") {
|
|
490
|
+
options.agentLaunchStaggerMs = parseNonNegativeInt(argv[++i], "--agent-launch-stagger-ms");
|
|
491
|
+
} else if (arg === "--executor") {
|
|
492
|
+
options.executorMode = normalizeExecutorMode(argv[++i], "--executor");
|
|
493
|
+
executorProvided = true;
|
|
494
|
+
} else if (arg === "--codex-sandbox") {
|
|
495
|
+
options.codexSandboxMode = normalizeCodexSandboxMode(argv[++i], "--codex-sandbox");
|
|
496
|
+
} else if (arg === "--manifest-out") {
|
|
497
|
+
options.manifestOut = path.resolve(REPO_ROOT, argv[++i] || "");
|
|
498
|
+
manifestOutProvided = true;
|
|
499
|
+
} else {
|
|
500
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
2878
501
|
}
|
|
2879
502
|
}
|
|
2880
|
-
return {
|
|
2881
|
-
changed,
|
|
2882
|
-
outcomes,
|
|
2883
|
-
};
|
|
2884
|
-
}
|
|
2885
503
|
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
}
|
|
2893
|
-
blockingFailures.push({
|
|
2894
|
-
agentId: failure.agentId,
|
|
2895
|
-
statusCode: outcome.statusCode,
|
|
2896
|
-
logPath: failure.logPath,
|
|
2897
|
-
detail: outcome.detail,
|
|
2898
|
-
});
|
|
504
|
+
lanePaths = buildLanePaths(options.lane, {
|
|
505
|
+
runVariant: options.dryRun ? "dry-run" : undefined,
|
|
506
|
+
adhocRunId: options.adhocRunId,
|
|
507
|
+
});
|
|
508
|
+
if (!stateFileProvided) {
|
|
509
|
+
options.runStatePath = lanePaths.defaultRunStatePath;
|
|
2899
510
|
}
|
|
2900
|
-
if (
|
|
2901
|
-
|
|
511
|
+
if (!manifestOutProvided) {
|
|
512
|
+
options.manifestOut = lanePaths.defaultManifestPath;
|
|
2902
513
|
}
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
detail: blockingFailures.map((failure) => failure.detail).join(" "),
|
|
2906
|
-
failures: blockingFailures,
|
|
2907
|
-
};
|
|
2908
|
-
}
|
|
2909
|
-
|
|
2910
|
-
export function readClarificationBarrier(derivedState) {
|
|
2911
|
-
const openClarifications = (derivedState?.coordinationState?.clarifications || []).filter(
|
|
2912
|
-
(record) => isOpenCoordinationStatus(record.status),
|
|
2913
|
-
);
|
|
2914
|
-
if (openClarifications.length > 0) {
|
|
2915
|
-
return {
|
|
2916
|
-
ok: false,
|
|
2917
|
-
statusCode: "clarification-open",
|
|
2918
|
-
detail: `Open clarifications remain (${openClarifications.map((record) => record.id).join(", ")}).`,
|
|
2919
|
-
};
|
|
514
|
+
if (!orchestratorBoardProvided) {
|
|
515
|
+
options.orchestratorBoardPath = lanePaths.defaultOrchestratorBoardPath;
|
|
2920
516
|
}
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
);
|
|
2924
|
-
if (openClarificationRequests.length > 0) {
|
|
2925
|
-
return {
|
|
2926
|
-
ok: false,
|
|
2927
|
-
statusCode: "clarification-follow-up-open",
|
|
2928
|
-
detail: `Clarification follow-up requests remain open (${openClarificationRequests.map((record) => record.id).join(", ")}).`,
|
|
2929
|
-
};
|
|
517
|
+
if (!executorProvided) {
|
|
518
|
+
options.executorMode = lanePaths.executors.default;
|
|
2930
519
|
}
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
...((derivedState?.coordinationState?.humanFeedback || []).filter((record) =>
|
|
2936
|
-
isOpenCoordinationStatus(record.status),
|
|
2937
|
-
)),
|
|
2938
|
-
];
|
|
2939
|
-
if (pendingHuman.length > 0) {
|
|
2940
|
-
return {
|
|
2941
|
-
ok: false,
|
|
2942
|
-
statusCode: "human-feedback-open",
|
|
2943
|
-
detail: `Pending human input remains (${pendingHuman.map((record) => record.id).join(", ")}).`,
|
|
520
|
+
if (!options.telemetryEnabled) {
|
|
521
|
+
lanePaths.waveControl = {
|
|
522
|
+
...(lanePaths.waveControl || {}),
|
|
523
|
+
enabled: false,
|
|
2944
524
|
};
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
statusCode: "pass",
|
|
2949
|
-
detail: "",
|
|
2950
|
-
};
|
|
2951
|
-
}
|
|
2952
|
-
|
|
2953
|
-
export function readWaveAssignmentBarrier(derivedState) {
|
|
2954
|
-
const blockingAssignments = (derivedState?.capabilityAssignments || []).filter(
|
|
2955
|
-
(assignment) => assignment.blocking,
|
|
2956
|
-
);
|
|
2957
|
-
if (blockingAssignments.length === 0) {
|
|
2958
|
-
return {
|
|
2959
|
-
ok: true,
|
|
2960
|
-
statusCode: "pass",
|
|
2961
|
-
detail: "",
|
|
525
|
+
lanePaths.laneProfile = {
|
|
526
|
+
...(lanePaths.laneProfile || {}),
|
|
527
|
+
waveControl: lanePaths.waveControl,
|
|
2962
528
|
};
|
|
2963
529
|
}
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
};
|
|
530
|
+
options.orchestratorId ||= sanitizeOrchestratorId(`${lanePaths.lane}-orch-${process.pid}`);
|
|
531
|
+
lanePaths.orchestratorId = options.orchestratorId;
|
|
532
|
+
if (options.agentRateLimitMaxDelaySeconds < options.agentRateLimitBaseDelaySeconds) {
|
|
533
|
+
throw new Error(
|
|
534
|
+
"--agent-rate-limit-max-delay-seconds must be >= --agent-rate-limit-base-delay-seconds",
|
|
535
|
+
);
|
|
2971
536
|
}
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
statusCode: "helper-assignment-open",
|
|
2975
|
-
detail: `Helper assignments remain open (${blockingAssignments.map((assignment) => assignment.requestId).join(", ")}).`,
|
|
2976
|
-
};
|
|
2977
|
-
}
|
|
2978
|
-
|
|
2979
|
-
export function readWaveDependencyBarrier(derivedState) {
|
|
2980
|
-
const requiredInbound = derivedState?.dependencySnapshot?.requiredInbound || [];
|
|
2981
|
-
const requiredOutbound = derivedState?.dependencySnapshot?.requiredOutbound || [];
|
|
2982
|
-
const unresolvedInboundAssignments =
|
|
2983
|
-
derivedState?.dependencySnapshot?.unresolvedInboundAssignments || [];
|
|
2984
|
-
if (unresolvedInboundAssignments.length > 0) {
|
|
2985
|
-
return {
|
|
2986
|
-
ok: false,
|
|
2987
|
-
statusCode: "dependency-assignment-unresolved",
|
|
2988
|
-
detail: `Required inbound dependencies are unassigned (${unresolvedInboundAssignments.map((record) => record.id).join(", ")}).`,
|
|
2989
|
-
};
|
|
537
|
+
if (!options.autoNext && options.endWave !== null && options.endWave < options.startWave) {
|
|
538
|
+
throw new Error("--end-wave must be >= --start-wave");
|
|
2990
539
|
}
|
|
2991
|
-
if (
|
|
2992
|
-
|
|
2993
|
-
ok: false,
|
|
2994
|
-
statusCode: "dependency-open",
|
|
2995
|
-
detail: `Open required dependencies remain (${[...requiredInbound, ...requiredOutbound].map((record) => record.id).join(", ")}).`,
|
|
2996
|
-
};
|
|
540
|
+
if (!options.dryRun && options.terminalSurface === "none") {
|
|
541
|
+
throw new Error("--terminal-surface none is only supported with --dry-run");
|
|
2997
542
|
}
|
|
2998
|
-
return {
|
|
2999
|
-
ok: true,
|
|
3000
|
-
statusCode: "pass",
|
|
3001
|
-
detail: "",
|
|
3002
|
-
};
|
|
543
|
+
return { help: false, lanePaths, options };
|
|
3003
544
|
}
|
|
3004
545
|
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
derivedState,
|
|
546
|
+
// --- Wrappers that bind local scope ---
|
|
547
|
+
|
|
548
|
+
export async function runClosureSweepPhase({
|
|
3009
549
|
lanePaths,
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
550
|
+
wave,
|
|
551
|
+
closureRuns,
|
|
552
|
+
coordinationLogPath,
|
|
553
|
+
refreshDerivedState,
|
|
554
|
+
dashboardState,
|
|
555
|
+
recordCombinedEvent,
|
|
556
|
+
flushDashboards,
|
|
557
|
+
options,
|
|
558
|
+
feedbackStateByRequestId,
|
|
559
|
+
appendCoordination,
|
|
560
|
+
launchAgentSessionFn = launchAgentSession,
|
|
561
|
+
waitForWaveCompletionFn = waitForWaveCompletion,
|
|
3013
562
|
}) {
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
evalTargets: wave.evalTargets,
|
|
3037
|
-
benchmarkCatalogPath: lanePaths?.laneProfile?.paths?.benchmarkCatalogPath,
|
|
3038
|
-
});
|
|
3039
|
-
const securityGate = readWaveSecurityGate(wave, agentRuns);
|
|
3040
|
-
const contQaGate = readWaveContQaGate(wave, agentRuns, {
|
|
3041
|
-
contQaAgentId: lanePaths?.contQaAgentId,
|
|
3042
|
-
mode: validationMode,
|
|
563
|
+
return runClosureSweepPhaseImpl({
|
|
564
|
+
lanePaths,
|
|
565
|
+
wave,
|
|
566
|
+
closureRuns,
|
|
567
|
+
coordinationLogPath,
|
|
568
|
+
refreshDerivedState,
|
|
569
|
+
dashboardState,
|
|
570
|
+
recordCombinedEvent,
|
|
571
|
+
flushDashboards,
|
|
572
|
+
options,
|
|
573
|
+
feedbackStateByRequestId,
|
|
574
|
+
appendCoordination,
|
|
575
|
+
launchAgentSessionFn,
|
|
576
|
+
waitForWaveCompletionFn,
|
|
577
|
+
readWaveContEvalGateFn: readWaveContEvalGate,
|
|
578
|
+
readWaveSecurityGateFn: readWaveSecurityGate,
|
|
579
|
+
readWaveIntegrationBarrierFn: readWaveIntegrationBarrier,
|
|
580
|
+
readWaveDocumentationGateFn: readWaveDocumentationGate,
|
|
581
|
+
readWaveComponentMatrixGateFn: readWaveComponentMatrixGate,
|
|
582
|
+
readWaveContQaGateFn: readWaveContQaGate,
|
|
583
|
+
materializeAgentExecutionSummaryForRunFn: materializeAgentExecutionSummaryForRun,
|
|
584
|
+
monitorWaveHumanFeedbackFn: monitorWaveHumanFeedback,
|
|
3043
585
|
});
|
|
3044
|
-
const infraGate = readWaveInfraGate(agentRuns);
|
|
3045
|
-
const clarificationBarrier = readClarificationBarrier(derivedState);
|
|
3046
|
-
const helperAssignmentBarrier = readWaveAssignmentBarrier(derivedState);
|
|
3047
|
-
const dependencyBarrier = readWaveDependencyBarrier(derivedState);
|
|
3048
|
-
const orderedGates = [
|
|
3049
|
-
["implementationGate", implementationGate],
|
|
3050
|
-
["componentGate", componentGate],
|
|
3051
|
-
["helperAssignmentBarrier", helperAssignmentBarrier],
|
|
3052
|
-
["dependencyBarrier", dependencyBarrier],
|
|
3053
|
-
["contEvalGate", contEvalGate],
|
|
3054
|
-
["securityGate", securityGate],
|
|
3055
|
-
["integrationBarrier", integrationBarrier],
|
|
3056
|
-
["documentationGate", documentationGate],
|
|
3057
|
-
["componentMatrixGate", componentMatrixGate],
|
|
3058
|
-
["contQaGate", contQaGate],
|
|
3059
|
-
["infraGate", infraGate],
|
|
3060
|
-
["clarificationBarrier", clarificationBarrier],
|
|
3061
|
-
];
|
|
3062
|
-
const firstFailure = orderedGates.find(([, gate]) => gate?.ok === false);
|
|
3063
|
-
return {
|
|
3064
|
-
implementationGate,
|
|
3065
|
-
componentGate,
|
|
3066
|
-
integrationGate,
|
|
3067
|
-
integrationBarrier,
|
|
3068
|
-
documentationGate,
|
|
3069
|
-
componentMatrixGate,
|
|
3070
|
-
contEvalGate,
|
|
3071
|
-
securityGate,
|
|
3072
|
-
contQaGate,
|
|
3073
|
-
infraGate,
|
|
3074
|
-
clarificationBarrier,
|
|
3075
|
-
helperAssignmentBarrier,
|
|
3076
|
-
dependencyBarrier,
|
|
3077
|
-
overall: firstFailure
|
|
3078
|
-
? {
|
|
3079
|
-
ok: false,
|
|
3080
|
-
gate: firstFailure[0],
|
|
3081
|
-
statusCode: firstFailure[1].statusCode,
|
|
3082
|
-
detail: firstFailure[1].detail,
|
|
3083
|
-
agentId: firstFailure[1].agentId || null,
|
|
3084
|
-
}
|
|
3085
|
-
: {
|
|
3086
|
-
ok: true,
|
|
3087
|
-
gate: "pass",
|
|
3088
|
-
statusCode: "pass",
|
|
3089
|
-
detail: "All replayed wave gates passed.",
|
|
3090
|
-
agentId: null,
|
|
3091
|
-
},
|
|
3092
|
-
};
|
|
3093
586
|
}
|
|
3094
587
|
|
|
3095
|
-
export function
|
|
3096
|
-
|
|
3097
|
-
const pendingFeedback = (derivedState?.coordinationState?.humanFeedback || []).filter((record) =>
|
|
3098
|
-
isOpenCoordinationStatus(record.status),
|
|
3099
|
-
);
|
|
3100
|
-
const pendingHumanEscalations = (derivedState?.coordinationState?.humanEscalations || []).filter(
|
|
3101
|
-
(record) => isOpenCoordinationStatus(record.status),
|
|
3102
|
-
);
|
|
3103
|
-
if (pendingFeedback.length > 0 || pendingHumanEscalations.length > 0) {
|
|
3104
|
-
return { runs: [], barrier: null };
|
|
3105
|
-
}
|
|
3106
|
-
const nextAttemptNumber = Number(derivedState?.ledger?.attempt || 0) + 1;
|
|
3107
|
-
const fallbackResolution = applyRetryFallbacks(
|
|
3108
|
-
agentRuns,
|
|
3109
|
-
failures,
|
|
3110
|
-
lanePaths,
|
|
3111
|
-
nextAttemptNumber,
|
|
3112
|
-
waveDefinition,
|
|
3113
|
-
);
|
|
3114
|
-
const retryBarrier = retryBarrierFromOutcomes(fallbackResolution.outcomes, failures);
|
|
3115
|
-
if (retryBarrier) {
|
|
3116
|
-
return { runs: [], barrier: retryBarrier };
|
|
3117
|
-
}
|
|
3118
|
-
const clarificationTargets = new Set();
|
|
3119
|
-
for (const record of openClarificationLinkedRequests(derivedState?.coordinationState)) {
|
|
3120
|
-
for (const target of record.targets || []) {
|
|
3121
|
-
if (String(target).startsWith("agent:")) {
|
|
3122
|
-
clarificationTargets.add(String(target).slice("agent:".length));
|
|
3123
|
-
} else if (runsByAgentId.has(target)) {
|
|
3124
|
-
clarificationTargets.add(target);
|
|
3125
|
-
}
|
|
3126
|
-
}
|
|
3127
|
-
}
|
|
3128
|
-
if (clarificationTargets.size > 0) {
|
|
3129
|
-
return {
|
|
3130
|
-
runs: Array.from(clarificationTargets)
|
|
3131
|
-
.map((agentId) => runsByAgentId.get(agentId))
|
|
3132
|
-
.filter(Boolean),
|
|
3133
|
-
barrier: null,
|
|
3134
|
-
};
|
|
3135
|
-
}
|
|
3136
|
-
const blockingAssignments = (derivedState?.capabilityAssignments || []).filter(
|
|
3137
|
-
(assignment) => assignment.blocking,
|
|
3138
|
-
);
|
|
3139
|
-
const effectiveAssignments =
|
|
3140
|
-
blockingAssignments.length > 0
|
|
3141
|
-
? blockingAssignments
|
|
3142
|
-
: buildRequestAssignments({
|
|
3143
|
-
coordinationState: derivedState?.coordinationState,
|
|
3144
|
-
agents: agentRuns.map((run) => run.agent),
|
|
3145
|
-
ledger: derivedState?.ledger,
|
|
3146
|
-
capabilityRouting: lanePaths?.capabilityRouting,
|
|
3147
|
-
}).filter((assignment) => assignment.blocking);
|
|
3148
|
-
const assignmentSource = effectiveAssignments.length > 0 ? effectiveAssignments : blockingAssignments;
|
|
3149
|
-
const unresolvedFromSource = assignmentSource.filter((assignment) => !assignment.assignedAgentId);
|
|
3150
|
-
if (unresolvedFromSource.length > 0) {
|
|
3151
|
-
return {
|
|
3152
|
-
runs: [],
|
|
3153
|
-
barrier: {
|
|
3154
|
-
statusCode: "helper-assignment-unresolved",
|
|
3155
|
-
detail: `No matching assignee exists for helper requests (${unresolvedFromSource.map((assignment) => assignment.requestId).join(", ")}).`,
|
|
3156
|
-
failures: unresolvedFromSource.map((assignment) => ({
|
|
3157
|
-
agentId: null,
|
|
3158
|
-
statusCode: "helper-assignment-unresolved",
|
|
3159
|
-
logPath: null,
|
|
3160
|
-
detail: assignment.assignmentDetail || assignment.summary || assignment.requestId,
|
|
3161
|
-
})),
|
|
3162
|
-
},
|
|
3163
|
-
};
|
|
3164
|
-
}
|
|
3165
|
-
const assignedAgentIds = new Set(
|
|
3166
|
-
assignmentSource.map((assignment) => assignment.assignedAgentId).filter(Boolean),
|
|
3167
|
-
);
|
|
3168
|
-
if (assignedAgentIds.size > 0) {
|
|
3169
|
-
return {
|
|
3170
|
-
runs: Array.from(assignedAgentIds)
|
|
3171
|
-
.map((agentId) => runsByAgentId.get(agentId))
|
|
3172
|
-
.filter(Boolean),
|
|
3173
|
-
barrier: null,
|
|
3174
|
-
};
|
|
3175
|
-
}
|
|
3176
|
-
const unresolvedInboundAssignments =
|
|
3177
|
-
derivedState?.dependencySnapshot?.unresolvedInboundAssignments || [];
|
|
3178
|
-
if (unresolvedInboundAssignments.length > 0) {
|
|
3179
|
-
return {
|
|
3180
|
-
runs: [],
|
|
3181
|
-
barrier: {
|
|
3182
|
-
statusCode: "dependency-assignment-unresolved",
|
|
3183
|
-
detail: `Required inbound dependencies are not assigned (${unresolvedInboundAssignments.map((record) => record.id).join(", ")}).`,
|
|
3184
|
-
failures: unresolvedInboundAssignments.map((record) => ({
|
|
3185
|
-
agentId: null,
|
|
3186
|
-
statusCode: "dependency-assignment-unresolved",
|
|
3187
|
-
logPath: null,
|
|
3188
|
-
detail: record.assignmentDetail || record.summary || record.id,
|
|
3189
|
-
})),
|
|
3190
|
-
},
|
|
3191
|
-
};
|
|
3192
|
-
}
|
|
3193
|
-
const inboundDependencyAgentIds = new Set(
|
|
3194
|
-
(derivedState?.dependencySnapshot?.openInbound || [])
|
|
3195
|
-
.map((record) => record.assignedAgentId)
|
|
3196
|
-
.filter(Boolean),
|
|
3197
|
-
);
|
|
3198
|
-
if (inboundDependencyAgentIds.size > 0) {
|
|
3199
|
-
return {
|
|
3200
|
-
runs: Array.from(inboundDependencyAgentIds)
|
|
3201
|
-
.map((agentId) => runsByAgentId.get(agentId))
|
|
3202
|
-
.filter(Boolean),
|
|
3203
|
-
barrier: null,
|
|
3204
|
-
};
|
|
3205
|
-
}
|
|
3206
|
-
const blockerAgentIds = new Set();
|
|
3207
|
-
for (const record of derivedState?.coordinationState?.blockers || []) {
|
|
3208
|
-
if (!isOpenCoordinationStatus(record.status)) {
|
|
3209
|
-
continue;
|
|
3210
|
-
}
|
|
3211
|
-
blockerAgentIds.add(record.agentId);
|
|
3212
|
-
for (const target of record.targets || []) {
|
|
3213
|
-
if (String(target).startsWith("agent:")) {
|
|
3214
|
-
blockerAgentIds.add(String(target).slice("agent:".length));
|
|
3215
|
-
}
|
|
3216
|
-
}
|
|
3217
|
-
}
|
|
3218
|
-
if (blockerAgentIds.size > 0) {
|
|
3219
|
-
return {
|
|
3220
|
-
runs: Array.from(blockerAgentIds)
|
|
3221
|
-
.map((agentId) => runsByAgentId.get(agentId))
|
|
3222
|
-
.filter(Boolean),
|
|
3223
|
-
barrier: null,
|
|
3224
|
-
};
|
|
3225
|
-
}
|
|
3226
|
-
if (derivedState?.ledger?.phase === "docs-closure") {
|
|
3227
|
-
return {
|
|
3228
|
-
runs: [runsByAgentId.get(lanePaths.documentationAgentId)].filter(Boolean),
|
|
3229
|
-
barrier: null,
|
|
3230
|
-
};
|
|
3231
|
-
}
|
|
3232
|
-
if (derivedState?.ledger?.phase === "security-review") {
|
|
3233
|
-
return {
|
|
3234
|
-
runs: agentRuns.filter((run) => isSecurityReviewAgent(run.agent)),
|
|
3235
|
-
barrier: null,
|
|
3236
|
-
};
|
|
3237
|
-
}
|
|
3238
|
-
if (derivedState?.ledger?.phase === "cont-eval") {
|
|
3239
|
-
return {
|
|
3240
|
-
runs: [runsByAgentId.get(lanePaths.contEvalAgentId)].filter(Boolean),
|
|
3241
|
-
barrier: null,
|
|
3242
|
-
};
|
|
3243
|
-
}
|
|
3244
|
-
if (derivedState?.ledger?.phase === "cont-qa-closure") {
|
|
3245
|
-
return {
|
|
3246
|
-
runs: [runsByAgentId.get(lanePaths.contQaAgentId)].filter(Boolean),
|
|
3247
|
-
barrier: null,
|
|
3248
|
-
};
|
|
3249
|
-
}
|
|
3250
|
-
if (derivedState?.ledger?.phase === "integrating") {
|
|
3251
|
-
return {
|
|
3252
|
-
runs: [runsByAgentId.get(lanePaths.integrationAgentId)].filter(Boolean),
|
|
3253
|
-
barrier: null,
|
|
3254
|
-
};
|
|
3255
|
-
}
|
|
3256
|
-
const sharedComponentWaitingAgentIds = new Set(
|
|
3257
|
-
(failures || [])
|
|
3258
|
-
.filter((failure) => failure.statusCode === "shared-component-sibling-pending")
|
|
3259
|
-
.flatMap((failure) => failure.waitingOnAgentIds || [])
|
|
3260
|
-
.filter((agentId) => runsByAgentId.has(agentId)),
|
|
3261
|
-
);
|
|
3262
|
-
if (sharedComponentWaitingAgentIds.size > 0) {
|
|
3263
|
-
return {
|
|
3264
|
-
runs: Array.from(sharedComponentWaitingAgentIds)
|
|
3265
|
-
.map((agentId) => runsByAgentId.get(agentId))
|
|
3266
|
-
.filter(Boolean),
|
|
3267
|
-
barrier: null,
|
|
3268
|
-
};
|
|
3269
|
-
}
|
|
3270
|
-
const failedAgentIds = new Set(failures.map((failure) => failure.agentId));
|
|
3271
|
-
return {
|
|
3272
|
-
runs: agentRuns.filter((run) => failedAgentIds.has(run.agent.agentId)),
|
|
3273
|
-
barrier: null,
|
|
3274
|
-
};
|
|
588
|
+
export function readWaveInfraGate(agentRuns) {
|
|
589
|
+
return readWaveInfraGateImpl(agentRuns);
|
|
3275
590
|
}
|
|
3276
591
|
|
|
3277
|
-
function
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
if (!mixValidation.ok) {
|
|
3283
|
-
throw new Error(
|
|
3284
|
-
`Wave ${wave.wave} exceeds lane runtime mix targets (${mixValidation.detail})`,
|
|
3285
|
-
);
|
|
3286
|
-
}
|
|
3287
|
-
for (const agent of wave.agents) {
|
|
3288
|
-
const executorState = agent.executorResolved;
|
|
3289
|
-
if (!executorState) {
|
|
3290
|
-
continue;
|
|
3291
|
-
}
|
|
3292
|
-
const chain = [executorState.id, ...executorFallbackChain(executorState)];
|
|
3293
|
-
const availableExecutorId = chain.find((executorId) =>
|
|
3294
|
-
isExecutorCommandAvailable(commandForExecutor(executorState, executorId)),
|
|
3295
|
-
);
|
|
3296
|
-
if (!availableExecutorId) {
|
|
3297
|
-
throw new Error(
|
|
3298
|
-
`Agent ${agent.agentId} has no available executor command in its configured chain (${chain.join(" -> ")})`,
|
|
3299
|
-
);
|
|
3300
|
-
}
|
|
3301
|
-
}
|
|
3302
|
-
}
|
|
592
|
+
export function buildGateSnapshot(params) {
|
|
593
|
+
return buildGateSnapshotImpl({
|
|
594
|
+
...params,
|
|
595
|
+
readWaveInfraGateFn: readWaveInfraGate,
|
|
596
|
+
});
|
|
3303
597
|
}
|
|
3304
598
|
|
|
599
|
+
// --- Main entry point ---
|
|
600
|
+
|
|
3305
601
|
export async function runLauncherCli(argv) {
|
|
3306
602
|
const parsed = parseArgs(argv);
|
|
3307
603
|
if (parsed.help) {
|
|
@@ -3469,6 +765,9 @@ export async function runLauncherCli(argv) {
|
|
|
3469
765
|
for (const blockedWave of reconciliation.blockedFromStatus || []) {
|
|
3470
766
|
console.log(formatReconcileBlockedWaveLine(blockedWave));
|
|
3471
767
|
}
|
|
768
|
+
for (const preservedWave of reconciliation.preservedWithDrift || []) {
|
|
769
|
+
console.log(formatReconcilePreservedWaveLine(preservedWave));
|
|
770
|
+
}
|
|
3472
771
|
console.log(`[reconcile] completed waves now: ${completedSummary}`);
|
|
3473
772
|
return;
|
|
3474
773
|
}
|
|
@@ -3628,7 +927,7 @@ export async function runLauncherCli(argv) {
|
|
|
3628
927
|
dashboardPath: lanePaths.globalDashboardPath,
|
|
3629
928
|
});
|
|
3630
929
|
console.log(
|
|
3631
|
-
`[dashboard]
|
|
930
|
+
`[dashboard] attach global: pnpm exec wave dashboard --lane ${lanePaths.lane} --attach global`,
|
|
3632
931
|
);
|
|
3633
932
|
}
|
|
3634
933
|
|
|
@@ -3730,6 +1029,12 @@ export async function runLauncherCli(argv) {
|
|
|
3730
1029
|
promptPath: path.join(lanePaths.promptsDir, `${safeName}.prompt.md`),
|
|
3731
1030
|
logPath: path.join(lanePaths.logsDir, `${safeName}.log`),
|
|
3732
1031
|
statusPath: path.join(lanePaths.statusDir, `${safeName}.status`),
|
|
1032
|
+
previewPath: path.join(
|
|
1033
|
+
lanePaths.executorOverlaysDir,
|
|
1034
|
+
`wave-${wave.wave}`,
|
|
1035
|
+
agent.slug,
|
|
1036
|
+
"launch-preview.json",
|
|
1037
|
+
),
|
|
3733
1038
|
messageBoardPath,
|
|
3734
1039
|
messageBoardSnapshot: derivedState.messageBoardText,
|
|
3735
1040
|
sharedSummaryPath: derivedState.sharedSummaryPath,
|
|
@@ -3777,6 +1082,16 @@ export async function runLauncherCli(argv) {
|
|
|
3777
1082
|
};
|
|
3778
1083
|
|
|
3779
1084
|
refreshDerivedState(0);
|
|
1085
|
+
const launchStateReset = resetPersistedWaveLaunchState(lanePaths, wave.wave, options);
|
|
1086
|
+
if (launchStateReset.clearedRelaunchPlan) {
|
|
1087
|
+
appendCoordination({
|
|
1088
|
+
event: "wave_launch_state_reset",
|
|
1089
|
+
waves: [wave.wave],
|
|
1090
|
+
status: "running",
|
|
1091
|
+
details: `cleared_relaunch_plan=yes; previous_agents=${(launchStateReset.relaunchPlan?.selectedAgentIds || []).join(",") || "none"}`,
|
|
1092
|
+
actionRequested: "None",
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
3780
1095
|
let persistedRelaunchPlan = readWaveRelaunchPlan(lanePaths, wave.wave);
|
|
3781
1096
|
let retryOverride = readWaveRetryOverride(lanePaths, wave.wave);
|
|
3782
1097
|
|
|
@@ -3891,6 +1206,9 @@ export async function runLauncherCli(argv) {
|
|
|
3891
1206
|
dashboardPath,
|
|
3892
1207
|
messageBoardPath,
|
|
3893
1208
|
});
|
|
1209
|
+
console.log(
|
|
1210
|
+
`[dashboard] attach current: pnpm exec wave dashboard --lane ${lanePaths.lane} --attach current`,
|
|
1211
|
+
);
|
|
3894
1212
|
}
|
|
3895
1213
|
|
|
3896
1214
|
if (options.residentOrchestrator) {
|