@chllming/wave-orchestration 0.8.9 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/README.md +135 -18
- package/docs/README.md +9 -3
- package/docs/architecture/README.md +1498 -0
- package/docs/concepts/context7-vs-skills.md +1 -1
- package/docs/concepts/operating-modes.md +3 -3
- package/docs/concepts/what-is-a-wave.md +1 -1
- package/docs/guides/author-and-run-waves.md +27 -4
- package/docs/guides/monorepo-projects.md +226 -0
- package/docs/guides/planner.md +10 -3
- package/docs/guides/{recommendations-0.8.9.md → recommendations-0.9.1.md} +8 -7
- package/docs/guides/sandboxed-environments.md +158 -0
- package/docs/guides/terminal-surfaces.md +14 -12
- package/docs/plans/current-state.md +11 -7
- package/docs/plans/end-state-architecture.md +3 -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 +48 -18
- package/docs/plans/sandbox-end-state-architecture.md +153 -0
- package/docs/plans/wave-orchestrator.md +4 -4
- package/docs/reference/cli-reference.md +125 -57
- package/docs/reference/coordination-and-closure.md +1 -1
- package/docs/reference/github-packages-setup.md +1 -1
- package/docs/reference/migration-0.2-to-0.5.md +9 -7
- package/docs/reference/npmjs-token-publishing.md +53 -0
- package/docs/reference/npmjs-trusted-publishing.md +4 -50
- package/docs/reference/package-publishing-flow.md +272 -0
- 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 +43 -201
- package/package.json +1 -1
- package/releases/manifest.json +38 -0
- package/scripts/wave-orchestrator/adhoc.mjs +49 -17
- package/scripts/wave-orchestrator/agent-process-runner.mjs +344 -0
- package/scripts/wave-orchestrator/agent-state.mjs +0 -1
- package/scripts/wave-orchestrator/artifact-schemas.mjs +7 -0
- package/scripts/wave-orchestrator/autonomous.mjs +96 -29
- package/scripts/wave-orchestrator/benchmark-external.mjs +23 -7
- package/scripts/wave-orchestrator/benchmark.mjs +33 -10
- package/scripts/wave-orchestrator/closure-engine.mjs +138 -17
- package/scripts/wave-orchestrator/config.mjs +239 -24
- package/scripts/wave-orchestrator/control-cli.mjs +71 -28
- 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 +123 -44
- package/scripts/wave-orchestrator/dep-cli.mjs +47 -21
- package/scripts/wave-orchestrator/derived-state-engine.mjs +6 -3
- package/scripts/wave-orchestrator/feedback.mjs +28 -11
- package/scripts/wave-orchestrator/gate-engine.mjs +106 -38
- package/scripts/wave-orchestrator/human-input-resolution.mjs +5 -1
- package/scripts/wave-orchestrator/install.mjs +13 -0
- package/scripts/wave-orchestrator/launcher-progress.mjs +91 -0
- package/scripts/wave-orchestrator/launcher-runtime.mjs +179 -68
- package/scripts/wave-orchestrator/launcher.mjs +222 -53
- package/scripts/wave-orchestrator/ledger.mjs +7 -2
- package/scripts/wave-orchestrator/planner.mjs +48 -27
- package/scripts/wave-orchestrator/project-profile.mjs +31 -8
- package/scripts/wave-orchestrator/projection-writer.mjs +13 -1
- package/scripts/wave-orchestrator/proof-cli.mjs +18 -12
- package/scripts/wave-orchestrator/reducer-snapshot.mjs +6 -0
- package/scripts/wave-orchestrator/retry-cli.mjs +19 -13
- package/scripts/wave-orchestrator/retry-control.mjs +3 -3
- package/scripts/wave-orchestrator/retry-engine.mjs +93 -6
- package/scripts/wave-orchestrator/role-helpers.mjs +30 -0
- package/scripts/wave-orchestrator/session-supervisor.mjs +94 -85
- package/scripts/wave-orchestrator/shared.mjs +77 -14
- package/scripts/wave-orchestrator/supervisor-cli.mjs +1306 -0
- package/scripts/wave-orchestrator/terminals.mjs +12 -32
- package/scripts/wave-orchestrator/tmux-adapter.mjs +300 -0
- package/scripts/wave-orchestrator/wave-control-client.mjs +84 -16
- package/scripts/wave-orchestrator/wave-files.mjs +43 -6
- package/scripts/wave.mjs +13 -0
|
@@ -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,
|
|
@@ -43,31 +42,32 @@ import {
|
|
|
43
42
|
import { readWaveRelaunchPlanSnapshot, readWaveRetryOverride, resolveRetryOverrideAgentIds, writeWaveRetryOverride, clearWaveRetryOverride } from "./retry-control.mjs";
|
|
44
43
|
import { flushWaveControlQueue, readWaveControlQueueState } from "./wave-control-client.mjs";
|
|
45
44
|
import { readAgentExecutionSummary, validateImplementationSummary } from "./agent-state.mjs";
|
|
46
|
-
import { isContEvalReportOnlyAgent,
|
|
45
|
+
import { isContEvalReportOnlyAgent, isSecurityReviewAgentForLane } from "./role-helpers.mjs";
|
|
47
46
|
import {
|
|
48
47
|
buildSignalStatusLine,
|
|
49
48
|
syncWaveSignalProjections,
|
|
50
49
|
} from "./signals.mjs";
|
|
50
|
+
import { summarizeSupervisorStateForWave } from "./supervisor-cli.mjs";
|
|
51
51
|
|
|
52
52
|
function printUsage() {
|
|
53
53
|
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]
|
|
54
|
+
wave control status --project <id> --lane <lane> --wave <n> [--agent <id>] [--json]
|
|
55
|
+
wave control telemetry status --project <id> --lane <lane> [--run <id>] [--json]
|
|
56
|
+
wave control telemetry flush --project <id> --lane <lane> [--run <id>] [--json]
|
|
57
57
|
|
|
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]
|
|
58
|
+
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]
|
|
59
|
+
wave control task list --project <id> --lane <lane> --wave <n> [--agent <id>] [--json]
|
|
60
|
+
wave control task get --project <id> --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> --project <id> --lane <lane> --wave <n> --id <task-id> [options]
|
|
62
62
|
|
|
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>
|
|
63
|
+
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]
|
|
64
|
+
wave control rerun get --project <id> --lane <lane> --wave <n> [--json]
|
|
65
|
+
wave control rerun clear --project <id> --lane <lane> --wave <n>
|
|
66
66
|
|
|
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]
|
|
67
|
+
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]
|
|
68
|
+
wave control proof get --project <id> --lane <lane> --wave <n> [--agent <id>] [--id <bundle-id>] [--json]
|
|
69
|
+
wave control proof supersede --project <id> --lane <lane> --wave <n> --id <bundle-id> --agent <id> --artifact <path> [--artifact <path> ...] [options]
|
|
70
|
+
wave control proof revoke --project <id> --lane <lane> --wave <n> --id <bundle-id> [--operator <name>] [--detail <text>] [--json]
|
|
71
71
|
`);
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -91,6 +91,7 @@ function parseArgs(argv) {
|
|
|
91
91
|
const operation = String(args[1] || "").trim().toLowerCase();
|
|
92
92
|
const action = String(args[2] || "").trim().toLowerCase();
|
|
93
93
|
const options = {
|
|
94
|
+
project: "",
|
|
94
95
|
lane: "main",
|
|
95
96
|
wave: null,
|
|
96
97
|
runId: "",
|
|
@@ -144,7 +145,9 @@ function parseArgs(argv) {
|
|
|
144
145
|
if (i < startIndex) {
|
|
145
146
|
continue;
|
|
146
147
|
}
|
|
147
|
-
if (arg === "--
|
|
148
|
+
if (arg === "--project") {
|
|
149
|
+
options.project = String(args[++i] || "").trim();
|
|
150
|
+
} else if (arg === "--lane") {
|
|
148
151
|
options.lane = sanitizeLaneName(args[++i]);
|
|
149
152
|
} else if (arg === "--run") {
|
|
150
153
|
options.runId = sanitizeAdhocRunId(args[++i]);
|
|
@@ -228,11 +231,12 @@ function parseArgs(argv) {
|
|
|
228
231
|
return { help: false, surface, operation, action, options };
|
|
229
232
|
}
|
|
230
233
|
|
|
231
|
-
function
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
234
|
+
function resolveRunContext(runId, fallbackProject, fallbackLane) {
|
|
235
|
+
const record = findAdhocRunRecord(runId);
|
|
236
|
+
return {
|
|
237
|
+
project: record?.project || fallbackProject,
|
|
238
|
+
lane: record?.result?.lane || fallbackLane,
|
|
239
|
+
};
|
|
236
240
|
}
|
|
237
241
|
|
|
238
242
|
function loadWave(lanePaths, waveNumber) {
|
|
@@ -363,7 +367,8 @@ function buildLogicalAgents({
|
|
|
363
367
|
proofRegistry || { entries: [] },
|
|
364
368
|
);
|
|
365
369
|
const proofValidation =
|
|
366
|
-
!
|
|
370
|
+
!isSecurityReviewAgentForLane(agent, lanePaths) &&
|
|
371
|
+
!isContEvalReportOnlyAgent(agent, { contEvalAgentId: lanePaths.contEvalAgentId })
|
|
367
372
|
? validateImplementationSummary(agent, summary ? summary : null)
|
|
368
373
|
: { ok: statusRecord?.code === 0, statusCode: statusRecord?.code === 0 ? "pass" : "pending" };
|
|
369
374
|
const targetedTasks = tasks.filter(
|
|
@@ -382,7 +387,7 @@ function buildLogicalAgents({
|
|
|
382
387
|
const satisfiedByStatus =
|
|
383
388
|
statusRecord?.code === 0 &&
|
|
384
389
|
(proofValidation.ok ||
|
|
385
|
-
|
|
390
|
+
isSecurityReviewAgentForLane(agent, lanePaths) ||
|
|
386
391
|
isContEvalReportOnlyAgent(agent, { contEvalAgentId: lanePaths.contEvalAgentId }));
|
|
387
392
|
let state = "planned";
|
|
388
393
|
let reason = "";
|
|
@@ -401,7 +406,7 @@ function buildLogicalAgents({
|
|
|
401
406
|
lanePaths.integrationAgentId || "A8",
|
|
402
407
|
lanePaths.documentationAgentId || "A9",
|
|
403
408
|
lanePaths.contQaAgentId || "A0",
|
|
404
|
-
].includes(agent.agentId) ||
|
|
409
|
+
].includes(agent.agentId) || isSecurityReviewAgentForLane(agent, lanePaths)
|
|
405
410
|
? "closed"
|
|
406
411
|
: "satisfied";
|
|
407
412
|
reason = "Completed wave preserves the latest satisfied agent state.";
|
|
@@ -422,7 +427,7 @@ function buildLogicalAgents({
|
|
|
422
427
|
lanePaths.integrationAgentId || "A8",
|
|
423
428
|
lanePaths.documentationAgentId || "A9",
|
|
424
429
|
lanePaths.contQaAgentId || "A0",
|
|
425
|
-
].includes(agent.agentId) ||
|
|
430
|
+
].includes(agent.agentId) || isSecurityReviewAgentForLane(agent, lanePaths)
|
|
426
431
|
? "closed"
|
|
427
432
|
: "satisfied";
|
|
428
433
|
reason = "Latest attempt satisfied current control-plane state.";
|
|
@@ -654,6 +659,12 @@ export function buildControlStatusPayload({ lanePaths, wave, agentId = "" }) {
|
|
|
654
659
|
const controlState = readWaveControlPlaneState(lanePaths, wave.wave);
|
|
655
660
|
const proofRegistry = readWaveProofRegistry(lanePaths, wave.wave) || { entries: [] };
|
|
656
661
|
const relaunchPlan = readWaveRelaunchPlanSnapshot(lanePaths, wave.wave);
|
|
662
|
+
const supervisor = summarizeSupervisorStateForWave(lanePaths, wave.wave, {
|
|
663
|
+
agentId,
|
|
664
|
+
});
|
|
665
|
+
const forwardedClosureGaps = Array.isArray(relaunchPlan?.forwardedClosureGaps)
|
|
666
|
+
? relaunchPlan.forwardedClosureGaps
|
|
667
|
+
: [];
|
|
657
668
|
const rerunRequest = controlState.activeRerunRequest
|
|
658
669
|
? {
|
|
659
670
|
...controlState.activeRerunRequest,
|
|
@@ -715,6 +726,8 @@ export function buildControlStatusPayload({ lanePaths, wave, agentId = "" }) {
|
|
|
715
726
|
selectionSource: selection.source,
|
|
716
727
|
rerunRequest,
|
|
717
728
|
relaunchPlan,
|
|
729
|
+
forwardedClosureGaps,
|
|
730
|
+
supervisor,
|
|
718
731
|
nextTimer: isCompletedPhase(phase) ? null : nextTaskDeadline(tasks),
|
|
719
732
|
activeAttempt: controlState.activeAttempt,
|
|
720
733
|
};
|
|
@@ -755,6 +768,33 @@ function printStatus(payload) {
|
|
|
755
768
|
console.log(buildSignalStatusLine(payload.signals.wave, payload));
|
|
756
769
|
}
|
|
757
770
|
console.log(`blocking=${blocking}`);
|
|
771
|
+
if (payload.supervisor) {
|
|
772
|
+
console.log(
|
|
773
|
+
`supervisor=${payload.supervisor.terminalDisposition || payload.supervisor.status} run_id=${payload.supervisor.runId} launcher_pid=${payload.supervisor.launcherPid || "none"}`,
|
|
774
|
+
);
|
|
775
|
+
if (payload.supervisor.sessionBackend || payload.supervisor.recoveryState || payload.supervisor.resumeAction) {
|
|
776
|
+
console.log(
|
|
777
|
+
`supervisor-backend=${payload.supervisor.sessionBackend || "unknown"} recovery=${payload.supervisor.recoveryState || "unknown"} resume=${payload.supervisor.resumeAction || "none"}`,
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
if ((payload.supervisor.agentRuntimeSummary || []).length > 0) {
|
|
781
|
+
console.log("supervisor-runtime:");
|
|
782
|
+
for (const record of payload.supervisor.agentRuntimeSummary) {
|
|
783
|
+
console.log(
|
|
784
|
+
`- ${record.agentId || "unknown"} ${record.terminalDisposition || "unknown"} pid=${record.pid || "none"} backend=${record.sessionBackend || "process"} attach=${record.attachMode || "log-tail"} heartbeat=${record.lastHeartbeatAt || "n/a"}`,
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if ((payload.forwardedClosureGaps || []).length > 0) {
|
|
790
|
+
console.log("forwarded-closure-gaps:");
|
|
791
|
+
for (const gap of payload.forwardedClosureGaps) {
|
|
792
|
+
const targets = Array.isArray(gap.targets) && gap.targets.length > 0 ? gap.targets.join(",") : "none";
|
|
793
|
+
console.log(
|
|
794
|
+
`- ${gap.stageKey} agent=${gap.agentId || "unknown"} attempt=${gap.attempt ?? "n/a"} targets=${targets}${gap.detail ? ` detail=${gap.detail}` : ""}`,
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
758
798
|
if (payload.nextTimer) {
|
|
759
799
|
console.log(`next-timer=${payload.nextTimer.kind} ${payload.nextTimer.taskId} at ${payload.nextTimer.at}`);
|
|
760
800
|
}
|
|
@@ -955,9 +995,12 @@ export async function runControlCli(argv) {
|
|
|
955
995
|
throw new Error("Expected control surface: status | telemetry | task | rerun | proof");
|
|
956
996
|
}
|
|
957
997
|
if (options.runId) {
|
|
958
|
-
|
|
998
|
+
const context = resolveRunContext(options.runId, options.project, options.lane);
|
|
999
|
+
options.project = context.project;
|
|
1000
|
+
options.lane = context.lane;
|
|
959
1001
|
}
|
|
960
1002
|
const lanePaths = buildLanePaths(options.lane, {
|
|
1003
|
+
project: options.project || undefined,
|
|
961
1004
|
runVariant: options.dryRun ? "dry-run" : undefined,
|
|
962
1005
|
adhocRunId: options.runId || null,
|
|
963
1006
|
});
|