@chllming/wave-orchestration 0.5.4 → 0.6.1
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 +52 -3
- package/README.md +33 -5
- package/docs/README.md +18 -4
- package/docs/agents/wave-cont-eval-role.md +36 -0
- package/docs/agents/{wave-evaluator-role.md → wave-cont-qa-role.md} +14 -11
- package/docs/agents/wave-documentation-role.md +1 -1
- package/docs/agents/wave-infra-role.md +1 -1
- package/docs/agents/wave-integration-role.md +3 -3
- package/docs/agents/wave-launcher-role.md +4 -3
- package/docs/agents/wave-security-role.md +40 -0
- package/docs/concepts/context7-vs-skills.md +1 -1
- package/docs/concepts/what-is-a-wave.md +56 -6
- package/docs/evals/README.md +166 -0
- package/docs/evals/benchmark-catalog.json +663 -0
- package/docs/guides/author-and-run-waves.md +135 -0
- package/docs/guides/planner.md +5 -0
- package/docs/guides/terminal-surfaces.md +2 -0
- package/docs/plans/component-cutover-matrix.json +1 -1
- package/docs/plans/component-cutover-matrix.md +1 -1
- package/docs/plans/current-state.md +19 -1
- package/docs/plans/examples/wave-example-live-proof.md +435 -0
- package/docs/plans/migration.md +42 -0
- package/docs/plans/wave-orchestrator.md +46 -7
- package/docs/plans/waves/wave-0.md +4 -4
- package/docs/reference/live-proof-waves.md +177 -0
- package/docs/reference/migration-0.2-to-0.5.md +26 -19
- package/docs/reference/npmjs-trusted-publishing.md +6 -5
- package/docs/reference/runtime-config/README.md +14 -4
- package/docs/reference/sample-waves.md +87 -0
- package/docs/reference/skills.md +110 -42
- package/docs/research/agent-context-sources.md +130 -11
- package/docs/research/coordination-failure-review.md +266 -0
- package/docs/roadmap.md +6 -2
- package/package.json +2 -2
- package/releases/manifest.json +35 -2
- package/scripts/research/agent-context-archive.mjs +83 -1
- package/scripts/research/manifests/agent-context-expanded-2026-03-22.mjs +811 -0
- package/scripts/wave-orchestrator/adhoc.mjs +1331 -0
- package/scripts/wave-orchestrator/agent-state.mjs +358 -6
- package/scripts/wave-orchestrator/artifact-schemas.mjs +173 -0
- package/scripts/wave-orchestrator/clarification-triage.mjs +10 -3
- package/scripts/wave-orchestrator/config.mjs +48 -12
- package/scripts/wave-orchestrator/context7.mjs +2 -0
- package/scripts/wave-orchestrator/coord-cli.mjs +51 -19
- package/scripts/wave-orchestrator/coordination-store.mjs +26 -4
- package/scripts/wave-orchestrator/coordination.mjs +83 -9
- package/scripts/wave-orchestrator/dashboard-state.mjs +20 -8
- package/scripts/wave-orchestrator/dep-cli.mjs +5 -2
- package/scripts/wave-orchestrator/docs-queue.mjs +8 -2
- package/scripts/wave-orchestrator/evals.mjs +451 -0
- package/scripts/wave-orchestrator/feedback.mjs +15 -1
- package/scripts/wave-orchestrator/install.mjs +32 -9
- package/scripts/wave-orchestrator/launcher-closure.mjs +281 -0
- package/scripts/wave-orchestrator/launcher-runtime.mjs +334 -0
- package/scripts/wave-orchestrator/launcher.mjs +709 -601
- package/scripts/wave-orchestrator/ledger.mjs +123 -20
- package/scripts/wave-orchestrator/local-executor.mjs +99 -12
- package/scripts/wave-orchestrator/planner.mjs +177 -42
- package/scripts/wave-orchestrator/replay.mjs +6 -3
- package/scripts/wave-orchestrator/role-helpers.mjs +84 -0
- package/scripts/wave-orchestrator/shared.mjs +75 -11
- package/scripts/wave-orchestrator/skills.mjs +637 -106
- package/scripts/wave-orchestrator/traces.mjs +71 -48
- package/scripts/wave-orchestrator/wave-files.mjs +947 -101
- package/scripts/wave.mjs +9 -0
- package/skills/README.md +202 -0
- package/skills/provider-aws/SKILL.md +111 -0
- package/skills/provider-aws/adapters/claude.md +1 -0
- package/skills/provider-aws/adapters/codex.md +1 -0
- package/skills/provider-aws/references/service-verification.md +39 -0
- package/skills/provider-aws/skill.json +50 -1
- package/skills/provider-custom-deploy/SKILL.md +59 -0
- package/skills/provider-custom-deploy/skill.json +46 -1
- package/skills/provider-docker-compose/SKILL.md +90 -0
- package/skills/provider-docker-compose/adapters/local.md +1 -0
- package/skills/provider-docker-compose/skill.json +49 -1
- package/skills/provider-github-release/SKILL.md +116 -1
- package/skills/provider-github-release/adapters/claude.md +1 -0
- package/skills/provider-github-release/adapters/codex.md +1 -0
- package/skills/provider-github-release/skill.json +51 -1
- package/skills/provider-kubernetes/SKILL.md +137 -0
- package/skills/provider-kubernetes/adapters/claude.md +1 -0
- package/skills/provider-kubernetes/adapters/codex.md +1 -0
- package/skills/provider-kubernetes/references/kubectl-patterns.md +58 -0
- package/skills/provider-kubernetes/skill.json +48 -1
- package/skills/provider-railway/SKILL.md +118 -1
- package/skills/provider-railway/references/verification-commands.md +39 -0
- package/skills/provider-railway/skill.json +67 -1
- package/skills/provider-ssh-manual/SKILL.md +91 -0
- package/skills/provider-ssh-manual/skill.json +50 -1
- package/skills/repo-coding-rules/SKILL.md +84 -0
- package/skills/repo-coding-rules/skill.json +30 -1
- package/skills/role-cont-eval/SKILL.md +90 -0
- package/skills/role-cont-eval/adapters/codex.md +1 -0
- package/skills/role-cont-eval/skill.json +36 -0
- package/skills/role-cont-qa/SKILL.md +93 -0
- package/skills/role-cont-qa/adapters/claude.md +1 -0
- package/skills/role-cont-qa/skill.json +36 -0
- package/skills/role-deploy/SKILL.md +90 -0
- package/skills/role-deploy/skill.json +32 -1
- package/skills/role-documentation/SKILL.md +66 -0
- package/skills/role-documentation/skill.json +32 -1
- package/skills/role-implementation/SKILL.md +62 -0
- package/skills/role-implementation/skill.json +32 -1
- package/skills/role-infra/SKILL.md +74 -0
- package/skills/role-infra/skill.json +32 -1
- package/skills/role-integration/SKILL.md +79 -1
- package/skills/role-integration/skill.json +32 -1
- package/skills/role-research/SKILL.md +58 -0
- package/skills/role-research/skill.json +32 -1
- package/skills/role-security/SKILL.md +60 -0
- package/skills/role-security/skill.json +36 -0
- package/skills/runtime-claude/SKILL.md +60 -1
- package/skills/runtime-claude/skill.json +32 -1
- package/skills/runtime-codex/SKILL.md +52 -1
- package/skills/runtime-codex/skill.json +32 -1
- package/skills/runtime-local/SKILL.md +39 -0
- package/skills/runtime-local/skill.json +32 -1
- package/skills/runtime-opencode/SKILL.md +51 -0
- package/skills/runtime-opencode/skill.json +32 -1
- package/skills/wave-core/SKILL.md +107 -0
- package/skills/wave-core/references/marker-syntax.md +62 -0
- package/skills/wave-core/skill.json +31 -1
- package/wave.config.json +35 -6
- package/skills/role-evaluator/SKILL.md +0 -6
- package/skills/role-evaluator/skill.json +0 -5
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
} from "./config.mjs";
|
|
12
12
|
import {
|
|
13
13
|
appendOrchestratorBoardEntry,
|
|
14
|
-
buildExecutionPrompt,
|
|
15
14
|
ensureOrchestratorBoard,
|
|
16
15
|
feedbackStateSignature,
|
|
17
16
|
readWaveHumanFeedbackRequests,
|
|
@@ -34,7 +33,6 @@ import {
|
|
|
34
33
|
describeContext7Libraries,
|
|
35
34
|
hashAgentPromptFingerprint,
|
|
36
35
|
loadContext7BundleIndex,
|
|
37
|
-
prefetchContext7ForSelection,
|
|
38
36
|
} from "./context7.mjs";
|
|
39
37
|
import {
|
|
40
38
|
buildGlobalDashboardState,
|
|
@@ -58,7 +56,6 @@ import {
|
|
|
58
56
|
DEFAULT_AGENT_RATE_LIMIT_RETRIES,
|
|
59
57
|
DEFAULT_MAX_RETRIES_PER_WAVE,
|
|
60
58
|
DEFAULT_TIMEOUT_MINUTES,
|
|
61
|
-
DEFAULT_WAIT_PROGRESS_INTERVAL_MS,
|
|
62
59
|
DEFAULT_WAVE_LANE,
|
|
63
60
|
compactSingleLine,
|
|
64
61
|
parseVerdictFromText,
|
|
@@ -71,6 +68,7 @@ import {
|
|
|
71
68
|
readFileTail,
|
|
72
69
|
readJsonOrNull,
|
|
73
70
|
REPORT_VERDICT_REGEX,
|
|
71
|
+
sanitizeAdhocRunId,
|
|
74
72
|
sanitizeOrchestratorId,
|
|
75
73
|
shellQuote,
|
|
76
74
|
sleep,
|
|
@@ -96,11 +94,12 @@ import {
|
|
|
96
94
|
} from "./terminals.mjs";
|
|
97
95
|
import {
|
|
98
96
|
buildCodexExecInvocation,
|
|
99
|
-
buildExecutorLaunchSpec,
|
|
100
97
|
commandForExecutor,
|
|
101
98
|
isExecutorCommandAvailable,
|
|
102
99
|
} from "./executors.mjs";
|
|
103
100
|
import {
|
|
101
|
+
agentRequiresProofCentricValidation,
|
|
102
|
+
buildRunStateEvidence,
|
|
104
103
|
buildManifest,
|
|
105
104
|
applyExecutorSelectionsToWave,
|
|
106
105
|
markWaveCompleted,
|
|
@@ -111,16 +110,19 @@ import {
|
|
|
111
110
|
validateWaveComponentMatrixCurrentLevels,
|
|
112
111
|
validateWaveComponentPromotions,
|
|
113
112
|
validateWaveDefinition,
|
|
113
|
+
waveRequiresProofCentricValidation,
|
|
114
114
|
writeManifest,
|
|
115
115
|
} from "./wave-files.mjs";
|
|
116
116
|
import {
|
|
117
117
|
agentSummaryPathFromStatusPath,
|
|
118
118
|
buildAgentExecutionSummary,
|
|
119
119
|
readAgentExecutionSummary,
|
|
120
|
+
validateContEvalSummary,
|
|
121
|
+
validateContQaSummary,
|
|
120
122
|
validateDocumentationClosureSummary,
|
|
121
|
-
validateEvaluatorSummary,
|
|
122
123
|
validateIntegrationSummary,
|
|
123
124
|
validateImplementationSummary,
|
|
125
|
+
validateSecuritySummary,
|
|
124
126
|
writeAgentExecutionSummary,
|
|
125
127
|
} from "./agent-state.mjs";
|
|
126
128
|
import { buildDocsQueue, readDocsQueue, writeDocsQueue } from "./docs-queue.mjs";
|
|
@@ -129,9 +131,13 @@ import { buildQualityMetrics, writeTraceBundle } from "./traces.mjs";
|
|
|
129
131
|
import { triageClarificationRequests } from "./clarification-triage.mjs";
|
|
130
132
|
import { readProjectProfile, resolveDefaultTerminalSurface } from "./project-profile.mjs";
|
|
131
133
|
import {
|
|
132
|
-
|
|
134
|
+
isContEvalImplementationOwningAgent,
|
|
135
|
+
isContEvalReportOnlyAgent,
|
|
136
|
+
isSecurityReviewAgent,
|
|
137
|
+
resolveSecurityReviewReportPath,
|
|
138
|
+
} from "./role-helpers.mjs";
|
|
139
|
+
import {
|
|
133
140
|
summarizeResolvedSkills,
|
|
134
|
-
writeResolvedSkillArtifacts,
|
|
135
141
|
} from "./skills.mjs";
|
|
136
142
|
import {
|
|
137
143
|
buildDependencySnapshot,
|
|
@@ -140,6 +146,22 @@ import {
|
|
|
140
146
|
syncAssignmentRecords,
|
|
141
147
|
writeDependencySnapshotMarkdown,
|
|
142
148
|
} from "./routing-state.mjs";
|
|
149
|
+
import {
|
|
150
|
+
readRelaunchPlan,
|
|
151
|
+
writeAssignmentSnapshot,
|
|
152
|
+
writeDependencySnapshot,
|
|
153
|
+
writeRelaunchPlan,
|
|
154
|
+
} from "./artifact-schemas.mjs";
|
|
155
|
+
import {
|
|
156
|
+
collectUnexpectedSessionFailures as collectUnexpectedSessionFailuresImpl,
|
|
157
|
+
launchAgentSession as launchAgentSessionImpl,
|
|
158
|
+
refreshResolvedSkillsForRun,
|
|
159
|
+
waitForWaveCompletion as waitForWaveCompletionImpl,
|
|
160
|
+
} from "./launcher-runtime.mjs";
|
|
161
|
+
import {
|
|
162
|
+
readWaveInfraGate as readWaveInfraGateImpl,
|
|
163
|
+
runClosureSweepPhase as runClosureSweepPhaseImpl,
|
|
164
|
+
} from "./launcher-closure.mjs";
|
|
143
165
|
export { CODEX_SANDBOX_MODES, DEFAULT_CODEX_SANDBOX_MODE, normalizeCodexSandboxMode, buildCodexExecInvocation };
|
|
144
166
|
|
|
145
167
|
export function formatReconcileBlockedWaveLine(blockedWave) {
|
|
@@ -228,6 +250,7 @@ function parseArgs(argv) {
|
|
|
228
250
|
orchestratorId: null,
|
|
229
251
|
orchestratorBoardPath: null,
|
|
230
252
|
coordinationNote: "",
|
|
253
|
+
adhocRunId: null,
|
|
231
254
|
};
|
|
232
255
|
let stateFileProvided = false;
|
|
233
256
|
let manifestOutProvided = false;
|
|
@@ -265,7 +288,14 @@ function parseArgs(argv) {
|
|
|
265
288
|
orchestratorBoardProvided = true;
|
|
266
289
|
} else if (arg === "--lane") {
|
|
267
290
|
options.lane = String(argv[++i] || "").trim();
|
|
268
|
-
lanePaths = buildLanePaths(options.lane
|
|
291
|
+
lanePaths = buildLanePaths(options.lane, {
|
|
292
|
+
adhocRunId: options.adhocRunId,
|
|
293
|
+
});
|
|
294
|
+
} else if (arg === "--adhoc-run") {
|
|
295
|
+
options.adhocRunId = sanitizeAdhocRunId(argv[++i]);
|
|
296
|
+
lanePaths = buildLanePaths(options.lane, {
|
|
297
|
+
adhocRunId: options.adhocRunId,
|
|
298
|
+
});
|
|
269
299
|
} else if (arg === "--orchestrator-id") {
|
|
270
300
|
options.orchestratorId = sanitizeOrchestratorId(argv[++i]);
|
|
271
301
|
} else if (arg === "--orchestrator-board") {
|
|
@@ -313,6 +343,7 @@ function parseArgs(argv) {
|
|
|
313
343
|
|
|
314
344
|
lanePaths = buildLanePaths(options.lane, {
|
|
315
345
|
runVariant: options.dryRun ? "dry-run" : undefined,
|
|
346
|
+
adhocRunId: options.adhocRunId,
|
|
316
347
|
});
|
|
317
348
|
if (!stateFileProvided) {
|
|
318
349
|
options.runStatePath = lanePaths.defaultRunStatePath;
|
|
@@ -353,68 +384,118 @@ function isProcessAlive(pid) {
|
|
|
353
384
|
}
|
|
354
385
|
}
|
|
355
386
|
|
|
356
|
-
export function
|
|
357
|
-
const
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
387
|
+
export function readWaveContQaGate(wave, agentRuns, options = {}) {
|
|
388
|
+
const mode = String(options.mode || "compat").trim().toLowerCase();
|
|
389
|
+
const strict = mode === "live";
|
|
390
|
+
const contQaAgentId = options.contQaAgentId || wave.contQaAgentId || "A0";
|
|
391
|
+
const contQaRun =
|
|
392
|
+
agentRuns.find((run) => run.agent.agentId === contQaAgentId) ?? null;
|
|
393
|
+
if (!contQaRun) {
|
|
361
394
|
return {
|
|
362
395
|
ok: false,
|
|
363
|
-
agentId:
|
|
364
|
-
statusCode: "missing-
|
|
365
|
-
detail: `Agent ${
|
|
396
|
+
agentId: contQaAgentId,
|
|
397
|
+
statusCode: "missing-cont-qa",
|
|
398
|
+
detail: `Agent ${contQaAgentId} is missing.`,
|
|
366
399
|
logPath: null,
|
|
367
400
|
};
|
|
368
401
|
}
|
|
369
|
-
const summary = readRunExecutionSummary(
|
|
402
|
+
const summary = readRunExecutionSummary(contQaRun, strict ? wave : null);
|
|
370
403
|
if (summary) {
|
|
371
|
-
const validation =
|
|
404
|
+
const validation = validateContQaSummary(contQaRun.agent, summary, { mode });
|
|
372
405
|
return {
|
|
373
406
|
ok: validation.ok,
|
|
374
|
-
agentId:
|
|
407
|
+
agentId: contQaRun.agent.agentId,
|
|
375
408
|
statusCode: validation.statusCode,
|
|
376
409
|
detail: validation.detail,
|
|
377
|
-
logPath: summary.logPath || path.relative(REPO_ROOT,
|
|
410
|
+
logPath: summary.logPath || path.relative(REPO_ROOT, contQaRun.logPath),
|
|
378
411
|
};
|
|
379
412
|
}
|
|
380
|
-
|
|
381
|
-
|
|
413
|
+
if (strict) {
|
|
414
|
+
return {
|
|
415
|
+
ok: false,
|
|
416
|
+
agentId: contQaRun.agent.agentId,
|
|
417
|
+
statusCode: "missing-wave-gate",
|
|
418
|
+
detail: `Missing structured cont-QA summary for ${contQaRun.agent.agentId}.`,
|
|
419
|
+
logPath: path.relative(REPO_ROOT, contQaRun.logPath),
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
const contQaReportPath = wave.contQaReportPath
|
|
423
|
+
? path.resolve(REPO_ROOT, wave.contQaReportPath)
|
|
382
424
|
: null;
|
|
383
425
|
const reportText =
|
|
384
|
-
|
|
385
|
-
? fs.readFileSync(
|
|
426
|
+
contQaReportPath && fs.existsSync(contQaReportPath)
|
|
427
|
+
? fs.readFileSync(contQaReportPath, "utf8")
|
|
386
428
|
: "";
|
|
387
429
|
const reportVerdict = parseVerdictFromText(reportText, REPORT_VERDICT_REGEX);
|
|
388
430
|
if (reportVerdict.verdict) {
|
|
389
431
|
return {
|
|
390
432
|
ok: reportVerdict.verdict === "pass",
|
|
391
|
-
agentId:
|
|
392
|
-
statusCode: reportVerdict.verdict === "pass" ? "pass" : `
|
|
393
|
-
detail: reportVerdict.detail || "Verdict read from
|
|
394
|
-
logPath: path.relative(REPO_ROOT,
|
|
433
|
+
agentId: contQaRun.agent.agentId,
|
|
434
|
+
statusCode: reportVerdict.verdict === "pass" ? "pass" : `cont-qa-${reportVerdict.verdict}`,
|
|
435
|
+
detail: reportVerdict.detail || "Verdict read from cont-QA report.",
|
|
436
|
+
logPath: path.relative(REPO_ROOT, contQaRun.logPath),
|
|
395
437
|
};
|
|
396
438
|
}
|
|
397
439
|
const logVerdict = parseVerdictFromText(
|
|
398
|
-
readFileTail(
|
|
440
|
+
readFileTail(contQaRun.logPath, 30000),
|
|
399
441
|
WAVE_VERDICT_REGEX,
|
|
400
442
|
);
|
|
401
443
|
if (logVerdict.verdict) {
|
|
402
444
|
return {
|
|
403
445
|
ok: logVerdict.verdict === "pass",
|
|
404
|
-
agentId:
|
|
405
|
-
statusCode: logVerdict.verdict === "pass" ? "pass" : `
|
|
406
|
-
detail: logVerdict.detail || "Verdict read from
|
|
407
|
-
logPath: path.relative(REPO_ROOT,
|
|
446
|
+
agentId: contQaRun.agent.agentId,
|
|
447
|
+
statusCode: logVerdict.verdict === "pass" ? "pass" : `cont-qa-${logVerdict.verdict}`,
|
|
448
|
+
detail: logVerdict.detail || "Verdict read from cont-QA log marker.",
|
|
449
|
+
logPath: path.relative(REPO_ROOT, contQaRun.logPath),
|
|
408
450
|
};
|
|
409
451
|
}
|
|
410
452
|
return {
|
|
411
453
|
ok: false,
|
|
412
|
-
agentId:
|
|
413
|
-
statusCode: "missing-
|
|
414
|
-
detail:
|
|
415
|
-
? `Missing Verdict line in ${path.relative(REPO_ROOT,
|
|
416
|
-
: `Missing
|
|
417
|
-
logPath: path.relative(REPO_ROOT,
|
|
454
|
+
agentId: contQaRun.agent.agentId,
|
|
455
|
+
statusCode: "missing-cont-qa-verdict",
|
|
456
|
+
detail: contQaReportPath
|
|
457
|
+
? `Missing Verdict line in ${path.relative(REPO_ROOT, contQaReportPath)} and no [wave-verdict] marker in ${path.relative(REPO_ROOT, contQaRun.logPath)}.`
|
|
458
|
+
: `Missing cont-QA report path and no [wave-verdict] marker in ${path.relative(REPO_ROOT, contQaRun.logPath)}.`,
|
|
459
|
+
logPath: path.relative(REPO_ROOT, contQaRun.logPath),
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
export function readWaveContEvalGate(wave, agentRuns, options = {}) {
|
|
464
|
+
const mode = String(options.mode || "compat").trim().toLowerCase();
|
|
465
|
+
const strict = mode === "live";
|
|
466
|
+
const contEvalAgentId = options.contEvalAgentId || wave.contEvalAgentId || "E0";
|
|
467
|
+
const contEvalRun =
|
|
468
|
+
agentRuns.find((run) => run.agent.agentId === contEvalAgentId) ?? null;
|
|
469
|
+
if (!contEvalRun) {
|
|
470
|
+
return {
|
|
471
|
+
ok: true,
|
|
472
|
+
agentId: null,
|
|
473
|
+
statusCode: "pass",
|
|
474
|
+
detail: "Wave does not include cont-EVAL.",
|
|
475
|
+
logPath: null,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
const summary = readRunExecutionSummary(contEvalRun, strict ? wave : null);
|
|
479
|
+
if (summary) {
|
|
480
|
+
const validation = validateContEvalSummary(contEvalRun.agent, summary, {
|
|
481
|
+
mode,
|
|
482
|
+
evalTargets: options.evalTargets || wave.evalTargets,
|
|
483
|
+
benchmarkCatalogPath: options.benchmarkCatalogPath,
|
|
484
|
+
});
|
|
485
|
+
return {
|
|
486
|
+
ok: validation.ok,
|
|
487
|
+
agentId: contEvalRun.agent.agentId,
|
|
488
|
+
statusCode: validation.statusCode,
|
|
489
|
+
detail: validation.detail,
|
|
490
|
+
logPath: summary.logPath || path.relative(REPO_ROOT, contEvalRun.logPath),
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
return {
|
|
494
|
+
ok: false,
|
|
495
|
+
agentId: contEvalRun.agent.agentId,
|
|
496
|
+
statusCode: "missing-wave-eval",
|
|
497
|
+
detail: `Missing [wave-eval] marker for ${contEvalRun.agent.agentId}.`,
|
|
498
|
+
logPath: path.relative(REPO_ROOT, contEvalRun.logPath),
|
|
418
499
|
};
|
|
419
500
|
}
|
|
420
501
|
|
|
@@ -423,10 +504,19 @@ function materializeAgentExecutionSummaryForRun(wave, runInfo) {
|
|
|
423
504
|
if (!statusRecord) {
|
|
424
505
|
return null;
|
|
425
506
|
}
|
|
426
|
-
const reportPath =
|
|
427
|
-
runInfo.agent.agentId === (wave.
|
|
428
|
-
|
|
429
|
-
|
|
507
|
+
const reportPath = (() => {
|
|
508
|
+
if (runInfo.agent.agentId === (wave.contQaAgentId || "A0") && wave.contQaReportPath) {
|
|
509
|
+
return path.resolve(REPO_ROOT, wave.contQaReportPath);
|
|
510
|
+
}
|
|
511
|
+
if (runInfo.agent.agentId === (wave.contEvalAgentId || "E0") && wave.contEvalReportPath) {
|
|
512
|
+
return path.resolve(REPO_ROOT, wave.contEvalReportPath);
|
|
513
|
+
}
|
|
514
|
+
if (isSecurityReviewAgent(runInfo.agent)) {
|
|
515
|
+
const securityReportPath = resolveSecurityReviewReportPath(runInfo.agent);
|
|
516
|
+
return securityReportPath ? path.resolve(REPO_ROOT, securityReportPath) : null;
|
|
517
|
+
}
|
|
518
|
+
return null;
|
|
519
|
+
})();
|
|
430
520
|
const summary = buildAgentExecutionSummary({
|
|
431
521
|
agent: runInfo.agent,
|
|
432
522
|
statusRecord,
|
|
@@ -437,7 +527,7 @@ function materializeAgentExecutionSummaryForRun(wave, runInfo) {
|
|
|
437
527
|
return summary;
|
|
438
528
|
}
|
|
439
529
|
|
|
440
|
-
function readRunExecutionSummary(runInfo) {
|
|
530
|
+
function readRunExecutionSummary(runInfo, wave = null) {
|
|
441
531
|
if (runInfo?.summary && typeof runInfo.summary === "object") {
|
|
442
532
|
return runInfo.summary;
|
|
443
533
|
}
|
|
@@ -447,9 +537,19 @@ function readRunExecutionSummary(runInfo) {
|
|
|
447
537
|
if (runInfo?.statusPath && fs.existsSync(agentSummaryPathFromStatusPath(runInfo.statusPath))) {
|
|
448
538
|
return readAgentExecutionSummary(runInfo.statusPath);
|
|
449
539
|
}
|
|
540
|
+
if (wave && runInfo?.statusPath && runInfo?.logPath && fs.existsSync(runInfo.statusPath)) {
|
|
541
|
+
return materializeAgentExecutionSummaryForRun(wave, runInfo);
|
|
542
|
+
}
|
|
450
543
|
return null;
|
|
451
544
|
}
|
|
452
545
|
|
|
546
|
+
export function readWaveEvaluatorGate(wave, agentRuns, options = {}) {
|
|
547
|
+
return readWaveContQaGate(wave, agentRuns, {
|
|
548
|
+
...options,
|
|
549
|
+
contQaAgentId: options.evaluatorAgentId || options.contQaAgentId,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
|
|
453
553
|
function materializeAgentExecutionSummaries(wave, agentRuns) {
|
|
454
554
|
return Object.fromEntries(
|
|
455
555
|
agentRuns.map((runInfo) => [runInfo.agent.agentId, materializeAgentExecutionSummaryForRun(wave, runInfo)]),
|
|
@@ -492,6 +592,37 @@ function waveIntegrationMarkdownPath(lanePaths, waveNumber) {
|
|
|
492
592
|
return path.join(lanePaths.integrationDir, `wave-${waveNumber}.md`);
|
|
493
593
|
}
|
|
494
594
|
|
|
595
|
+
function waveRelaunchPlanPath(lanePaths, waveNumber) {
|
|
596
|
+
return path.join(lanePaths.statusDir, `relaunch-plan-wave-${waveNumber}.json`);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function readWaveRelaunchPlan(lanePaths, waveNumber) {
|
|
600
|
+
return readRelaunchPlan(waveRelaunchPlanPath(lanePaths, waveNumber), { wave: waveNumber });
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function writeWaveRelaunchPlan(lanePaths, waveNumber, payload) {
|
|
604
|
+
const filePath = waveRelaunchPlanPath(lanePaths, waveNumber);
|
|
605
|
+
writeRelaunchPlan(filePath, payload, { wave: waveNumber });
|
|
606
|
+
return filePath;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function clearWaveRelaunchPlan(lanePaths, waveNumber) {
|
|
610
|
+
const filePath = waveRelaunchPlanPath(lanePaths, waveNumber);
|
|
611
|
+
try {
|
|
612
|
+
fs.rmSync(filePath, { force: true });
|
|
613
|
+
} catch {
|
|
614
|
+
// no-op
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function waveSecurityPath(lanePaths, waveNumber) {
|
|
619
|
+
return path.join(lanePaths.securityDir, `wave-${waveNumber}.json`);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function waveSecurityMarkdownPath(lanePaths, waveNumber) {
|
|
623
|
+
return path.join(lanePaths.securityDir, `wave-${waveNumber}.md`);
|
|
624
|
+
}
|
|
625
|
+
|
|
495
626
|
function uniqueStringEntries(values) {
|
|
496
627
|
return Array.from(
|
|
497
628
|
new Set(
|
|
@@ -607,6 +738,116 @@ function inferIntegrationRecommendation(evidence) {
|
|
|
607
738
|
};
|
|
608
739
|
}
|
|
609
740
|
|
|
741
|
+
export function buildWaveSecuritySummary({
|
|
742
|
+
lanePaths,
|
|
743
|
+
wave,
|
|
744
|
+
attempt,
|
|
745
|
+
summariesByAgentId = {},
|
|
746
|
+
}) {
|
|
747
|
+
const createdAt = toIsoTimestamp();
|
|
748
|
+
const securityAgents = (wave.agents || []).filter((agent) => isSecurityReviewAgent(agent));
|
|
749
|
+
if (securityAgents.length === 0) {
|
|
750
|
+
return {
|
|
751
|
+
wave: wave.wave,
|
|
752
|
+
lane: lanePaths.lane,
|
|
753
|
+
attempt,
|
|
754
|
+
overallState: "not-applicable",
|
|
755
|
+
totalFindings: 0,
|
|
756
|
+
totalApprovals: 0,
|
|
757
|
+
concernAgentIds: [],
|
|
758
|
+
blockedAgentIds: [],
|
|
759
|
+
detail: "No security reviewer declared for this wave.",
|
|
760
|
+
agents: [],
|
|
761
|
+
createdAt,
|
|
762
|
+
updatedAt: createdAt,
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
const agents = securityAgents.map((agent) => {
|
|
766
|
+
const summary = summariesByAgentId?.[agent.agentId] || null;
|
|
767
|
+
const validation = validateSecuritySummary(agent, summary);
|
|
768
|
+
const explicitState = summary?.security?.state || null;
|
|
769
|
+
return {
|
|
770
|
+
agentId: agent.agentId,
|
|
771
|
+
title: agent.title || agent.agentId,
|
|
772
|
+
state: validation.ok
|
|
773
|
+
? explicitState || "clear"
|
|
774
|
+
: explicitState === "blocked"
|
|
775
|
+
? "blocked"
|
|
776
|
+
: "pending",
|
|
777
|
+
findings: summary?.security?.findings || 0,
|
|
778
|
+
approvals: summary?.security?.approvals || 0,
|
|
779
|
+
detail: validation.ok
|
|
780
|
+
? summary?.security?.detail || validation.detail || ""
|
|
781
|
+
: validation.detail,
|
|
782
|
+
reportPath: summary?.reportPath || resolveSecurityReviewReportPath(agent) || null,
|
|
783
|
+
statusCode: validation.statusCode,
|
|
784
|
+
ok: validation.ok,
|
|
785
|
+
};
|
|
786
|
+
});
|
|
787
|
+
const blockedAgentIds = agents
|
|
788
|
+
.filter((entry) => entry.state === "blocked")
|
|
789
|
+
.map((entry) => entry.agentId);
|
|
790
|
+
const concernAgentIds = agents
|
|
791
|
+
.filter((entry) => entry.state === "concerns")
|
|
792
|
+
.map((entry) => entry.agentId);
|
|
793
|
+
const pendingAgentIds = agents
|
|
794
|
+
.filter((entry) => entry.state === "pending")
|
|
795
|
+
.map((entry) => entry.agentId);
|
|
796
|
+
const overallState =
|
|
797
|
+
blockedAgentIds.length > 0
|
|
798
|
+
? "blocked"
|
|
799
|
+
: pendingAgentIds.length > 0
|
|
800
|
+
? "pending"
|
|
801
|
+
: concernAgentIds.length > 0
|
|
802
|
+
? "concerns"
|
|
803
|
+
: "clear";
|
|
804
|
+
const totalFindings = agents.reduce((sum, entry) => sum + (entry.findings || 0), 0);
|
|
805
|
+
const totalApprovals = agents.reduce((sum, entry) => sum + (entry.approvals || 0), 0);
|
|
806
|
+
const detail =
|
|
807
|
+
overallState === "blocked"
|
|
808
|
+
? `Security review blocked by ${blockedAgentIds.join(", ")}.`
|
|
809
|
+
: overallState === "pending"
|
|
810
|
+
? `Security review output is incomplete for ${pendingAgentIds.join(", ")}.`
|
|
811
|
+
: overallState === "concerns"
|
|
812
|
+
? `Security review reported advisory concerns from ${concernAgentIds.join(", ")}.`
|
|
813
|
+
: "Security review is clear.";
|
|
814
|
+
return {
|
|
815
|
+
wave: wave.wave,
|
|
816
|
+
lane: lanePaths.lane,
|
|
817
|
+
attempt,
|
|
818
|
+
overallState,
|
|
819
|
+
totalFindings,
|
|
820
|
+
totalApprovals,
|
|
821
|
+
concernAgentIds,
|
|
822
|
+
blockedAgentIds,
|
|
823
|
+
detail,
|
|
824
|
+
agents,
|
|
825
|
+
createdAt,
|
|
826
|
+
updatedAt: createdAt,
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function renderWaveSecuritySummaryMarkdown(securitySummary) {
|
|
831
|
+
return [
|
|
832
|
+
`# Wave ${securitySummary.wave} Security Summary`,
|
|
833
|
+
"",
|
|
834
|
+
`- State: ${securitySummary.overallState || "unknown"}`,
|
|
835
|
+
`- Detail: ${securitySummary.detail || "n/a"}`,
|
|
836
|
+
`- Total findings: ${securitySummary.totalFindings || 0}`,
|
|
837
|
+
`- Total approvals: ${securitySummary.totalApprovals || 0}`,
|
|
838
|
+
`- Reviewers: ${(securitySummary.agents || []).length}`,
|
|
839
|
+
"",
|
|
840
|
+
"## Reviews",
|
|
841
|
+
...((securitySummary.agents || []).length > 0
|
|
842
|
+
? securitySummary.agents.map(
|
|
843
|
+
(entry) =>
|
|
844
|
+
`- ${entry.agentId}: state=${entry.state || "unknown"} findings=${entry.findings || 0} approvals=${entry.approvals || 0}${entry.reportPath ? ` report=${entry.reportPath}` : ""}${entry.detail ? ` detail=${entry.detail}` : ""}`,
|
|
845
|
+
)
|
|
846
|
+
: ["- None."]),
|
|
847
|
+
"",
|
|
848
|
+
].join("\n");
|
|
849
|
+
}
|
|
850
|
+
|
|
610
851
|
function padReportedEntries(entries, minimumCount, label) {
|
|
611
852
|
const padded = [...entries];
|
|
612
853
|
for (let index = padded.length + 1; index <= minimumCount; index += 1) {
|
|
@@ -624,6 +865,7 @@ function buildIntegrationEvidence({
|
|
|
624
865
|
agentRuns,
|
|
625
866
|
dependencySnapshot = null,
|
|
626
867
|
capabilityAssignments = [],
|
|
868
|
+
securitySummary = null,
|
|
627
869
|
}) {
|
|
628
870
|
const openClaims = (coordinationState?.claims || [])
|
|
629
871
|
.filter((record) => isOpenCoordinationStatus(record.status))
|
|
@@ -677,14 +919,37 @@ function buildIntegrationEvidence({
|
|
|
677
919
|
? docsQueue.items.map((item) => summarizeDocsQueueItem(item))
|
|
678
920
|
: [];
|
|
679
921
|
const deployRiskEntries = [];
|
|
922
|
+
const securityFindingEntries = [];
|
|
923
|
+
const securityApprovalEntries = [];
|
|
680
924
|
for (const agent of wave.agents || []) {
|
|
681
925
|
const summary = summariesByAgentId?.[agent.agentId] || null;
|
|
926
|
+
const contEvalImplementationOwning =
|
|
927
|
+
agent.agentId === lanePaths.contEvalAgentId &&
|
|
928
|
+
isContEvalImplementationOwningAgent(agent, {
|
|
929
|
+
contEvalAgentId: lanePaths.contEvalAgentId,
|
|
930
|
+
});
|
|
931
|
+
if (isSecurityReviewAgent(agent)) {
|
|
932
|
+
continue;
|
|
933
|
+
}
|
|
934
|
+
if (agent.agentId === lanePaths.contEvalAgentId) {
|
|
935
|
+
const validation = validateContEvalSummary(agent, summary, {
|
|
936
|
+
mode: "live",
|
|
937
|
+
evalTargets: wave.evalTargets,
|
|
938
|
+
benchmarkCatalogPath: lanePaths.laneProfile?.paths?.benchmarkCatalogPath,
|
|
939
|
+
});
|
|
940
|
+
if (!validation.ok) {
|
|
941
|
+
proofGapEntries.push(
|
|
942
|
+
summarizeGap(agent.agentId, validation.detail, "cont-EVAL target is not yet satisfied."),
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
682
946
|
if (
|
|
683
947
|
![
|
|
684
|
-
lanePaths.
|
|
948
|
+
lanePaths.contQaAgentId,
|
|
685
949
|
lanePaths.integrationAgentId,
|
|
686
950
|
lanePaths.documentationAgentId,
|
|
687
|
-
].includes(agent.agentId)
|
|
951
|
+
].includes(agent.agentId) &&
|
|
952
|
+
(agent.agentId !== lanePaths.contEvalAgentId || contEvalImplementationOwning)
|
|
688
953
|
) {
|
|
689
954
|
const validation = validateImplementationSummary(agent, summary);
|
|
690
955
|
if (!validation.ok) {
|
|
@@ -754,6 +1019,29 @@ function buildIntegrationEvidence({
|
|
|
754
1019
|
`${assignment.requestId}: ${assignment.target}${assignment.assignedAgentId ? ` -> ${assignment.assignedAgentId}` : " -> unresolved"} (${assignment.assignmentReason || "n/a"})`,
|
|
755
1020
|
);
|
|
756
1021
|
|
|
1022
|
+
for (const review of securitySummary?.agents || []) {
|
|
1023
|
+
if (review.state === "blocked" || review.state === "concerns") {
|
|
1024
|
+
securityFindingEntries.push(
|
|
1025
|
+
summarizeGap(
|
|
1026
|
+
review.agentId,
|
|
1027
|
+
review.detail,
|
|
1028
|
+
review.state === "blocked"
|
|
1029
|
+
? "Security review blocked the wave."
|
|
1030
|
+
: "Security review reported advisory concerns.",
|
|
1031
|
+
),
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
if ((review.approvals || 0) > 0) {
|
|
1035
|
+
securityApprovalEntries.push(
|
|
1036
|
+
summarizeGap(
|
|
1037
|
+
review.agentId,
|
|
1038
|
+
review.detail,
|
|
1039
|
+
`${review.approvals} security approval(s) remain open.`,
|
|
1040
|
+
),
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
757
1045
|
return {
|
|
758
1046
|
openClaims: uniqueStringEntries(openClaims),
|
|
759
1047
|
conflictingClaims: uniqueStringEntries(conflictingClaims),
|
|
@@ -766,6 +1054,9 @@ function buildIntegrationEvidence({
|
|
|
766
1054
|
inboundDependencies: uniqueStringEntries(inboundDependencies),
|
|
767
1055
|
outboundDependencies: uniqueStringEntries(outboundDependencies),
|
|
768
1056
|
helperAssignments: uniqueStringEntries(helperAssignments),
|
|
1057
|
+
securityState: securitySummary?.overallState || "not-applicable",
|
|
1058
|
+
securityFindings: uniqueStringEntries(securityFindingEntries),
|
|
1059
|
+
securityApprovals: uniqueStringEntries(securityApprovalEntries),
|
|
769
1060
|
};
|
|
770
1061
|
}
|
|
771
1062
|
|
|
@@ -780,6 +1071,7 @@ export function buildWaveIntegrationSummary({
|
|
|
780
1071
|
agentRuns,
|
|
781
1072
|
capabilityAssignments = [],
|
|
782
1073
|
dependencySnapshot = null,
|
|
1074
|
+
securitySummary = null,
|
|
783
1075
|
}) {
|
|
784
1076
|
const explicitIntegration = summariesByAgentId[lanePaths.integrationAgentId]?.integration || null;
|
|
785
1077
|
const evidence = buildIntegrationEvidence({
|
|
@@ -791,6 +1083,7 @@ export function buildWaveIntegrationSummary({
|
|
|
791
1083
|
agentRuns,
|
|
792
1084
|
capabilityAssignments,
|
|
793
1085
|
dependencySnapshot,
|
|
1086
|
+
securitySummary,
|
|
794
1087
|
});
|
|
795
1088
|
if (explicitIntegration) {
|
|
796
1089
|
return {
|
|
@@ -818,6 +1111,9 @@ export function buildWaveIntegrationSummary({
|
|
|
818
1111
|
proofGaps: evidence.proofGaps,
|
|
819
1112
|
docGaps: evidence.docGaps,
|
|
820
1113
|
deployRisks: evidence.deployRisks,
|
|
1114
|
+
securityState: evidence.securityState,
|
|
1115
|
+
securityFindings: evidence.securityFindings,
|
|
1116
|
+
securityApprovals: evidence.securityApprovals,
|
|
821
1117
|
inboundDependencies: evidence.inboundDependencies,
|
|
822
1118
|
outboundDependencies: evidence.outboundDependencies,
|
|
823
1119
|
helperAssignments: evidence.helperAssignments,
|
|
@@ -865,6 +1161,9 @@ function renderIntegrationSummaryMarkdown(integrationSummary) {
|
|
|
865
1161
|
`- Proof gaps: ${(integrationSummary.proofGaps || []).length}`,
|
|
866
1162
|
`- Deploy risks: ${(integrationSummary.deployRisks || []).length}`,
|
|
867
1163
|
`- Documentation gaps: ${(integrationSummary.docGaps || []).length}`,
|
|
1164
|
+
`- Security review: ${integrationSummary.securityState || "not-applicable"}`,
|
|
1165
|
+
`- Security findings: ${(integrationSummary.securityFindings || []).length}`,
|
|
1166
|
+
`- Security approvals: ${(integrationSummary.securityApprovals || []).length}`,
|
|
868
1167
|
`- Inbound dependencies: ${(integrationSummary.inboundDependencies || []).length}`,
|
|
869
1168
|
`- Outbound dependencies: ${(integrationSummary.outboundDependencies || []).length}`,
|
|
870
1169
|
`- Helper assignments: ${(integrationSummary.helperAssignments || []).length}`,
|
|
@@ -879,6 +1178,8 @@ function renderIntegrationSummaryMarkdown(integrationSummary) {
|
|
|
879
1178
|
),
|
|
880
1179
|
...renderIntegrationSection("## Proof Gaps", integrationSummary.proofGaps),
|
|
881
1180
|
...renderIntegrationSection("## Deploy Risks", integrationSummary.deployRisks),
|
|
1181
|
+
...renderIntegrationSection("## Security Findings", integrationSummary.securityFindings),
|
|
1182
|
+
...renderIntegrationSection("## Security Approvals", integrationSummary.securityApprovals),
|
|
882
1183
|
...renderIntegrationSection("## Inbound Dependencies", integrationSummary.inboundDependencies),
|
|
883
1184
|
...renderIntegrationSection("## Outbound Dependencies", integrationSummary.outboundDependencies),
|
|
884
1185
|
...renderIntegrationSection("## Helper Assignments", integrationSummary.helperAssignments),
|
|
@@ -913,7 +1214,8 @@ function writeWaveDerivedState({
|
|
|
913
1214
|
agents: wave.agents,
|
|
914
1215
|
componentPromotions: wave.componentPromotions,
|
|
915
1216
|
sharedPlanDocs: lanePaths.sharedPlanDocs,
|
|
916
|
-
|
|
1217
|
+
contQaAgentId: lanePaths.contQaAgentId,
|
|
1218
|
+
contEvalAgentId: lanePaths.contEvalAgentId,
|
|
917
1219
|
integrationAgentId: lanePaths.integrationAgentId,
|
|
918
1220
|
documentationAgentId: lanePaths.documentationAgentId,
|
|
919
1221
|
feedbackRequests,
|
|
@@ -956,8 +1258,14 @@ function writeWaveDerivedState({
|
|
|
956
1258
|
ledger: existingLedger,
|
|
957
1259
|
capabilityRouting: lanePaths.capabilityRouting,
|
|
958
1260
|
});
|
|
959
|
-
|
|
960
|
-
|
|
1261
|
+
writeAssignmentSnapshot(waveAssignmentsPath(lanePaths, wave.wave), capabilityAssignments, {
|
|
1262
|
+
lane: lanePaths.lane,
|
|
1263
|
+
wave: wave.wave,
|
|
1264
|
+
});
|
|
1265
|
+
writeDependencySnapshot(waveDependencySnapshotPath(lanePaths, wave.wave), dependencySnapshot, {
|
|
1266
|
+
lane: lanePaths.lane,
|
|
1267
|
+
wave: wave.wave,
|
|
1268
|
+
});
|
|
961
1269
|
writeDependencySnapshotMarkdown(
|
|
962
1270
|
waveDependencySnapshotMarkdownPath(lanePaths, wave.wave),
|
|
963
1271
|
dependencySnapshot,
|
|
@@ -969,6 +1277,8 @@ function writeWaveDerivedState({
|
|
|
969
1277
|
executorId: agent.executorResolved?.id || null,
|
|
970
1278
|
profile: agent.executorResolved?.profile || null,
|
|
971
1279
|
selectedBy: agent.executorResolved?.selectedBy || null,
|
|
1280
|
+
retryPolicy: agent.executorResolved?.retryPolicy || null,
|
|
1281
|
+
allowFallbackOnRetry: agent.executorResolved?.allowFallbackOnRetry !== false,
|
|
972
1282
|
fallbacks: agent.executorResolved?.fallbacks || [],
|
|
973
1283
|
fallbackUsed: agent.executorResolved?.fallbackUsed === true,
|
|
974
1284
|
fallbackReason: agent.executorResolved?.fallbackReason || null,
|
|
@@ -983,6 +1293,17 @@ function writeWaveDerivedState({
|
|
|
983
1293
|
runtimeAssignments,
|
|
984
1294
|
});
|
|
985
1295
|
writeDocsQueue(waveDocsQueuePath(lanePaths, wave.wave), docsQueue);
|
|
1296
|
+
const securitySummary = buildWaveSecuritySummary({
|
|
1297
|
+
lanePaths,
|
|
1298
|
+
wave,
|
|
1299
|
+
attempt,
|
|
1300
|
+
summariesByAgentId,
|
|
1301
|
+
});
|
|
1302
|
+
writeJsonArtifact(waveSecurityPath(lanePaths, wave.wave), securitySummary);
|
|
1303
|
+
writeTextAtomic(
|
|
1304
|
+
waveSecurityMarkdownPath(lanePaths, wave.wave),
|
|
1305
|
+
`${renderWaveSecuritySummaryMarkdown(securitySummary)}\n`,
|
|
1306
|
+
);
|
|
986
1307
|
const integrationSummary = buildWaveIntegrationSummary({
|
|
987
1308
|
lanePaths,
|
|
988
1309
|
wave,
|
|
@@ -994,6 +1315,7 @@ function writeWaveDerivedState({
|
|
|
994
1315
|
agentRuns,
|
|
995
1316
|
capabilityAssignments,
|
|
996
1317
|
dependencySnapshot,
|
|
1318
|
+
securitySummary,
|
|
997
1319
|
});
|
|
998
1320
|
writeJsonArtifact(waveIntegrationPath(lanePaths, wave.wave), integrationSummary);
|
|
999
1321
|
writeTextAtomic(
|
|
@@ -1008,9 +1330,11 @@ function writeWaveDerivedState({
|
|
|
1008
1330
|
integrationSummary,
|
|
1009
1331
|
docsQueue,
|
|
1010
1332
|
attempt,
|
|
1011
|
-
|
|
1333
|
+
contQaAgentId: lanePaths.contQaAgentId,
|
|
1334
|
+
contEvalAgentId: lanePaths.contEvalAgentId,
|
|
1012
1335
|
integrationAgentId: lanePaths.integrationAgentId,
|
|
1013
1336
|
documentationAgentId: lanePaths.documentationAgentId,
|
|
1337
|
+
benchmarkCatalogPath: lanePaths.laneProfile?.paths?.benchmarkCatalogPath,
|
|
1014
1338
|
capabilityAssignments,
|
|
1015
1339
|
dependencySnapshot,
|
|
1016
1340
|
});
|
|
@@ -1067,8 +1391,10 @@ function writeWaveDerivedState({
|
|
|
1067
1391
|
docsQueue,
|
|
1068
1392
|
capabilityAssignments,
|
|
1069
1393
|
dependencySnapshot,
|
|
1394
|
+
securitySummary,
|
|
1070
1395
|
integrationSummary,
|
|
1071
1396
|
integrationMarkdownPath: waveIntegrationMarkdownPath(lanePaths, wave.wave),
|
|
1397
|
+
securityMarkdownPath: waveSecurityMarkdownPath(lanePaths, wave.wave),
|
|
1072
1398
|
ledger,
|
|
1073
1399
|
sharedSummaryPath,
|
|
1074
1400
|
sharedSummaryText: sharedSummary.text,
|
|
@@ -1090,14 +1416,19 @@ function applyDerivedStateToDashboard(dashboardState, derivedState) {
|
|
|
1090
1416
|
}
|
|
1091
1417
|
|
|
1092
1418
|
export function readWaveImplementationGate(wave, agentRuns) {
|
|
1093
|
-
const
|
|
1419
|
+
const contQaAgentId = wave.contQaAgentId || "A0";
|
|
1420
|
+
const contEvalAgentId = wave.contEvalAgentId || "E0";
|
|
1094
1421
|
const integrationAgentId = wave.integrationAgentId || "A8";
|
|
1095
1422
|
const documentationAgentId = wave.documentationAgentId || "A9";
|
|
1096
1423
|
for (const runInfo of agentRuns) {
|
|
1097
|
-
if (
|
|
1424
|
+
if (
|
|
1425
|
+
[contQaAgentId, integrationAgentId, documentationAgentId].includes(runInfo.agent.agentId) ||
|
|
1426
|
+
isContEvalReportOnlyAgent(runInfo.agent, { contEvalAgentId }) ||
|
|
1427
|
+
isSecurityReviewAgent(runInfo.agent)
|
|
1428
|
+
) {
|
|
1098
1429
|
continue;
|
|
1099
1430
|
}
|
|
1100
|
-
const summary = readRunExecutionSummary(runInfo);
|
|
1431
|
+
const summary = readRunExecutionSummary(runInfo, wave);
|
|
1101
1432
|
const validation = validateImplementationSummary(runInfo.agent, summary);
|
|
1102
1433
|
if (!validation.ok) {
|
|
1103
1434
|
return {
|
|
@@ -1120,7 +1451,7 @@ export function readWaveImplementationGate(wave, agentRuns) {
|
|
|
1120
1451
|
|
|
1121
1452
|
export function readWaveComponentGate(wave, agentRuns, options = {}) {
|
|
1122
1453
|
const summariesByAgentId = Object.fromEntries(
|
|
1123
|
-
agentRuns.map((runInfo) => [runInfo.agent.agentId, readRunExecutionSummary(runInfo)]),
|
|
1454
|
+
agentRuns.map((runInfo) => [runInfo.agent.agentId, readRunExecutionSummary(runInfo, wave)]),
|
|
1124
1455
|
);
|
|
1125
1456
|
const validation = validateWaveComponentPromotions(wave, summariesByAgentId, options);
|
|
1126
1457
|
if (validation.ok) {
|
|
@@ -1184,7 +1515,7 @@ export function readWaveDocumentationGate(wave, agentRuns) {
|
|
|
1184
1515
|
logPath: null,
|
|
1185
1516
|
};
|
|
1186
1517
|
}
|
|
1187
|
-
const summary = readRunExecutionSummary(docRun);
|
|
1518
|
+
const summary = readRunExecutionSummary(docRun, wave);
|
|
1188
1519
|
const validation = validateDocumentationClosureSummary(docRun.agent, summary);
|
|
1189
1520
|
return {
|
|
1190
1521
|
ok: validation.ok,
|
|
@@ -1195,6 +1526,52 @@ export function readWaveDocumentationGate(wave, agentRuns) {
|
|
|
1195
1526
|
};
|
|
1196
1527
|
}
|
|
1197
1528
|
|
|
1529
|
+
export function readWaveSecurityGate(wave, agentRuns) {
|
|
1530
|
+
const securityRuns = (agentRuns || []).filter((run) => isSecurityReviewAgent(run.agent));
|
|
1531
|
+
if (securityRuns.length === 0) {
|
|
1532
|
+
return {
|
|
1533
|
+
ok: true,
|
|
1534
|
+
agentId: null,
|
|
1535
|
+
statusCode: "pass",
|
|
1536
|
+
detail: "No security reviewer declared for this wave.",
|
|
1537
|
+
logPath: null,
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
const concernAgentIds = [];
|
|
1541
|
+
for (const runInfo of securityRuns) {
|
|
1542
|
+
const summary = readRunExecutionSummary(runInfo, wave);
|
|
1543
|
+
const validation = validateSecuritySummary(runInfo.agent, summary);
|
|
1544
|
+
if (!validation.ok) {
|
|
1545
|
+
return {
|
|
1546
|
+
ok: false,
|
|
1547
|
+
agentId: runInfo.agent.agentId,
|
|
1548
|
+
statusCode: validation.statusCode,
|
|
1549
|
+
detail: validation.detail,
|
|
1550
|
+
logPath: summary?.logPath || path.relative(REPO_ROOT, runInfo.logPath),
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
if (summary?.security?.state === "concerns") {
|
|
1554
|
+
concernAgentIds.push(runInfo.agent.agentId);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
if (concernAgentIds.length > 0) {
|
|
1558
|
+
return {
|
|
1559
|
+
ok: true,
|
|
1560
|
+
agentId: null,
|
|
1561
|
+
statusCode: "security-concerns",
|
|
1562
|
+
detail: `Security review reported advisory concerns (${concernAgentIds.join(", ")}).`,
|
|
1563
|
+
logPath: null,
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
return {
|
|
1567
|
+
ok: true,
|
|
1568
|
+
agentId: null,
|
|
1569
|
+
statusCode: "pass",
|
|
1570
|
+
detail: "Security review is clear.",
|
|
1571
|
+
logPath: null,
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1198
1575
|
export function readWaveIntegrationGate(wave, agentRuns, options = {}) {
|
|
1199
1576
|
const integrationAgentId =
|
|
1200
1577
|
options.integrationAgentId || wave.integrationAgentId || "A8";
|
|
@@ -1216,7 +1593,7 @@ export function readWaveIntegrationGate(wave, agentRuns, options = {}) {
|
|
|
1216
1593
|
logPath: null,
|
|
1217
1594
|
};
|
|
1218
1595
|
}
|
|
1219
|
-
const summary = readRunExecutionSummary(integrationRun);
|
|
1596
|
+
const summary = readRunExecutionSummary(integrationRun, wave);
|
|
1220
1597
|
const validation = validateIntegrationSummary(integrationRun.agent, summary);
|
|
1221
1598
|
return {
|
|
1222
1599
|
ok: validation.ok,
|
|
@@ -1256,45 +1633,6 @@ export function readWaveIntegrationBarrier(wave, agentRuns, derivedState, option
|
|
|
1256
1633
|
return markerGate;
|
|
1257
1634
|
}
|
|
1258
1635
|
|
|
1259
|
-
function failureResultFromGate(gate, fallbackLogPath) {
|
|
1260
|
-
return {
|
|
1261
|
-
failures: [
|
|
1262
|
-
{
|
|
1263
|
-
agentId: gate.agentId,
|
|
1264
|
-
statusCode: gate.statusCode,
|
|
1265
|
-
logPath: gate.logPath || fallbackLogPath,
|
|
1266
|
-
detail: gate.detail,
|
|
1267
|
-
},
|
|
1268
|
-
],
|
|
1269
|
-
timedOut: false,
|
|
1270
|
-
};
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
function recordClosureGateFailure({
|
|
1274
|
-
wave,
|
|
1275
|
-
lanePaths,
|
|
1276
|
-
gate,
|
|
1277
|
-
label,
|
|
1278
|
-
recordCombinedEvent,
|
|
1279
|
-
appendCoordination,
|
|
1280
|
-
actionRequested,
|
|
1281
|
-
}) {
|
|
1282
|
-
recordCombinedEvent({
|
|
1283
|
-
level: "error",
|
|
1284
|
-
agentId: gate.agentId,
|
|
1285
|
-
message: `${label} blocked wave ${wave.wave}: ${gate.detail}`,
|
|
1286
|
-
});
|
|
1287
|
-
appendCoordination({
|
|
1288
|
-
event: "wave_gate_blocked",
|
|
1289
|
-
waves: [wave.wave],
|
|
1290
|
-
status: "blocked",
|
|
1291
|
-
details: `agent=${gate.agentId}; reason=${gate.statusCode}; ${gate.detail}`,
|
|
1292
|
-
actionRequested:
|
|
1293
|
-
actionRequested ||
|
|
1294
|
-
`Lane ${lanePaths.lane} owners should resolve the ${label.toLowerCase()} before wave progression.`,
|
|
1295
|
-
});
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
1636
|
export async function runClosureSweepPhase({
|
|
1299
1637
|
lanePaths,
|
|
1300
1638
|
wave,
|
|
@@ -1310,180 +1648,33 @@ export async function runClosureSweepPhase({
|
|
|
1310
1648
|
launchAgentSessionFn = launchAgentSession,
|
|
1311
1649
|
waitForWaveCompletionFn = waitForWaveCompletion,
|
|
1312
1650
|
}) {
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
return documentationGate;
|
|
1337
|
-
}
|
|
1338
|
-
return readWaveComponentMatrixGate(wave, closureRuns, {
|
|
1339
|
-
laneProfile: lanePaths.laneProfile,
|
|
1340
|
-
documentationAgentId: lanePaths.documentationAgentId,
|
|
1341
|
-
});
|
|
1342
|
-
},
|
|
1343
|
-
actionRequested:
|
|
1344
|
-
`Lane ${lanePaths.lane} owners should resolve the shared-plan or component-matrix closure state before evaluator progression.`,
|
|
1345
|
-
},
|
|
1346
|
-
{
|
|
1347
|
-
agentId: evaluatorAgentId,
|
|
1348
|
-
label: "Evaluator gate",
|
|
1349
|
-
runs: closureRuns.filter((run) => run.agent.agentId === evaluatorAgentId),
|
|
1350
|
-
validate: () => readWaveEvaluatorGate(wave, closureRuns),
|
|
1351
|
-
actionRequested:
|
|
1352
|
-
`Lane ${lanePaths.lane} owners should resolve the evaluator gate before wave progression.`,
|
|
1353
|
-
},
|
|
1354
|
-
];
|
|
1355
|
-
for (const stage of stagedRuns) {
|
|
1356
|
-
if (stage.runs.length === 0) {
|
|
1357
|
-
continue;
|
|
1358
|
-
}
|
|
1359
|
-
const runInfo = stage.runs[0];
|
|
1360
|
-
const existing = dashboardState.agents.find((entry) => entry.agentId === runInfo.agent.agentId);
|
|
1361
|
-
setWaveDashboardAgent(dashboardState, runInfo.agent.agentId, {
|
|
1362
|
-
state: "launching",
|
|
1363
|
-
attempts: (existing?.attempts || 0) + 1,
|
|
1364
|
-
startedAt: existing?.startedAt || toIsoTimestamp(),
|
|
1365
|
-
completedAt: null,
|
|
1366
|
-
exitCode: null,
|
|
1367
|
-
detail: "Launching closure sweep",
|
|
1368
|
-
});
|
|
1369
|
-
flushDashboards();
|
|
1370
|
-
const launchResult = await launchAgentSessionFn(lanePaths, {
|
|
1371
|
-
wave: wave.wave,
|
|
1372
|
-
waveDefinition: wave,
|
|
1373
|
-
agent: runInfo.agent,
|
|
1374
|
-
sessionName: runInfo.sessionName,
|
|
1375
|
-
promptPath: runInfo.promptPath,
|
|
1376
|
-
logPath: runInfo.logPath,
|
|
1377
|
-
statusPath: runInfo.statusPath,
|
|
1378
|
-
messageBoardPath: runInfo.messageBoardPath,
|
|
1379
|
-
messageBoardSnapshot: runInfo.messageBoardSnapshot || "",
|
|
1380
|
-
sharedSummaryPath: runInfo.sharedSummaryPath,
|
|
1381
|
-
sharedSummaryText: runInfo.sharedSummaryText,
|
|
1382
|
-
inboxPath: runInfo.inboxPath,
|
|
1383
|
-
inboxText: runInfo.inboxText,
|
|
1384
|
-
orchestratorId: options.orchestratorId,
|
|
1385
|
-
executorMode: options.executorMode,
|
|
1386
|
-
codexSandboxMode: options.codexSandboxMode,
|
|
1387
|
-
agentRateLimitRetries: options.agentRateLimitRetries,
|
|
1388
|
-
agentRateLimitBaseDelaySeconds: options.agentRateLimitBaseDelaySeconds,
|
|
1389
|
-
agentRateLimitMaxDelaySeconds: options.agentRateLimitMaxDelaySeconds,
|
|
1390
|
-
context7Enabled: options.context7Enabled,
|
|
1391
|
-
});
|
|
1392
|
-
runInfo.lastLaunchAttempt = dashboardState?.attempt || null;
|
|
1393
|
-
runInfo.lastPromptHash = launchResult?.promptHash || null;
|
|
1394
|
-
runInfo.lastContext7 = launchResult?.context7 || null;
|
|
1395
|
-
runInfo.lastExecutorId = launchResult?.executorId || runInfo.agent.executorResolved?.id || null;
|
|
1396
|
-
runInfo.lastSkillProjection = launchResult?.skills || summarizeResolvedSkills(runInfo.agent.skillsResolved);
|
|
1397
|
-
setWaveDashboardAgent(dashboardState, runInfo.agent.agentId, {
|
|
1398
|
-
state: "running",
|
|
1399
|
-
detail: `Closure sweep launched${launchResult?.context7?.mode ? ` (${launchResult.context7.mode})` : ""}`,
|
|
1400
|
-
});
|
|
1401
|
-
recordCombinedEvent({
|
|
1402
|
-
agentId: runInfo.agent.agentId,
|
|
1403
|
-
message: `Closure sweep launched in tmux session ${runInfo.sessionName}`,
|
|
1404
|
-
});
|
|
1405
|
-
flushDashboards();
|
|
1406
|
-
const result = await waitForWaveCompletionFn(
|
|
1407
|
-
lanePaths,
|
|
1408
|
-
[runInfo],
|
|
1409
|
-
options.timeoutMinutes,
|
|
1410
|
-
({ pendingAgentIds }) => {
|
|
1411
|
-
refreshWaveDashboardAgentStates(dashboardState, [runInfo], pendingAgentIds, (event) =>
|
|
1412
|
-
recordCombinedEvent(event),
|
|
1413
|
-
);
|
|
1414
|
-
monitorWaveHumanFeedback({
|
|
1415
|
-
lanePaths,
|
|
1416
|
-
waveNumber: wave.wave,
|
|
1417
|
-
agentRuns: [runInfo],
|
|
1418
|
-
orchestratorId: options.orchestratorId,
|
|
1419
|
-
coordinationLogPath,
|
|
1420
|
-
feedbackStateByRequestId,
|
|
1421
|
-
recordCombinedEvent,
|
|
1422
|
-
appendCoordination,
|
|
1423
|
-
});
|
|
1424
|
-
updateWaveDashboardMessageBoard(dashboardState, runInfo.messageBoardPath);
|
|
1425
|
-
flushDashboards();
|
|
1426
|
-
},
|
|
1427
|
-
);
|
|
1428
|
-
materializeAgentExecutionSummaryForRun(wave, runInfo);
|
|
1429
|
-
refreshDerivedState?.(dashboardState?.attempt || 0);
|
|
1430
|
-
if (result.failures.length > 0) {
|
|
1431
|
-
return result;
|
|
1432
|
-
}
|
|
1433
|
-
const gate = stage.validate();
|
|
1434
|
-
if (!gate.ok) {
|
|
1435
|
-
recordClosureGateFailure({
|
|
1436
|
-
wave,
|
|
1437
|
-
lanePaths,
|
|
1438
|
-
gate,
|
|
1439
|
-
label: stage.label,
|
|
1440
|
-
recordCombinedEvent,
|
|
1441
|
-
appendCoordination,
|
|
1442
|
-
actionRequested: stage.actionRequested,
|
|
1443
|
-
});
|
|
1444
|
-
return failureResultFromGate(gate, path.relative(REPO_ROOT, runInfo.logPath));
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
return { failures: [], timedOut: false };
|
|
1651
|
+
return runClosureSweepPhaseImpl({
|
|
1652
|
+
lanePaths,
|
|
1653
|
+
wave,
|
|
1654
|
+
closureRuns,
|
|
1655
|
+
coordinationLogPath,
|
|
1656
|
+
refreshDerivedState,
|
|
1657
|
+
dashboardState,
|
|
1658
|
+
recordCombinedEvent,
|
|
1659
|
+
flushDashboards,
|
|
1660
|
+
options,
|
|
1661
|
+
feedbackStateByRequestId,
|
|
1662
|
+
appendCoordination,
|
|
1663
|
+
launchAgentSessionFn,
|
|
1664
|
+
waitForWaveCompletionFn,
|
|
1665
|
+
readWaveContEvalGateFn: readWaveContEvalGate,
|
|
1666
|
+
readWaveSecurityGateFn: readWaveSecurityGate,
|
|
1667
|
+
readWaveIntegrationBarrierFn: readWaveIntegrationBarrier,
|
|
1668
|
+
readWaveDocumentationGateFn: readWaveDocumentationGate,
|
|
1669
|
+
readWaveComponentMatrixGateFn: readWaveComponentMatrixGate,
|
|
1670
|
+
readWaveContQaGateFn: readWaveContQaGate,
|
|
1671
|
+
materializeAgentExecutionSummaryForRunFn: materializeAgentExecutionSummaryForRun,
|
|
1672
|
+
monitorWaveHumanFeedbackFn: monitorWaveHumanFeedback,
|
|
1673
|
+
});
|
|
1448
1674
|
}
|
|
1449
1675
|
|
|
1450
|
-
const NON_BLOCKING_INFRA_SIGNAL_STATES = new Set([
|
|
1451
|
-
"conformant",
|
|
1452
|
-
"setup-required",
|
|
1453
|
-
"setup-in-progress",
|
|
1454
|
-
"action-required",
|
|
1455
|
-
"action-approved",
|
|
1456
|
-
"action-complete",
|
|
1457
|
-
]);
|
|
1458
|
-
|
|
1459
1676
|
export function readWaveInfraGate(agentRuns) {
|
|
1460
|
-
|
|
1461
|
-
const signals = parseStructuredSignalsFromLog(run.logPath);
|
|
1462
|
-
if (!signals?.infra) {
|
|
1463
|
-
continue;
|
|
1464
|
-
}
|
|
1465
|
-
const infra = signals.infra;
|
|
1466
|
-
const normalizedState = String(infra.state || "")
|
|
1467
|
-
.trim()
|
|
1468
|
-
.toLowerCase();
|
|
1469
|
-
if (NON_BLOCKING_INFRA_SIGNAL_STATES.has(normalizedState)) {
|
|
1470
|
-
continue;
|
|
1471
|
-
}
|
|
1472
|
-
return {
|
|
1473
|
-
ok: false,
|
|
1474
|
-
agentId: run.agent.agentId,
|
|
1475
|
-
statusCode: `infra-${normalizedState || "blocked"}`,
|
|
1476
|
-
detail: `Infra signal ${infra.kind || "unknown"} on ${infra.target || "unknown"} ended in state ${normalizedState || "unknown"}${infra.detail ? ` (${infra.detail})` : ""}.`,
|
|
1477
|
-
logPath: path.relative(REPO_ROOT, run.logPath),
|
|
1478
|
-
};
|
|
1479
|
-
}
|
|
1480
|
-
return {
|
|
1481
|
-
ok: true,
|
|
1482
|
-
agentId: null,
|
|
1483
|
-
statusCode: "pass",
|
|
1484
|
-
detail: "",
|
|
1485
|
-
logPath: null,
|
|
1486
|
-
};
|
|
1677
|
+
return readWaveInfraGateImpl(agentRuns);
|
|
1487
1678
|
}
|
|
1488
1679
|
|
|
1489
1680
|
export function markLauncherFailed(
|
|
@@ -1739,23 +1930,9 @@ function cleanupLaneTmuxSessions(lanePaths, { excludeSessionNames = new Set() }
|
|
|
1739
1930
|
}
|
|
1740
1931
|
|
|
1741
1932
|
export function collectUnexpectedSessionFailures(lanePaths, agentRuns, pendingAgentIds) {
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
if (!pendingAgentIds.has(run.agent.agentId) || fs.existsSync(run.statusPath)) {
|
|
1746
|
-
continue;
|
|
1747
|
-
}
|
|
1748
|
-
if (activeSessionNames.has(run.sessionName)) {
|
|
1749
|
-
continue;
|
|
1750
|
-
}
|
|
1751
|
-
failures.push({
|
|
1752
|
-
agentId: run.agent.agentId,
|
|
1753
|
-
statusCode: "session-missing",
|
|
1754
|
-
logPath: path.relative(REPO_ROOT, run.logPath),
|
|
1755
|
-
detail: `tmux session ${run.sessionName} disappeared before ${path.relative(REPO_ROOT, run.statusPath)} was written.`,
|
|
1756
|
-
});
|
|
1757
|
-
}
|
|
1758
|
-
return failures;
|
|
1933
|
+
return collectUnexpectedSessionFailuresImpl(lanePaths, agentRuns, pendingAgentIds, {
|
|
1934
|
+
listLaneTmuxSessionNamesFn: listLaneTmuxSessionNames,
|
|
1935
|
+
});
|
|
1759
1936
|
}
|
|
1760
1937
|
|
|
1761
1938
|
function launchWaveDashboardSession(lanePaths, { sessionName, dashboardPath, messageBoardPath }) {
|
|
@@ -1777,281 +1954,14 @@ function launchWaveDashboardSession(lanePaths, { sessionName, dashboardPath, mes
|
|
|
1777
1954
|
);
|
|
1778
1955
|
}
|
|
1779
1956
|
|
|
1780
|
-
function refreshResolvedSkillsForRun(runInfo, waveDefinition, lanePaths) {
|
|
1781
|
-
runInfo.agent.skillsResolved = resolveAgentSkills(
|
|
1782
|
-
runInfo.agent,
|
|
1783
|
-
waveDefinition || { deployEnvironments: [] },
|
|
1784
|
-
{ laneProfile: lanePaths.laneProfile },
|
|
1785
|
-
);
|
|
1786
|
-
return runInfo.agent.skillsResolved;
|
|
1787
|
-
}
|
|
1788
|
-
|
|
1789
1957
|
async function launchAgentSession(lanePaths, params) {
|
|
1790
|
-
|
|
1791
|
-
wave,
|
|
1792
|
-
waveDefinition = null,
|
|
1793
|
-
agent,
|
|
1794
|
-
sessionName,
|
|
1795
|
-
promptPath,
|
|
1796
|
-
logPath,
|
|
1797
|
-
statusPath,
|
|
1798
|
-
messageBoardPath,
|
|
1799
|
-
messageBoardSnapshot,
|
|
1800
|
-
sharedSummaryPath,
|
|
1801
|
-
sharedSummaryText,
|
|
1802
|
-
inboxPath,
|
|
1803
|
-
inboxText,
|
|
1804
|
-
orchestratorId,
|
|
1805
|
-
agentRateLimitRetries,
|
|
1806
|
-
agentRateLimitBaseDelaySeconds,
|
|
1807
|
-
agentRateLimitMaxDelaySeconds,
|
|
1808
|
-
context7Enabled,
|
|
1809
|
-
dryRun = false,
|
|
1810
|
-
} = params;
|
|
1811
|
-
ensureDirectory(path.dirname(promptPath));
|
|
1812
|
-
ensureDirectory(path.dirname(logPath));
|
|
1813
|
-
ensureDirectory(path.dirname(statusPath));
|
|
1814
|
-
fs.rmSync(statusPath, { force: true });
|
|
1815
|
-
|
|
1816
|
-
const context7 = await prefetchContext7ForSelection(agent.context7Resolved, {
|
|
1817
|
-
cacheDir: lanePaths.context7CacheDir,
|
|
1818
|
-
disabled: !context7Enabled,
|
|
1819
|
-
});
|
|
1820
|
-
const overlayDir = path.join(lanePaths.executorOverlaysDir, `wave-${wave}`, agent.slug);
|
|
1821
|
-
ensureDirectory(overlayDir);
|
|
1822
|
-
const skillsResolved =
|
|
1823
|
-
agent.skillsResolved || resolveAgentSkills(agent, waveDefinition || { deployEnvironments: [] }, {
|
|
1824
|
-
laneProfile: lanePaths.laneProfile,
|
|
1825
|
-
});
|
|
1826
|
-
agent.skillsResolved = skillsResolved;
|
|
1827
|
-
const skillArtifacts = writeResolvedSkillArtifacts(overlayDir, skillsResolved);
|
|
1828
|
-
if (skillArtifacts) {
|
|
1829
|
-
agent.skillsResolved = {
|
|
1830
|
-
...skillsResolved,
|
|
1831
|
-
artifacts: skillArtifacts,
|
|
1832
|
-
};
|
|
1833
|
-
}
|
|
1834
|
-
const prompt = buildExecutionPrompt({
|
|
1835
|
-
lane: lanePaths.lane,
|
|
1836
|
-
wave,
|
|
1837
|
-
agent,
|
|
1838
|
-
orchestratorId,
|
|
1839
|
-
messageBoardPath,
|
|
1840
|
-
messageBoardSnapshot,
|
|
1841
|
-
sharedSummaryPath,
|
|
1842
|
-
sharedSummaryText,
|
|
1843
|
-
inboxPath,
|
|
1844
|
-
inboxText,
|
|
1845
|
-
context7,
|
|
1846
|
-
componentPromotions: wave.componentPromotions,
|
|
1847
|
-
sharedPlanDocs: lanePaths.sharedPlanDocs,
|
|
1848
|
-
evaluatorAgentId: lanePaths.evaluatorAgentId,
|
|
1849
|
-
integrationAgentId: lanePaths.integrationAgentId,
|
|
1850
|
-
documentationAgentId: lanePaths.documentationAgentId,
|
|
1851
|
-
});
|
|
1852
|
-
const promptHash = hashAgentPromptFingerprint(agent);
|
|
1853
|
-
fs.writeFileSync(promptPath, `${prompt}\n`, "utf8");
|
|
1854
|
-
const launchSpec = buildExecutorLaunchSpec({
|
|
1855
|
-
agent,
|
|
1856
|
-
promptPath,
|
|
1857
|
-
logPath,
|
|
1858
|
-
overlayDir,
|
|
1859
|
-
skillProjection: agent.skillsResolved,
|
|
1860
|
-
});
|
|
1861
|
-
const resolvedExecutorMode = launchSpec.executorId || agent.executorResolved?.id || "codex";
|
|
1862
|
-
if (dryRun) {
|
|
1863
|
-
writeJsonAtomic(path.join(overlayDir, "launch-preview.json"), {
|
|
1864
|
-
executorId: resolvedExecutorMode,
|
|
1865
|
-
command: launchSpec.command,
|
|
1866
|
-
env: launchSpec.env || {},
|
|
1867
|
-
useRateLimitRetries: launchSpec.useRateLimitRetries === true,
|
|
1868
|
-
invocationLines: launchSpec.invocationLines,
|
|
1869
|
-
skills: summarizeResolvedSkills(agent.skillsResolved),
|
|
1870
|
-
});
|
|
1871
|
-
return {
|
|
1872
|
-
promptHash,
|
|
1873
|
-
context7,
|
|
1874
|
-
executorId: resolvedExecutorMode,
|
|
1875
|
-
launchSpec,
|
|
1876
|
-
dryRun: true,
|
|
1877
|
-
skills: summarizeResolvedSkills(agent.skillsResolved),
|
|
1878
|
-
};
|
|
1879
|
-
}
|
|
1880
|
-
killTmuxSessionIfExists(lanePaths.tmuxSocketName, sessionName);
|
|
1881
|
-
|
|
1882
|
-
const executionLines = [];
|
|
1883
|
-
if (launchSpec.env) {
|
|
1884
|
-
for (const [key, value] of Object.entries(launchSpec.env)) {
|
|
1885
|
-
executionLines.push(`export ${key}=${shellQuote(value)}`);
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
if (!launchSpec.useRateLimitRetries) {
|
|
1889
|
-
executionLines.push(...launchSpec.invocationLines);
|
|
1890
|
-
executionLines.push("status=$?");
|
|
1891
|
-
} else {
|
|
1892
|
-
executionLines.push(`: > ${shellQuote(logPath)}`);
|
|
1893
|
-
executionLines.push(
|
|
1894
|
-
`max_rate_attempts=${Math.max(1, Number.parseInt(String(agentRateLimitRetries || 0), 10) + 1)}`,
|
|
1895
|
-
);
|
|
1896
|
-
executionLines.push(
|
|
1897
|
-
`rate_delay_base=${Math.max(1, Number.parseInt(String(agentRateLimitBaseDelaySeconds || DEFAULT_AGENT_RATE_LIMIT_BASE_DELAY_SECONDS), 10))}`,
|
|
1898
|
-
);
|
|
1899
|
-
executionLines.push(
|
|
1900
|
-
`rate_delay_max=${Math.max(1, Number.parseInt(String(agentRateLimitMaxDelaySeconds || DEFAULT_AGENT_RATE_LIMIT_MAX_DELAY_SECONDS), 10))}`,
|
|
1901
|
-
);
|
|
1902
|
-
executionLines.push("rate_attempt=1");
|
|
1903
|
-
executionLines.push("status=1");
|
|
1904
|
-
executionLines.push('while [ "$rate_attempt" -le "$max_rate_attempts" ]; do');
|
|
1905
|
-
for (const line of launchSpec.invocationLines) {
|
|
1906
|
-
executionLines.push(` ${line}`);
|
|
1907
|
-
}
|
|
1908
|
-
executionLines.push(" status=$?");
|
|
1909
|
-
executionLines.push(' if [ "$status" -eq 0 ]; then');
|
|
1910
|
-
executionLines.push(" break");
|
|
1911
|
-
executionLines.push(" fi");
|
|
1912
|
-
executionLines.push(' if [ "$rate_attempt" -ge "$max_rate_attempts" ]; then');
|
|
1913
|
-
executionLines.push(" break");
|
|
1914
|
-
executionLines.push(" fi");
|
|
1915
|
-
executionLines.push(
|
|
1916
|
-
` if tail -n 120 ${shellQuote(logPath)} | grep -Eqi '429 Too Many Requests|exceeded retry limit|last status: 429|rate limit'; then`,
|
|
1917
|
-
);
|
|
1918
|
-
executionLines.push(" sleep_seconds=$((rate_delay_base * (2 ** (rate_attempt - 1))))");
|
|
1919
|
-
executionLines.push(
|
|
1920
|
-
' if [ "$sleep_seconds" -gt "$rate_delay_max" ]; then sleep_seconds=$rate_delay_max; fi',
|
|
1921
|
-
);
|
|
1922
|
-
executionLines.push(" jitter=$((RANDOM % 5))");
|
|
1923
|
-
executionLines.push(" sleep_seconds=$((sleep_seconds + jitter))");
|
|
1924
|
-
executionLines.push(
|
|
1925
|
-
` echo "[${lanePaths.lane}-wave-launcher] rate-limit detected for ${agent.agentId}; retry \${rate_attempt}/\${max_rate_attempts} after \${sleep_seconds}s" | tee -a ${shellQuote(logPath)}`,
|
|
1926
|
-
);
|
|
1927
|
-
executionLines.push(' sleep "$sleep_seconds"');
|
|
1928
|
-
executionLines.push(" rate_attempt=$((rate_attempt + 1))");
|
|
1929
|
-
executionLines.push(" continue");
|
|
1930
|
-
executionLines.push(" fi");
|
|
1931
|
-
executionLines.push(" break");
|
|
1932
|
-
executionLines.push("done");
|
|
1933
|
-
}
|
|
1934
|
-
|
|
1935
|
-
const command = [
|
|
1936
|
-
`cd ${shellQuote(REPO_ROOT)}`,
|
|
1937
|
-
"set -o pipefail",
|
|
1938
|
-
`export WAVE_ORCHESTRATOR_ID=${shellQuote(orchestratorId || "")}`,
|
|
1939
|
-
`export WAVE_EXECUTOR_MODE=${shellQuote(resolvedExecutorMode)}`,
|
|
1940
|
-
...executionLines,
|
|
1941
|
-
`node -e ${shellQuote(
|
|
1942
|
-
"const fs=require('node:fs'); const statusPath=process.argv[1]; const payload={code:Number(process.argv[2]),promptHash:process.argv[3]||null,orchestratorId:process.argv[4]||null,completedAt:new Date().toISOString()}; fs.writeFileSync(statusPath, JSON.stringify(payload, null, 2)+'\\n', 'utf8');",
|
|
1943
|
-
)} ${shellQuote(statusPath)} "$status" ${shellQuote(promptHash)} ${shellQuote(orchestratorId || "")}`,
|
|
1944
|
-
`echo "[${lanePaths.lane}-wave-launcher] ${sessionName} finished with code $status"`,
|
|
1945
|
-
"exec bash -l",
|
|
1946
|
-
].join("\n");
|
|
1947
|
-
|
|
1948
|
-
runTmux(
|
|
1949
|
-
lanePaths,
|
|
1950
|
-
["new-session", "-d", "-s", sessionName, `bash -lc ${shellQuote(command)}`],
|
|
1951
|
-
`launch session ${sessionName}`,
|
|
1952
|
-
);
|
|
1953
|
-
return {
|
|
1954
|
-
promptHash,
|
|
1955
|
-
context7,
|
|
1956
|
-
executorId: resolvedExecutorMode,
|
|
1957
|
-
skills: summarizeResolvedSkills(agent.skillsResolved),
|
|
1958
|
-
};
|
|
1958
|
+
return launchAgentSessionImpl(lanePaths, params, { runTmuxFn: runTmux });
|
|
1959
1959
|
}
|
|
1960
1960
|
|
|
1961
1961
|
async function waitForWaveCompletion(lanePaths, agentRuns, timeoutMinutes, onProgress = null) {
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
const timeoutAtByAgentId = new Map(
|
|
1965
|
-
agentRuns.map((run) => {
|
|
1966
|
-
const budgetMinutes = Number(run.agent.executorResolved?.budget?.minutes || 0);
|
|
1967
|
-
const effectiveBudgetMs =
|
|
1968
|
-
Number.isFinite(budgetMinutes) && budgetMinutes > 0
|
|
1969
|
-
? Math.min(defaultTimeoutMs, budgetMinutes * 60 * 1000)
|
|
1970
|
-
: defaultTimeoutMs;
|
|
1971
|
-
return [run.agent.agentId, startedAt + effectiveBudgetMs];
|
|
1972
|
-
}),
|
|
1973
|
-
);
|
|
1974
|
-
const pending = new Set(agentRuns.map((run) => run.agent.agentId));
|
|
1975
|
-
const timedOutAgentIds = new Set();
|
|
1976
|
-
let sessionFailures = [];
|
|
1977
|
-
|
|
1978
|
-
const refreshPending = () => {
|
|
1979
|
-
for (const run of agentRuns) {
|
|
1980
|
-
if (pending.has(run.agent.agentId) && fs.existsSync(run.statusPath)) {
|
|
1981
|
-
pending.delete(run.agent.agentId);
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
};
|
|
1985
|
-
|
|
1986
|
-
await new Promise((resolve) => {
|
|
1987
|
-
const interval = setInterval(() => {
|
|
1988
|
-
refreshPending();
|
|
1989
|
-
onProgress?.({ pendingAgentIds: new Set(pending), timedOut: false });
|
|
1990
|
-
if (pending.size === 0) {
|
|
1991
|
-
clearInterval(interval);
|
|
1992
|
-
resolve();
|
|
1993
|
-
return;
|
|
1994
|
-
}
|
|
1995
|
-
sessionFailures = collectUnexpectedSessionFailures(lanePaths, agentRuns, pending);
|
|
1996
|
-
if (sessionFailures.length > 0) {
|
|
1997
|
-
onProgress?.({
|
|
1998
|
-
pendingAgentIds: new Set(pending),
|
|
1999
|
-
timedOut: false,
|
|
2000
|
-
failures: sessionFailures,
|
|
2001
|
-
});
|
|
2002
|
-
clearInterval(interval);
|
|
2003
|
-
resolve();
|
|
2004
|
-
return;
|
|
2005
|
-
}
|
|
2006
|
-
const now = Date.now();
|
|
2007
|
-
for (const run of agentRuns) {
|
|
2008
|
-
if (!pending.has(run.agent.agentId)) {
|
|
2009
|
-
continue;
|
|
2010
|
-
}
|
|
2011
|
-
const deadline = timeoutAtByAgentId.get(run.agent.agentId) || startedAt + defaultTimeoutMs;
|
|
2012
|
-
if (now <= deadline) {
|
|
2013
|
-
continue;
|
|
2014
|
-
}
|
|
2015
|
-
timedOutAgentIds.add(run.agent.agentId);
|
|
2016
|
-
pending.delete(run.agent.agentId);
|
|
2017
|
-
killTmuxSessionIfExists(lanePaths.tmuxSocketName, run.sessionName);
|
|
2018
|
-
}
|
|
2019
|
-
if (pending.size === 0) {
|
|
2020
|
-
clearInterval(interval);
|
|
2021
|
-
resolve();
|
|
2022
|
-
}
|
|
2023
|
-
}, DEFAULT_WAIT_PROGRESS_INTERVAL_MS);
|
|
2024
|
-
refreshPending();
|
|
2025
|
-
onProgress?.({ pendingAgentIds: new Set(pending), timedOut: false });
|
|
1962
|
+
return waitForWaveCompletionImpl(lanePaths, agentRuns, timeoutMinutes, onProgress, {
|
|
1963
|
+
collectUnexpectedSessionFailuresFn: collectUnexpectedSessionFailures,
|
|
2026
1964
|
});
|
|
2027
|
-
|
|
2028
|
-
if (sessionFailures.length > 0) {
|
|
2029
|
-
onProgress?.({ pendingAgentIds: new Set(), timedOut: false, failures: sessionFailures });
|
|
2030
|
-
return { failures: sessionFailures, timedOut: false };
|
|
2031
|
-
}
|
|
2032
|
-
|
|
2033
|
-
const failures = [];
|
|
2034
|
-
for (const run of agentRuns) {
|
|
2035
|
-
const code = readStatusCodeIfPresent(run.statusPath);
|
|
2036
|
-
if (code === 0) {
|
|
2037
|
-
continue;
|
|
2038
|
-
}
|
|
2039
|
-
if (code === null || timedOutAgentIds.has(run.agent.agentId)) {
|
|
2040
|
-
failures.push({
|
|
2041
|
-
agentId: run.agent.agentId,
|
|
2042
|
-
statusCode: timedOutAgentIds.has(run.agent.agentId) ? "timeout-no-status" : "missing-status",
|
|
2043
|
-
logPath: path.relative(REPO_ROOT, run.logPath),
|
|
2044
|
-
});
|
|
2045
|
-
continue;
|
|
2046
|
-
}
|
|
2047
|
-
failures.push({
|
|
2048
|
-
agentId: run.agent.agentId,
|
|
2049
|
-
statusCode: String(code),
|
|
2050
|
-
logPath: path.relative(REPO_ROOT, run.logPath),
|
|
2051
|
-
});
|
|
2052
|
-
}
|
|
2053
|
-
onProgress?.({ pendingAgentIds: new Set(), timedOut: timedOutAgentIds.size > 0 });
|
|
2054
|
-
return { failures, timedOut: timedOutAgentIds.size > 0 };
|
|
2055
1965
|
}
|
|
2056
1966
|
|
|
2057
1967
|
function monitorWaveHumanFeedback({
|
|
@@ -2192,24 +2102,107 @@ function monitorWaveHumanFeedback({
|
|
|
2192
2102
|
}
|
|
2193
2103
|
}
|
|
2194
2104
|
|
|
2195
|
-
|
|
2105
|
+
function proofCentricReuseBlocked(derivedState) {
|
|
2106
|
+
if (!derivedState) {
|
|
2107
|
+
return false;
|
|
2108
|
+
}
|
|
2109
|
+
return (
|
|
2110
|
+
readClarificationBarrier(derivedState).ok === false ||
|
|
2111
|
+
readWaveAssignmentBarrier(derivedState).ok === false ||
|
|
2112
|
+
readWaveDependencyBarrier(derivedState).ok === false
|
|
2113
|
+
);
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
function applyPersistedRelaunchPlan(agentRuns, persistedPlan, lanePaths, waveDefinition) {
|
|
2117
|
+
if (!persistedPlan || !Array.isArray(persistedPlan.selectedAgentIds)) {
|
|
2118
|
+
return [];
|
|
2119
|
+
}
|
|
2120
|
+
const runsByAgentId = new Map(agentRuns.map((run) => [run.agent.agentId, run]));
|
|
2121
|
+
for (const [agentId, executorState] of Object.entries(persistedPlan.executorStates || {})) {
|
|
2122
|
+
const run = runsByAgentId.get(agentId);
|
|
2123
|
+
if (!run || !executorState || typeof executorState !== "object") {
|
|
2124
|
+
continue;
|
|
2125
|
+
}
|
|
2126
|
+
run.agent.executorResolved = executorState;
|
|
2127
|
+
refreshResolvedSkillsForRun(run, waveDefinition, lanePaths);
|
|
2128
|
+
}
|
|
2129
|
+
return persistedPlan.selectedAgentIds
|
|
2130
|
+
.map((agentId) => runsByAgentId.get(agentId))
|
|
2131
|
+
.filter(Boolean);
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
function relaunchReasonBuckets(runs, failures, derivedState) {
|
|
2135
|
+
const selectedAgentIds = new Set((runs || []).map((run) => run.agent.agentId));
|
|
2136
|
+
return {
|
|
2137
|
+
clarification: openClarificationLinkedRequests(derivedState?.coordinationState)
|
|
2138
|
+
.flatMap((record) => record.targets || [])
|
|
2139
|
+
.some((target) => {
|
|
2140
|
+
const agentId = String(target || "").startsWith("agent:")
|
|
2141
|
+
? String(target).slice("agent:".length)
|
|
2142
|
+
: String(target || "");
|
|
2143
|
+
return selectedAgentIds.has(agentId);
|
|
2144
|
+
}),
|
|
2145
|
+
helperAssignment: (derivedState?.capabilityAssignments || []).some(
|
|
2146
|
+
(assignment) => assignment.blocking && selectedAgentIds.has(assignment.assignedAgentId),
|
|
2147
|
+
),
|
|
2148
|
+
dependency: ((derivedState?.dependencySnapshot?.openInbound || []).some((record) =>
|
|
2149
|
+
selectedAgentIds.has(record.assignedAgentId),
|
|
2150
|
+
)),
|
|
2151
|
+
blocker: (derivedState?.coordinationState?.blockers || []).some(
|
|
2152
|
+
(record) =>
|
|
2153
|
+
isOpenCoordinationStatus(record.status) &&
|
|
2154
|
+
(selectedAgentIds.has(record.agentId) ||
|
|
2155
|
+
(record.targets || []).some((target) => {
|
|
2156
|
+
const agentId = String(target || "").startsWith("agent:")
|
|
2157
|
+
? String(target).slice("agent:".length)
|
|
2158
|
+
: String(target || "");
|
|
2159
|
+
return selectedAgentIds.has(agentId);
|
|
2160
|
+
})),
|
|
2161
|
+
),
|
|
2162
|
+
closureGate: (failures || []).some(
|
|
2163
|
+
(failure) => failure.agentId && selectedAgentIds.has(failure.agentId),
|
|
2164
|
+
),
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
export function hasReusableSuccessStatus(agent, statusPath, options = {}) {
|
|
2196
2169
|
const statusRecord = readStatusRecordIfPresent(statusPath);
|
|
2197
|
-
|
|
2170
|
+
const basicReuseOk = Boolean(
|
|
2198
2171
|
statusRecord && statusRecord.code === 0 && statusRecord.promptHash === hashAgentPromptFingerprint(agent),
|
|
2199
2172
|
);
|
|
2173
|
+
if (!basicReuseOk) {
|
|
2174
|
+
return false;
|
|
2175
|
+
}
|
|
2176
|
+
const proofCentric =
|
|
2177
|
+
agentRequiresProofCentricValidation(agent) || waveRequiresProofCentricValidation(options.wave);
|
|
2178
|
+
if (!proofCentric) {
|
|
2179
|
+
return true;
|
|
2180
|
+
}
|
|
2181
|
+
const summary = readAgentExecutionSummary(statusPath);
|
|
2182
|
+
if (!summary) {
|
|
2183
|
+
return false;
|
|
2184
|
+
}
|
|
2185
|
+
if (!validateImplementationSummary(agent, summary).ok) {
|
|
2186
|
+
return false;
|
|
2187
|
+
}
|
|
2188
|
+
if (proofCentricReuseBlocked(options.derivedState)) {
|
|
2189
|
+
return false;
|
|
2190
|
+
}
|
|
2191
|
+
return true;
|
|
2200
2192
|
}
|
|
2201
2193
|
|
|
2202
|
-
function isClosureAgentId(
|
|
2194
|
+
function isClosureAgentId(agent, lanePaths) {
|
|
2203
2195
|
return [
|
|
2204
|
-
lanePaths.
|
|
2205
|
-
lanePaths.
|
|
2206
|
-
lanePaths.
|
|
2207
|
-
|
|
2196
|
+
lanePaths.contEvalAgentId || "E0",
|
|
2197
|
+
lanePaths.integrationAgentId || "A8",
|
|
2198
|
+
lanePaths.documentationAgentId || "A9",
|
|
2199
|
+
lanePaths.contQaAgentId || "A0",
|
|
2200
|
+
].includes(agent?.agentId) || isSecurityReviewAgent(agent);
|
|
2208
2201
|
}
|
|
2209
2202
|
|
|
2210
2203
|
export function selectInitialWaveRuns(agentRuns, lanePaths) {
|
|
2211
2204
|
const implementationRuns = (agentRuns || []).filter(
|
|
2212
|
-
(run) => !isClosureAgentId(run?.agent
|
|
2205
|
+
(run) => !isClosureAgentId(run?.agent, lanePaths),
|
|
2213
2206
|
);
|
|
2214
2207
|
return implementationRuns.length > 0 ? implementationRuns : agentRuns;
|
|
2215
2208
|
}
|
|
@@ -2244,6 +2237,12 @@ function nextExecutorModel(executorState, executorId) {
|
|
|
2244
2237
|
}
|
|
2245
2238
|
|
|
2246
2239
|
function executorFallbackChain(executorState) {
|
|
2240
|
+
if (
|
|
2241
|
+
executorState?.retryPolicy === "sticky" ||
|
|
2242
|
+
executorState?.allowFallbackOnRetry === false
|
|
2243
|
+
) {
|
|
2244
|
+
return [];
|
|
2245
|
+
}
|
|
2247
2246
|
return Array.isArray(executorState?.fallbacks)
|
|
2248
2247
|
? executorState.fallbacks.filter(Boolean)
|
|
2249
2248
|
: [];
|
|
@@ -2490,6 +2489,7 @@ export function buildGateSnapshot({
|
|
|
2490
2489
|
lanePaths,
|
|
2491
2490
|
componentMatrixPayload,
|
|
2492
2491
|
componentMatrixJsonPath,
|
|
2492
|
+
validationMode = "compat",
|
|
2493
2493
|
}) {
|
|
2494
2494
|
const implementationGate = readWaveImplementationGate(wave, agentRuns);
|
|
2495
2495
|
const componentGate = readWaveComponentGate(wave, agentRuns, {
|
|
@@ -2510,8 +2510,16 @@ export function buildGateSnapshot({
|
|
|
2510
2510
|
componentMatrixPayload,
|
|
2511
2511
|
componentMatrixJsonPath,
|
|
2512
2512
|
});
|
|
2513
|
-
const
|
|
2514
|
-
|
|
2513
|
+
const contEvalGate = readWaveContEvalGate(wave, agentRuns, {
|
|
2514
|
+
contEvalAgentId: lanePaths?.contEvalAgentId,
|
|
2515
|
+
mode: validationMode,
|
|
2516
|
+
evalTargets: wave.evalTargets,
|
|
2517
|
+
benchmarkCatalogPath: lanePaths?.laneProfile?.paths?.benchmarkCatalogPath,
|
|
2518
|
+
});
|
|
2519
|
+
const securityGate = readWaveSecurityGate(wave, agentRuns);
|
|
2520
|
+
const contQaGate = readWaveContQaGate(wave, agentRuns, {
|
|
2521
|
+
contQaAgentId: lanePaths?.contQaAgentId,
|
|
2522
|
+
mode: validationMode,
|
|
2515
2523
|
});
|
|
2516
2524
|
const infraGate = readWaveInfraGate(agentRuns);
|
|
2517
2525
|
const clarificationBarrier = readClarificationBarrier(derivedState);
|
|
@@ -2522,10 +2530,12 @@ export function buildGateSnapshot({
|
|
|
2522
2530
|
["componentGate", componentGate],
|
|
2523
2531
|
["helperAssignmentBarrier", helperAssignmentBarrier],
|
|
2524
2532
|
["dependencyBarrier", dependencyBarrier],
|
|
2533
|
+
["contEvalGate", contEvalGate],
|
|
2534
|
+
["securityGate", securityGate],
|
|
2525
2535
|
["integrationBarrier", integrationBarrier],
|
|
2526
2536
|
["documentationGate", documentationGate],
|
|
2527
2537
|
["componentMatrixGate", componentMatrixGate],
|
|
2528
|
-
["
|
|
2538
|
+
["contQaGate", contQaGate],
|
|
2529
2539
|
["infraGate", infraGate],
|
|
2530
2540
|
["clarificationBarrier", clarificationBarrier],
|
|
2531
2541
|
];
|
|
@@ -2537,7 +2547,9 @@ export function buildGateSnapshot({
|
|
|
2537
2547
|
integrationBarrier,
|
|
2538
2548
|
documentationGate,
|
|
2539
2549
|
componentMatrixGate,
|
|
2540
|
-
|
|
2550
|
+
contEvalGate,
|
|
2551
|
+
securityGate,
|
|
2552
|
+
contQaGate,
|
|
2541
2553
|
infraGate,
|
|
2542
2554
|
clarificationBarrier,
|
|
2543
2555
|
helperAssignmentBarrier,
|
|
@@ -2697,9 +2709,21 @@ export function resolveRelaunchRuns(agentRuns, failures, derivedState, lanePaths
|
|
|
2697
2709
|
barrier: null,
|
|
2698
2710
|
};
|
|
2699
2711
|
}
|
|
2700
|
-
if (derivedState?.ledger?.phase === "
|
|
2712
|
+
if (derivedState?.ledger?.phase === "security-review") {
|
|
2713
|
+
return {
|
|
2714
|
+
runs: agentRuns.filter((run) => isSecurityReviewAgent(run.agent)),
|
|
2715
|
+
barrier: null,
|
|
2716
|
+
};
|
|
2717
|
+
}
|
|
2718
|
+
if (derivedState?.ledger?.phase === "cont-eval") {
|
|
2701
2719
|
return {
|
|
2702
|
-
runs: [runsByAgentId.get(lanePaths.
|
|
2720
|
+
runs: [runsByAgentId.get(lanePaths.contEvalAgentId)].filter(Boolean),
|
|
2721
|
+
barrier: null,
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
if (derivedState?.ledger?.phase === "cont-qa-closure") {
|
|
2725
|
+
return {
|
|
2726
|
+
runs: [runsByAgentId.get(lanePaths.contQaAgentId)].filter(Boolean),
|
|
2703
2727
|
barrier: null,
|
|
2704
2728
|
};
|
|
2705
2729
|
}
|
|
@@ -2786,6 +2810,7 @@ export async function runLauncherCli(argv) {
|
|
|
2786
2810
|
ensureDirectory(lanePaths.inboxesDir);
|
|
2787
2811
|
ensureDirectory(lanePaths.ledgerDir);
|
|
2788
2812
|
ensureDirectory(lanePaths.integrationDir);
|
|
2813
|
+
ensureDirectory(lanePaths.securityDir);
|
|
2789
2814
|
ensureDirectory(lanePaths.dependencySnapshotsDir);
|
|
2790
2815
|
ensureDirectory(lanePaths.docsQueueDir);
|
|
2791
2816
|
ensureDirectory(lanePaths.tracesDir);
|
|
@@ -2837,7 +2862,8 @@ export async function runLauncherCli(argv) {
|
|
|
2837
2862
|
lane: lanePaths.lane,
|
|
2838
2863
|
bundleIndex: context7BundleIndex,
|
|
2839
2864
|
}),
|
|
2840
|
-
|
|
2865
|
+
contQaAgentId: lanePaths.contQaAgentId,
|
|
2866
|
+
contEvalAgentId: lanePaths.contEvalAgentId,
|
|
2841
2867
|
integrationAgentId: lanePaths.integrationAgentId,
|
|
2842
2868
|
documentationAgentId: lanePaths.documentationAgentId,
|
|
2843
2869
|
}),
|
|
@@ -2850,7 +2876,8 @@ export async function runLauncherCli(argv) {
|
|
|
2850
2876
|
{
|
|
2851
2877
|
logsDir: lanePaths.logsDir,
|
|
2852
2878
|
coordinationDir: lanePaths.coordinationDir,
|
|
2853
|
-
|
|
2879
|
+
contQaAgentId: lanePaths.contQaAgentId,
|
|
2880
|
+
contEvalAgentId: lanePaths.contEvalAgentId,
|
|
2854
2881
|
integrationAgentId: lanePaths.integrationAgentId,
|
|
2855
2882
|
documentationAgentId: lanePaths.documentationAgentId,
|
|
2856
2883
|
requireExitContractsFromWave: lanePaths.requireExitContractsFromWave,
|
|
@@ -2945,7 +2972,7 @@ export async function runLauncherCli(argv) {
|
|
|
2945
2972
|
event: "launcher_start",
|
|
2946
2973
|
waves: selectedWavesForCoordination,
|
|
2947
2974
|
status: options.dryRun ? "dry-run" : "running",
|
|
2948
|
-
details: `pid=${process.pid}; range=${filteredWaves[0]?.wave ?? "?"}..${filteredWaves.at(-1)?.wave ?? "?"}; timeout_minutes=${options.timeoutMinutes}; retries=${options.maxRetriesPerWave}; ${options.coordinationNote ? `note=${options.coordinationNote}` : "note=n/a"}`,
|
|
2975
|
+
details: `pid=${process.pid}; run_kind=${lanePaths.runKind}; run_id=${lanePaths.runId || "none"}; range=${filteredWaves[0]?.wave ?? "?"}..${filteredWaves.at(-1)?.wave ?? "?"}; timeout_minutes=${options.timeoutMinutes}; retries=${options.maxRetriesPerWave}; ${options.coordinationNote ? `note=${options.coordinationNote}` : "note=n/a"}`,
|
|
2949
2976
|
});
|
|
2950
2977
|
|
|
2951
2978
|
if (options.dryRun) {
|
|
@@ -3159,7 +3186,7 @@ export async function runLauncherCli(argv) {
|
|
|
3159
3186
|
const refreshDerivedState = (attemptNumber = 0) => {
|
|
3160
3187
|
const summariesByAgentId = Object.fromEntries(
|
|
3161
3188
|
agentRuns
|
|
3162
|
-
.map((run) => [run.agent.agentId, readRunExecutionSummary(run)])
|
|
3189
|
+
.map((run) => [run.agent.agentId, readRunExecutionSummary(run, wave)])
|
|
3163
3190
|
.filter(([, summary]) => summary),
|
|
3164
3191
|
);
|
|
3165
3192
|
const feedbackRequests = readWaveHumanFeedbackRequests({
|
|
@@ -3190,6 +3217,7 @@ export async function runLauncherCli(argv) {
|
|
|
3190
3217
|
};
|
|
3191
3218
|
|
|
3192
3219
|
refreshDerivedState(0);
|
|
3220
|
+
const persistedRelaunchPlan = readWaveRelaunchPlan(lanePaths, wave.wave);
|
|
3193
3221
|
|
|
3194
3222
|
dashboardState = buildWaveDashboardState({
|
|
3195
3223
|
lane: lanePaths.lane,
|
|
@@ -3207,7 +3235,10 @@ export async function runLauncherCli(argv) {
|
|
|
3207
3235
|
.filter(
|
|
3208
3236
|
(run) =>
|
|
3209
3237
|
!isClosureAgentId(run.agent.agentId, lanePaths) &&
|
|
3210
|
-
hasReusableSuccessStatus(run.agent, run.statusPath
|
|
3238
|
+
hasReusableSuccessStatus(run.agent, run.statusPath, {
|
|
3239
|
+
wave,
|
|
3240
|
+
derivedState,
|
|
3241
|
+
}),
|
|
3211
3242
|
)
|
|
3212
3243
|
.map((run) => run.agent.agentId),
|
|
3213
3244
|
);
|
|
@@ -3245,12 +3276,19 @@ export async function runLauncherCli(argv) {
|
|
|
3245
3276
|
});
|
|
3246
3277
|
}
|
|
3247
3278
|
|
|
3248
|
-
|
|
3249
|
-
|
|
3279
|
+
const availableRuns = agentRuns.filter((run) => !preCompletedAgentIds.has(run.agent.agentId));
|
|
3280
|
+
const persistedRuns = applyPersistedRelaunchPlan(
|
|
3281
|
+
availableRuns,
|
|
3282
|
+
persistedRelaunchPlan,
|
|
3250
3283
|
lanePaths,
|
|
3284
|
+
wave,
|
|
3251
3285
|
);
|
|
3286
|
+
let runsToLaunch =
|
|
3287
|
+
persistedRuns.length > 0 ? persistedRuns : selectInitialWaveRuns(availableRuns, lanePaths);
|
|
3252
3288
|
let attempt = 1;
|
|
3253
3289
|
const feedbackStateByRequestId = new Map();
|
|
3290
|
+
let completionGateSnapshot = null;
|
|
3291
|
+
let completionTraceDir = null;
|
|
3254
3292
|
|
|
3255
3293
|
while (attempt <= options.maxRetriesPerWave + 1) {
|
|
3256
3294
|
refreshDerivedState(attempt - 1);
|
|
@@ -3264,19 +3302,21 @@ export async function runLauncherCli(argv) {
|
|
|
3264
3302
|
const launchedImplementationRuns = runsToLaunch.filter(
|
|
3265
3303
|
(run) =>
|
|
3266
3304
|
![
|
|
3267
|
-
lanePaths.
|
|
3305
|
+
lanePaths.contEvalAgentId,
|
|
3306
|
+
lanePaths.contQaAgentId,
|
|
3268
3307
|
lanePaths.integrationAgentId,
|
|
3269
3308
|
lanePaths.documentationAgentId,
|
|
3270
3309
|
].includes(
|
|
3271
3310
|
run.agent.agentId,
|
|
3272
3311
|
),
|
|
3273
3312
|
);
|
|
3274
|
-
const
|
|
3313
|
+
const closureOnlyRetry =
|
|
3275
3314
|
runsToLaunch.length > 0 &&
|
|
3276
3315
|
launchedImplementationRuns.length === 0 &&
|
|
3277
3316
|
runsToLaunch.every((run) =>
|
|
3278
3317
|
[
|
|
3279
|
-
lanePaths.
|
|
3318
|
+
lanePaths.contEvalAgentId,
|
|
3319
|
+
lanePaths.contQaAgentId,
|
|
3280
3320
|
lanePaths.integrationAgentId,
|
|
3281
3321
|
lanePaths.documentationAgentId,
|
|
3282
3322
|
].includes(
|
|
@@ -3286,7 +3326,7 @@ export async function runLauncherCli(argv) {
|
|
|
3286
3326
|
|
|
3287
3327
|
let failures = [];
|
|
3288
3328
|
let timedOut = false;
|
|
3289
|
-
if (
|
|
3329
|
+
if (closureOnlyRetry) {
|
|
3290
3330
|
const closureResult = await runClosureSweepPhase({
|
|
3291
3331
|
lanePaths,
|
|
3292
3332
|
wave,
|
|
@@ -3515,7 +3555,8 @@ export async function runLauncherCli(argv) {
|
|
|
3515
3555
|
wave,
|
|
3516
3556
|
closureRuns: agentRuns.filter((run) =>
|
|
3517
3557
|
[
|
|
3518
|
-
lanePaths.
|
|
3558
|
+
lanePaths.contEvalAgentId,
|
|
3559
|
+
lanePaths.contQaAgentId,
|
|
3519
3560
|
lanePaths.integrationAgentId,
|
|
3520
3561
|
lanePaths.documentationAgentId,
|
|
3521
3562
|
].includes(
|
|
@@ -3594,6 +3635,36 @@ export async function runLauncherCli(argv) {
|
|
|
3594
3635
|
}
|
|
3595
3636
|
}
|
|
3596
3637
|
|
|
3638
|
+
if (failures.length === 0) {
|
|
3639
|
+
const contEvalGate = readWaveContEvalGate(wave, agentRuns, {
|
|
3640
|
+
contEvalAgentId: lanePaths.contEvalAgentId,
|
|
3641
|
+
mode: "live",
|
|
3642
|
+
evalTargets: wave.evalTargets,
|
|
3643
|
+
benchmarkCatalogPath: lanePaths.laneProfile?.paths?.benchmarkCatalogPath,
|
|
3644
|
+
});
|
|
3645
|
+
if (!contEvalGate.ok) {
|
|
3646
|
+
failures = [
|
|
3647
|
+
{
|
|
3648
|
+
agentId: contEvalGate.agentId,
|
|
3649
|
+
statusCode: contEvalGate.statusCode,
|
|
3650
|
+
logPath: contEvalGate.logPath || path.relative(REPO_ROOT, messageBoardPath),
|
|
3651
|
+
},
|
|
3652
|
+
];
|
|
3653
|
+
recordCombinedEvent({
|
|
3654
|
+
level: "error",
|
|
3655
|
+
agentId: contEvalGate.agentId,
|
|
3656
|
+
message: `cont-EVAL blocked wave ${wave.wave}: ${contEvalGate.detail}`,
|
|
3657
|
+
});
|
|
3658
|
+
appendCoordination({
|
|
3659
|
+
event: "wave_gate_blocked",
|
|
3660
|
+
waves: [wave.wave],
|
|
3661
|
+
status: "blocked",
|
|
3662
|
+
details: `agent=${contEvalGate.agentId}; reason=${contEvalGate.statusCode}; ${contEvalGate.detail}`,
|
|
3663
|
+
actionRequested: `Lane ${lanePaths.lane} owners should resolve cont-EVAL tuning gaps before integration closure.`,
|
|
3664
|
+
});
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
|
|
3597
3668
|
if (failures.length === 0) {
|
|
3598
3669
|
const integrationGate = readWaveIntegrationGate(wave, agentRuns, {
|
|
3599
3670
|
integrationAgentId: lanePaths.integrationAgentId,
|
|
@@ -3617,7 +3688,7 @@ export async function runLauncherCli(argv) {
|
|
|
3617
3688
|
waves: [wave.wave],
|
|
3618
3689
|
status: "blocked",
|
|
3619
3690
|
details: `agent=${integrationGate.agentId}; reason=${integrationGate.statusCode}; ${integrationGate.detail}`,
|
|
3620
|
-
actionRequested: `Lane ${lanePaths.lane} owners should resolve integration contradictions or blockers before
|
|
3691
|
+
actionRequested: `Lane ${lanePaths.lane} owners should resolve integration contradictions or blockers before documentation and cont-QA closure.`,
|
|
3621
3692
|
});
|
|
3622
3693
|
}
|
|
3623
3694
|
}
|
|
@@ -3677,38 +3748,38 @@ export async function runLauncherCli(argv) {
|
|
|
3677
3748
|
}
|
|
3678
3749
|
|
|
3679
3750
|
if (failures.length === 0) {
|
|
3680
|
-
const
|
|
3681
|
-
if (!
|
|
3751
|
+
const contQaGate = readWaveContQaGate(wave, agentRuns, { mode: "live" });
|
|
3752
|
+
if (!contQaGate.ok) {
|
|
3682
3753
|
failures = [
|
|
3683
3754
|
{
|
|
3684
|
-
agentId:
|
|
3685
|
-
statusCode:
|
|
3686
|
-
logPath:
|
|
3755
|
+
agentId: contQaGate.agentId,
|
|
3756
|
+
statusCode: contQaGate.statusCode,
|
|
3757
|
+
logPath: contQaGate.logPath || path.relative(REPO_ROOT, messageBoardPath),
|
|
3687
3758
|
},
|
|
3688
3759
|
];
|
|
3689
3760
|
recordCombinedEvent({
|
|
3690
3761
|
level: "error",
|
|
3691
|
-
agentId:
|
|
3692
|
-
message: `
|
|
3762
|
+
agentId: contQaGate.agentId,
|
|
3763
|
+
message: `cont-QA gate blocked wave ${wave.wave}: ${contQaGate.detail}`,
|
|
3693
3764
|
});
|
|
3694
3765
|
appendCoordination({
|
|
3695
3766
|
event: "wave_gate_blocked",
|
|
3696
3767
|
waves: [wave.wave],
|
|
3697
3768
|
status: "blocked",
|
|
3698
|
-
details: `agent=${
|
|
3699
|
-
actionRequested: `Lane ${lanePaths.lane} owners should resolve the
|
|
3769
|
+
details: `agent=${contQaGate.agentId}; reason=${contQaGate.statusCode}; ${contQaGate.detail}`,
|
|
3770
|
+
actionRequested: `Lane ${lanePaths.lane} owners should resolve the cont-QA gate before wave progression.`,
|
|
3700
3771
|
});
|
|
3701
3772
|
} else {
|
|
3702
|
-
setWaveDashboardAgent(dashboardState,
|
|
3703
|
-
detail:
|
|
3704
|
-
? `Exit 0;
|
|
3705
|
-
: "Exit 0;
|
|
3773
|
+
setWaveDashboardAgent(dashboardState, contQaGate.agentId, {
|
|
3774
|
+
detail: contQaGate.detail
|
|
3775
|
+
? `Exit 0; cont-QA PASS (${contQaGate.detail})`
|
|
3776
|
+
: "Exit 0; cont-QA PASS",
|
|
3706
3777
|
});
|
|
3707
3778
|
recordCombinedEvent({
|
|
3708
|
-
agentId:
|
|
3709
|
-
message:
|
|
3710
|
-
? `
|
|
3711
|
-
: "
|
|
3779
|
+
agentId: contQaGate.agentId,
|
|
3780
|
+
message: contQaGate.detail
|
|
3781
|
+
? `cont-QA verdict PASS: ${contQaGate.detail}`
|
|
3782
|
+
: "cont-QA verdict PASS.",
|
|
3712
3783
|
});
|
|
3713
3784
|
}
|
|
3714
3785
|
}
|
|
@@ -3769,7 +3840,7 @@ export async function runLauncherCli(argv) {
|
|
|
3769
3840
|
);
|
|
3770
3841
|
const summariesByAgentId = Object.fromEntries(
|
|
3771
3842
|
agentRuns
|
|
3772
|
-
.map((run) => [run.agent.agentId, readRunExecutionSummary(run)])
|
|
3843
|
+
.map((run) => [run.agent.agentId, readRunExecutionSummary(run, wave)])
|
|
3773
3844
|
.filter(([, summary]) => summary),
|
|
3774
3845
|
);
|
|
3775
3846
|
const gateSnapshot = buildGateSnapshot({
|
|
@@ -3777,7 +3848,9 @@ export async function runLauncherCli(argv) {
|
|
|
3777
3848
|
agentRuns,
|
|
3778
3849
|
derivedState,
|
|
3779
3850
|
lanePaths,
|
|
3851
|
+
validationMode: "live",
|
|
3780
3852
|
});
|
|
3853
|
+
completionGateSnapshot = gateSnapshot;
|
|
3781
3854
|
const traceDir = writeTraceBundle({
|
|
3782
3855
|
tracesDir: lanePaths.tracesDir,
|
|
3783
3856
|
lanePaths,
|
|
@@ -3791,6 +3864,7 @@ export async function runLauncherCli(argv) {
|
|
|
3791
3864
|
docsQueue: derivedState.docsQueue,
|
|
3792
3865
|
capabilityAssignments: derivedState.capabilityAssignments,
|
|
3793
3866
|
dependencySnapshot: derivedState.dependencySnapshot,
|
|
3867
|
+
securitySummary: derivedState.securitySummary,
|
|
3794
3868
|
integrationSummary: derivedState.integrationSummary,
|
|
3795
3869
|
integrationMarkdownPath: derivedState.integrationMarkdownPath,
|
|
3796
3870
|
clarificationTriage: derivedState.clarificationTriage,
|
|
@@ -3813,6 +3887,7 @@ export async function runLauncherCli(argv) {
|
|
|
3813
3887
|
coordinationLogPath: derivedState.coordinationLogPath,
|
|
3814
3888
|
}),
|
|
3815
3889
|
});
|
|
3890
|
+
completionTraceDir = traceDir;
|
|
3816
3891
|
|
|
3817
3892
|
if (failures.length === 0) {
|
|
3818
3893
|
dashboardState.status = "completed";
|
|
@@ -3846,8 +3921,8 @@ export async function runLauncherCli(argv) {
|
|
|
3846
3921
|
if (
|
|
3847
3922
|
failures.every(
|
|
3848
3923
|
(failure) =>
|
|
3849
|
-
String(failure.statusCode).startsWith("
|
|
3850
|
-
String(failure.statusCode) === "missing-
|
|
3924
|
+
String(failure.statusCode).startsWith("cont-qa-") ||
|
|
3925
|
+
String(failure.statusCode) === "missing-cont-qa-verdict",
|
|
3851
3926
|
)
|
|
3852
3927
|
) {
|
|
3853
3928
|
error.exitCode = 42;
|
|
@@ -3875,6 +3950,7 @@ export async function runLauncherCli(argv) {
|
|
|
3875
3950
|
wave,
|
|
3876
3951
|
);
|
|
3877
3952
|
if (relaunchResolution.barrier) {
|
|
3953
|
+
clearWaveRelaunchPlan(lanePaths, wave.wave);
|
|
3878
3954
|
for (const failure of relaunchResolution.barrier.failures) {
|
|
3879
3955
|
recordCombinedEvent({
|
|
3880
3956
|
level: "error",
|
|
@@ -3902,6 +3978,7 @@ export async function runLauncherCli(argv) {
|
|
|
3902
3978
|
}
|
|
3903
3979
|
runsToLaunch = relaunchResolution.runs;
|
|
3904
3980
|
if (runsToLaunch.length === 0) {
|
|
3981
|
+
clearWaveRelaunchPlan(lanePaths, wave.wave);
|
|
3905
3982
|
const error = new Error(
|
|
3906
3983
|
`Wave ${wave.wave} is waiting on human feedback or unresolved coordination state; no safe relaunch target is available.`,
|
|
3907
3984
|
);
|
|
@@ -3914,11 +3991,42 @@ export async function runLauncherCli(argv) {
|
|
|
3914
3991
|
detail: "Queued for retry",
|
|
3915
3992
|
});
|
|
3916
3993
|
}
|
|
3994
|
+
writeWaveRelaunchPlan(lanePaths, wave.wave, {
|
|
3995
|
+
wave: wave.wave,
|
|
3996
|
+
attempt: attempt + 1,
|
|
3997
|
+
phase: derivedState?.ledger?.phase || null,
|
|
3998
|
+
selectedAgentIds: runsToLaunch.map((run) => run.agent.agentId),
|
|
3999
|
+
reasonBuckets: relaunchReasonBuckets(runsToLaunch, failures, derivedState),
|
|
4000
|
+
executorStates: Object.fromEntries(
|
|
4001
|
+
runsToLaunch.map((run) => [run.agent.agentId, run.agent.executorResolved || null]),
|
|
4002
|
+
),
|
|
4003
|
+
fallbackHistory: Object.fromEntries(
|
|
4004
|
+
runsToLaunch.map((run) => [
|
|
4005
|
+
run.agent.agentId,
|
|
4006
|
+
run.agent.executorResolved?.executorHistory || [],
|
|
4007
|
+
]),
|
|
4008
|
+
),
|
|
4009
|
+
createdAt: toIsoTimestamp(),
|
|
4010
|
+
});
|
|
3917
4011
|
flushDashboards();
|
|
3918
4012
|
attempt += 1;
|
|
3919
4013
|
}
|
|
3920
4014
|
|
|
3921
|
-
|
|
4015
|
+
clearWaveRelaunchPlan(lanePaths, wave.wave);
|
|
4016
|
+
const runState = markWaveCompleted(options.runStatePath, wave.wave, {
|
|
4017
|
+
source: "live-launcher",
|
|
4018
|
+
reasonCode: "wave-complete",
|
|
4019
|
+
detail: `Wave ${wave.wave} completed after ${dashboardState?.attempt || 1} attempt(s).`,
|
|
4020
|
+
evidence: buildRunStateEvidence({
|
|
4021
|
+
wave,
|
|
4022
|
+
agentRuns,
|
|
4023
|
+
coordinationLogPath: derivedState.coordinationLogPath,
|
|
4024
|
+
assignmentsPath: waveAssignmentsPath(lanePaths, wave.wave),
|
|
4025
|
+
dependencySnapshotPath: waveDependencySnapshotPath(lanePaths, wave.wave),
|
|
4026
|
+
gateSnapshot: completionGateSnapshot,
|
|
4027
|
+
traceDir: completionTraceDir,
|
|
4028
|
+
}),
|
|
4029
|
+
});
|
|
3922
4030
|
console.log(
|
|
3923
4031
|
`[state] completed waves (${path.relative(REPO_ROOT, options.runStatePath)}): ${runState.completedWaves.join(", ") || "none"}`,
|
|
3924
4032
|
);
|