@chllming/wave-orchestration 0.8.4 → 0.8.5
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 +17 -1
- package/README.md +25 -12
- package/docs/README.md +2 -0
- package/docs/agents/wave-design-role.md +47 -0
- package/docs/concepts/what-is-a-wave.md +11 -7
- package/docs/guides/author-and-run-waves.md +24 -0
- package/docs/guides/planner.md +44 -0
- package/docs/plans/current-state.md +5 -1
- package/docs/plans/end-state-architecture.md +7 -2
- 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 +208 -75
- package/docs/plans/wave-orchestrator.md +13 -3
- package/docs/reference/cli-reference.md +12 -0
- package/docs/reference/npmjs-trusted-publishing.md +2 -2
- package/docs/reference/sample-waves.md +14 -7
- package/docs/reference/skills.md +10 -0
- package/package.json +1 -1
- package/releases/manifest.json +19 -0
- package/scripts/wave-orchestrator/agent-state.mjs +64 -0
- package/scripts/wave-orchestrator/config.mjs +5 -0
- package/scripts/wave-orchestrator/coordination.mjs +42 -1
- package/scripts/wave-orchestrator/gate-engine.mjs +106 -2
- package/scripts/wave-orchestrator/install.mjs +3 -0
- package/scripts/wave-orchestrator/launcher-runtime.mjs +7 -1
- package/scripts/wave-orchestrator/launcher.mjs +55 -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/shared.mjs +1 -0
- package/scripts/wave-orchestrator/skills.mjs +1 -0
- package/scripts/wave-orchestrator/task-entity.mjs +65 -45
- package/scripts/wave-orchestrator/wave-files.mjs +85 -1
- package/scripts/wave-orchestrator/wave-state-reducer.mjs +24 -7
- package/skills/README.md +7 -0
- package/skills/role-design/SKILL.md +50 -0
- package/skills/role-design/skill.json +36 -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
package/docs/reference/skills.md
CHANGED
|
@@ -109,6 +109,7 @@ Top-level and lane-local skill attachment use the same shape:
|
|
|
109
109
|
"dir": "skills",
|
|
110
110
|
"base": ["wave-core", "repo-coding-rules"],
|
|
111
111
|
"byRole": {
|
|
112
|
+
"design": ["role-design"],
|
|
112
113
|
"deploy": ["role-deploy"]
|
|
113
114
|
},
|
|
114
115
|
"byRuntime": {
|
|
@@ -123,6 +124,8 @@ Top-level and lane-local skill attachment use the same shape:
|
|
|
123
124
|
|
|
124
125
|
Lane-local `lanes.<lane>.skills` extends the global config instead of replacing it.
|
|
125
126
|
|
|
127
|
+
Optional design workers in the shipped `0.8.5` surface normally attach `role-design`. That bundle is intended for docs/spec-first design packets and explicit implementation handoff work before implementation starts. When the design packet covers terminal UX, dashboards, or other operator surfaces, add `tui-design` explicitly in the wave's `### Skills`.
|
|
128
|
+
|
|
126
129
|
## Resolution Order
|
|
127
130
|
|
|
128
131
|
Resolved skills are gathered in this order:
|
|
@@ -195,6 +198,12 @@ Runtime delivery:
|
|
|
195
198
|
|
|
196
199
|
These runtime projections are guidance surfaces. They should stay aligned with the canonical authority model, but they are not replay inputs or decision state on their own.
|
|
197
200
|
|
|
201
|
+
For the optional `design` worker role, the default pattern is:
|
|
202
|
+
|
|
203
|
+
- `role-design` for the design packet contract
|
|
204
|
+
- `tui-design` only when the packet covers terminal UX, dashboards, or other operator surfaces
|
|
205
|
+
- no runtime-specific coding bundle unless the wave explicitly gives the design steward code ownership and makes it a hybrid design steward
|
|
206
|
+
|
|
198
207
|
## Generated Artifacts
|
|
199
208
|
|
|
200
209
|
Executor overlay directories can contain:
|
|
@@ -225,3 +234,4 @@ Missing or malformed bundles are configuration errors, not silent no-ops.
|
|
|
225
234
|
- Use explicit per-agent `### Skills` for true exceptions, not as a substitute for missing activation metadata.
|
|
226
235
|
- Keep provider skills role-scoped unless every role genuinely needs the provider context.
|
|
227
236
|
- Keep bundle ids stable so traces and prompt fingerprints remain intelligible across runs.
|
|
237
|
+
- Keep `role-design` docs/spec-first by default; add `tui-design` when terminal or operator-surface work is in scope, and only attach broader coding bundles when the wave explicitly assigns code ownership and expects the same design steward to return for implementation.
|
package/package.json
CHANGED
package/releases/manifest.json
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "@chllming/wave-orchestration",
|
|
4
4
|
"releases": [
|
|
5
|
+
{
|
|
6
|
+
"version": "0.8.5",
|
|
7
|
+
"date": "2026-03-25",
|
|
8
|
+
"summary": "Shipped design-role support, hybrid design-steward execution, release-surface alignment, and a practical migration guide.",
|
|
9
|
+
"features": [
|
|
10
|
+
"The optional `design` worker role is now part of the published release surface, including the standing design prompt, `role-design`, and `tui-design` starter bundles.",
|
|
11
|
+
"Design stewards are docs-first by default, but waves can now explicitly give them implementation ownership so the same agent runs a design pass first and then rejoins implementation with normal proof obligations.",
|
|
12
|
+
"Design-aware validation, prompts, gates, retry or resume planning, reducer state, local-executor smoke behavior, and result-envelope projection now all agree on the same hybrid-design contract.",
|
|
13
|
+
"The migration guide now covers fresh adoption plus upgrades from `0.8.4`, `0.8.0`-`0.8.4`, `0.6.x`-`0.7.x`, and `0.5.x` or earlier, including repo-owned starter-surface sync guidance and `planner-agentic` corpus notes.",
|
|
14
|
+
"Shipped package metadata, README, current-state notes, sample-wave docs, and publishing guidance now point at the `0.8.5` release surface."
|
|
15
|
+
],
|
|
16
|
+
"manualSteps": [
|
|
17
|
+
"Run `pnpm exec wave doctor` and `pnpm exec wave launch --lane main --dry-run --no-dashboard` after upgrading so the repo validates against the `0.8.5` design-role and hybrid-design behavior.",
|
|
18
|
+
"If your repo copied starter prompts, skills, or authoring docs, sync `docs/agents/wave-design-role.md`, `skills/role-design/`, `skills/tui-design/`, `wave.config.json` design-role keys, and any local planner or runbook pages that should describe the new design-steward model.",
|
|
19
|
+
"If a repo uses hybrid design stewards, make sure each one still owns a design packet path and that any explicit implementation ownership also carries the expected exit contract, deliverables, proof artifacts, and component declarations where your lane validation requires them.",
|
|
20
|
+
"If your repo uses planner workflows and copied the planner starter corpus, keep `docs/agents/wave-planner-role.md`, `skills/role-planner/`, `docs/context7/planner-agent/`, `docs/reference/wave-planning-lessons.md`, and the `planner-agentic` bundle entry in sync before relying on local planner docs."
|
|
21
|
+
],
|
|
22
|
+
"breaking": false
|
|
23
|
+
},
|
|
5
24
|
{
|
|
6
25
|
"version": "0.8.4",
|
|
7
26
|
"date": "2026-03-25",
|
|
@@ -47,6 +47,8 @@ const WAVE_EVAL_REGEX =
|
|
|
47
47
|
/^\[wave-eval\]\s*state=(satisfied|needs-more-work|blocked)\s+targets=(\d+)\s+benchmarks=(\d+)\s+regressions=(\d+)(?:\s+target_ids=([^\s]+))?(?:\s+benchmark_ids=([^\s]+))?\s*(?:detail=(.*))?$/gim;
|
|
48
48
|
const WAVE_SECURITY_REGEX =
|
|
49
49
|
/^\[wave-security\]\s*state=(clear|concerns|blocked)\s+findings=(\d+)\s+approvals=(\d+)\s*(?:detail=(.*))?$/gim;
|
|
50
|
+
const WAVE_DESIGN_REGEX =
|
|
51
|
+
/^\[wave-design\]\s*state=(ready-for-implementation|needs-clarification|blocked)\s+decisions=(\d+)\s+assumptions=(\d+)\s+open_questions=(\d+)\s*(?:detail=(.*))?$/gim;
|
|
50
52
|
const WAVE_GATE_REGEX =
|
|
51
53
|
/^\[wave-gate\]\s*architecture=(pass|concerns|blocked)\s+integration=(pass|concerns|blocked)\s+durability=(pass|concerns|blocked)\s+live=(pass|concerns|blocked)\s+docs=(pass|concerns|blocked)\s*(?:detail=(.*))?$/gim;
|
|
52
54
|
const WAVE_GAP_REGEX =
|
|
@@ -64,6 +66,7 @@ const STRUCTURED_SIGNAL_KIND_BY_TAG = {
|
|
|
64
66
|
integration: "integration",
|
|
65
67
|
eval: "eval",
|
|
66
68
|
security: "security",
|
|
69
|
+
design: "design",
|
|
67
70
|
gate: "gate",
|
|
68
71
|
gap: "gap",
|
|
69
72
|
component: "component",
|
|
@@ -76,6 +79,7 @@ const STRUCTURED_SIGNAL_LINE_REGEX_BY_KIND = {
|
|
|
76
79
|
integration: new RegExp(WAVE_INTEGRATION_REGEX.source, "i"),
|
|
77
80
|
eval: new RegExp(WAVE_EVAL_REGEX.source, "i"),
|
|
78
81
|
security: new RegExp(WAVE_SECURITY_REGEX.source, "i"),
|
|
82
|
+
design: new RegExp(WAVE_DESIGN_REGEX.source, "i"),
|
|
79
83
|
gate: new RegExp(WAVE_GATE_REGEX.source, "i"),
|
|
80
84
|
gap: new RegExp(WAVE_GAP_REGEX.source, "i"),
|
|
81
85
|
component: new RegExp(WAVE_COMPONENT_REGEX.source, "i"),
|
|
@@ -89,6 +93,7 @@ function buildEmptyStructuredSignalDiagnostics() {
|
|
|
89
93
|
integration: { rawCount: 0, acceptedCount: 0, rejectedSamples: [] },
|
|
90
94
|
eval: { rawCount: 0, acceptedCount: 0, rejectedSamples: [] },
|
|
91
95
|
security: { rawCount: 0, acceptedCount: 0, rejectedSamples: [] },
|
|
96
|
+
design: { rawCount: 0, acceptedCount: 0, rejectedSamples: [] },
|
|
92
97
|
gate: { rawCount: 0, acceptedCount: 0, rejectedSamples: [] },
|
|
93
98
|
gap: { rawCount: 0, acceptedCount: 0, rejectedSamples: [] },
|
|
94
99
|
component: { rawCount: 0, acceptedCount: 0, rejectedSamples: [], seenComponentIds: [] },
|
|
@@ -509,6 +514,13 @@ export function buildAgentExecutionSummary({ agent, statusRecord, logPath, repor
|
|
|
509
514
|
approvals: Number.parseInt(String(match[3] || "0"), 10) || 0,
|
|
510
515
|
detail: cleanText(match[4]),
|
|
511
516
|
})),
|
|
517
|
+
design: findLastMatch(signalText, WAVE_DESIGN_REGEX, (match) => ({
|
|
518
|
+
state: match[1],
|
|
519
|
+
decisions: Number.parseInt(String(match[2] || "0"), 10) || 0,
|
|
520
|
+
assumptions: Number.parseInt(String(match[3] || "0"), 10) || 0,
|
|
521
|
+
openQuestions: Number.parseInt(String(match[4] || "0"), 10) || 0,
|
|
522
|
+
detail: cleanText(match[5]),
|
|
523
|
+
})),
|
|
512
524
|
gate: findLastMatch(signalText, WAVE_GATE_REGEX, (match) => ({
|
|
513
525
|
architecture: match[1],
|
|
514
526
|
integration: match[2],
|
|
@@ -951,6 +963,58 @@ export function validateSecuritySummary(agent, summary) {
|
|
|
951
963
|
};
|
|
952
964
|
}
|
|
953
965
|
|
|
966
|
+
export function validateDesignSummary(agent, summary) {
|
|
967
|
+
if (!summary?.design) {
|
|
968
|
+
return {
|
|
969
|
+
ok: false,
|
|
970
|
+
statusCode: "missing-wave-design",
|
|
971
|
+
detail: appendTerminationHint(
|
|
972
|
+
`Missing [wave-design] marker for ${agent?.agentId || "D1"}.`,
|
|
973
|
+
summary,
|
|
974
|
+
),
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
if (!summary.reportPath) {
|
|
978
|
+
return {
|
|
979
|
+
ok: false,
|
|
980
|
+
statusCode: "missing-design-packet",
|
|
981
|
+
detail: `Missing design packet path for ${agent?.agentId || "D1"}.`,
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
if (!fs.existsSync(path.resolve(REPO_ROOT, summary.reportPath))) {
|
|
985
|
+
return {
|
|
986
|
+
ok: false,
|
|
987
|
+
statusCode: "missing-design-packet",
|
|
988
|
+
detail: `Missing design packet at ${summary.reportPath}.`,
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
if (summary.design.state === "blocked") {
|
|
992
|
+
return {
|
|
993
|
+
ok: false,
|
|
994
|
+
statusCode: "design-blocked",
|
|
995
|
+
detail:
|
|
996
|
+
summary.design.detail ||
|
|
997
|
+
`Design packet reported blocked for ${agent?.agentId || "D1"}.`,
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
if (summary.design.state === "needs-clarification") {
|
|
1001
|
+
return {
|
|
1002
|
+
ok: false,
|
|
1003
|
+
statusCode: "design-needs-clarification",
|
|
1004
|
+
detail:
|
|
1005
|
+
summary.design.detail ||
|
|
1006
|
+
`Design packet requested clarification for ${agent?.agentId || "D1"}.`,
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
return {
|
|
1010
|
+
ok: true,
|
|
1011
|
+
statusCode: "pass",
|
|
1012
|
+
detail:
|
|
1013
|
+
summary.design.detail ||
|
|
1014
|
+
"Design packet is ready for implementation.",
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
|
|
954
1018
|
export function validateIntegrationSummary(agent, summary) {
|
|
955
1019
|
if (!summary?.integration) {
|
|
956
1020
|
return {
|
|
@@ -28,6 +28,7 @@ export const DEFAULT_INTEGRATION_ROLE_PROMPT_PATH = "docs/agents/wave-integratio
|
|
|
28
28
|
export const DEFAULT_DOCUMENTATION_ROLE_PROMPT_PATH =
|
|
29
29
|
"docs/agents/wave-documentation-role.md";
|
|
30
30
|
export const DEFAULT_SECURITY_ROLE_PROMPT_PATH = "docs/agents/wave-security-role.md";
|
|
31
|
+
export const DEFAULT_DESIGN_ROLE_PROMPT_PATH = "docs/agents/wave-design-role.md";
|
|
31
32
|
export const DEFAULT_TERMINALS_PATH = ".vscode/terminals.json";
|
|
32
33
|
export const DEFAULT_DOCS_DIR = "docs";
|
|
33
34
|
export const DEFAULT_STATE_ROOT = ".tmp";
|
|
@@ -348,6 +349,10 @@ function normalizeRoles(rawRoles = {}) {
|
|
|
348
349
|
rawRoles.securityRolePromptPath || DEFAULT_SECURITY_ROLE_PROMPT_PATH,
|
|
349
350
|
"roles.securityRolePromptPath",
|
|
350
351
|
),
|
|
352
|
+
designRolePromptPath: normalizeRepoRelativePath(
|
|
353
|
+
rawRoles.designRolePromptPath || DEFAULT_DESIGN_ROLE_PROMPT_PATH,
|
|
354
|
+
"roles.designRolePromptPath",
|
|
355
|
+
),
|
|
351
356
|
};
|
|
352
357
|
}
|
|
353
358
|
|
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
import { resolveEvalTargetsAgainstCatalog } from "./evals.mjs";
|
|
18
18
|
import {
|
|
19
19
|
isContEvalImplementationOwningAgent,
|
|
20
|
+
isDesignAgent,
|
|
21
|
+
isImplementationOwningDesignAgent,
|
|
20
22
|
isSecurityReviewAgent,
|
|
21
23
|
} from "./role-helpers.mjs";
|
|
22
24
|
|
|
@@ -199,6 +201,8 @@ export function buildExecutionPrompt({
|
|
|
199
201
|
evalTargets = null,
|
|
200
202
|
benchmarkCatalogPath = null,
|
|
201
203
|
sharedPlanDocs = null,
|
|
204
|
+
designPacketPaths = null,
|
|
205
|
+
designExecutionMode = null,
|
|
202
206
|
contQaAgentId = "A0",
|
|
203
207
|
contEvalAgentId = "E0",
|
|
204
208
|
integrationAgentId = "A8",
|
|
@@ -222,6 +226,10 @@ export function buildExecutionPrompt({
|
|
|
222
226
|
const contEvalImplementationOwning = isContEvalImplementationOwningAgent(agent, {
|
|
223
227
|
contEvalAgentId,
|
|
224
228
|
});
|
|
229
|
+
const hybridDesignAgent = isImplementationOwningDesignAgent(agent);
|
|
230
|
+
const designAgent = isDesignAgent(agent);
|
|
231
|
+
const designImplementationPass = designAgent && hybridDesignAgent && designExecutionMode === "implementation-pass";
|
|
232
|
+
const designPacketPass = designAgent && !designImplementationPass;
|
|
225
233
|
const resolvedEvalTargets = (() => {
|
|
226
234
|
try {
|
|
227
235
|
return resolveEvalTargetsAgainstCatalog(evalTargets, { benchmarkCatalogPath }).targets;
|
|
@@ -269,6 +277,22 @@ export function buildExecutionPrompt({
|
|
|
269
277
|
"- Use `clear` only when no unresolved findings or approvals remain. Use `blocked` only when the wave must stop before integration.",
|
|
270
278
|
]
|
|
271
279
|
: [];
|
|
280
|
+
const designRequirements = designAgent
|
|
281
|
+
? [
|
|
282
|
+
designImplementationPass
|
|
283
|
+
? "- You are in the hybrid design steward's implementation follow-through pass. Keep the design packet current, implement only your explicit owned paths, and finish with both `[wave-design]` and the normal implementation proof markers."
|
|
284
|
+
: "- You are the wave's design steward. Stay packet-first and docs/spec-owned unless the wave explicitly assigns more.",
|
|
285
|
+
"- Leave one design packet with these sections in order: `Problem`, `Constraints`, `Decisions`, `Assumptions`, `Open Questions`, `Interface Impacts`, `Validation Plan`, `Implementation Handoff`.",
|
|
286
|
+
"- Emit one final structured design marker: `[wave-design] state=<ready-for-implementation|needs-clarification|blocked> decisions=<n> assumptions=<n> open_questions=<n> detail=<short-note>`.",
|
|
287
|
+
"- Use `ready-for-implementation` only when downstream implementation owners can start without unresolved design ambiguity.",
|
|
288
|
+
"- Use `needs-clarification` when the wave should stop for a specific question or decision before coding starts.",
|
|
289
|
+
...(hybridDesignAgent && !designImplementationPass
|
|
290
|
+
? [
|
|
291
|
+
"- This wave also assigns you explicit implementation-owned files, but this first pass is still design-only. Do not claim implementation proof yet; the code-owning pass starts only after the design packet is ready.",
|
|
292
|
+
]
|
|
293
|
+
: []),
|
|
294
|
+
]
|
|
295
|
+
: [];
|
|
272
296
|
const coordinationCommand = [
|
|
273
297
|
"pnpm exec wave coord post",
|
|
274
298
|
`--lane ${lane}`,
|
|
@@ -280,6 +304,7 @@ export function buildExecutionPrompt({
|
|
|
280
304
|
].join(" ");
|
|
281
305
|
const implementationRequirements =
|
|
282
306
|
![contQaAgentId, documentationAgentId].includes(agent.agentId) &&
|
|
307
|
+
(!designAgent || designImplementationPass) &&
|
|
283
308
|
!isSecurityReviewAgent(agent) &&
|
|
284
309
|
(agent.agentId !== contEvalAgentId || contEvalImplementationOwning)
|
|
285
310
|
? [
|
|
@@ -294,7 +319,8 @@ export function buildExecutionPrompt({
|
|
|
294
319
|
`- Route unresolved architecture, integration, durability, ops, or docs issues through \`${coordinationCommand}\`. Do not append \`[wave-gap]\` lines after the final implementation markers.`,
|
|
295
320
|
]
|
|
296
321
|
: [];
|
|
297
|
-
const exitContractLines =
|
|
322
|
+
const exitContractLines =
|
|
323
|
+
implementationRequirements.length > 0 && agent.exitContract
|
|
298
324
|
? [
|
|
299
325
|
"Exit contract for this run:",
|
|
300
326
|
`- completion: ${agent.exitContract.completion}`,
|
|
@@ -385,6 +411,7 @@ export function buildExecutionPrompt({
|
|
|
385
411
|
: [];
|
|
386
412
|
const ownedComponentLines =
|
|
387
413
|
![contQaAgentId, documentationAgentId].includes(agent.agentId) &&
|
|
414
|
+
(!designAgent || designImplementationPass) &&
|
|
388
415
|
(agent.agentId !== contEvalAgentId || contEvalImplementationOwning) &&
|
|
389
416
|
Array.isArray(agent.components) &&
|
|
390
417
|
agent.components.length > 0
|
|
@@ -398,6 +425,7 @@ export function buildExecutionPrompt({
|
|
|
398
425
|
]
|
|
399
426
|
: [];
|
|
400
427
|
const deliverableLines =
|
|
428
|
+
(!designAgent || designImplementationPass) &&
|
|
401
429
|
Array.isArray(agent.deliverables) && agent.deliverables.length > 0
|
|
402
430
|
? [
|
|
403
431
|
"Deliverables required for this agent:",
|
|
@@ -405,7 +433,18 @@ export function buildExecutionPrompt({
|
|
|
405
433
|
"",
|
|
406
434
|
]
|
|
407
435
|
: [];
|
|
436
|
+
const designPacketLines =
|
|
437
|
+
!designAgent &&
|
|
438
|
+
Array.isArray(designPacketPaths) &&
|
|
439
|
+
designPacketPaths.length > 0
|
|
440
|
+
? [
|
|
441
|
+
"Same-wave design packets to read before coding:",
|
|
442
|
+
...designPacketPaths.map((designPacketPath) => `- ${designPacketPath}`),
|
|
443
|
+
"",
|
|
444
|
+
]
|
|
445
|
+
: [];
|
|
408
446
|
const proofArtifactLines =
|
|
447
|
+
implementationRequirements.length > 0 &&
|
|
409
448
|
Array.isArray(agent.proofArtifacts) && agent.proofArtifacts.length > 0
|
|
410
449
|
? [
|
|
411
450
|
"Proof artifacts required for this agent:",
|
|
@@ -473,6 +512,7 @@ export function buildExecutionPrompt({
|
|
|
473
512
|
...contEvalRequirements,
|
|
474
513
|
...docStewardRequirements,
|
|
475
514
|
...securityRequirements,
|
|
515
|
+
...designRequirements,
|
|
476
516
|
...implementationRequirements,
|
|
477
517
|
`- Update docs impacted by your implementation. If your work changes status, sequencing, ownership, or explicit proof expectations, update the relevant docs. If shared plan docs need changes outside your owned files, post the exact doc paths and exact delta needed for ${sharedPlanDocList} as a coordination record instead of leaving documentation drift for later cleanup.`,
|
|
478
518
|
"- If the wave defines a documentation steward or other explicit owner for shared plan docs, coordinate those updates through that owner, notify them as soon as the delta is known, and stay engaged until they confirm `closed` or `no-change`. Do not treat the ownership boundary as the definition of done.",
|
|
@@ -503,6 +543,7 @@ export function buildExecutionPrompt({
|
|
|
503
543
|
...evalTargetLines,
|
|
504
544
|
...ownedComponentLines,
|
|
505
545
|
...deliverableLines,
|
|
546
|
+
...designPacketLines,
|
|
506
547
|
...proofArtifactLines,
|
|
507
548
|
...skillLines,
|
|
508
549
|
...context7PromptLines,
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
agentSummaryPathFromStatusPath,
|
|
5
5
|
buildAgentExecutionSummary,
|
|
6
6
|
readAgentExecutionSummary,
|
|
7
|
+
validateDesignSummary,
|
|
7
8
|
validateContQaSummary,
|
|
8
9
|
validateContEvalSummary,
|
|
9
10
|
validateImplementationSummary,
|
|
@@ -33,7 +34,10 @@ import {
|
|
|
33
34
|
writeJsonAtomic,
|
|
34
35
|
} from "./shared.mjs";
|
|
35
36
|
import {
|
|
37
|
+
isDocsOnlyDesignAgent,
|
|
36
38
|
isSecurityReviewAgent,
|
|
39
|
+
isDesignAgent,
|
|
40
|
+
resolveDesignReportPath,
|
|
37
41
|
resolveSecurityReviewReportPath,
|
|
38
42
|
isContEvalReportOnlyAgent,
|
|
39
43
|
} from "./role-helpers.mjs";
|
|
@@ -94,6 +98,10 @@ function resolveRunReportPath(wave, runInfo) {
|
|
|
94
98
|
const securityReportPath = resolveSecurityReviewReportPath(runInfo.agent);
|
|
95
99
|
return securityReportPath ? path.resolve(REPO_ROOT, securityReportPath) : null;
|
|
96
100
|
}
|
|
101
|
+
if (isDesignAgent(runInfo.agent)) {
|
|
102
|
+
const designReportPath = resolveDesignReportPath(runInfo.agent);
|
|
103
|
+
return designReportPath ? path.resolve(REPO_ROOT, designReportPath) : null;
|
|
104
|
+
}
|
|
97
105
|
return null;
|
|
98
106
|
}
|
|
99
107
|
|
|
@@ -513,6 +521,7 @@ export function readWaveImplementationGate(wave, agentRuns, options = {}) {
|
|
|
513
521
|
if (
|
|
514
522
|
[contQaAgentId, integrationAgentId, documentationAgentId].includes(runInfo.agent.agentId) ||
|
|
515
523
|
isContEvalReportOnlyAgent(runInfo.agent, { contEvalAgentId }) ||
|
|
524
|
+
isDocsOnlyDesignAgent(runInfo.agent) ||
|
|
516
525
|
isSecurityReviewAgent(runInfo.agent)
|
|
517
526
|
) {
|
|
518
527
|
continue;
|
|
@@ -563,6 +572,65 @@ export function readWaveImplementationGate(wave, agentRuns, options = {}) {
|
|
|
563
572
|
};
|
|
564
573
|
}
|
|
565
574
|
|
|
575
|
+
export function readWaveDesignGate(wave, agentRuns, options = {}) {
|
|
576
|
+
const mode = normalizeReadMode(options.mode || "live");
|
|
577
|
+
const designRuns = (agentRuns || []).filter((run) => isDesignAgent(run.agent));
|
|
578
|
+
if (designRuns.length === 0) {
|
|
579
|
+
return {
|
|
580
|
+
ok: true,
|
|
581
|
+
agentId: null,
|
|
582
|
+
statusCode: "pass",
|
|
583
|
+
detail: "No design agent declared for this wave.",
|
|
584
|
+
logPath: null,
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
for (const runInfo of designRuns) {
|
|
588
|
+
const envelopeResult = readRunResultEnvelope(runInfo, wave, { mode });
|
|
589
|
+
if (mode === "live" && !envelopeResult.valid) {
|
|
590
|
+
return {
|
|
591
|
+
ok: false,
|
|
592
|
+
agentId: runInfo.agent.agentId,
|
|
593
|
+
statusCode:
|
|
594
|
+
envelopeResult.source === "missing-envelope"
|
|
595
|
+
? "missing-result-envelope"
|
|
596
|
+
: "invalid-result-envelope",
|
|
597
|
+
detail:
|
|
598
|
+
envelopeResult.detail ||
|
|
599
|
+
`Missing structured design result envelope for ${runInfo.agent.agentId}.`,
|
|
600
|
+
logPath: path.relative(REPO_ROOT, runInfo.logPath),
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
const summary = envelopeResult.valid
|
|
604
|
+
? projectLegacySummaryFromEnvelope(
|
|
605
|
+
envelopeResult.envelope,
|
|
606
|
+
buildEnvelopeReadOptions(
|
|
607
|
+
runInfo,
|
|
608
|
+
wave,
|
|
609
|
+
runInfo?.statusPath ? readStatusRecordIfPresent(runInfo.statusPath) : null,
|
|
610
|
+
resolveRunReportPath(wave, runInfo),
|
|
611
|
+
),
|
|
612
|
+
)
|
|
613
|
+
: readRunExecutionSummary(runInfo, wave, { mode });
|
|
614
|
+
const validation = validateDesignSummary(runInfo.agent, summary);
|
|
615
|
+
if (!validation.ok) {
|
|
616
|
+
return {
|
|
617
|
+
ok: false,
|
|
618
|
+
agentId: runInfo.agent.agentId,
|
|
619
|
+
statusCode: validation.statusCode,
|
|
620
|
+
detail: validation.detail,
|
|
621
|
+
logPath: summary?.logPath || path.relative(REPO_ROOT, runInfo.logPath),
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return {
|
|
626
|
+
ok: true,
|
|
627
|
+
agentId: null,
|
|
628
|
+
statusCode: "pass",
|
|
629
|
+
detail: "All design packets are ready for implementation.",
|
|
630
|
+
logPath: null,
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
566
634
|
export function analyzePromotedComponentOwners(componentId, agentRuns, summariesByAgentId) {
|
|
567
635
|
const ownerRuns = (agentRuns || []).filter((runInfo) =>
|
|
568
636
|
runInfo.agent.components?.includes(componentId),
|
|
@@ -1088,6 +1156,7 @@ export function readWaveImplementationGatePure(wave, agentResults, options = {})
|
|
|
1088
1156
|
if (
|
|
1089
1157
|
[contQaAgentId, integrationAgentId, documentationAgentId].includes(agent.agentId) ||
|
|
1090
1158
|
isContEvalReportOnlyAgent(agent, { contEvalAgentId }) ||
|
|
1159
|
+
isDocsOnlyDesignAgent(agent) ||
|
|
1091
1160
|
isSecurityReviewAgent(agent)
|
|
1092
1161
|
) {
|
|
1093
1162
|
continue;
|
|
@@ -1110,6 +1179,40 @@ export function readWaveImplementationGatePure(wave, agentResults, options = {})
|
|
|
1110
1179
|
};
|
|
1111
1180
|
}
|
|
1112
1181
|
|
|
1182
|
+
export function readWaveDesignGatePure(wave, agentResults) {
|
|
1183
|
+
const agents = Array.isArray(wave.agents) ? wave.agents : [];
|
|
1184
|
+
const designAgents = agents.filter((agent) => isDesignAgent(agent));
|
|
1185
|
+
if (designAgents.length === 0) {
|
|
1186
|
+
return {
|
|
1187
|
+
ok: true,
|
|
1188
|
+
agentId: null,
|
|
1189
|
+
statusCode: "pass",
|
|
1190
|
+
detail: "No design agent declared for this wave.",
|
|
1191
|
+
logPath: null,
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
for (const agent of designAgents) {
|
|
1195
|
+
const summary = agentResults?.[agent.agentId] || null;
|
|
1196
|
+
const validation = validateDesignSummary(agent, summary);
|
|
1197
|
+
if (!validation.ok) {
|
|
1198
|
+
return {
|
|
1199
|
+
ok: false,
|
|
1200
|
+
agentId: agent.agentId,
|
|
1201
|
+
statusCode: validation.statusCode,
|
|
1202
|
+
detail: validation.detail,
|
|
1203
|
+
logPath: summary?.logPath || null,
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
return {
|
|
1208
|
+
ok: true,
|
|
1209
|
+
agentId: null,
|
|
1210
|
+
statusCode: "pass",
|
|
1211
|
+
detail: "All design packets are ready for implementation.",
|
|
1212
|
+
logPath: null,
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1113
1216
|
export function readWaveContQaGatePure(wave, agentResults, options = {}) {
|
|
1114
1217
|
const mode = String(options.mode || "live").trim().toLowerCase();
|
|
1115
1218
|
const contQaAgentId = options.contQaAgentId || wave.contQaAgentId || "A0";
|
|
@@ -1312,6 +1415,7 @@ export function readWaveInfraGatePure(wave, agentResults, options = {}) {
|
|
|
1312
1415
|
}
|
|
1313
1416
|
|
|
1314
1417
|
export function buildGateSnapshotPure({ wave, agentResults, derivedState, validationMode = "live", laneConfig = {} }) {
|
|
1418
|
+
const designGate = readWaveDesignGatePure(wave, agentResults);
|
|
1315
1419
|
const implementationGate = readWaveImplementationGatePure(wave, agentResults, {
|
|
1316
1420
|
contQaAgentId: laneConfig.contQaAgentId, contEvalAgentId: laneConfig.contEvalAgentId,
|
|
1317
1421
|
integrationAgentId: laneConfig.integrationAgentId, documentationAgentId: laneConfig.documentationAgentId,
|
|
@@ -1363,7 +1467,7 @@ export function buildGateSnapshotPure({ wave, agentResults, derivedState, valida
|
|
|
1363
1467
|
const helperAssignmentBarrier = derivedState?.helperAssignmentBarrier || { ok: true, statusCode: "pass", detail: "" };
|
|
1364
1468
|
const dependencyBarrier = derivedState?.dependencyBarrier || { ok: true, statusCode: "pass", detail: "" };
|
|
1365
1469
|
const orderedGates = [
|
|
1366
|
-
["implementationGate", implementationGate], ["componentGate", componentGate],
|
|
1470
|
+
["designGate", designGate], ["implementationGate", implementationGate], ["componentGate", componentGate],
|
|
1367
1471
|
["helperAssignmentBarrier", helperAssignmentBarrier], ["dependencyBarrier", dependencyBarrier],
|
|
1368
1472
|
["contEvalGate", contEvalGate], ["securityGate", securityGate],
|
|
1369
1473
|
["integrationBarrier", integrationBarrier], ["documentationGate", documentationGate],
|
|
@@ -1372,7 +1476,7 @@ export function buildGateSnapshotPure({ wave, agentResults, derivedState, valida
|
|
|
1372
1476
|
];
|
|
1373
1477
|
const firstFailure = orderedGates.find(([, gate]) => gate?.ok === false);
|
|
1374
1478
|
return {
|
|
1375
|
-
implementationGate, componentGate, integrationGate: integrationMarkerGate,
|
|
1479
|
+
designGate, implementationGate, componentGate, integrationGate: integrationMarkerGate,
|
|
1376
1480
|
integrationBarrier, documentationGate, componentMatrixGate,
|
|
1377
1481
|
contEvalGate, securityGate, contQaGate, infraGate,
|
|
1378
1482
|
clarificationBarrier, helperAssignmentBarrier, dependencyBarrier,
|
|
@@ -29,6 +29,7 @@ export const STARTER_TEMPLATE_PATHS = [
|
|
|
29
29
|
"wave.config.json",
|
|
30
30
|
"docs/README.md",
|
|
31
31
|
"docs/agents/wave-documentation-role.md",
|
|
32
|
+
"docs/agents/wave-design-role.md",
|
|
32
33
|
"docs/agents/wave-cont-qa-role.md",
|
|
33
34
|
"docs/agents/wave-cont-eval-role.md",
|
|
34
35
|
"docs/agents/wave-integration-role.md",
|
|
@@ -74,6 +75,8 @@ export const STARTER_TEMPLATE_PATHS = [
|
|
|
74
75
|
"docs/reference/sample-waves.md",
|
|
75
76
|
"docs/reference/skills.md",
|
|
76
77
|
"docs/reference/wave-planning-lessons.md",
|
|
78
|
+
"skills/role-design/SKILL.md",
|
|
79
|
+
"skills/role-design/skill.json",
|
|
77
80
|
"docs/reference/runtime-config/README.md",
|
|
78
81
|
"docs/reference/runtime-config/codex.md",
|
|
79
82
|
"docs/reference/runtime-config/claude.md",
|
|
@@ -14,7 +14,7 @@ import { readStatusCodeIfPresent } from "./dashboard-state.mjs";
|
|
|
14
14
|
import { buildExecutorLaunchSpec } from "./executors.mjs";
|
|
15
15
|
import { hashAgentPromptFingerprint, prefetchContext7ForSelection } from "./context7.mjs";
|
|
16
16
|
import { killTmuxSessionIfExists } from "./terminals.mjs";
|
|
17
|
-
import { resolveWaveRoleBindings } from "./role-helpers.mjs";
|
|
17
|
+
import { isDesignAgent, resolveDesignReportPath, resolveWaveRoleBindings } from "./role-helpers.mjs";
|
|
18
18
|
import {
|
|
19
19
|
resolveAgentSkills,
|
|
20
20
|
summarizeResolvedSkills,
|
|
@@ -77,6 +77,7 @@ export async function launchAgentSession(lanePaths, params, { runTmuxFn }) {
|
|
|
77
77
|
agentRateLimitBaseDelaySeconds,
|
|
78
78
|
agentRateLimitMaxDelaySeconds,
|
|
79
79
|
context7Enabled,
|
|
80
|
+
designExecutionMode = null,
|
|
80
81
|
dryRun = false,
|
|
81
82
|
} = params;
|
|
82
83
|
ensureDirectory(path.dirname(promptPath));
|
|
@@ -123,6 +124,11 @@ export async function launchAgentSession(lanePaths, params, { runTmuxFn }) {
|
|
|
123
124
|
evalTargets: resolvedWaveDefinition.evalTargets,
|
|
124
125
|
benchmarkCatalogPath: lanePaths.laneProfile?.paths?.benchmarkCatalogPath,
|
|
125
126
|
sharedPlanDocs: lanePaths.sharedPlanDocs,
|
|
127
|
+
designPacketPaths: (resolvedWaveDefinition.agents || [])
|
|
128
|
+
.filter((waveAgent) => isDesignAgent(waveAgent))
|
|
129
|
+
.map((waveAgent) => resolveDesignReportPath(waveAgent))
|
|
130
|
+
.filter(Boolean),
|
|
131
|
+
designExecutionMode,
|
|
126
132
|
});
|
|
127
133
|
const promptHash = hashAgentPromptFingerprint(agent);
|
|
128
134
|
fs.writeFileSync(promptPath, `${prompt}\n`, "utf8");
|
|
@@ -131,6 +131,8 @@ import {
|
|
|
131
131
|
isContEvalImplementationOwningAgent,
|
|
132
132
|
isContEvalReportOnlyAgent,
|
|
133
133
|
isClosureRoleAgentId,
|
|
134
|
+
isDesignAgent,
|
|
135
|
+
isImplementationOwningDesignAgent,
|
|
134
136
|
isSecurityReviewAgent,
|
|
135
137
|
resolveWaveRoleBindings,
|
|
136
138
|
resolveSecurityReviewReportPath,
|
|
@@ -496,6 +498,8 @@ function buildGateSnapshot(params) {
|
|
|
496
498
|
|
|
497
499
|
function waveGateLabel(gateName) {
|
|
498
500
|
switch (gateName) {
|
|
501
|
+
case "designGate":
|
|
502
|
+
return "Design packet";
|
|
499
503
|
case "implementationGate":
|
|
500
504
|
return "Implementation exit contract";
|
|
501
505
|
case "componentGate":
|
|
@@ -527,6 +531,8 @@ function waveGateLabel(gateName) {
|
|
|
527
531
|
|
|
528
532
|
function waveGateActionRequested(gateName, lanePaths) {
|
|
529
533
|
switch (gateName) {
|
|
534
|
+
case "designGate":
|
|
535
|
+
return `Lane ${lanePaths.lane} owners should close the design packet or clarification gap before implementation starts.`;
|
|
530
536
|
case "implementationGate":
|
|
531
537
|
return `Lane ${lanePaths.lane} owners should resolve the implementation contract gap before wave progression.`;
|
|
532
538
|
case "componentGate":
|
|
@@ -1335,8 +1341,11 @@ export async function runLauncherCli(argv) {
|
|
|
1335
1341
|
});
|
|
1336
1342
|
|
|
1337
1343
|
const launchedImplementationRuns = runsToLaunch.filter(
|
|
1338
|
-
(run) =>
|
|
1344
|
+
(run) =>
|
|
1345
|
+
!isClosureRoleAgentId(run.agent.agentId, roleBindings) &&
|
|
1346
|
+
(!isDesignAgent(run.agent) || isImplementationOwningDesignAgent(run.agent)),
|
|
1339
1347
|
);
|
|
1348
|
+
const launchedDesignRuns = runsToLaunch.filter((run) => isDesignAgent(run.agent));
|
|
1340
1349
|
const closureOnlyRetry =
|
|
1341
1350
|
runsToLaunch.length > 0 &&
|
|
1342
1351
|
launchedImplementationRuns.length === 0 &&
|
|
@@ -1395,6 +1404,14 @@ export async function runLauncherCli(argv) {
|
|
|
1395
1404
|
agentRateLimitBaseDelaySeconds: options.agentRateLimitBaseDelaySeconds,
|
|
1396
1405
|
agentRateLimitMaxDelaySeconds: options.agentRateLimitMaxDelaySeconds,
|
|
1397
1406
|
context7Enabled: options.context7Enabled,
|
|
1407
|
+
designExecutionMode:
|
|
1408
|
+
isDesignAgent(runInfo.agent)
|
|
1409
|
+
? launchedImplementationRuns.some(
|
|
1410
|
+
(candidate) => candidate.agent.agentId === runInfo.agent.agentId,
|
|
1411
|
+
)
|
|
1412
|
+
? "implementation-pass"
|
|
1413
|
+
: "design-pass"
|
|
1414
|
+
: null,
|
|
1398
1415
|
attempt,
|
|
1399
1416
|
controlPlane: {
|
|
1400
1417
|
waveNumber: wave.wave,
|
|
@@ -1507,6 +1524,43 @@ export async function runLauncherCli(argv) {
|
|
|
1507
1524
|
}
|
|
1508
1525
|
|
|
1509
1526
|
if (failures.length === 0) {
|
|
1527
|
+
if (launchedDesignRuns.length > 0 && launchedImplementationRuns.length === 0) {
|
|
1528
|
+
const reducerDecision = refreshReducerSnapshot(attempt);
|
|
1529
|
+
const designGate = reducerDecision?.reducerState?.gateSnapshot?.designGate || null;
|
|
1530
|
+
const remainingImplementationRuns = agentRuns.filter(
|
|
1531
|
+
(run) =>
|
|
1532
|
+
!preCompletedAgentIds.has(run.agent.agentId) &&
|
|
1533
|
+
!isClosureRoleAgentId(run.agent.agentId, roleBindings) &&
|
|
1534
|
+
(!isDesignAgent(run.agent) || isImplementationOwningDesignAgent(run.agent)),
|
|
1535
|
+
);
|
|
1536
|
+
if (designGate?.ok && remainingImplementationRuns.length > 0) {
|
|
1537
|
+
recordAttemptState(lanePaths, wave.wave, attempt, "completed", {
|
|
1538
|
+
selectedAgentIds: runsToLaunch.map((run) => run.agent.agentId),
|
|
1539
|
+
detail: `Design pass complete; continuing with implementation agents ${remainingImplementationRuns.map((run) => run.agent.agentId).join(", ")}.`,
|
|
1540
|
+
});
|
|
1541
|
+
recordCombinedEvent({
|
|
1542
|
+
message: `Design pass complete; launching implementation agents next: ${remainingImplementationRuns.map((run) => run.agent.agentId).join(", ")}.`,
|
|
1543
|
+
});
|
|
1544
|
+
appendCoordination({
|
|
1545
|
+
event: "wave_design_ready",
|
|
1546
|
+
waves: [wave.wave],
|
|
1547
|
+
status: "running",
|
|
1548
|
+
details: `next_agents=${remainingImplementationRuns.map((run) => run.agent.agentId).join(",")}`,
|
|
1549
|
+
actionRequested: "None",
|
|
1550
|
+
});
|
|
1551
|
+
runsToLaunch = remainingImplementationRuns;
|
|
1552
|
+
for (const run of runsToLaunch) {
|
|
1553
|
+
setWaveDashboardAgent(dashboardState, run.agent.agentId, {
|
|
1554
|
+
state: "pending",
|
|
1555
|
+
detail: "Queued after design handoff",
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
flushDashboards();
|
|
1559
|
+
attempt += 1;
|
|
1560
|
+
traceAttempt += 1;
|
|
1561
|
+
continue;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1510
1564
|
const implementationGate = readWaveImplementationGate(wave, agentRuns);
|
|
1511
1565
|
if (!implementationGate.ok) {
|
|
1512
1566
|
failures = [
|