@chllming/wave-orchestration 0.5.2 → 0.5.4
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 +15 -0
- package/README.md +56 -501
- package/docs/README.md +39 -0
- package/docs/concepts/context7-vs-skills.md +94 -0
- package/docs/concepts/operating-modes.md +91 -0
- package/docs/concepts/runtime-agnostic-orchestration.md +95 -0
- package/docs/concepts/what-is-a-wave.md +133 -0
- package/docs/guides/planner.md +113 -0
- package/docs/guides/terminal-surfaces.md +80 -0
- package/docs/image.png +0 -0
- package/docs/plans/context7-wave-orchestrator.md +2 -0
- package/docs/plans/current-state.md +10 -0
- package/docs/plans/master-plan.md +3 -3
- package/docs/plans/migration.md +4 -3
- package/docs/plans/wave-orchestrator.md +27 -3
- package/docs/reference/runtime-config/README.md +19 -0
- package/docs/reference/skills.md +156 -0
- package/docs/roadmap.md +160 -564
- package/package.json +2 -1
- package/releases/manifest.json +32 -0
- package/scripts/wave-orchestrator/config.mjs +17 -0
- package/scripts/wave-orchestrator/context7.mjs +9 -0
- package/scripts/wave-orchestrator/coordination.mjs +16 -0
- package/scripts/wave-orchestrator/executors.mjs +24 -11
- package/scripts/wave-orchestrator/install.mjs +41 -2
- package/scripts/wave-orchestrator/launcher.mjs +131 -25
- package/scripts/wave-orchestrator/planner.mjs +1328 -0
- package/scripts/wave-orchestrator/project-profile.mjs +190 -0
- package/scripts/wave-orchestrator/shared.mjs +2 -0
- package/scripts/wave-orchestrator/skills.mjs +448 -0
- package/scripts/wave-orchestrator/terminals.mjs +16 -0
- package/scripts/wave-orchestrator/traces.mjs +23 -0
- package/scripts/wave-orchestrator/wave-files.mjs +299 -84
- package/scripts/wave.mjs +11 -0
- package/skills/provider-aws/SKILL.md +6 -0
- package/skills/provider-aws/skill.json +5 -0
- package/skills/provider-custom-deploy/SKILL.md +5 -0
- package/skills/provider-custom-deploy/skill.json +5 -0
- package/skills/provider-docker-compose/SKILL.md +6 -0
- package/skills/provider-docker-compose/skill.json +5 -0
- package/skills/provider-github-release/SKILL.md +6 -0
- package/skills/provider-github-release/skill.json +5 -0
- package/skills/provider-kubernetes/SKILL.md +6 -0
- package/skills/provider-kubernetes/skill.json +5 -0
- package/skills/provider-railway/SKILL.md +6 -0
- package/skills/provider-railway/adapters/claude.md +1 -0
- package/skills/provider-railway/adapters/codex.md +1 -0
- package/skills/provider-railway/adapters/local.md +1 -0
- package/skills/provider-railway/adapters/opencode.md +1 -0
- package/skills/provider-railway/skill.json +5 -0
- package/skills/provider-ssh-manual/SKILL.md +6 -0
- package/skills/provider-ssh-manual/skill.json +5 -0
- package/skills/repo-coding-rules/SKILL.md +7 -0
- package/skills/repo-coding-rules/skill.json +5 -0
- package/skills/role-deploy/SKILL.md +6 -0
- package/skills/role-deploy/skill.json +5 -0
- package/skills/role-documentation/SKILL.md +6 -0
- package/skills/role-documentation/skill.json +5 -0
- package/skills/role-evaluator/SKILL.md +6 -0
- package/skills/role-evaluator/skill.json +5 -0
- package/skills/role-implementation/SKILL.md +6 -0
- package/skills/role-implementation/skill.json +5 -0
- package/skills/role-infra/SKILL.md +6 -0
- package/skills/role-infra/skill.json +5 -0
- package/skills/role-integration/SKILL.md +6 -0
- package/skills/role-integration/skill.json +5 -0
- package/skills/role-research/SKILL.md +6 -0
- package/skills/role-research/skill.json +5 -0
- package/skills/runtime-claude/SKILL.md +6 -0
- package/skills/runtime-claude/skill.json +5 -0
- package/skills/runtime-codex/SKILL.md +6 -0
- package/skills/runtime-codex/skill.json +5 -0
- package/skills/runtime-local/SKILL.md +5 -0
- package/skills/runtime-local/skill.json +5 -0
- package/skills/runtime-opencode/SKILL.md +6 -0
- package/skills/runtime-opencode/skill.json +5 -0
- package/skills/wave-core/SKILL.md +7 -0
- package/skills/wave-core/skill.json +5 -0
- package/wave.config.json +27 -0
|
@@ -671,6 +671,29 @@ function buildAgentMetadata(dir, run, attempt, artifacts) {
|
|
|
671
671
|
run.lastContext7?.snippetHash ||
|
|
672
672
|
(run.lastContext7?.promptText ? hashText(run.lastContext7.promptText) : ""),
|
|
673
673
|
},
|
|
674
|
+
skills:
|
|
675
|
+
run.lastSkillProjection ||
|
|
676
|
+
(run.agent?.skillsResolved
|
|
677
|
+
? {
|
|
678
|
+
ids: run.agent.skillsResolved.ids || [],
|
|
679
|
+
role: run.agent.skillsResolved.role || null,
|
|
680
|
+
runtime: run.agent.skillsResolved.runtime || null,
|
|
681
|
+
deployKind: run.agent.skillsResolved.deployKind || null,
|
|
682
|
+
promptHash: run.agent.skillsResolved.promptHash || null,
|
|
683
|
+
bundles: Array.isArray(run.agent.skillsResolved.bundles)
|
|
684
|
+
? run.agent.skillsResolved.bundles.map((bundle) => ({
|
|
685
|
+
id: bundle.id,
|
|
686
|
+
bundlePath: bundle.bundlePath,
|
|
687
|
+
manifestPath: bundle.manifestPath,
|
|
688
|
+
skillPath: bundle.skillPath,
|
|
689
|
+
adapterPath: bundle.adapterPath || null,
|
|
690
|
+
bundleHash: bundle.bundleHash || null,
|
|
691
|
+
sourceFiles: Array.isArray(bundle.sourceFiles) ? bundle.sourceFiles.slice() : [],
|
|
692
|
+
}))
|
|
693
|
+
: [],
|
|
694
|
+
artifacts: run.agent.skillsResolved.artifacts || null,
|
|
695
|
+
}
|
|
696
|
+
: null),
|
|
674
697
|
};
|
|
675
698
|
}
|
|
676
699
|
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
} from "./shared.mjs";
|
|
31
31
|
import { normalizeContext7Config, hashAgentPromptFingerprint } from "./context7.mjs";
|
|
32
32
|
import {
|
|
33
|
+
isOpenCoordinationStatus,
|
|
33
34
|
openClarificationLinkedRequests,
|
|
34
35
|
readMaterializedCoordinationState,
|
|
35
36
|
} from "./coordination-store.mjs";
|
|
@@ -42,6 +43,7 @@ import {
|
|
|
42
43
|
validateIntegrationSummary,
|
|
43
44
|
validateImplementationSummary,
|
|
44
45
|
} from "./agent-state.mjs";
|
|
46
|
+
import { normalizeSkillId, resolveAgentSkills } from "./skills.mjs";
|
|
45
47
|
|
|
46
48
|
export const WAVE_EVALUATOR_ROLE_PROMPT_PATH = DEFAULT_EVALUATOR_ROLE_PROMPT_PATH;
|
|
47
49
|
export const WAVE_INTEGRATION_ROLE_PROMPT_PATH = DEFAULT_INTEGRATION_ROLE_PROMPT_PATH;
|
|
@@ -201,6 +203,76 @@ function parsePathList(blockText, filePath, label) {
|
|
|
201
203
|
return paths;
|
|
202
204
|
}
|
|
203
205
|
|
|
206
|
+
function parseSkillsList(blockText, filePath, label) {
|
|
207
|
+
if (!blockText) {
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
const skills = [];
|
|
211
|
+
const seen = new Set();
|
|
212
|
+
for (const line of String(blockText || "").split(/\r?\n/)) {
|
|
213
|
+
const trimmed = line.trim();
|
|
214
|
+
if (!trimmed) {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const bulletMatch = trimmed.match(/^-\s+(.+?)\s*$/);
|
|
218
|
+
if (!bulletMatch) {
|
|
219
|
+
throw new Error(`Malformed skill entry "${trimmed}" in ${label} (${filePath})`);
|
|
220
|
+
}
|
|
221
|
+
const skillId = normalizeSkillId(
|
|
222
|
+
bulletMatch[1].replace(/[`"']/g, "").trim(),
|
|
223
|
+
`${label} (${filePath})`,
|
|
224
|
+
);
|
|
225
|
+
if (seen.has(skillId)) {
|
|
226
|
+
throw new Error(`Duplicate skill "${skillId}" in ${label} (${filePath})`);
|
|
227
|
+
}
|
|
228
|
+
seen.add(skillId);
|
|
229
|
+
skills.push(skillId);
|
|
230
|
+
}
|
|
231
|
+
return skills;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function parseDeployEnvironments(blockText, filePath) {
|
|
235
|
+
if (!blockText) {
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
const environments = [];
|
|
239
|
+
const seen = new Set();
|
|
240
|
+
for (const line of String(blockText || "").split(/\r?\n/)) {
|
|
241
|
+
const trimmed = line.trim();
|
|
242
|
+
if (!trimmed) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const match = trimmed.match(
|
|
246
|
+
/^-\s+`?([a-z0-9][a-z0-9._-]*)`?\s*:\s*`?([a-z0-9][a-z0-9._-]*)`?\s*(default)?(?:\s+\((.*)\))?$/i,
|
|
247
|
+
);
|
|
248
|
+
if (!match) {
|
|
249
|
+
throw new Error(`Malformed deploy environment "${trimmed}" in ${filePath}`);
|
|
250
|
+
}
|
|
251
|
+
const id = String(match[1] || "").trim().toLowerCase();
|
|
252
|
+
const kind = String(match[2] || "").trim().toLowerCase();
|
|
253
|
+
if (seen.has(id)) {
|
|
254
|
+
throw new Error(`Duplicate deploy environment "${id}" in ${filePath}`);
|
|
255
|
+
}
|
|
256
|
+
seen.add(id);
|
|
257
|
+
environments.push({
|
|
258
|
+
id,
|
|
259
|
+
kind,
|
|
260
|
+
isDefault: cleanBooleanToken(match[3]),
|
|
261
|
+
notes: String(match[4] || "").trim() || null,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
if (!environments.some((environment) => environment.isDefault) && environments.length > 0) {
|
|
265
|
+
environments[0].isDefault = true;
|
|
266
|
+
}
|
|
267
|
+
return environments;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function cleanBooleanToken(value) {
|
|
271
|
+
return String(value || "")
|
|
272
|
+
.trim()
|
|
273
|
+
.toLowerCase() === "default";
|
|
274
|
+
}
|
|
275
|
+
|
|
204
276
|
function normalizeRepoRelativePath(relPath) {
|
|
205
277
|
return String(relPath || "")
|
|
206
278
|
.replaceAll("\\", "/")
|
|
@@ -259,6 +331,13 @@ export function extractPromptFromSection(sectionText, filePath, agentId) {
|
|
|
259
331
|
return extractFencedBlock(promptBlock, `Agent ${agentId} in ${filePath}`);
|
|
260
332
|
}
|
|
261
333
|
|
|
334
|
+
function extractAgentSkillsFromSection(sectionText, filePath, agentId) {
|
|
335
|
+
const skillsBlock = extractSectionBody(sectionText, "Skills", filePath, agentId, {
|
|
336
|
+
required: false,
|
|
337
|
+
});
|
|
338
|
+
return parseSkillsList(skillsBlock, filePath, `agent ${agentId} skills`);
|
|
339
|
+
}
|
|
340
|
+
|
|
262
341
|
function parseContext7Settings(blockText, filePath, label) {
|
|
263
342
|
if (!blockText) {
|
|
264
343
|
return null;
|
|
@@ -1280,6 +1359,7 @@ export function parseWaveContent(content, filePath, options = {}) {
|
|
|
1280
1359
|
const executorConfig = extractExecutorConfigFromSection(sectionText, filePath, current.agentId);
|
|
1281
1360
|
const components = extractAgentComponentsFromSection(sectionText, filePath, current.agentId);
|
|
1282
1361
|
const capabilities = extractAgentCapabilitiesFromSection(sectionText, filePath, current.agentId);
|
|
1362
|
+
const skills = extractAgentSkillsFromSection(sectionText, filePath, current.agentId);
|
|
1283
1363
|
const deliverables = extractAgentDeliverablesFromSection(
|
|
1284
1364
|
sectionText,
|
|
1285
1365
|
filePath,
|
|
@@ -1309,6 +1389,7 @@ export function parseWaveContent(content, filePath, options = {}) {
|
|
|
1309
1389
|
executorConfig,
|
|
1310
1390
|
components,
|
|
1311
1391
|
capabilities,
|
|
1392
|
+
skills,
|
|
1312
1393
|
deliverables,
|
|
1313
1394
|
ownedPaths,
|
|
1314
1395
|
});
|
|
@@ -1329,6 +1410,12 @@ export function parseWaveContent(content, filePath, options = {}) {
|
|
|
1329
1410
|
wave: waveNumber,
|
|
1330
1411
|
file: path.relative(REPO_ROOT, filePath),
|
|
1331
1412
|
commitMessage: commitMessageMatch ? commitMessageMatch[1] : null,
|
|
1413
|
+
deployEnvironments: parseDeployEnvironments(
|
|
1414
|
+
extractTopLevelSectionBody(content, "Deploy environments", filePath, {
|
|
1415
|
+
required: false,
|
|
1416
|
+
}),
|
|
1417
|
+
filePath,
|
|
1418
|
+
),
|
|
1332
1419
|
context7Defaults: extractWaveContext7Defaults(content, filePath),
|
|
1333
1420
|
componentPromotions,
|
|
1334
1421
|
agents: agentsWithComponentTargets,
|
|
@@ -1608,11 +1695,19 @@ export function resolveAgentExecutor(agent, options = {}) {
|
|
|
1608
1695
|
}
|
|
1609
1696
|
|
|
1610
1697
|
export function applyExecutorSelectionsToWave(wave, options = {}) {
|
|
1611
|
-
|
|
1698
|
+
const laneProfile = resolveLaneProfileForOptions(options);
|
|
1699
|
+
const withExecutors = {
|
|
1612
1700
|
...wave,
|
|
1613
1701
|
agents: wave.agents.map((agent) => ({
|
|
1614
1702
|
...agent,
|
|
1615
|
-
executorResolved: resolveAgentExecutor(agent, options),
|
|
1703
|
+
executorResolved: resolveAgentExecutor(agent, { ...options, laneProfile }),
|
|
1704
|
+
})),
|
|
1705
|
+
};
|
|
1706
|
+
return {
|
|
1707
|
+
...withExecutors,
|
|
1708
|
+
agents: withExecutors.agents.map((agent) => ({
|
|
1709
|
+
...agent,
|
|
1710
|
+
skillsResolved: resolveAgentSkills(agent, withExecutors, { laneProfile }),
|
|
1616
1711
|
})),
|
|
1617
1712
|
};
|
|
1618
1713
|
}
|
|
@@ -1878,7 +1973,21 @@ export function readWaveEvaluatorArtifacts(wave, { logsDir, evaluatorAgentId } =
|
|
|
1878
1973
|
};
|
|
1879
1974
|
}
|
|
1880
1975
|
|
|
1881
|
-
|
|
1976
|
+
function pushWaveCompletionReason(reasons, code, detail) {
|
|
1977
|
+
const normalizedCode = String(code || "").trim();
|
|
1978
|
+
const normalizedDetail = String(detail || "").trim();
|
|
1979
|
+
if (!normalizedCode || !normalizedDetail) {
|
|
1980
|
+
return;
|
|
1981
|
+
}
|
|
1982
|
+
if (
|
|
1983
|
+
reasons.some((reason) => reason.code === normalizedCode && reason.detail === normalizedDetail)
|
|
1984
|
+
) {
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1987
|
+
reasons.push({ code: normalizedCode, detail: normalizedDetail });
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
function analyzeWaveCompletionFromStatusFiles(wave, statusDir, options = {}) {
|
|
1882
1991
|
const logsDir = options.logsDir || path.join(path.resolve(statusDir, ".."), "logs");
|
|
1883
1992
|
const coordinationDir =
|
|
1884
1993
|
options.coordinationDir || path.join(path.resolve(statusDir, ".."), "coordination");
|
|
@@ -1893,96 +2002,195 @@ export function completedWavesFromStatusFiles(allWaves, statusDir, options = {})
|
|
|
1893
2002
|
const componentThreshold =
|
|
1894
2003
|
options.requireComponentPromotionsFromWave ??
|
|
1895
2004
|
laneProfile.validation.requireComponentPromotionsFromWave;
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
)
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
if (
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
2005
|
+
|
|
2006
|
+
const reasons = [];
|
|
2007
|
+
const summariesByAgentId = {};
|
|
2008
|
+
const missingStatusAgents = [];
|
|
2009
|
+
let statusesReady = wave.agents.length > 0;
|
|
2010
|
+
|
|
2011
|
+
for (const agent of wave.agents) {
|
|
2012
|
+
const statusPath = path.join(statusDir, `wave-${wave.wave}-${agent.slug}.status`);
|
|
2013
|
+
const statusRecord = readStatusRecordIfPresent(statusPath);
|
|
2014
|
+
if (!statusRecord) {
|
|
2015
|
+
missingStatusAgents.push(agent.agentId);
|
|
2016
|
+
statusesReady = false;
|
|
2017
|
+
continue;
|
|
2018
|
+
}
|
|
2019
|
+
const expectedPromptHash = hashAgentPromptFingerprint(agent);
|
|
2020
|
+
if (statusRecord.code !== 0) {
|
|
2021
|
+
pushWaveCompletionReason(
|
|
2022
|
+
reasons,
|
|
2023
|
+
"nonzero-status",
|
|
2024
|
+
`${agent.agentId} exited ${statusRecord.code} in ${path.relative(REPO_ROOT, statusPath)}.`,
|
|
2025
|
+
);
|
|
2026
|
+
statusesReady = false;
|
|
2027
|
+
continue;
|
|
2028
|
+
}
|
|
2029
|
+
if (statusRecord.promptHash !== expectedPromptHash) {
|
|
2030
|
+
pushWaveCompletionReason(
|
|
2031
|
+
reasons,
|
|
2032
|
+
"prompt-hash-mismatch",
|
|
2033
|
+
`${agent.agentId} status in ${path.relative(REPO_ROOT, statusPath)} does not match the current prompt fingerprint.`,
|
|
2034
|
+
);
|
|
2035
|
+
statusesReady = false;
|
|
2036
|
+
continue;
|
|
2037
|
+
}
|
|
2038
|
+
const summary = readAgentExecutionSummary(statusPath);
|
|
2039
|
+
summariesByAgentId[agent.agentId] = summary;
|
|
2040
|
+
if (agent.agentId === evaluatorAgentId) {
|
|
2041
|
+
if (summary) {
|
|
2042
|
+
const validation = validateEvaluatorSummary(agent, summary);
|
|
2043
|
+
if (!validation.ok) {
|
|
2044
|
+
pushWaveCompletionReason(
|
|
2045
|
+
reasons,
|
|
2046
|
+
"invalid-evaluator-summary",
|
|
2047
|
+
`${agent.agentId}: ${validation.statusCode}: ${validation.detail}`,
|
|
2048
|
+
);
|
|
2049
|
+
statusesReady = false;
|
|
1936
2050
|
}
|
|
1937
|
-
continue;
|
|
1938
|
-
}
|
|
1939
|
-
if (!validateImplementationSummary(agent, summary).ok) {
|
|
1940
|
-
waveIsComplete = false;
|
|
1941
|
-
break;
|
|
1942
2051
|
}
|
|
2052
|
+
continue;
|
|
1943
2053
|
}
|
|
1944
2054
|
if (
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
wave.wave >=
|
|
1948
|
-
!validateWaveComponentPromotions(wave, summariesByAgentId, options).ok
|
|
2055
|
+
agent.agentId === integrationAgentId &&
|
|
2056
|
+
integrationThreshold !== null &&
|
|
2057
|
+
wave.wave >= integrationThreshold
|
|
1949
2058
|
) {
|
|
1950
|
-
|
|
2059
|
+
const validation = validateIntegrationSummary(agent, summary);
|
|
2060
|
+
if (!validation.ok) {
|
|
2061
|
+
pushWaveCompletionReason(
|
|
2062
|
+
reasons,
|
|
2063
|
+
"invalid-integration-summary",
|
|
2064
|
+
`${agent.agentId}: ${validation.statusCode}: ${validation.detail}`,
|
|
2065
|
+
);
|
|
2066
|
+
statusesReady = false;
|
|
2067
|
+
}
|
|
2068
|
+
continue;
|
|
1951
2069
|
}
|
|
1952
|
-
if (
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
2070
|
+
if (agent.agentId === documentationAgentId) {
|
|
2071
|
+
const validation = validateDocumentationClosureSummary(agent, summary);
|
|
2072
|
+
if (!validation.ok) {
|
|
2073
|
+
pushWaveCompletionReason(
|
|
2074
|
+
reasons,
|
|
2075
|
+
"invalid-documentation-summary",
|
|
2076
|
+
`${agent.agentId}: ${validation.statusCode}: ${validation.detail}`,
|
|
2077
|
+
);
|
|
2078
|
+
statusesReady = false;
|
|
2079
|
+
}
|
|
2080
|
+
continue;
|
|
1959
2081
|
}
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
2082
|
+
const validation = validateImplementationSummary(agent, summary);
|
|
2083
|
+
if (!validation.ok) {
|
|
2084
|
+
pushWaveCompletionReason(
|
|
2085
|
+
reasons,
|
|
2086
|
+
"invalid-implementation-summary",
|
|
2087
|
+
`${agent.agentId}: ${validation.statusCode}: ${validation.detail}`,
|
|
2088
|
+
);
|
|
2089
|
+
statusesReady = false;
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
if (missingStatusAgents.length > 0) {
|
|
2094
|
+
pushWaveCompletionReason(
|
|
2095
|
+
reasons,
|
|
2096
|
+
"missing-status",
|
|
2097
|
+
`Missing status files for ${missingStatusAgents.join(", ")}.`,
|
|
2098
|
+
);
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
if (
|
|
2102
|
+
statusesReady &&
|
|
2103
|
+
componentThreshold !== null &&
|
|
2104
|
+
wave.wave >= componentThreshold
|
|
2105
|
+
) {
|
|
2106
|
+
const promotionsValidation = validateWaveComponentPromotions(wave, summariesByAgentId, options);
|
|
2107
|
+
if (!promotionsValidation.ok) {
|
|
2108
|
+
pushWaveCompletionReason(
|
|
2109
|
+
reasons,
|
|
2110
|
+
"component-promotions-invalid",
|
|
2111
|
+
promotionsValidation.detail,
|
|
2112
|
+
);
|
|
2113
|
+
statusesReady = false;
|
|
1965
2114
|
}
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
2115
|
+
const matrixValidation = validateWaveComponentMatrixCurrentLevels(wave, {
|
|
2116
|
+
...options,
|
|
2117
|
+
laneProfile,
|
|
2118
|
+
});
|
|
2119
|
+
if (!matrixValidation.ok) {
|
|
2120
|
+
pushWaveCompletionReason(
|
|
2121
|
+
reasons,
|
|
2122
|
+
"component-matrix-invalid",
|
|
2123
|
+
matrixValidation.detail,
|
|
1969
2124
|
);
|
|
1970
|
-
|
|
1971
|
-
coordinationState.clarifications.some((record) =>
|
|
1972
|
-
["open", "acknowledged", "in_progress"].includes(record.status),
|
|
1973
|
-
) ||
|
|
1974
|
-
openClarificationLinkedRequests(coordinationState).length > 0 ||
|
|
1975
|
-
coordinationState.humanEscalations.some((record) =>
|
|
1976
|
-
["open", "acknowledged", "in_progress"].includes(record.status),
|
|
1977
|
-
) ||
|
|
1978
|
-
coordinationState.humanFeedback.some((record) =>
|
|
1979
|
-
["open", "acknowledged", "in_progress"].includes(record.status),
|
|
1980
|
-
)
|
|
1981
|
-
) {
|
|
1982
|
-
waveIsComplete = false;
|
|
1983
|
-
}
|
|
2125
|
+
statusesReady = false;
|
|
1984
2126
|
}
|
|
1985
|
-
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
if (statusesReady) {
|
|
2130
|
+
const evaluatorArtifacts = readWaveEvaluatorArtifacts(wave, {
|
|
2131
|
+
logsDir,
|
|
2132
|
+
evaluatorAgentId,
|
|
2133
|
+
});
|
|
2134
|
+
if (!evaluatorArtifacts.ok) {
|
|
2135
|
+
pushWaveCompletionReason(reasons, evaluatorArtifacts.statusCode, evaluatorArtifacts.detail);
|
|
2136
|
+
statusesReady = false;
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
const coordinationState = readMaterializedCoordinationState(
|
|
2141
|
+
path.join(coordinationDir, `wave-${wave.wave}.jsonl`),
|
|
2142
|
+
);
|
|
2143
|
+
const openClarificationIds = coordinationState.clarifications
|
|
2144
|
+
.filter((record) => isOpenCoordinationStatus(record.status))
|
|
2145
|
+
.map((record) => record.id);
|
|
2146
|
+
if (openClarificationIds.length > 0) {
|
|
2147
|
+
pushWaveCompletionReason(
|
|
2148
|
+
reasons,
|
|
2149
|
+
"open-clarification",
|
|
2150
|
+
`Open clarification records: ${openClarificationIds.join(", ")}.`,
|
|
2151
|
+
);
|
|
2152
|
+
}
|
|
2153
|
+
const openClarificationRequestIds = openClarificationLinkedRequests(coordinationState).map(
|
|
2154
|
+
(record) => record.id,
|
|
2155
|
+
);
|
|
2156
|
+
if (openClarificationRequestIds.length > 0) {
|
|
2157
|
+
pushWaveCompletionReason(
|
|
2158
|
+
reasons,
|
|
2159
|
+
"open-clarification-request",
|
|
2160
|
+
`Open clarification-linked requests: ${openClarificationRequestIds.join(", ")}.`,
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
const openHumanEscalationIds = coordinationState.humanEscalations
|
|
2164
|
+
.filter((record) => isOpenCoordinationStatus(record.status))
|
|
2165
|
+
.map((record) => record.id);
|
|
2166
|
+
if (openHumanEscalationIds.length > 0) {
|
|
2167
|
+
pushWaveCompletionReason(
|
|
2168
|
+
reasons,
|
|
2169
|
+
"open-human-escalation",
|
|
2170
|
+
`Open human escalation records: ${openHumanEscalationIds.join(", ")}.`,
|
|
2171
|
+
);
|
|
2172
|
+
}
|
|
2173
|
+
const openHumanFeedbackIds = coordinationState.humanFeedback
|
|
2174
|
+
.filter((record) => isOpenCoordinationStatus(record.status))
|
|
2175
|
+
.map((record) => record.id);
|
|
2176
|
+
if (openHumanFeedbackIds.length > 0) {
|
|
2177
|
+
pushWaveCompletionReason(
|
|
2178
|
+
reasons,
|
|
2179
|
+
"open-human-feedback",
|
|
2180
|
+
`Open human feedback records: ${openHumanFeedbackIds.join(", ")}.`,
|
|
2181
|
+
);
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
return {
|
|
2185
|
+
ok: reasons.length === 0,
|
|
2186
|
+
reasons,
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
export function completedWavesFromStatusFiles(allWaves, statusDir, options = {}) {
|
|
2191
|
+
const completed = [];
|
|
2192
|
+
for (const wave of allWaves) {
|
|
2193
|
+
if (analyzeWaveCompletionFromStatusFiles(wave, statusDir, options).ok) {
|
|
1986
2194
|
completed.push(wave.wave);
|
|
1987
2195
|
}
|
|
1988
2196
|
}
|
|
@@ -1990,7 +2198,13 @@ export function completedWavesFromStatusFiles(allWaves, statusDir, options = {})
|
|
|
1990
2198
|
}
|
|
1991
2199
|
|
|
1992
2200
|
export function reconcileRunStateFromStatusFiles(allWaves, runStatePath, statusDir, options = {}) {
|
|
1993
|
-
const
|
|
2201
|
+
const diagnostics = allWaves.map((wave) => ({
|
|
2202
|
+
wave: wave.wave,
|
|
2203
|
+
...analyzeWaveCompletionFromStatusFiles(wave, statusDir, options),
|
|
2204
|
+
}));
|
|
2205
|
+
const completedFromStatus = diagnostics
|
|
2206
|
+
.filter((diagnostic) => diagnostic.ok)
|
|
2207
|
+
.map((diagnostic) => diagnostic.wave);
|
|
1994
2208
|
const before = readRunState(runStatePath);
|
|
1995
2209
|
const firstMerge = normalizeCompletedWaves([...before.completedWaves, ...completedFromStatus]);
|
|
1996
2210
|
const latest = readRunState(runStatePath);
|
|
@@ -2003,6 +2217,7 @@ export function reconcileRunStateFromStatusFiles(allWaves, runStatePath, statusD
|
|
|
2003
2217
|
completedFromStatus,
|
|
2004
2218
|
addedFromBefore: firstMerge.filter((waveNumber) => !before.completedWaves.includes(waveNumber)),
|
|
2005
2219
|
addedFromLatest: merged.filter((waveNumber) => !latest.completedWaves.includes(waveNumber)),
|
|
2220
|
+
blockedFromStatus: diagnostics.filter((diagnostic) => !diagnostic.ok),
|
|
2006
2221
|
state,
|
|
2007
2222
|
};
|
|
2008
2223
|
}
|
package/scripts/wave.mjs
CHANGED
|
@@ -14,6 +14,9 @@ function printHelp() {
|
|
|
14
14
|
wave upgrade [options]
|
|
15
15
|
wave changelog [options]
|
|
16
16
|
wave doctor [options]
|
|
17
|
+
wave project setup [options]
|
|
18
|
+
wave project show [options]
|
|
19
|
+
wave draft [draft options]
|
|
17
20
|
wave launch [launcher options]
|
|
18
21
|
wave autonomous [autonomous options]
|
|
19
22
|
wave feedback [feedback options]
|
|
@@ -40,6 +43,14 @@ if (["init", "upgrade", "changelog", "doctor"].includes(subcommand)) {
|
|
|
40
43
|
console.error(`[wave] ${error instanceof Error ? error.message : String(error)}`);
|
|
41
44
|
process.exit(Number.isInteger(error?.exitCode) ? error.exitCode : 1);
|
|
42
45
|
}
|
|
46
|
+
} else if (subcommand === "project" || subcommand === "draft") {
|
|
47
|
+
try {
|
|
48
|
+
const { runPlannerCli } = await import("./wave-orchestrator/planner.mjs");
|
|
49
|
+
await runPlannerCli([subcommand, ...rest]);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error(`[wave] ${error instanceof Error ? error.message : String(error)}`);
|
|
52
|
+
process.exit(Number.isInteger(error?.exitCode) ? error.exitCode : 1);
|
|
53
|
+
}
|
|
43
54
|
} else if (subcommand === "launch") {
|
|
44
55
|
try {
|
|
45
56
|
const { runLauncherCli } = await import("./wave-orchestrator/launcher.mjs");
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# AWS
|
|
2
|
+
|
|
3
|
+
- Name the exact AWS service, account, region, and resource involved.
|
|
4
|
+
- Prefer explicit CLI or console-equivalent evidence for deployment and environment state.
|
|
5
|
+
- Separate IAM or identity issues from workload health or rollout issues.
|
|
6
|
+
- If AWS state is inferred indirectly, mark the proof gap instead of implying live verification.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Custom Deploy
|
|
2
|
+
|
|
3
|
+
- Make the custom environment contract explicit before treating it as proved.
|
|
4
|
+
- Name the exact verification surface, command, or operator artifact used as evidence.
|
|
5
|
+
- If the environment lacks a stable verification path, record the resulting deploy risk.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Docker Compose
|
|
2
|
+
|
|
3
|
+
- Use compose file names, service names, ports, and health checks exactly.
|
|
4
|
+
- Distinguish local container health from production readiness.
|
|
5
|
+
- Record the exact compose commands or logs used as proof.
|
|
6
|
+
- Make service dependency and readiness ordering explicit when rollout depends on it.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# GitHub Release
|
|
2
|
+
|
|
3
|
+
- Keep tag names, release ids, asset names, and notes exact.
|
|
4
|
+
- Distinguish draft, prerelease, and published release state explicitly.
|
|
5
|
+
- Treat release notes, attached artifacts, and publication state as separate proof surfaces.
|
|
6
|
+
- If publication depends on another deploy system, keep that dependency explicit.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Kubernetes
|
|
2
|
+
|
|
3
|
+
- Name the exact cluster, namespace, workload, and rollout surface involved.
|
|
4
|
+
- Prefer explicit `kubectl` state, health, and event evidence over generic rollout notes.
|
|
5
|
+
- Distinguish manifest drift, admission failure, image failure, and readiness failure.
|
|
6
|
+
- If rollback or restart is involved, make the operator-visible recovery posture explicit.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Railway
|
|
2
|
+
|
|
3
|
+
- Prefer the Railway MCP or Railway CLI as the source of truth for deployment, environment, and service state.
|
|
4
|
+
- Keep service names, environment names, domains, and deployment ids exact.
|
|
5
|
+
- Record what was verified: build logs, deploy logs, variables, domains, or rollout state.
|
|
6
|
+
- If Railway state is degraded or ambiguous, leave a concrete deploy risk instead of implying healthy rollout.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Prefer Railway MCP or CLI commands that produce exact deployment, environment, and service evidence. Avoid vague rollout claims without Railway-backed proof.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Use Railway MCP tools when they are available in-session. Fall back to the Railway CLI only when MCP coverage is missing or insufficient.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
The local runtime cannot prove live Railway state. Limit output to prompt smoke validation and identify any missing live verification.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Use injected Railway files and commands as the authoritative path for service and deployment verification. Keep Railway identifiers exact in output.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# SSH Manual
|
|
2
|
+
|
|
3
|
+
- Treat manual host access as high-risk and fail closed on missing proof.
|
|
4
|
+
- Record the exact host or surface touched and the exact checks performed.
|
|
5
|
+
- Avoid destructive manual changes unless explicitly approved by the task and repo policy.
|
|
6
|
+
- Convert shell observations into explicit environment or deploy status markers.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Repo Coding Rules
|
|
2
|
+
|
|
3
|
+
- Read `AGENTS.md` before making material edits if it exists.
|
|
4
|
+
- Prefer small, reviewable changes that preserve existing repo patterns.
|
|
5
|
+
- Run the relevant tests or checks for touched surfaces and fix regressions caused by your changes.
|
|
6
|
+
- Keep docs aligned when implementation changes status, ownership, or proof expectations.
|
|
7
|
+
- Do not push by default unless the task explicitly asks for it.
|