@chllming/wave-orchestration 0.8.4 → 0.8.6
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 +41 -1
- package/README.md +31 -13
- package/docs/README.md +4 -0
- package/docs/agents/wave-design-role.md +47 -0
- package/docs/concepts/what-is-a-wave.md +11 -7
- package/docs/context7/bundles.json +19 -20
- package/docs/context7/planner-agent/README.md +4 -1
- package/docs/guides/author-and-run-waves.md +27 -0
- package/docs/guides/planner.md +46 -0
- package/docs/guides/signal-wrappers.md +165 -0
- package/docs/guides/terminal-surfaces.md +13 -0
- package/docs/plans/context7-wave-orchestrator.md +24 -7
- package/docs/plans/current-state.md +10 -2
- package/docs/plans/end-state-architecture.md +22 -5
- package/docs/plans/examples/wave-example-design-handoff.md +262 -0
- package/docs/plans/examples/wave-example-live-proof.md +1 -1
- package/docs/plans/migration.md +301 -75
- package/docs/plans/wave-orchestrator.md +16 -3
- package/docs/reference/cli-reference.md +22 -1
- package/docs/reference/npmjs-trusted-publishing.md +2 -2
- package/docs/reference/sample-waves.md +14 -7
- package/docs/reference/skills.md +18 -0
- package/docs/reference/wave-control.md +2 -0
- package/package.json +1 -1
- package/releases/manifest.json +38 -0
- package/scripts/context7-api-check.sh +57 -13
- package/scripts/wave-orchestrator/agent-state.mjs +64 -0
- package/scripts/wave-orchestrator/config.mjs +5 -0
- package/scripts/wave-orchestrator/control-cli.mjs +19 -0
- package/scripts/wave-orchestrator/coordination.mjs +77 -1
- package/scripts/wave-orchestrator/gate-engine.mjs +106 -2
- package/scripts/wave-orchestrator/install.mjs +5 -0
- package/scripts/wave-orchestrator/launcher-runtime.mjs +18 -1
- package/scripts/wave-orchestrator/launcher.mjs +75 -1
- package/scripts/wave-orchestrator/ledger.mjs +56 -27
- package/scripts/wave-orchestrator/local-executor.mjs +37 -0
- package/scripts/wave-orchestrator/planner.mjs +24 -4
- package/scripts/wave-orchestrator/result-envelope.mjs +32 -1
- package/scripts/wave-orchestrator/retry-control.mjs +17 -2
- package/scripts/wave-orchestrator/retry-engine.mjs +85 -0
- package/scripts/wave-orchestrator/role-helpers.mjs +73 -1
- package/scripts/wave-orchestrator/session-supervisor.mjs +113 -0
- package/scripts/wave-orchestrator/shared.mjs +2 -0
- package/scripts/wave-orchestrator/signals.mjs +681 -0
- package/scripts/wave-orchestrator/skills.mjs +1 -0
- package/scripts/wave-orchestrator/task-entity.mjs +65 -45
- package/scripts/wave-orchestrator/wave-control-schema.mjs +2 -0
- package/scripts/wave-orchestrator/wave-files.mjs +85 -1
- package/scripts/wave-orchestrator/wave-state-reducer.mjs +24 -7
- package/scripts/wave-status.sh +200 -0
- package/scripts/wave-watch.sh +200 -0
- package/skills/README.md +10 -0
- package/skills/role-design/SKILL.md +50 -0
- package/skills/role-design/skill.json +36 -0
- package/skills/signal-hygiene/SKILL.md +51 -0
- package/skills/signal-hygiene/skill.json +20 -0
- package/skills/tui-design/SKILL.md +77 -0
- package/skills/tui-design/references/tui-design.md +259 -0
- package/skills/tui-design/skill.json +36 -0
- package/wave.config.json +15 -1
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "./config.mjs";
|
|
7
7
|
import {
|
|
8
8
|
validateContEvalSummary,
|
|
9
|
+
validateDesignSummary,
|
|
9
10
|
validateDocumentationClosureSummary,
|
|
10
11
|
validateContQaSummary,
|
|
11
12
|
validateImplementationSummary,
|
|
@@ -13,6 +14,8 @@ import {
|
|
|
13
14
|
} from "./agent-state.mjs";
|
|
14
15
|
import {
|
|
15
16
|
isContEvalImplementationOwningAgent,
|
|
17
|
+
isDesignAgent,
|
|
18
|
+
isImplementationOwningDesignAgent,
|
|
16
19
|
isSecurityReviewAgent,
|
|
17
20
|
} from "./role-helpers.mjs";
|
|
18
21
|
import { openClarificationLinkedRequests } from "./coordination-store.mjs";
|
|
@@ -54,6 +57,7 @@ export function buildSeedWaveLedger({
|
|
|
54
57
|
}) {
|
|
55
58
|
const tasks = [];
|
|
56
59
|
for (const agent of wave.agents) {
|
|
60
|
+
const hybridDesignAgent = isImplementationOwningDesignAgent(agent);
|
|
57
61
|
const kind =
|
|
58
62
|
agent.agentId === contQaAgentId
|
|
59
63
|
? "cont-qa"
|
|
@@ -63,35 +67,46 @@ export function buildSeedWaveLedger({
|
|
|
63
67
|
? "integration"
|
|
64
68
|
: agent.agentId === documentationAgentId
|
|
65
69
|
? "documentation"
|
|
70
|
+
: isDesignAgent(agent)
|
|
71
|
+
? "design"
|
|
66
72
|
: isSecurityReviewAgent(agent)
|
|
67
73
|
? "security"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
74
|
+
: "implementation";
|
|
75
|
+
const runtime = agent.executorResolved
|
|
76
|
+
? {
|
|
77
|
+
executorId: agent.executorResolved.id,
|
|
78
|
+
role: agent.executorResolved.role,
|
|
79
|
+
profile: agent.executorResolved.profile,
|
|
80
|
+
selectedBy: agent.executorResolved.selectedBy,
|
|
81
|
+
retryPolicy: agent.executorResolved.retryPolicy || null,
|
|
82
|
+
allowFallbackOnRetry: agent.executorResolved.allowFallbackOnRetry !== false,
|
|
83
|
+
fallbacks: agent.executorResolved.fallbacks || [],
|
|
84
|
+
fallbackUsed: agent.executorResolved.fallbackUsed === true,
|
|
85
|
+
}
|
|
86
|
+
: null;
|
|
87
|
+
const pushTask = (taskKind) => {
|
|
88
|
+
tasks.push({
|
|
89
|
+
id: taskId(taskKind, agent.agentId),
|
|
90
|
+
title: `${agent.agentId}: ${agent.title}`,
|
|
91
|
+
owner: agent.agentId,
|
|
92
|
+
kind: taskKind,
|
|
93
|
+
dependsOn: [],
|
|
94
|
+
state: "planned",
|
|
95
|
+
proofState: "pending",
|
|
96
|
+
docState: "pending",
|
|
97
|
+
infraState: "n/a",
|
|
98
|
+
priority:
|
|
99
|
+
taskKind === "implementation" ? "normal" : taskKind === "integration" ? "high" : "high",
|
|
100
|
+
artifactRefs: agent.ownedPaths || [],
|
|
101
|
+
runtime,
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
if (hybridDesignAgent && kind === "design") {
|
|
105
|
+
pushTask("design");
|
|
106
|
+
pushTask("implementation");
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
pushTask(kind);
|
|
95
110
|
}
|
|
96
111
|
for (const promotion of wave.componentPromotions || []) {
|
|
97
112
|
tasks.push({
|
|
@@ -163,6 +178,11 @@ function derivePhase({
|
|
|
163
178
|
return blockingHelperTasks.some((task) => task.state === "blocked") ? "blocked" : "running";
|
|
164
179
|
}
|
|
165
180
|
const implementationTasks = tasks.filter((task) => task.kind === "implementation");
|
|
181
|
+
const designTasks = tasks.filter((task) => task.kind === "design");
|
|
182
|
+
const allDesignDone = designTasks.every((task) => task.state === "done");
|
|
183
|
+
if (!allDesignDone && designTasks.length > 0) {
|
|
184
|
+
return "design";
|
|
185
|
+
}
|
|
166
186
|
const allImplementationDone = implementationTasks.every((task) => task.state === "done");
|
|
167
187
|
if (!allImplementationDone) {
|
|
168
188
|
return "running";
|
|
@@ -221,6 +241,15 @@ export function deriveWaveLedger({
|
|
|
221
241
|
docState: summary?.docDelta?.state || "pending",
|
|
222
242
|
};
|
|
223
243
|
}
|
|
244
|
+
if (task.kind === "design" && agent) {
|
|
245
|
+
const validation = validateDesignSummary(agent, summary);
|
|
246
|
+
return {
|
|
247
|
+
...task,
|
|
248
|
+
state: taskStateFromValidation(validation),
|
|
249
|
+
proofState: validation.ok ? "met" : "gap",
|
|
250
|
+
docState: validation.ok ? "met" : "gap",
|
|
251
|
+
};
|
|
252
|
+
}
|
|
224
253
|
if (task.kind === "documentation" && agent) {
|
|
225
254
|
const validation = validateDocumentationClosureSummary(agent, summary);
|
|
226
255
|
return {
|
|
@@ -173,6 +173,11 @@ function formatWaveEvalLine(evalMarker, detail) {
|
|
|
173
173
|
return `[wave-eval] state=satisfied targets=${targetIds.length} benchmarks=${benchmarkIds.length} regressions=0${targetIdSegment}${benchmarkIdSegment} detail=${detail}`;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
function isDesignAgentPrompt(rawPrompt) {
|
|
177
|
+
const text = String(rawPrompt || "");
|
|
178
|
+
return /\[wave-design\]/i.test(text) || /\bwave design\b/i.test(text);
|
|
179
|
+
}
|
|
180
|
+
|
|
176
181
|
export function resolveRepoOwnedDeliverablePath(relPath) {
|
|
177
182
|
if (!relPath || path.isAbsolute(relPath)) {
|
|
178
183
|
throw new Error(`Unsafe deliverable path: ${String(relPath || "")}`);
|
|
@@ -277,6 +282,8 @@ export function runLocalExecutorCli(argv) {
|
|
|
277
282
|
const contQaAgent = agentId === contQaAgentId;
|
|
278
283
|
const contEvalAgent = agentId === contEvalAgentId;
|
|
279
284
|
const integrationAgent = agentId === integrationAgentId;
|
|
285
|
+
const designAgent = isDesignAgentPrompt(rawPrompt);
|
|
286
|
+
const implementationMarkersRequired = /\[wave-proof\]/i.test(rawPrompt);
|
|
280
287
|
const ownedComponents = extractOwnedComponents(rawPrompt);
|
|
281
288
|
const assignedPrompt = extractAssignedPrompt(rawPrompt);
|
|
282
289
|
const ownedPaths = extractFileOwnershipPaths(assignedPrompt);
|
|
@@ -304,6 +311,21 @@ export function runLocalExecutorCli(argv) {
|
|
|
304
311
|
console.log(
|
|
305
312
|
"[wave-integration] state=ready-for-doc-closure claims=0 conflicts=0 blockers=0 detail=local-executor-no-deliverables",
|
|
306
313
|
);
|
|
314
|
+
} else if (designAgent) {
|
|
315
|
+
console.log(
|
|
316
|
+
"[wave-design] state=ready-for-implementation decisions=1 assumptions=1 open_questions=0 detail=local-executor-no-deliverables",
|
|
317
|
+
);
|
|
318
|
+
if (implementationMarkersRequired) {
|
|
319
|
+
console.log(
|
|
320
|
+
"[wave-proof] completion=contract durability=none proof=unit state=met detail=local-executor-no-deliverables",
|
|
321
|
+
);
|
|
322
|
+
console.log("[wave-doc-delta] state=none detail=local-executor-no-deliverables");
|
|
323
|
+
for (const component of ownedComponents) {
|
|
324
|
+
console.log(
|
|
325
|
+
`[wave-component] component=${component.componentId} level=${component.level || "repo-landed"} state=met detail=local-executor-no-deliverables`,
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
307
329
|
} else if (agentId === documentationAgentId) {
|
|
308
330
|
console.log("[wave-doc-closure] state=no-change detail=local-executor-no-deliverables");
|
|
309
331
|
} else if (agentId) {
|
|
@@ -348,6 +370,21 @@ export function runLocalExecutorCli(argv) {
|
|
|
348
370
|
console.log(
|
|
349
371
|
"[wave-integration] state=ready-for-doc-closure claims=0 conflicts=0 blockers=0 detail=local-executor-smoke",
|
|
350
372
|
);
|
|
373
|
+
} else if (designAgent) {
|
|
374
|
+
console.log(
|
|
375
|
+
"[wave-design] state=ready-for-implementation decisions=2 assumptions=1 open_questions=0 detail=local-executor-smoke",
|
|
376
|
+
);
|
|
377
|
+
if (implementationMarkersRequired) {
|
|
378
|
+
console.log(
|
|
379
|
+
"[wave-proof] completion=contract durability=none proof=unit state=met detail=local-executor-smoke",
|
|
380
|
+
);
|
|
381
|
+
console.log("[wave-doc-delta] state=owned detail=local-executor-smoke");
|
|
382
|
+
for (const component of ownedComponents) {
|
|
383
|
+
console.log(
|
|
384
|
+
`[wave-component] component=${component.componentId} level=${component.level || "repo-landed"} state=met detail=local-executor-smoke`,
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
351
388
|
} else if (agentId === documentationAgentId) {
|
|
352
389
|
console.log("[wave-doc-closure] state=no-change detail=local-executor-smoke");
|
|
353
390
|
} else if (agentId) {
|
|
@@ -317,6 +317,9 @@ function defaultTargetLevel(template) {
|
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
function defaultExecutorProfile(roleKind) {
|
|
320
|
+
if (roleKind === "design") {
|
|
321
|
+
return "design-pass";
|
|
322
|
+
}
|
|
320
323
|
if (roleKind === "infra" || roleKind === "deploy" || roleKind === "research") {
|
|
321
324
|
return "ops-triage";
|
|
322
325
|
}
|
|
@@ -327,7 +330,7 @@ function defaultExecutorProfile(roleKind) {
|
|
|
327
330
|
}
|
|
328
331
|
|
|
329
332
|
function defaultExitContract(roleKind) {
|
|
330
|
-
if (roleKind === "security") {
|
|
333
|
+
if (roleKind === "security" || roleKind === "design") {
|
|
331
334
|
return null;
|
|
332
335
|
}
|
|
333
336
|
if (roleKind === "infra" || roleKind === "deploy") {
|
|
@@ -355,6 +358,9 @@ function defaultExitContract(roleKind) {
|
|
|
355
358
|
}
|
|
356
359
|
|
|
357
360
|
function buildDefaultValidationCommand(template, roleKind) {
|
|
361
|
+
if (roleKind === "design") {
|
|
362
|
+
return "Manual review of the design packet against the wave scope, constraints, and downstream ownership.";
|
|
363
|
+
}
|
|
358
364
|
if (roleKind === "security") {
|
|
359
365
|
return "Manual review of the changed security-sensitive surfaces plus required proofs.";
|
|
360
366
|
}
|
|
@@ -368,6 +374,9 @@ function buildDefaultValidationCommand(template, roleKind) {
|
|
|
368
374
|
}
|
|
369
375
|
|
|
370
376
|
function buildDefaultOutputSummary(template, roleKind) {
|
|
377
|
+
if (roleKind === "design") {
|
|
378
|
+
return "Summarize the design packet, key decisions, assumptions, open questions, and exact implementation handoff.";
|
|
379
|
+
}
|
|
371
380
|
if (roleKind === "security") {
|
|
372
381
|
return "Summarize the threat model, findings, required approvals, requested fixes, and final security disposition.";
|
|
373
382
|
}
|
|
@@ -381,6 +390,9 @@ function buildDefaultOutputSummary(template, roleKind) {
|
|
|
381
390
|
}
|
|
382
391
|
|
|
383
392
|
function buildDefaultPrimaryGoal(template, roleKind, title) {
|
|
393
|
+
if (roleKind === "design") {
|
|
394
|
+
return `Produce an implementation-ready design packet for the ${title.toLowerCase()} slice before coding starts.`;
|
|
395
|
+
}
|
|
384
396
|
if (roleKind === "security") {
|
|
385
397
|
return `Review the ${title.toLowerCase()} slice for security risks and route exact fixes before integration.`;
|
|
386
398
|
}
|
|
@@ -1067,6 +1079,9 @@ function buildWorkerAgentSpec({
|
|
|
1067
1079
|
if (roleKind === "research" && !capabilities.includes("research")) {
|
|
1068
1080
|
capabilities.push("research");
|
|
1069
1081
|
}
|
|
1082
|
+
if (roleKind === "design" && !capabilities.includes("design")) {
|
|
1083
|
+
capabilities.push("design");
|
|
1084
|
+
}
|
|
1070
1085
|
return {
|
|
1071
1086
|
agentId,
|
|
1072
1087
|
title,
|
|
@@ -1075,6 +1090,8 @@ function buildWorkerAgentSpec({
|
|
|
1075
1090
|
? values.rolePromptPaths
|
|
1076
1091
|
: roleKind === "security"
|
|
1077
1092
|
? [lanePaths.securityRolePromptPath]
|
|
1093
|
+
: roleKind === "design"
|
|
1094
|
+
? [lanePaths.designRolePromptPath]
|
|
1078
1095
|
: [],
|
|
1079
1096
|
skills: values.skills || [],
|
|
1080
1097
|
executor: {
|
|
@@ -1934,6 +1951,7 @@ function normalizePlannerContext7Bundle(bundle, bundleIndex) {
|
|
|
1934
1951
|
function normalizePlannerWorkerAgent(rawAgent, context) {
|
|
1935
1952
|
const agentId = cleanText(rawAgent?.agentId) || `A${context.index + 1}`;
|
|
1936
1953
|
const roleKind = [
|
|
1954
|
+
"design",
|
|
1937
1955
|
"implementation",
|
|
1938
1956
|
"qa",
|
|
1939
1957
|
"infra",
|
|
@@ -3067,12 +3085,12 @@ async function collectWorkerAgents({
|
|
|
3067
3085
|
const title = cleanText(await prompt.ask(`Worker ${agentId} title`, defaults.title));
|
|
3068
3086
|
const roleKind = await prompt.askChoice(
|
|
3069
3087
|
`Worker ${agentId} role kind`,
|
|
3070
|
-
["implementation", "qa", "infra", "deploy", "research", "security"],
|
|
3088
|
+
["design", "implementation", "qa", "infra", "deploy", "research", "security"],
|
|
3071
3089
|
defaultRoleKind,
|
|
3072
3090
|
);
|
|
3073
3091
|
const executorProfile = await prompt.askChoice(
|
|
3074
3092
|
`Worker ${agentId} executor profile`,
|
|
3075
|
-
["implement-fast", "deep-review", "eval-tuning", "docs-pass", "ops-triage", "security-review"],
|
|
3093
|
+
["implement-fast", "design-pass", "deep-review", "eval-tuning", "docs-pass", "ops-triage", "security-review"],
|
|
3076
3094
|
defaultExecutorProfile(roleKind),
|
|
3077
3095
|
);
|
|
3078
3096
|
const ownedPaths = normalizeRepoPathList(
|
|
@@ -3081,6 +3099,8 @@ async function collectWorkerAgents({
|
|
|
3081
3099
|
`Worker ${agentId} owned paths (comma or | separated)`,
|
|
3082
3100
|
roleKind === "security"
|
|
3083
3101
|
? `.tmp/${lane}-wave-launcher/security/wave-${waveNumber}-review.md`
|
|
3102
|
+
: roleKind === "design"
|
|
3103
|
+
? `docs/plans/waves/design/wave-${waveNumber}-${agentId}.md`
|
|
3084
3104
|
: template === "infra"
|
|
3085
3105
|
? "scripts/,docs/plans/"
|
|
3086
3106
|
: template === "release"
|
|
@@ -3093,7 +3113,7 @@ async function collectWorkerAgents({
|
|
|
3093
3113
|
const components = normalizeListText(
|
|
3094
3114
|
await prompt.ask(
|
|
3095
3115
|
`Worker ${agentId} component ids (comma or | separated)`,
|
|
3096
|
-
roleKind === "security"
|
|
3116
|
+
roleKind === "security" || roleKind === "design"
|
|
3097
3117
|
? ""
|
|
3098
3118
|
: componentPromotions.map((promotion) => promotion.componentId).join(", "),
|
|
3099
3119
|
),
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "./shared.mjs";
|
|
8
8
|
|
|
9
9
|
export const ENVELOPE_VALID_ROLES = [
|
|
10
|
+
"design",
|
|
10
11
|
"implementation",
|
|
11
12
|
"integration",
|
|
12
13
|
"documentation",
|
|
@@ -24,6 +25,9 @@ function inferEnvelopeRole(agent, summary) {
|
|
|
24
25
|
if (summary?.integration) {
|
|
25
26
|
return "integration";
|
|
26
27
|
}
|
|
28
|
+
if (summary?.design) {
|
|
29
|
+
return "design";
|
|
30
|
+
}
|
|
27
31
|
if (summary?.docClosure) {
|
|
28
32
|
return "documentation";
|
|
29
33
|
}
|
|
@@ -205,7 +209,11 @@ export function buildAgentResultEnvelope(agent, summary, options = {}) {
|
|
|
205
209
|
facts,
|
|
206
210
|
};
|
|
207
211
|
|
|
208
|
-
if (
|
|
212
|
+
if (
|
|
213
|
+
role === "implementation" ||
|
|
214
|
+
safeSummary.docDelta ||
|
|
215
|
+
Array.isArray(safeSummary.components)
|
|
216
|
+
) {
|
|
209
217
|
const docDelta = safeSummary.docDelta || {};
|
|
210
218
|
const components = Array.isArray(safeSummary.components)
|
|
211
219
|
? safeSummary.components.map((component) => ({
|
|
@@ -225,6 +233,17 @@ export function buildAgentResultEnvelope(agent, summary, options = {}) {
|
|
|
225
233
|
};
|
|
226
234
|
}
|
|
227
235
|
|
|
236
|
+
if (role === "design") {
|
|
237
|
+
const design = safeSummary.design || {};
|
|
238
|
+
envelope.design = {
|
|
239
|
+
state: design.state || null,
|
|
240
|
+
decisions: design.decisions || 0,
|
|
241
|
+
assumptions: design.assumptions || 0,
|
|
242
|
+
openQuestions: design.openQuestions || 0,
|
|
243
|
+
detail: design.detail || null,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
228
247
|
if (role === "integration") {
|
|
229
248
|
const integration = safeSummary.integration || {};
|
|
230
249
|
envelope.integration = {
|
|
@@ -392,6 +411,16 @@ export function projectLegacySummaryFromEnvelope(envelope, options = {}) {
|
|
|
392
411
|
: [];
|
|
393
412
|
}
|
|
394
413
|
|
|
414
|
+
if (envelope.design) {
|
|
415
|
+
summary.design = {
|
|
416
|
+
state: envelope.design.state || null,
|
|
417
|
+
decisions: envelope.design.decisions || 0,
|
|
418
|
+
assumptions: envelope.design.assumptions || 0,
|
|
419
|
+
openQuestions: envelope.design.openQuestions || 0,
|
|
420
|
+
detail: envelope.design.detail || null,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
395
424
|
if (envelope.documentation?.docClosure) {
|
|
396
425
|
summary.docClosure = {
|
|
397
426
|
state: envelope.documentation.docClosure.state || "no-change",
|
|
@@ -466,6 +495,8 @@ export function projectLegacySummaryFromEnvelope(envelope, options = {}) {
|
|
|
466
495
|
|
|
467
496
|
function requiredRoleSection(role) {
|
|
468
497
|
switch (role) {
|
|
498
|
+
case "design":
|
|
499
|
+
return "design";
|
|
469
500
|
case "implementation":
|
|
470
501
|
return "implementation";
|
|
471
502
|
case "integration":
|
|
@@ -10,7 +10,11 @@ import {
|
|
|
10
10
|
readWaveControlPlaneState,
|
|
11
11
|
syncWaveControlPlaneProjections,
|
|
12
12
|
} from "./control-plane.mjs";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
isDesignAgent,
|
|
15
|
+
isDocsOnlyDesignAgent,
|
|
16
|
+
isSecurityReviewAgent,
|
|
17
|
+
} from "./role-helpers.mjs";
|
|
14
18
|
import { ensureDirectory, parseNonNegativeInt } from "./shared.mjs";
|
|
15
19
|
|
|
16
20
|
function uniqueAgentIds(values) {
|
|
@@ -188,7 +192,18 @@ export function resolveRetryOverrideAgentIds(waveDefinition, lanePaths, override
|
|
|
188
192
|
);
|
|
189
193
|
if (resumePhase === "implementation") {
|
|
190
194
|
return agents
|
|
191
|
-
.filter(
|
|
195
|
+
.filter(
|
|
196
|
+
(agent) =>
|
|
197
|
+
agent?.agentId &&
|
|
198
|
+
!closureAgentIds.has(agent.agentId) &&
|
|
199
|
+
!isSecurityReviewAgent(agent) &&
|
|
200
|
+
!isDocsOnlyDesignAgent(agent),
|
|
201
|
+
)
|
|
202
|
+
.map((agent) => agent.agentId);
|
|
203
|
+
}
|
|
204
|
+
if (resumePhase === "design") {
|
|
205
|
+
return agents
|
|
206
|
+
.filter((agent) => isDesignAgent(agent))
|
|
192
207
|
.map((agent) => agent.agentId);
|
|
193
208
|
}
|
|
194
209
|
if (resumePhase === "integrating") {
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from "./shared.mjs";
|
|
21
21
|
import {
|
|
22
22
|
readAgentExecutionSummary,
|
|
23
|
+
validateDesignSummary,
|
|
23
24
|
validateImplementationSummary,
|
|
24
25
|
} from "./agent-state.mjs";
|
|
25
26
|
import {
|
|
@@ -32,6 +33,9 @@ import {
|
|
|
32
33
|
} from "./proof-registry.mjs";
|
|
33
34
|
import { hashAgentPromptFingerprint } from "./context7.mjs";
|
|
34
35
|
import {
|
|
36
|
+
isDocsOnlyDesignAgent,
|
|
37
|
+
isDesignAgent,
|
|
38
|
+
isImplementationOwningDesignAgent,
|
|
35
39
|
isSecurityReviewAgent,
|
|
36
40
|
resolveWaveRoleBindings,
|
|
37
41
|
} from "./role-helpers.mjs";
|
|
@@ -315,6 +319,28 @@ export function hasReusableSuccessStatus(agent, statusPath, options = {}) {
|
|
|
315
319
|
if (!basicReuseOk) {
|
|
316
320
|
return false;
|
|
317
321
|
}
|
|
322
|
+
if (isDocsOnlyDesignAgent(agent) || isImplementationOwningDesignAgent(agent)) {
|
|
323
|
+
const summary = readAgentExecutionSummary(statusPath, {
|
|
324
|
+
agent,
|
|
325
|
+
statusPath,
|
|
326
|
+
statusRecord,
|
|
327
|
+
logPath: options.logPath || null,
|
|
328
|
+
reportPath: options.reportPath || null,
|
|
329
|
+
});
|
|
330
|
+
if (!summary) {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
const effectiveSummary = options.proofRegistry
|
|
334
|
+
? augmentSummaryWithProofRegistry(agent, summary, options.proofRegistry)
|
|
335
|
+
: summary;
|
|
336
|
+
if (isDocsOnlyDesignAgent(agent)) {
|
|
337
|
+
return validateDesignSummary(agent, effectiveSummary).ok;
|
|
338
|
+
}
|
|
339
|
+
return (
|
|
340
|
+
validateDesignSummary(agent, effectiveSummary).ok &&
|
|
341
|
+
validateImplementationSummary(agent, effectiveSummary).ok
|
|
342
|
+
);
|
|
343
|
+
}
|
|
318
344
|
const proofCentric =
|
|
319
345
|
agentRequiresProofCentricValidation(agent) || waveRequiresProofCentricValidation(options.wave);
|
|
320
346
|
if (!proofCentric) {
|
|
@@ -342,6 +368,37 @@ export function hasReusableSuccessStatus(agent, statusPath, options = {}) {
|
|
|
342
368
|
return true;
|
|
343
369
|
}
|
|
344
370
|
|
|
371
|
+
function hasReusableDesignPassStatus(agent, statusPath, options = {}) {
|
|
372
|
+
if (!isDesignAgent(agent)) {
|
|
373
|
+
return hasReusableSuccessStatus(agent, statusPath, options);
|
|
374
|
+
}
|
|
375
|
+
const statusRecord = readStatusRecordIfPresent(statusPath);
|
|
376
|
+
const basicReuseOk = Boolean(
|
|
377
|
+
statusRecord && statusRecord.code === 0 && statusRecord.promptHash === hashAgentPromptFingerprint(agent),
|
|
378
|
+
);
|
|
379
|
+
if (!basicReuseOk) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
const summary = readAgentExecutionSummary(statusPath, {
|
|
383
|
+
agent: {
|
|
384
|
+
...agent,
|
|
385
|
+
exitContract: null,
|
|
386
|
+
components: [],
|
|
387
|
+
componentTargets: {},
|
|
388
|
+
deliverables: [],
|
|
389
|
+
proofArtifacts: [],
|
|
390
|
+
},
|
|
391
|
+
statusPath,
|
|
392
|
+
statusRecord,
|
|
393
|
+
logPath: options.logPath || null,
|
|
394
|
+
reportPath: options.reportPath || null,
|
|
395
|
+
});
|
|
396
|
+
if (!summary) {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
return validateDesignSummary(agent, summary).ok;
|
|
400
|
+
}
|
|
401
|
+
|
|
345
402
|
function isClosureAgentId(agent, lanePaths, waveDefinition = null) {
|
|
346
403
|
return (
|
|
347
404
|
resolveWaveRoleBindings(waveDefinition, lanePaths, waveDefinition?.agents).closureAgentIds.includes(
|
|
@@ -377,6 +434,23 @@ export function selectInitialWaveRuns(agentRuns, lanePaths, waveDefinition = nul
|
|
|
377
434
|
const implementationRuns = (agentRuns || []).filter(
|
|
378
435
|
(run) => !isClosureAgentId(run?.agent, lanePaths, waveDefinition),
|
|
379
436
|
);
|
|
437
|
+
const pendingDesignRuns = implementationRuns.filter(
|
|
438
|
+
(run) =>
|
|
439
|
+
isDesignAgent(run.agent) &&
|
|
440
|
+
!hasReusableDesignPassStatus(run.agent, run.statusPath, {
|
|
441
|
+
wave: waveDefinition,
|
|
442
|
+
logPath: run.logPath,
|
|
443
|
+
}),
|
|
444
|
+
);
|
|
445
|
+
if (pendingDesignRuns.length > 0) {
|
|
446
|
+
return pendingDesignRuns;
|
|
447
|
+
}
|
|
448
|
+
const implementationFanoutRuns = implementationRuns.filter(
|
|
449
|
+
(run) => !isDocsOnlyDesignAgent(run.agent),
|
|
450
|
+
);
|
|
451
|
+
if (implementationFanoutRuns.length > 0) {
|
|
452
|
+
return implementationFanoutRuns;
|
|
453
|
+
}
|
|
380
454
|
return implementationRuns.length > 0 ? implementationRuns : agentRuns;
|
|
381
455
|
}
|
|
382
456
|
|
|
@@ -573,6 +647,9 @@ function runsFromAgentIds(agentRuns, agentIds) {
|
|
|
573
647
|
|
|
574
648
|
function resolveRunsForResumePhase(agentRuns, lanePaths, resumePhase, waveDefinition = null) {
|
|
575
649
|
const roleBindings = resolveWaveRoleBindings(waveDefinition, lanePaths, waveDefinition?.agents);
|
|
650
|
+
if (resumePhase === "design") {
|
|
651
|
+
return (agentRuns || []).filter((run) => isDesignAgent(run.agent));
|
|
652
|
+
}
|
|
576
653
|
if (resumePhase === "integrating") {
|
|
577
654
|
return runsFromAgentIds(agentRuns, [roleBindings.integrationAgentId]);
|
|
578
655
|
}
|
|
@@ -729,6 +806,12 @@ function resolveRelaunchRunsLegacy(agentRuns, failures, derivedState, lanePaths,
|
|
|
729
806
|
barrier: null,
|
|
730
807
|
};
|
|
731
808
|
}
|
|
809
|
+
if (derivedState?.ledger?.phase === "design") {
|
|
810
|
+
return {
|
|
811
|
+
runs: agentRuns.filter((run) => isDesignAgent(run.agent)),
|
|
812
|
+
barrier: null,
|
|
813
|
+
};
|
|
814
|
+
}
|
|
732
815
|
if (derivedState?.ledger?.phase === "security-review") {
|
|
733
816
|
return {
|
|
734
817
|
runs: agentRuns.filter((run) => isSecurityReviewAgent(run.agent)),
|
|
@@ -1018,6 +1101,8 @@ export function preflightWavesForExecutorAvailability(waves, lanePaths) {
|
|
|
1018
1101
|
|
|
1019
1102
|
function phaseFromGate(gateName) {
|
|
1020
1103
|
switch (gateName) {
|
|
1104
|
+
case "designGate":
|
|
1105
|
+
return "design";
|
|
1021
1106
|
case "implementationGate":
|
|
1022
1107
|
case "componentGate":
|
|
1023
1108
|
case "helperAssignmentBarrier":
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_CONT_QA_AGENT_ID,
|
|
3
3
|
DEFAULT_CONT_EVAL_AGENT_ID,
|
|
4
|
+
DEFAULT_DESIGN_ROLE_PROMPT_PATH,
|
|
4
5
|
DEFAULT_DOCUMENTATION_AGENT_ID,
|
|
5
6
|
DEFAULT_INTEGRATION_AGENT_ID,
|
|
6
7
|
DEFAULT_SECURITY_ROLE_PROMPT_PATH,
|
|
@@ -37,6 +38,29 @@ export function isSecurityReportPath(relPath) {
|
|
|
37
38
|
return /(?:^|\/).*security.*\.(?:md|txt)$/i.test(cleanPath(relPath));
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
export function isDesignRolePromptPath(
|
|
42
|
+
relPath,
|
|
43
|
+
designRolePromptPath = DEFAULT_DESIGN_ROLE_PROMPT_PATH,
|
|
44
|
+
) {
|
|
45
|
+
const normalized = cleanPath(relPath);
|
|
46
|
+
const configured = cleanPath(designRolePromptPath);
|
|
47
|
+
return (
|
|
48
|
+
normalized === configured ||
|
|
49
|
+
normalized === DEFAULT_DESIGN_ROLE_PROMPT_PATH ||
|
|
50
|
+
normalized.endsWith("/wave-design-role.md")
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function isDesignReportPath(relPath) {
|
|
55
|
+
return /(?:^|\/).*(?:design|handoff|decision-lineage).*\.(?:md|txt)$/i.test(cleanPath(relPath));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function normalizedOwnedPaths(agent) {
|
|
59
|
+
return Array.isArray(agent?.ownedPaths)
|
|
60
|
+
? agent.ownedPaths.map(cleanPath).filter(Boolean)
|
|
61
|
+
: [];
|
|
62
|
+
}
|
|
63
|
+
|
|
40
64
|
export function isContEvalImplementationOwningAgent(
|
|
41
65
|
agent,
|
|
42
66
|
{ contEvalAgentId = DEFAULT_CONT_EVAL_AGENT_ID } = {},
|
|
@@ -81,11 +105,59 @@ export function isSecurityReviewAgent(
|
|
|
81
105
|
return capabilities.includes("security-review");
|
|
82
106
|
}
|
|
83
107
|
|
|
108
|
+
export function isDesignAgent(
|
|
109
|
+
agent,
|
|
110
|
+
{ designRolePromptPath = DEFAULT_DESIGN_ROLE_PROMPT_PATH } = {},
|
|
111
|
+
) {
|
|
112
|
+
if (!agent || typeof agent !== "object") {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
const rolePromptPaths = Array.isArray(agent.rolePromptPaths) ? agent.rolePromptPaths : [];
|
|
116
|
+
if (
|
|
117
|
+
rolePromptPaths.some((rolePromptPath) =>
|
|
118
|
+
isDesignRolePromptPath(rolePromptPath, designRolePromptPath),
|
|
119
|
+
)
|
|
120
|
+
) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
const capabilities = Array.isArray(agent.capabilities)
|
|
124
|
+
? agent.capabilities.map((entry) => String(entry || "").trim().toLowerCase())
|
|
125
|
+
: [];
|
|
126
|
+
return capabilities.includes("design");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function isImplementationOwningDesignAgent(
|
|
130
|
+
agent,
|
|
131
|
+
{ designRolePromptPath = DEFAULT_DESIGN_ROLE_PROMPT_PATH } = {},
|
|
132
|
+
) {
|
|
133
|
+
if (!isDesignAgent(agent, { designRolePromptPath })) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const ownedPaths = normalizedOwnedPaths(agent);
|
|
137
|
+
if (ownedPaths.length === 0) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return ownedPaths.some((ownedPath) => !isDesignReportPath(ownedPath));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function isDocsOnlyDesignAgent(
|
|
144
|
+
agent,
|
|
145
|
+
{ designRolePromptPath = DEFAULT_DESIGN_ROLE_PROMPT_PATH } = {},
|
|
146
|
+
) {
|
|
147
|
+
return isDesignAgent(agent, { designRolePromptPath }) &&
|
|
148
|
+
!isImplementationOwningDesignAgent(agent, { designRolePromptPath });
|
|
149
|
+
}
|
|
150
|
+
|
|
84
151
|
export function resolveSecurityReviewReportPath(agent) {
|
|
85
|
-
const ownedPaths =
|
|
152
|
+
const ownedPaths = normalizedOwnedPaths(agent);
|
|
86
153
|
return ownedPaths.find((ownedPath) => isSecurityReportPath(ownedPath)) || null;
|
|
87
154
|
}
|
|
88
155
|
|
|
156
|
+
export function resolveDesignReportPath(agent) {
|
|
157
|
+
const ownedPaths = normalizedOwnedPaths(agent);
|
|
158
|
+
return ownedPaths.find((ownedPath) => isDesignReportPath(ownedPath)) || null;
|
|
159
|
+
}
|
|
160
|
+
|
|
89
161
|
export function resolveWaveRoleBindings(wave = {}, lanePaths = {}, agents = wave?.agents || []) {
|
|
90
162
|
const contQaAgentId =
|
|
91
163
|
wave?.contQaAgentId || lanePaths?.contQaAgentId || DEFAULT_CONT_QA_AGENT_ID;
|