@chllming/wave-orchestration 0.8.9 → 0.9.0
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 +29 -0
- package/README.md +23 -7
- package/docs/README.md +5 -3
- package/docs/concepts/context7-vs-skills.md +1 -1
- package/docs/concepts/operating-modes.md +1 -1
- package/docs/concepts/what-is-a-wave.md +1 -1
- package/docs/guides/author-and-run-waves.md +14 -1
- package/docs/guides/monorepo-projects.md +226 -0
- package/docs/guides/planner.md +9 -2
- package/docs/guides/{recommendations-0.8.9.md → recommendations-0.9.0.md} +7 -7
- package/docs/plans/current-state.md +8 -6
- package/docs/plans/end-state-architecture.md +1 -1
- package/docs/plans/examples/wave-example-design-handoff.md +3 -1
- package/docs/plans/examples/wave-example-live-proof.md +6 -1
- package/docs/plans/examples/wave-example-rollout-fidelity.md +2 -0
- package/docs/plans/migration.md +21 -18
- package/docs/plans/wave-orchestrator.md +4 -4
- package/docs/reference/cli-reference.md +55 -51
- package/docs/reference/coordination-and-closure.md +1 -1
- package/docs/reference/npmjs-trusted-publishing.md +2 -2
- package/docs/reference/runtime-config/README.md +140 -12
- package/docs/reference/sample-waves.md +100 -5
- package/docs/reference/skills.md +1 -1
- package/docs/reference/wave-control.md +23 -5
- package/docs/roadmap.md +2 -2
- package/package.json +1 -1
- package/releases/manifest.json +19 -0
- package/scripts/wave-orchestrator/adhoc.mjs +49 -17
- package/scripts/wave-orchestrator/autonomous.mjs +49 -15
- package/scripts/wave-orchestrator/benchmark-external.mjs +23 -7
- package/scripts/wave-orchestrator/benchmark.mjs +33 -10
- package/scripts/wave-orchestrator/config.mjs +239 -24
- package/scripts/wave-orchestrator/control-cli.mjs +29 -23
- package/scripts/wave-orchestrator/coord-cli.mjs +22 -14
- package/scripts/wave-orchestrator/coordination-store.mjs +8 -0
- package/scripts/wave-orchestrator/dashboard-renderer.mjs +10 -3
- package/scripts/wave-orchestrator/dep-cli.mjs +47 -21
- package/scripts/wave-orchestrator/feedback.mjs +28 -11
- package/scripts/wave-orchestrator/human-input-resolution.mjs +5 -1
- package/scripts/wave-orchestrator/launcher.mjs +24 -3
- package/scripts/wave-orchestrator/planner.mjs +48 -27
- package/scripts/wave-orchestrator/project-profile.mjs +31 -8
- package/scripts/wave-orchestrator/proof-cli.mjs +18 -12
- package/scripts/wave-orchestrator/retry-cli.mjs +19 -13
- package/scripts/wave-orchestrator/shared.mjs +77 -14
- package/scripts/wave-orchestrator/wave-control-client.mjs +84 -16
- package/scripts/wave-orchestrator/wave-files.mjs +5 -1
|
@@ -17,6 +17,7 @@ const REPO_ROOT = WORKSPACE_ROOT;
|
|
|
17
17
|
|
|
18
18
|
export const DEFAULT_WAVE_CONFIG_PATH = path.join(REPO_ROOT, "wave.config.json");
|
|
19
19
|
export const DEFAULT_WAVE_LANE = "main";
|
|
20
|
+
export const DEFAULT_PROJECT_ID = "default";
|
|
20
21
|
export const DEFAULT_CONT_QA_AGENT_ID = "A0";
|
|
21
22
|
export const DEFAULT_CONT_EVAL_AGENT_ID = "E0";
|
|
22
23
|
export const DEFAULT_INTEGRATION_AGENT_ID = "A8";
|
|
@@ -73,7 +74,8 @@ export const CODEX_SANDBOX_MODES = ["read-only", "workspace-write", "danger-full
|
|
|
73
74
|
export const DEFAULT_CLAUDE_COMMAND = "claude";
|
|
74
75
|
export const DEFAULT_OPENCODE_COMMAND = "opencode";
|
|
75
76
|
export const DEFAULT_WAVE_CONTROL_AUTH_TOKEN_ENV_VAR = "WAVE_CONTROL_AUTH_TOKEN";
|
|
76
|
-
export const
|
|
77
|
+
export const DEFAULT_WAVE_CONTROL_ENDPOINT = "https://wave-control.up.railway.app/api/v1";
|
|
78
|
+
export const DEFAULT_WAVE_CONTROL_REPORT_MODE = "metadata-only";
|
|
77
79
|
export const DEFAULT_WAVE_CONTROL_REQUEST_TIMEOUT_MS = 5000;
|
|
78
80
|
export const DEFAULT_WAVE_CONTROL_FLUSH_BATCH_SIZE = 25;
|
|
79
81
|
export const DEFAULT_WAVE_CONTROL_MAX_PENDING_EVENTS = 1000;
|
|
@@ -137,6 +139,22 @@ function sanitizeLaneName(value) {
|
|
|
137
139
|
return lane;
|
|
138
140
|
}
|
|
139
141
|
|
|
142
|
+
export function sanitizeProjectId(value) {
|
|
143
|
+
const projectId = String(value || "")
|
|
144
|
+
.trim()
|
|
145
|
+
.toLowerCase()
|
|
146
|
+
.replace(/[^a-z0-9_-]+/g, "-")
|
|
147
|
+
.replace(/-+/g, "-")
|
|
148
|
+
.replace(/^-+|-+$/g, "");
|
|
149
|
+
if (!projectId) {
|
|
150
|
+
throw new Error("Project id is required");
|
|
151
|
+
}
|
|
152
|
+
if (!/^[a-z0-9][a-z0-9_-]*$/.test(projectId)) {
|
|
153
|
+
throw new Error(`Invalid project id: ${value}`);
|
|
154
|
+
}
|
|
155
|
+
return projectId;
|
|
156
|
+
}
|
|
157
|
+
|
|
140
158
|
function readJsonOrNull(filePath) {
|
|
141
159
|
try {
|
|
142
160
|
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
@@ -185,6 +203,26 @@ function normalizeOptionalString(value, fallback = null) {
|
|
|
185
203
|
return normalized || fallback;
|
|
186
204
|
}
|
|
187
205
|
|
|
206
|
+
function joinRepoPath(basePath, childPath) {
|
|
207
|
+
const base = String(basePath || "")
|
|
208
|
+
.replaceAll("\\", "/")
|
|
209
|
+
.replace(/^\.\/+/, "")
|
|
210
|
+
.replace(/\/+/g, "/")
|
|
211
|
+
.replace(/\/$/, "");
|
|
212
|
+
const child = String(childPath || "")
|
|
213
|
+
.replaceAll("\\", "/")
|
|
214
|
+
.replace(/^\.\/+/, "")
|
|
215
|
+
.replace(/\/+/g, "/")
|
|
216
|
+
.replace(/\/$/, "");
|
|
217
|
+
if (!base || base === ".") {
|
|
218
|
+
return child;
|
|
219
|
+
}
|
|
220
|
+
if (!child) {
|
|
221
|
+
return base;
|
|
222
|
+
}
|
|
223
|
+
return `${base}/${child}`.replace(/\/+/g, "/");
|
|
224
|
+
}
|
|
225
|
+
|
|
188
226
|
function normalizeOptionalPositiveInt(value, label, fallback = null) {
|
|
189
227
|
if (value === null || value === undefined || value === "") {
|
|
190
228
|
return fallback;
|
|
@@ -282,11 +320,11 @@ function normalizeThreshold(value, fallback) {
|
|
|
282
320
|
return parsed;
|
|
283
321
|
}
|
|
284
322
|
|
|
285
|
-
function defaultDocsDirForLane(lane, defaultLane, repoMode) {
|
|
323
|
+
function defaultDocsDirForLane(lane, defaultLane, repoMode, projectDocsDir = DEFAULT_DOCS_DIR) {
|
|
286
324
|
if (repoMode === "single-repo" && lane === defaultLane) {
|
|
287
|
-
return
|
|
325
|
+
return projectDocsDir;
|
|
288
326
|
}
|
|
289
|
-
return
|
|
327
|
+
return joinRepoPath(projectDocsDir, lane);
|
|
290
328
|
}
|
|
291
329
|
|
|
292
330
|
function defaultPlansDir(docsDir) {
|
|
@@ -549,7 +587,7 @@ function normalizeWaveControl(rawWaveControl = {}, label = "waveControl") {
|
|
|
549
587
|
reportMode !== "disabled" && normalizeOptionalBoolean(waveControl.enabled, true);
|
|
550
588
|
return {
|
|
551
589
|
enabled,
|
|
552
|
-
endpoint: normalizeOptionalString(waveControl.endpoint,
|
|
590
|
+
endpoint: normalizeOptionalString(waveControl.endpoint, DEFAULT_WAVE_CONTROL_ENDPOINT),
|
|
553
591
|
workspaceId: normalizeOptionalString(waveControl.workspaceId, null),
|
|
554
592
|
projectId: normalizeOptionalString(waveControl.projectId, null),
|
|
555
593
|
authTokenEnvVar:
|
|
@@ -931,6 +969,7 @@ export function loadWaveConfig(configPath = DEFAULT_WAVE_CONFIG_PATH) {
|
|
|
931
969
|
throw new Error(`Unsupported repoMode in ${path.relative(REPO_ROOT, configPath)}: ${repoMode}`);
|
|
932
970
|
}
|
|
933
971
|
const defaultLane = sanitizeLaneName(rawConfig.defaultLane || DEFAULT_WAVE_LANE);
|
|
972
|
+
const defaultProject = sanitizeProjectId(rawConfig.defaultProject || DEFAULT_PROJECT_ID);
|
|
934
973
|
const paths = {
|
|
935
974
|
docsDir: normalizeRepoRelativePath(rawConfig.paths?.docsDir || DEFAULT_DOCS_DIR, "paths.docsDir"),
|
|
936
975
|
stateRoot: normalizeRepoRelativePath(
|
|
@@ -966,17 +1005,122 @@ export function loadWaveConfig(configPath = DEFAULT_WAVE_CONFIG_PATH) {
|
|
|
966
1005
|
};
|
|
967
1006
|
const sharedPlanDocs =
|
|
968
1007
|
normalizeOptionalPathArray(rawConfig.sharedPlanDocs, "sharedPlanDocs") || null;
|
|
969
|
-
const
|
|
1008
|
+
const legacyLanes = Object.fromEntries(
|
|
970
1009
|
Object.entries(rawConfig.lanes || {}).map(([laneName, laneConfig]) => [
|
|
971
1010
|
sanitizeLaneName(laneName),
|
|
972
1011
|
laneConfig || {},
|
|
973
1012
|
]),
|
|
974
1013
|
);
|
|
1014
|
+
const rawProjects =
|
|
1015
|
+
rawConfig.projects && typeof rawConfig.projects === "object" && !Array.isArray(rawConfig.projects)
|
|
1016
|
+
? rawConfig.projects
|
|
1017
|
+
: null;
|
|
1018
|
+
const projects = Object.fromEntries(
|
|
1019
|
+
Object.entries(
|
|
1020
|
+
rawProjects || {
|
|
1021
|
+
[defaultProject]: {
|
|
1022
|
+
projectName: rawConfig.projectName,
|
|
1023
|
+
rootDir: ".",
|
|
1024
|
+
sharedPlanDocs: rawConfig.sharedPlanDocs,
|
|
1025
|
+
lanes: legacyLanes,
|
|
1026
|
+
},
|
|
1027
|
+
},
|
|
1028
|
+
).map(([projectId, projectConfig]) => {
|
|
1029
|
+
const normalizedProjectId = sanitizeProjectId(projectId);
|
|
1030
|
+
const rawProject =
|
|
1031
|
+
projectConfig && typeof projectConfig === "object" && !Array.isArray(projectConfig)
|
|
1032
|
+
? projectConfig
|
|
1033
|
+
: {};
|
|
1034
|
+
const rootDir = normalizeRepoRelativePath(
|
|
1035
|
+
rawProject.rootDir || ".",
|
|
1036
|
+
`projects.${projectId}.rootDir`,
|
|
1037
|
+
);
|
|
1038
|
+
const projectSharedPlanDocs =
|
|
1039
|
+
normalizeOptionalPathArray(
|
|
1040
|
+
rawProject.sharedPlanDocs,
|
|
1041
|
+
`projects.${projectId}.sharedPlanDocs`,
|
|
1042
|
+
) || null;
|
|
1043
|
+
const projectDocsDir = normalizeRepoRelativePath(
|
|
1044
|
+
rawProject.paths?.docsDir || joinRepoPath(rootDir, rawConfig.paths?.docsDir || DEFAULT_DOCS_DIR),
|
|
1045
|
+
`projects.${projectId}.paths.docsDir`,
|
|
1046
|
+
);
|
|
1047
|
+
const projectPaths = {
|
|
1048
|
+
docsDir: projectDocsDir,
|
|
1049
|
+
stateRoot: normalizeRepoRelativePath(
|
|
1050
|
+
rawProject.paths?.stateRoot || rawConfig.paths?.stateRoot || DEFAULT_STATE_ROOT,
|
|
1051
|
+
`projects.${projectId}.paths.stateRoot`,
|
|
1052
|
+
),
|
|
1053
|
+
orchestratorStateDir: normalizeRepoRelativePath(
|
|
1054
|
+
rawProject.paths?.orchestratorStateDir ||
|
|
1055
|
+
rawConfig.paths?.orchestratorStateDir ||
|
|
1056
|
+
DEFAULT_ORCHESTRATOR_STATE_DIR,
|
|
1057
|
+
`projects.${projectId}.paths.orchestratorStateDir`,
|
|
1058
|
+
),
|
|
1059
|
+
terminalsPath: normalizeRepoRelativePath(
|
|
1060
|
+
rawProject.paths?.terminalsPath || rawConfig.paths?.terminalsPath || DEFAULT_TERMINALS_PATH,
|
|
1061
|
+
`projects.${projectId}.paths.terminalsPath`,
|
|
1062
|
+
),
|
|
1063
|
+
context7BundleIndexPath: normalizeRepoRelativePath(
|
|
1064
|
+
rawProject.paths?.context7BundleIndexPath ||
|
|
1065
|
+
rawConfig.paths?.context7BundleIndexPath ||
|
|
1066
|
+
DEFAULT_CONTEXT7_BUNDLE_INDEX_PATH,
|
|
1067
|
+
`projects.${projectId}.paths.context7BundleIndexPath`,
|
|
1068
|
+
),
|
|
1069
|
+
benchmarkCatalogPath: normalizeRepoRelativePath(
|
|
1070
|
+
rawProject.paths?.benchmarkCatalogPath ||
|
|
1071
|
+
rawConfig.paths?.benchmarkCatalogPath ||
|
|
1072
|
+
DEFAULT_BENCHMARK_CATALOG_PATH,
|
|
1073
|
+
`projects.${projectId}.paths.benchmarkCatalogPath`,
|
|
1074
|
+
),
|
|
1075
|
+
componentCutoverMatrixDocPath: normalizeRepoRelativePath(
|
|
1076
|
+
rawProject.paths?.componentCutoverMatrixDocPath ||
|
|
1077
|
+
rawConfig.paths?.componentCutoverMatrixDocPath ||
|
|
1078
|
+
defaultComponentCutoverMatrixDocPath(defaultPlansDir(projectDocsDir)),
|
|
1079
|
+
`projects.${projectId}.paths.componentCutoverMatrixDocPath`,
|
|
1080
|
+
),
|
|
1081
|
+
componentCutoverMatrixJsonPath: normalizeRepoRelativePath(
|
|
1082
|
+
rawProject.paths?.componentCutoverMatrixJsonPath ||
|
|
1083
|
+
rawConfig.paths?.componentCutoverMatrixJsonPath ||
|
|
1084
|
+
defaultComponentCutoverMatrixJsonPath(defaultPlansDir(projectDocsDir)),
|
|
1085
|
+
`projects.${projectId}.paths.componentCutoverMatrixJsonPath`,
|
|
1086
|
+
),
|
|
1087
|
+
};
|
|
1088
|
+
const projectLanes = Object.fromEntries(
|
|
1089
|
+
Object.entries(rawProject.lanes || {}).map(([laneName, laneConfig]) => [
|
|
1090
|
+
sanitizeLaneName(laneName),
|
|
1091
|
+
laneConfig || {},
|
|
1092
|
+
]),
|
|
1093
|
+
);
|
|
1094
|
+
return [
|
|
1095
|
+
normalizedProjectId,
|
|
1096
|
+
{
|
|
1097
|
+
projectId: normalizedProjectId,
|
|
1098
|
+
projectName: String(
|
|
1099
|
+
rawProject.projectName || rawConfig.projectName || normalizedProjectId,
|
|
1100
|
+
).trim(),
|
|
1101
|
+
rootDir,
|
|
1102
|
+
sharedPlanDocs: projectSharedPlanDocs,
|
|
1103
|
+
paths: projectPaths,
|
|
1104
|
+
roles: rawProject.roles || {},
|
|
1105
|
+
validation: rawProject.validation || {},
|
|
1106
|
+
executors: rawProject.executors || {},
|
|
1107
|
+
planner: rawProject.planner || {},
|
|
1108
|
+
skills: rawProject.skills || {},
|
|
1109
|
+
capabilityRouting: rawProject.capabilityRouting || {},
|
|
1110
|
+
runtimePolicy: rawProject.runtimePolicy || {},
|
|
1111
|
+
waveControl: rawProject.waveControl || {},
|
|
1112
|
+
lanes: projectLanes,
|
|
1113
|
+
explicit: Boolean(rawProjects),
|
|
1114
|
+
},
|
|
1115
|
+
];
|
|
1116
|
+
}),
|
|
1117
|
+
);
|
|
975
1118
|
return {
|
|
976
1119
|
version: Number.parseInt(String(rawConfig.version ?? "1"), 10) || 1,
|
|
977
1120
|
projectName: String(rawConfig.projectName || "Wave Orchestrator").trim(),
|
|
978
1121
|
repoMode,
|
|
979
1122
|
defaultLane,
|
|
1123
|
+
defaultProject,
|
|
980
1124
|
paths,
|
|
981
1125
|
roles: normalizeRoles(rawConfig.roles),
|
|
982
1126
|
validation: normalizeValidation(rawConfig.validation),
|
|
@@ -987,16 +1131,82 @@ export function loadWaveConfig(configPath = DEFAULT_WAVE_CONFIG_PATH) {
|
|
|
987
1131
|
runtimePolicy: normalizeRuntimePolicy(rawConfig.runtimePolicy),
|
|
988
1132
|
waveControl: normalizeWaveControl(rawConfig.waveControl, "waveControl"),
|
|
989
1133
|
sharedPlanDocs,
|
|
990
|
-
lanes,
|
|
1134
|
+
lanes: legacyLanes,
|
|
1135
|
+
projects,
|
|
991
1136
|
configPath,
|
|
992
1137
|
};
|
|
993
1138
|
}
|
|
994
1139
|
|
|
995
|
-
export function
|
|
1140
|
+
export function resolveProjectProfile(config, projectInput = config.defaultProject) {
|
|
1141
|
+
const projectId = sanitizeProjectId(projectInput || config.defaultProject || DEFAULT_PROJECT_ID);
|
|
1142
|
+
const projectConfig = config.projects?.[projectId];
|
|
1143
|
+
if (!projectConfig) {
|
|
1144
|
+
throw new Error(`Unknown project: ${projectInput}`);
|
|
1145
|
+
}
|
|
1146
|
+
const paths = {
|
|
1147
|
+
...config.paths,
|
|
1148
|
+
...(projectConfig.paths || {}),
|
|
1149
|
+
};
|
|
1150
|
+
return {
|
|
1151
|
+
projectId,
|
|
1152
|
+
projectName: projectConfig.projectName || config.projectName,
|
|
1153
|
+
rootDir: projectConfig.rootDir || ".",
|
|
1154
|
+
paths,
|
|
1155
|
+
sharedPlanDocs: projectConfig.sharedPlanDocs || null,
|
|
1156
|
+
roles: normalizeRoles({
|
|
1157
|
+
...config.roles,
|
|
1158
|
+
...(projectConfig.roles || {}),
|
|
1159
|
+
}),
|
|
1160
|
+
validation: normalizeValidation({
|
|
1161
|
+
...config.validation,
|
|
1162
|
+
...(projectConfig.validation || {}),
|
|
1163
|
+
}),
|
|
1164
|
+
executors: normalizeExecutors(
|
|
1165
|
+
mergeExecutors(config.executors, projectConfig.executors || {}),
|
|
1166
|
+
),
|
|
1167
|
+
planner: normalizePlanner({
|
|
1168
|
+
...config.planner,
|
|
1169
|
+
...(projectConfig.planner || {}),
|
|
1170
|
+
}),
|
|
1171
|
+
skills: mergeSkillsConfig(
|
|
1172
|
+
config.skills || emptySkillsConfig(),
|
|
1173
|
+
normalizeLaneSkills(projectConfig.skills || {}, `${projectId}.skills`, {
|
|
1174
|
+
preserveOmittedDir: true,
|
|
1175
|
+
}),
|
|
1176
|
+
),
|
|
1177
|
+
capabilityRouting: normalizeCapabilityRouting({
|
|
1178
|
+
...config.capabilityRouting,
|
|
1179
|
+
...(projectConfig.capabilityRouting || {}),
|
|
1180
|
+
}),
|
|
1181
|
+
runtimePolicy: normalizeRuntimePolicy({
|
|
1182
|
+
...config.runtimePolicy,
|
|
1183
|
+
...(projectConfig.runtimePolicy || {}),
|
|
1184
|
+
}),
|
|
1185
|
+
waveControl: normalizeWaveControl(
|
|
1186
|
+
{
|
|
1187
|
+
...config.waveControl,
|
|
1188
|
+
projectId,
|
|
1189
|
+
...(projectConfig.waveControl || {}),
|
|
1190
|
+
},
|
|
1191
|
+
`projects.${projectId}.waveControl`,
|
|
1192
|
+
),
|
|
1193
|
+
lanes: projectConfig.lanes || {},
|
|
1194
|
+
explicit: projectConfig.explicit === true,
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
export function resolveLaneProfile(config, laneInput = config.defaultLane, projectInput = config.defaultProject) {
|
|
1199
|
+
const projectProfile = resolveProjectProfile(config, projectInput);
|
|
996
1200
|
const lane = sanitizeLaneName(laneInput || config.defaultLane);
|
|
997
|
-
const laneConfig = config.lanes[lane] || {};
|
|
1201
|
+
const laneConfig = projectProfile.lanes[lane] || config.lanes[lane] || {};
|
|
998
1202
|
const docsDir = normalizeRepoRelativePath(
|
|
999
|
-
laneConfig.docsDir ||
|
|
1203
|
+
laneConfig.docsDir ||
|
|
1204
|
+
defaultDocsDirForLane(
|
|
1205
|
+
lane,
|
|
1206
|
+
config.defaultLane,
|
|
1207
|
+
config.repoMode,
|
|
1208
|
+
projectProfile.paths.docsDir,
|
|
1209
|
+
),
|
|
1000
1210
|
`${lane}.docsDir`,
|
|
1001
1211
|
);
|
|
1002
1212
|
const plansDir = normalizeRepoRelativePath(
|
|
@@ -1008,28 +1218,28 @@ export function resolveLaneProfile(config, laneInput = config.defaultLane) {
|
|
|
1008
1218
|
`${lane}.wavesDir`,
|
|
1009
1219
|
);
|
|
1010
1220
|
const roles = normalizeRoles({
|
|
1011
|
-
...
|
|
1221
|
+
...projectProfile.roles,
|
|
1012
1222
|
...(laneConfig.roles || {}),
|
|
1013
1223
|
});
|
|
1014
1224
|
const validation = normalizeValidation({
|
|
1015
|
-
...
|
|
1225
|
+
...projectProfile.validation,
|
|
1016
1226
|
...(laneConfig.validation || {}),
|
|
1017
1227
|
});
|
|
1018
1228
|
const executors = normalizeExecutors(
|
|
1019
|
-
mergeExecutors(
|
|
1229
|
+
mergeExecutors(projectProfile.executors, laneConfig.executors),
|
|
1020
1230
|
);
|
|
1021
1231
|
const skills = mergeSkillsConfig(
|
|
1022
|
-
|
|
1232
|
+
projectProfile.skills || emptySkillsConfig(),
|
|
1023
1233
|
normalizeLaneSkills(laneConfig.skills, `${lane}.skills`, {
|
|
1024
1234
|
preserveOmittedDir: true,
|
|
1025
1235
|
}),
|
|
1026
1236
|
);
|
|
1027
1237
|
const capabilityRouting = normalizeCapabilityRouting({
|
|
1028
|
-
...
|
|
1238
|
+
...projectProfile.capabilityRouting,
|
|
1029
1239
|
...(laneConfig.capabilityRouting || {}),
|
|
1030
1240
|
});
|
|
1031
1241
|
const runtimePolicy = normalizeRuntimePolicy({
|
|
1032
|
-
...
|
|
1242
|
+
...projectProfile.runtimePolicy,
|
|
1033
1243
|
...(laneConfig.runtimePolicy || {}),
|
|
1034
1244
|
...(laneConfig.runtimeMixTargets ? { runtimeMixTargets: laneConfig.runtimeMixTargets } : {}),
|
|
1035
1245
|
...(laneConfig.defaultExecutorByRole
|
|
@@ -1041,18 +1251,23 @@ export function resolveLaneProfile(config, laneInput = config.defaultLane) {
|
|
|
1041
1251
|
});
|
|
1042
1252
|
const waveControl = normalizeWaveControl(
|
|
1043
1253
|
{
|
|
1044
|
-
...
|
|
1254
|
+
...projectProfile.waveControl,
|
|
1045
1255
|
...(laneConfig.waveControl || {}),
|
|
1046
1256
|
},
|
|
1047
1257
|
`${lane}.waveControl`,
|
|
1048
1258
|
);
|
|
1049
1259
|
return {
|
|
1260
|
+
projectId: projectProfile.projectId,
|
|
1261
|
+
projectName: projectProfile.projectName,
|
|
1262
|
+
projectRootDir: projectProfile.rootDir,
|
|
1263
|
+
explicitProject: projectProfile.explicit === true,
|
|
1050
1264
|
lane,
|
|
1051
1265
|
docsDir,
|
|
1052
1266
|
plansDir,
|
|
1053
1267
|
wavesDir,
|
|
1054
1268
|
sharedPlanDocs:
|
|
1055
1269
|
normalizeOptionalPathArray(laneConfig.sharedPlanDocs, `${lane}.sharedPlanDocs`) ||
|
|
1270
|
+
projectProfile.sharedPlanDocs ||
|
|
1056
1271
|
config.sharedPlanDocs ||
|
|
1057
1272
|
defaultSharedPlanDocs(plansDir),
|
|
1058
1273
|
roles,
|
|
@@ -1064,34 +1279,34 @@ export function resolveLaneProfile(config, laneInput = config.defaultLane) {
|
|
|
1064
1279
|
waveControl,
|
|
1065
1280
|
paths: {
|
|
1066
1281
|
terminalsPath: normalizeRepoRelativePath(
|
|
1067
|
-
laneConfig.terminalsPath ||
|
|
1282
|
+
laneConfig.terminalsPath || projectProfile.paths.terminalsPath,
|
|
1068
1283
|
`${lane}.terminalsPath`,
|
|
1069
1284
|
),
|
|
1070
1285
|
stateRoot: normalizeRepoRelativePath(
|
|
1071
|
-
laneConfig.stateRoot ||
|
|
1286
|
+
laneConfig.stateRoot || projectProfile.paths.stateRoot,
|
|
1072
1287
|
`${lane}.stateRoot`,
|
|
1073
1288
|
),
|
|
1074
1289
|
orchestratorStateDir: normalizeRepoRelativePath(
|
|
1075
|
-
laneConfig.orchestratorStateDir ||
|
|
1290
|
+
laneConfig.orchestratorStateDir || projectProfile.paths.orchestratorStateDir,
|
|
1076
1291
|
`${lane}.orchestratorStateDir`,
|
|
1077
1292
|
),
|
|
1078
1293
|
context7BundleIndexPath: normalizeRepoRelativePath(
|
|
1079
|
-
laneConfig.context7BundleIndexPath ||
|
|
1294
|
+
laneConfig.context7BundleIndexPath || projectProfile.paths.context7BundleIndexPath,
|
|
1080
1295
|
`${lane}.context7BundleIndexPath`,
|
|
1081
1296
|
),
|
|
1082
1297
|
benchmarkCatalogPath: normalizeRepoRelativePath(
|
|
1083
|
-
laneConfig.benchmarkCatalogPath ||
|
|
1298
|
+
laneConfig.benchmarkCatalogPath || projectProfile.paths.benchmarkCatalogPath,
|
|
1084
1299
|
`${lane}.benchmarkCatalogPath`,
|
|
1085
1300
|
),
|
|
1086
1301
|
componentCutoverMatrixDocPath: normalizeRepoRelativePath(
|
|
1087
1302
|
laneConfig.componentCutoverMatrixDocPath ||
|
|
1088
|
-
|
|
1303
|
+
projectProfile.paths.componentCutoverMatrixDocPath ||
|
|
1089
1304
|
defaultComponentCutoverMatrixDocPath(plansDir),
|
|
1090
1305
|
`${lane}.componentCutoverMatrixDocPath`,
|
|
1091
1306
|
),
|
|
1092
1307
|
componentCutoverMatrixJsonPath: normalizeRepoRelativePath(
|
|
1093
1308
|
laneConfig.componentCutoverMatrixJsonPath ||
|
|
1094
|
-
|
|
1309
|
+
projectProfile.paths.componentCutoverMatrixJsonPath ||
|
|
1095
1310
|
defaultComponentCutoverMatrixJsonPath(plansDir),
|
|
1096
1311
|
`${lane}.componentCutoverMatrixJsonPath`,
|
|
1097
1312
|
),
|
|
@@ -19,10 +19,9 @@ import {
|
|
|
19
19
|
DEFAULT_COORDINATION_ACK_TIMEOUT_MS,
|
|
20
20
|
DEFAULT_COORDINATION_RESOLUTION_STALE_MS,
|
|
21
21
|
ensureDirectory,
|
|
22
|
+
findAdhocRunRecord,
|
|
22
23
|
parseNonNegativeInt,
|
|
23
|
-
readJsonOrNull,
|
|
24
24
|
readStatusRecordIfPresent,
|
|
25
|
-
REPO_ROOT,
|
|
26
25
|
sanitizeAdhocRunId,
|
|
27
26
|
sanitizeLaneName,
|
|
28
27
|
toIsoTimestamp,
|
|
@@ -51,23 +50,23 @@ import {
|
|
|
51
50
|
|
|
52
51
|
function printUsage() {
|
|
53
52
|
console.log(`Usage:
|
|
54
|
-
wave control status --lane <lane> --wave <n> [--agent <id>] [--json]
|
|
55
|
-
wave control telemetry status --lane <lane> [--run <id>] [--json]
|
|
56
|
-
wave control telemetry flush --lane <lane> [--run <id>] [--json]
|
|
53
|
+
wave control status --project <id> --lane <lane> --wave <n> [--agent <id>] [--json]
|
|
54
|
+
wave control telemetry status --project <id> --lane <lane> [--run <id>] [--json]
|
|
55
|
+
wave control telemetry flush --project <id> --lane <lane> [--run <id>] [--json]
|
|
57
56
|
|
|
58
|
-
wave control task create --lane <lane> --wave <n> --agent <id> --kind <request|blocker|clarification|handoff|evidence|claim|decision|human-input> --summary <text> [options]
|
|
59
|
-
wave control task list --lane <lane> --wave <n> [--agent <id>] [--json]
|
|
60
|
-
wave control task get --lane <lane> --wave <n> --id <task-id> [--json]
|
|
61
|
-
wave control task act <start|resolve|dismiss|cancel|reassign|answer|escalate|defer|mark-advisory|mark-stale|resolve-policy> --lane <lane> --wave <n> --id <task-id> [options]
|
|
57
|
+
wave control task create --project <id> --lane <lane> --wave <n> --agent <id> --kind <request|blocker|clarification|handoff|evidence|claim|decision|human-input> --summary <text> [options]
|
|
58
|
+
wave control task list --project <id> --lane <lane> --wave <n> [--agent <id>] [--json]
|
|
59
|
+
wave control task get --project <id> --lane <lane> --wave <n> --id <task-id> [--json]
|
|
60
|
+
wave control task act <start|resolve|dismiss|cancel|reassign|answer|escalate|defer|mark-advisory|mark-stale|resolve-policy> --project <id> --lane <lane> --wave <n> --id <task-id> [options]
|
|
62
61
|
|
|
63
|
-
wave control rerun request --lane <lane> --wave <n> [--agent <id> ...] [--resume-cursor <cursor>] [--reuse-attempt <id> ...] [--reuse-proof <id> ...] [--reuse-derived-summaries <true|false>] [--invalidate-component <id> ...] [--clear-reuse <id> ...] [--preserve-reuse <id> ...] [--requested-by <name>] [--reason <text>] [--json]
|
|
64
|
-
wave control rerun get --lane <lane> --wave <n> [--json]
|
|
65
|
-
wave control rerun clear --lane <lane> --wave <n>
|
|
62
|
+
wave control rerun request --project <id> --lane <lane> --wave <n> [--agent <id> ...] [--resume-cursor <cursor>] [--reuse-attempt <id> ...] [--reuse-proof <id> ...] [--reuse-derived-summaries <true|false>] [--invalidate-component <id> ...] [--clear-reuse <id> ...] [--preserve-reuse <id> ...] [--requested-by <name>] [--reason <text>] [--json]
|
|
63
|
+
wave control rerun get --project <id> --lane <lane> --wave <n> [--json]
|
|
64
|
+
wave control rerun clear --project <id> --lane <lane> --wave <n>
|
|
66
65
|
|
|
67
|
-
wave control proof register --lane <lane> --wave <n> --agent <id> --artifact <path> [--artifact <path> ...] [--component <id[:level]> ...] [--authoritative] [--satisfy-owned-components] [--completion <level>] [--durability <level>] [--proof-level <level>] [--doc-delta <state>] [--operator <name>] [--detail <text>] [--json]
|
|
68
|
-
wave control proof get --lane <lane> --wave <n> [--agent <id>] [--id <bundle-id>] [--json]
|
|
69
|
-
wave control proof supersede --lane <lane> --wave <n> --id <bundle-id> --agent <id> --artifact <path> [--artifact <path> ...] [options]
|
|
70
|
-
wave control proof revoke --lane <lane> --wave <n> --id <bundle-id> [--operator <name>] [--detail <text>] [--json]
|
|
66
|
+
wave control proof register --project <id> --lane <lane> --wave <n> --agent <id> --artifact <path> [--artifact <path> ...] [--component <id[:level]> ...] [--authoritative] [--satisfy-owned-components] [--completion <level>] [--durability <level>] [--proof-level <level>] [--doc-delta <state>] [--operator <name>] [--detail <text>] [--json]
|
|
67
|
+
wave control proof get --project <id> --lane <lane> --wave <n> [--agent <id>] [--id <bundle-id>] [--json]
|
|
68
|
+
wave control proof supersede --project <id> --lane <lane> --wave <n> --id <bundle-id> --agent <id> --artifact <path> [--artifact <path> ...] [options]
|
|
69
|
+
wave control proof revoke --project <id> --lane <lane> --wave <n> --id <bundle-id> [--operator <name>] [--detail <text>] [--json]
|
|
71
70
|
`);
|
|
72
71
|
}
|
|
73
72
|
|
|
@@ -91,6 +90,7 @@ function parseArgs(argv) {
|
|
|
91
90
|
const operation = String(args[1] || "").trim().toLowerCase();
|
|
92
91
|
const action = String(args[2] || "").trim().toLowerCase();
|
|
93
92
|
const options = {
|
|
93
|
+
project: "",
|
|
94
94
|
lane: "main",
|
|
95
95
|
wave: null,
|
|
96
96
|
runId: "",
|
|
@@ -144,7 +144,9 @@ function parseArgs(argv) {
|
|
|
144
144
|
if (i < startIndex) {
|
|
145
145
|
continue;
|
|
146
146
|
}
|
|
147
|
-
if (arg === "--
|
|
147
|
+
if (arg === "--project") {
|
|
148
|
+
options.project = String(args[++i] || "").trim();
|
|
149
|
+
} else if (arg === "--lane") {
|
|
148
150
|
options.lane = sanitizeLaneName(args[++i]);
|
|
149
151
|
} else if (arg === "--run") {
|
|
150
152
|
options.runId = sanitizeAdhocRunId(args[++i]);
|
|
@@ -228,11 +230,12 @@ function parseArgs(argv) {
|
|
|
228
230
|
return { help: false, surface, operation, action, options };
|
|
229
231
|
}
|
|
230
232
|
|
|
231
|
-
function
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
233
|
+
function resolveRunContext(runId, fallbackProject, fallbackLane) {
|
|
234
|
+
const record = findAdhocRunRecord(runId);
|
|
235
|
+
return {
|
|
236
|
+
project: record?.project || fallbackProject,
|
|
237
|
+
lane: record?.result?.lane || fallbackLane,
|
|
238
|
+
};
|
|
236
239
|
}
|
|
237
240
|
|
|
238
241
|
function loadWave(lanePaths, waveNumber) {
|
|
@@ -955,9 +958,12 @@ export async function runControlCli(argv) {
|
|
|
955
958
|
throw new Error("Expected control surface: status | telemetry | task | rerun | proof");
|
|
956
959
|
}
|
|
957
960
|
if (options.runId) {
|
|
958
|
-
|
|
961
|
+
const context = resolveRunContext(options.runId, options.project, options.lane);
|
|
962
|
+
options.project = context.project;
|
|
963
|
+
options.lane = context.lane;
|
|
959
964
|
}
|
|
960
965
|
const lanePaths = buildLanePaths(options.lane, {
|
|
966
|
+
project: options.project || undefined,
|
|
961
967
|
runVariant: options.dryRun ? "dry-run" : undefined,
|
|
962
968
|
adhocRunId: options.runId || null,
|
|
963
969
|
});
|
|
@@ -35,22 +35,21 @@ import { writeAssignmentSnapshot, writeDependencySnapshot } from "./artifact-sch
|
|
|
35
35
|
import {
|
|
36
36
|
buildLanePaths,
|
|
37
37
|
ensureDirectory,
|
|
38
|
+
findAdhocRunRecord,
|
|
38
39
|
parseNonNegativeInt,
|
|
39
|
-
readJsonOrNull,
|
|
40
|
-
REPO_ROOT,
|
|
41
40
|
sanitizeAdhocRunId,
|
|
42
41
|
} from "./shared.mjs";
|
|
43
42
|
import { parseWaveFiles } from "./wave-files.mjs";
|
|
44
43
|
|
|
45
44
|
function printUsage() {
|
|
46
45
|
console.log(`Usage:
|
|
47
|
-
wave coord post --lane <lane> --wave <n> --agent <id> --kind <kind> --summary <text> [--dry-run] [options]
|
|
48
|
-
wave coord show --lane <lane> --wave <n> [--dry-run] [--json]
|
|
49
|
-
wave coord render --lane <lane> --wave <n> [--dry-run]
|
|
50
|
-
wave coord inbox --lane <lane> --wave <n> --agent <id> [--dry-run]
|
|
51
|
-
wave coord explain --lane <lane> --wave <n> [--agent <id>] [--json]
|
|
52
|
-
wave coord act <resolve|dismiss|reroute|reassign|escalate|answer-human> --lane <lane> --wave <n> [options]
|
|
53
|
-
wave coord <subcommand> --run <id> [--wave 0] ...
|
|
46
|
+
wave coord post --project <id> --lane <lane> --wave <n> --agent <id> --kind <kind> --summary <text> [--dry-run] [options]
|
|
47
|
+
wave coord show --project <id> --lane <lane> --wave <n> [--dry-run] [--json]
|
|
48
|
+
wave coord render --project <id> --lane <lane> --wave <n> [--dry-run]
|
|
49
|
+
wave coord inbox --project <id> --lane <lane> --wave <n> --agent <id> [--dry-run]
|
|
50
|
+
wave coord explain --project <id> --lane <lane> --wave <n> [--agent <id>] [--json]
|
|
51
|
+
wave coord act <resolve|dismiss|reroute|reassign|escalate|answer-human> --project <id> --lane <lane> --wave <n> [options]
|
|
52
|
+
wave coord <subcommand> --run <id> [--project <id>] [--wave 0] ...
|
|
54
53
|
`);
|
|
55
54
|
}
|
|
56
55
|
|
|
@@ -58,6 +57,7 @@ function parseArgs(argv) {
|
|
|
58
57
|
const args = argv[0] === "--" ? argv.slice(1) : argv;
|
|
59
58
|
const subcommand = String(args[0] || "").trim().toLowerCase();
|
|
60
59
|
const options = {
|
|
60
|
+
project: "",
|
|
61
61
|
lane: "main",
|
|
62
62
|
wave: null,
|
|
63
63
|
runId: "",
|
|
@@ -85,7 +85,9 @@ function parseArgs(argv) {
|
|
|
85
85
|
}
|
|
86
86
|
for (let i = startIndex; i < args.length; i += 1) {
|
|
87
87
|
const arg = args[i];
|
|
88
|
-
if (arg === "--
|
|
88
|
+
if (arg === "--project") {
|
|
89
|
+
options.project = String(args[++i] || "").trim();
|
|
90
|
+
} else if (arg === "--lane") {
|
|
89
91
|
options.lane = String(args[++i] || "").trim();
|
|
90
92
|
} else if (arg === "--run") {
|
|
91
93
|
options.runId = sanitizeAdhocRunId(args[++i]);
|
|
@@ -131,9 +133,12 @@ function parseArgs(argv) {
|
|
|
131
133
|
return { subcommand, options };
|
|
132
134
|
}
|
|
133
135
|
|
|
134
|
-
function
|
|
135
|
-
const
|
|
136
|
-
return
|
|
136
|
+
function resolveRunContext(runId, fallbackProject, fallbackLane) {
|
|
137
|
+
const record = findAdhocRunRecord(runId);
|
|
138
|
+
return {
|
|
139
|
+
project: record?.project || fallbackProject,
|
|
140
|
+
lane: record?.result?.lane || fallbackLane,
|
|
141
|
+
};
|
|
137
142
|
}
|
|
138
143
|
|
|
139
144
|
function loadWave(lanePaths, waveNumber) {
|
|
@@ -329,9 +334,12 @@ export async function runCoordinationCli(argv) {
|
|
|
329
334
|
}
|
|
330
335
|
const { subcommand, options } = parseArgs(argv);
|
|
331
336
|
if (options.runId) {
|
|
332
|
-
|
|
337
|
+
const context = resolveRunContext(options.runId, options.project, options.lane);
|
|
338
|
+
options.project = context.project;
|
|
339
|
+
options.lane = context.lane;
|
|
333
340
|
}
|
|
334
341
|
const lanePaths = buildLanePaths(options.lane, {
|
|
342
|
+
project: options.project || undefined,
|
|
335
343
|
runVariant: options.dryRun ? "dry-run" : undefined,
|
|
336
344
|
adhocRunId: options.runId || null,
|
|
337
345
|
});
|
|
@@ -242,8 +242,11 @@ export function normalizeCoordinationRecord(rawRecord, defaults = {}) {
|
|
|
242
242
|
: Number.parseInt(String(rawRecord.attempt), 10),
|
|
243
243
|
source: normalizeString(rawRecord.source ?? defaults.source, "launcher"),
|
|
244
244
|
executorId: normalizeString(rawRecord.executorId ?? defaults.executorId, ""),
|
|
245
|
+
project: normalizeString(rawRecord.project ?? defaults.project, ""),
|
|
245
246
|
requesterLane: normalizeString(rawRecord.requesterLane ?? defaults.requesterLane, ""),
|
|
246
247
|
ownerLane: normalizeString(rawRecord.ownerLane ?? defaults.ownerLane, ""),
|
|
248
|
+
requesterProject: normalizeString(rawRecord.requesterProject ?? defaults.requesterProject, ""),
|
|
249
|
+
ownerProject: normalizeString(rawRecord.ownerProject ?? defaults.ownerProject, ""),
|
|
247
250
|
requesterWave:
|
|
248
251
|
rawRecord.requesterWave === null || rawRecord.requesterWave === undefined || rawRecord.requesterWave === ""
|
|
249
252
|
? defaults.requesterWave ?? null
|
|
@@ -264,8 +267,10 @@ export function appendCoordinationRecord(filePath, rawRecord, defaults = {}) {
|
|
|
264
267
|
ensureDirectory(path.dirname(filePath));
|
|
265
268
|
fs.appendFileSync(filePath, `${JSON.stringify(record)}\n`, "utf8");
|
|
266
269
|
const runIdHint = normalizeString(rawRecord?.runId ?? defaults.runId, "");
|
|
270
|
+
const projectHint = normalizeString(rawRecord?.project ?? defaults.project, "");
|
|
267
271
|
try {
|
|
268
272
|
const lanePaths = buildLanePaths(record.lane, {
|
|
273
|
+
...(projectHint ? { project: projectHint } : {}),
|
|
269
274
|
...(runIdHint ? { adhocRunId: runIdHint } : {}),
|
|
270
275
|
});
|
|
271
276
|
if (lanePaths?.waveControl?.captureCoordinationRecords !== false) {
|
|
@@ -301,8 +306,11 @@ export function appendCoordinationRecord(filePath, rawRecord, defaults = {}) {
|
|
|
301
306
|
closureCondition: record.closureCondition,
|
|
302
307
|
required: record.required,
|
|
303
308
|
executorId: record.executorId || null,
|
|
309
|
+
project: record.project || null,
|
|
304
310
|
requesterLane: record.requesterLane || null,
|
|
305
311
|
ownerLane: record.ownerLane || null,
|
|
312
|
+
requesterProject: record.requesterProject || null,
|
|
313
|
+
ownerProject: record.ownerProject || null,
|
|
306
314
|
},
|
|
307
315
|
});
|
|
308
316
|
}
|
|
@@ -36,6 +36,7 @@ function normalizeDashboardAttachTarget(value) {
|
|
|
36
36
|
|
|
37
37
|
export function parseDashboardArgs(argv) {
|
|
38
38
|
const options = {
|
|
39
|
+
project: null,
|
|
39
40
|
lane: DEFAULT_WAVE_LANE,
|
|
40
41
|
dashboardFile: null,
|
|
41
42
|
messageBoard: null,
|
|
@@ -50,6 +51,8 @@ export function parseDashboardArgs(argv) {
|
|
|
50
51
|
}
|
|
51
52
|
if (arg === "--watch") {
|
|
52
53
|
options.watch = true;
|
|
54
|
+
} else if (arg === "--project") {
|
|
55
|
+
options.project = String(argv[++i] || "").trim() || null;
|
|
53
56
|
} else if (arg === "--lane") {
|
|
54
57
|
options.lane =
|
|
55
58
|
String(argv[++i] || "")
|
|
@@ -98,9 +101,12 @@ function tmuxSessionExists(socketName, sessionName) {
|
|
|
98
101
|
throw new Error((result.stderr || result.stdout || "tmux has-session failed").trim());
|
|
99
102
|
}
|
|
100
103
|
|
|
101
|
-
function attachDashboardSession(lane, target) {
|
|
104
|
+
function attachDashboardSession(project, lane, target) {
|
|
102
105
|
const config = loadWaveConfig();
|
|
103
|
-
const lanePaths = buildLanePaths(lane, {
|
|
106
|
+
const lanePaths = buildLanePaths(lane, {
|
|
107
|
+
config,
|
|
108
|
+
project: project || config.defaultProject,
|
|
109
|
+
});
|
|
104
110
|
const entry =
|
|
105
111
|
target === "global"
|
|
106
112
|
? createGlobalDashboardTerminalEntry(lanePaths, "current")
|
|
@@ -449,6 +455,7 @@ export async function runDashboardCli(argv) {
|
|
|
449
455
|
console.log(`Usage: pnpm exec wave dashboard --dashboard-file <path> [options]
|
|
450
456
|
|
|
451
457
|
Options:
|
|
458
|
+
--project <id> Project id (default: config default)
|
|
452
459
|
--lane <name> Wave lane name (default: ${DEFAULT_WAVE_LANE})
|
|
453
460
|
--dashboard-file <path> Path to wave/global dashboard JSON
|
|
454
461
|
--message-board <path> Optional message board path override
|
|
@@ -461,7 +468,7 @@ Options:
|
|
|
461
468
|
}
|
|
462
469
|
|
|
463
470
|
if (options.attach) {
|
|
464
|
-
attachDashboardSession(options.lane, options.attach);
|
|
471
|
+
attachDashboardSession(options.project, options.lane, options.attach);
|
|
465
472
|
return;
|
|
466
473
|
}
|
|
467
474
|
|