@h-rig/supervisor-plugin 0.0.6-alpha.156 → 0.0.6-alpha.158
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/dist/src/analysis/blockers.d.ts +35 -0
- package/dist/src/analysis/blockers.js +355 -0
- package/dist/src/analysis/taskGraphPrimitives.d.ts +58 -0
- package/dist/src/analysis/taskGraphPrimitives.js +528 -0
- package/dist/src/analysis/taskRanking.d.ts +24 -0
- package/dist/src/analysis/taskRanking.js +374 -0
- package/dist/src/analysis/taskScore.d.ts +17 -0
- package/dist/src/analysis/taskScore.js +49 -0
- package/dist/src/cli.d.ts +1 -7
- package/dist/src/cli.js +611 -91
- package/dist/src/index.js +682 -155
- package/dist/src/loop.d.ts +3 -5
- package/dist/src/loop.js +503 -23
- package/dist/src/plugin.js +655 -134
- package/dist/src/run-status.d.ts +2 -0
- package/dist/src/run-status.js +20 -0
- package/dist/src/supervisor.js +345 -6
- package/package.json +3 -7
package/dist/src/index.js
CHANGED
|
@@ -1,102 +1,468 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
2
17
|
var __require = import.meta.require;
|
|
3
18
|
|
|
4
|
-
// packages/supervisor-plugin/src/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
19
|
+
// packages/supervisor-plugin/src/run-status.ts
|
|
20
|
+
function coerceRunStatus(value, fallback) {
|
|
21
|
+
return KNOWN_RUN_STATUS.has(value) ? value : fallback;
|
|
22
|
+
}
|
|
23
|
+
var KNOWN_RUN_STATUS;
|
|
24
|
+
var init_run_status = __esm(() => {
|
|
25
|
+
KNOWN_RUN_STATUS = new Set([
|
|
26
|
+
"created",
|
|
27
|
+
"queued",
|
|
28
|
+
"preparing",
|
|
29
|
+
"running",
|
|
30
|
+
"validating",
|
|
31
|
+
"reviewing",
|
|
32
|
+
"closing-out",
|
|
33
|
+
"completed",
|
|
34
|
+
"failed",
|
|
35
|
+
"stopped"
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// packages/supervisor-plugin/src/analysis/taskGraphPrimitives.ts
|
|
40
|
+
import {
|
|
41
|
+
OPERATOR_INACTIVE_RUN_STATUSES
|
|
42
|
+
} from "@rig/contracts";
|
|
43
|
+
function isObjectRecord(value) {
|
|
44
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
45
|
+
}
|
|
46
|
+
function readStringList(value) {
|
|
47
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
|
|
48
|
+
}
|
|
49
|
+
function unique(values) {
|
|
50
|
+
return Array.from(new Set(values));
|
|
51
|
+
}
|
|
52
|
+
function readTaskMetadataStringList(task, key) {
|
|
53
|
+
const taskRecord = task;
|
|
54
|
+
const topLevel = readStringList(taskRecord[key]);
|
|
55
|
+
if (topLevel.length > 0)
|
|
56
|
+
return topLevel;
|
|
57
|
+
const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
|
|
58
|
+
const metadataList = readStringList(metadata?.[key]);
|
|
59
|
+
if (metadataList.length > 0)
|
|
60
|
+
return metadataList;
|
|
61
|
+
if (key === "dependencies") {
|
|
62
|
+
return readStringList(metadata?.deps);
|
|
63
|
+
}
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
function readTaskBlockingDependencyRefs(task) {
|
|
67
|
+
return readTaskMetadataStringList(task, "dependencies");
|
|
68
|
+
}
|
|
69
|
+
function readTaskSourceIssueId(task) {
|
|
70
|
+
if (typeof task.sourceIssueId === "string" && task.sourceIssueId.length > 0) {
|
|
71
|
+
return task.sourceIssueId;
|
|
72
|
+
}
|
|
73
|
+
const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
|
|
74
|
+
if (typeof metadata?.sourceIssueId === "string" && metadata.sourceIssueId.length > 0) {
|
|
75
|
+
return metadata.sourceIssueId;
|
|
76
|
+
}
|
|
77
|
+
const rigMetadata = isObjectRecord(metadata?._rig) ? metadata._rig : null;
|
|
78
|
+
return typeof rigMetadata?.sourceIssueId === "string" && rigMetadata.sourceIssueId.length > 0 ? rigMetadata.sourceIssueId : null;
|
|
79
|
+
}
|
|
80
|
+
function readTaskScope(task) {
|
|
81
|
+
const taskRecord = task;
|
|
82
|
+
const topLevel = readStringList(taskRecord.scope);
|
|
83
|
+
if (topLevel.length > 0)
|
|
84
|
+
return unique(topLevel.map((entry) => entry.trim()).filter((entry) => entry.length > 0));
|
|
85
|
+
const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
|
|
86
|
+
const metadataScope = readStringList(metadata?.scope);
|
|
87
|
+
if (metadataScope.length > 0)
|
|
88
|
+
return unique(metadataScope.map((entry) => entry.trim()).filter((entry) => entry.length > 0));
|
|
89
|
+
return unique([
|
|
90
|
+
...readStringList(metadata?.files),
|
|
91
|
+
...readStringList(metadata?.paths)
|
|
92
|
+
].map((entry) => entry.trim()).filter((entry) => entry.length > 0));
|
|
93
|
+
}
|
|
94
|
+
function isScopeList(input) {
|
|
95
|
+
return Array.isArray(input);
|
|
96
|
+
}
|
|
97
|
+
function normalizeScopeInput(input) {
|
|
98
|
+
return isScopeList(input) ? unique(input.map((entry) => entry.trim()).filter((entry) => entry.length > 0)) : readTaskScope(input);
|
|
99
|
+
}
|
|
100
|
+
function disjointScope(left, right) {
|
|
101
|
+
const leftScope = new Set(normalizeScopeInput(left));
|
|
102
|
+
if (leftScope.size === 0)
|
|
103
|
+
return true;
|
|
104
|
+
return normalizeScopeInput(right).every((entry) => !leftScope.has(entry));
|
|
105
|
+
}
|
|
106
|
+
function resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId) {
|
|
107
|
+
if (tasksById.has(ref))
|
|
108
|
+
return ref;
|
|
109
|
+
return taskIdBySourceIssueId.get(ref) ?? taskIdByExternalRef.get(ref) ?? null;
|
|
110
|
+
}
|
|
111
|
+
function buildTaskReferenceIndex(tasks) {
|
|
112
|
+
return {
|
|
113
|
+
tasksById: new Map(tasks.map((task) => [task.id, task])),
|
|
114
|
+
taskIdByExternalRef: new Map(tasks.flatMap((task) => task.externalId ? [[task.externalId, task.id]] : [])),
|
|
115
|
+
taskIdBySourceIssueId: new Map(tasks.flatMap((task) => {
|
|
116
|
+
const sourceIssueId = readTaskSourceIssueId(task);
|
|
117
|
+
return sourceIssueId ? [[sourceIssueId, task.id]] : [];
|
|
118
|
+
}))
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function computeTaskBlockingDepths(tasks) {
|
|
122
|
+
const { tasksById, taskIdByExternalRef, taskIdBySourceIssueId } = buildTaskReferenceIndex(tasks);
|
|
123
|
+
const memo = new Map;
|
|
124
|
+
const visit = (taskId, stack) => {
|
|
125
|
+
const cached = memo.get(taskId);
|
|
126
|
+
if (cached !== undefined)
|
|
127
|
+
return cached;
|
|
128
|
+
if (stack.has(taskId))
|
|
129
|
+
return 0;
|
|
130
|
+
const task = tasksById.get(taskId);
|
|
131
|
+
if (!task)
|
|
132
|
+
return 0;
|
|
133
|
+
stack.add(taskId);
|
|
134
|
+
const blockers = readTaskBlockingDependencyRefs(task).map((ref) => resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId)).filter((ref) => ref !== null && ref !== taskId);
|
|
135
|
+
const depth = blockers.length === 0 ? 0 : Math.max(...blockers.map((blockerId) => visit(blockerId, stack) + 1));
|
|
136
|
+
stack.delete(taskId);
|
|
137
|
+
memo.set(taskId, depth);
|
|
138
|
+
return depth;
|
|
139
|
+
};
|
|
140
|
+
for (const task of tasks) {
|
|
141
|
+
visit(task.id, new Set);
|
|
142
|
+
}
|
|
143
|
+
return memo;
|
|
144
|
+
}
|
|
145
|
+
function isTaskTerminalStatus(status) {
|
|
146
|
+
switch (status) {
|
|
147
|
+
case "closed":
|
|
148
|
+
case "completed":
|
|
149
|
+
case "done":
|
|
150
|
+
case "cancelled":
|
|
151
|
+
case "canceled":
|
|
152
|
+
return true;
|
|
153
|
+
default:
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function isTaskBlockedStatus(status) {
|
|
158
|
+
return status === "blocked";
|
|
159
|
+
}
|
|
160
|
+
function isTaskRunnableStatus(status) {
|
|
161
|
+
if (status === null || status === undefined || status === "")
|
|
162
|
+
return true;
|
|
163
|
+
if (isTaskTerminalStatus(status) || isTaskBlockedStatus(status))
|
|
164
|
+
return false;
|
|
165
|
+
switch (status) {
|
|
166
|
+
case "ready":
|
|
167
|
+
case "open":
|
|
168
|
+
case "failed":
|
|
169
|
+
return true;
|
|
170
|
+
default:
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function computeTaskDependencyBadges(tasks) {
|
|
175
|
+
const index = buildTaskReferenceIndex(tasks);
|
|
176
|
+
const blockingDepths = computeTaskBlockingDepths(tasks);
|
|
177
|
+
const dependencyIdsByTask = new Map;
|
|
178
|
+
const unresolvedRefsByTask = new Map;
|
|
179
|
+
const blocksByTask = new Map;
|
|
180
|
+
for (const task of tasks) {
|
|
181
|
+
const dependencyIds = [];
|
|
182
|
+
const unresolvedRefs = [];
|
|
183
|
+
for (const ref of readTaskBlockingDependencyRefs(task)) {
|
|
184
|
+
const dependencyId = resolveTaskReference(ref, index.tasksById, index.taskIdByExternalRef, index.taskIdBySourceIssueId);
|
|
185
|
+
if (dependencyId && dependencyId !== task.id) {
|
|
186
|
+
dependencyIds.push(dependencyId);
|
|
187
|
+
const blocks = blocksByTask.get(dependencyId);
|
|
188
|
+
if (blocks) {
|
|
189
|
+
blocks.push(task.id);
|
|
190
|
+
} else {
|
|
191
|
+
blocksByTask.set(dependencyId, [task.id]);
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
unresolvedRefs.push(ref);
|
|
195
|
+
}
|
|
32
196
|
}
|
|
33
|
-
|
|
34
|
-
|
|
197
|
+
dependencyIdsByTask.set(task.id, unique(dependencyIds));
|
|
198
|
+
unresolvedRefsByTask.set(task.id, unique(unresolvedRefs));
|
|
199
|
+
}
|
|
200
|
+
const summaries = new Map;
|
|
201
|
+
for (const task of tasks) {
|
|
202
|
+
const dependencyIds = dependencyIdsByTask.get(task.id) ?? [];
|
|
203
|
+
const unresolvedDependencyRefs = unresolvedRefsByTask.get(task.id) ?? [];
|
|
204
|
+
const blockedBy = dependencyIds.filter((dependencyId) => {
|
|
205
|
+
const dependency = index.tasksById.get(dependencyId);
|
|
206
|
+
return dependency ? !isTaskTerminalStatus(dependency.status) : false;
|
|
207
|
+
});
|
|
208
|
+
const blocks = unique(blocksByTask.get(task.id) ?? []);
|
|
209
|
+
const blocked = isTaskBlockedStatus(task.status) || blockedBy.length > 0;
|
|
210
|
+
const ready = isTaskRunnableStatus(task.status) && !blocked;
|
|
211
|
+
const badges = [];
|
|
212
|
+
if (blocked) {
|
|
213
|
+
badges.push({
|
|
214
|
+
kind: "blocked",
|
|
215
|
+
label: blockedBy.length > 0 ? `blocked \xD7${blockedBy.length}` : "blocked",
|
|
216
|
+
description: blockedBy.length > 0 ? `Waiting on ${blockedBy.join(", ")}.` : "Task source marks this task blocked.",
|
|
217
|
+
...blockedBy.length > 0 ? { count: blockedBy.length } : {},
|
|
218
|
+
taskIds: blockedBy
|
|
219
|
+
});
|
|
220
|
+
} else if (ready) {
|
|
221
|
+
badges.push({
|
|
222
|
+
kind: "ready",
|
|
223
|
+
label: "ready",
|
|
224
|
+
description: "No open dependencies block this task."
|
|
225
|
+
});
|
|
35
226
|
}
|
|
36
|
-
|
|
227
|
+
if (dependencyIds.length > 0 || blocks.length > 0 || unresolvedDependencyRefs.length > 0) {
|
|
228
|
+
badges.push({
|
|
229
|
+
kind: "dependency",
|
|
230
|
+
label: `deps ${dependencyIds.length}/${blocks.length}`,
|
|
231
|
+
description: [
|
|
232
|
+
dependencyIds.length > 0 ? `Depends on ${dependencyIds.join(", ")}.` : null,
|
|
233
|
+
blocks.length > 0 ? `Blocks ${blocks.join(", ")}.` : null,
|
|
234
|
+
unresolvedDependencyRefs.length > 0 ? `Unresolved refs: ${unresolvedDependencyRefs.join(", ")}.` : null
|
|
235
|
+
].filter((part) => part !== null).join(" "),
|
|
236
|
+
count: dependencyIds.length + blocks.length,
|
|
237
|
+
taskIds: unique([...dependencyIds, ...blocks])
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
summaries.set(task.id, {
|
|
241
|
+
taskId: task.id,
|
|
242
|
+
blockingDepth: blockingDepths.get(task.id) ?? 0,
|
|
243
|
+
dependencyIds,
|
|
244
|
+
unresolvedDependencyRefs,
|
|
245
|
+
blockedBy,
|
|
246
|
+
blocks,
|
|
247
|
+
blocked,
|
|
248
|
+
ready,
|
|
249
|
+
dependencyCount: dependencyIds.length,
|
|
250
|
+
dependentCount: blocks.length,
|
|
251
|
+
badges
|
|
252
|
+
});
|
|
37
253
|
}
|
|
254
|
+
return summaries;
|
|
38
255
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
function createClosureStage(port) {
|
|
42
|
-
return async (ctx) => {
|
|
43
|
-
const summary = await port.summarize(ctx);
|
|
44
|
-
if (!summary)
|
|
45
|
-
return { kind: "continue", ctx };
|
|
46
|
-
await port.record?.(summary);
|
|
47
|
-
const metadata = { ...ctx.metadata ?? {}, supervisorClosure: summary };
|
|
48
|
-
return { kind: "continue", ctx: { ...ctx, metadata } };
|
|
49
|
-
};
|
|
256
|
+
function stringValue(value) {
|
|
257
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
50
258
|
}
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
259
|
+
function stringArray(value) {
|
|
260
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0).map((entry) => entry.trim()) : [];
|
|
261
|
+
}
|
|
262
|
+
function numberOrNull(value) {
|
|
263
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
264
|
+
}
|
|
265
|
+
function metadataOf(task) {
|
|
266
|
+
return isObjectRecord(task.metadata) ? task.metadata : {};
|
|
267
|
+
}
|
|
268
|
+
function normalizeTaskStatus(status) {
|
|
269
|
+
const token = typeof status === "string" ? status.trim().toLowerCase() : "";
|
|
270
|
+
if (token === "done")
|
|
271
|
+
return "completed";
|
|
272
|
+
if (token === "canceled")
|
|
273
|
+
return "cancelled";
|
|
274
|
+
return TASK_STATUSES.has(token) ? token : "unknown";
|
|
275
|
+
}
|
|
276
|
+
function toTaskDependencyProjection(task) {
|
|
277
|
+
const metadata = metadataOf(task);
|
|
56
278
|
return {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
279
|
+
id: String(task.id),
|
|
280
|
+
title: stringValue(task.title),
|
|
281
|
+
status: normalizeTaskStatus(task.status),
|
|
282
|
+
priority: numberOrNull(task.priority),
|
|
283
|
+
metadata,
|
|
284
|
+
externalId: stringValue(task.externalId),
|
|
285
|
+
sourceIssueId: stringValue(task.sourceIssueId),
|
|
286
|
+
dependencies: stringArray(task.dependencies),
|
|
287
|
+
parentChildDeps: stringArray(task.parentChildDeps),
|
|
288
|
+
createdAt: stringValue(task.createdAt) ?? "",
|
|
289
|
+
updatedAt: stringValue(task.updatedAt) ?? "",
|
|
290
|
+
role: stringValue(task.role),
|
|
291
|
+
scope: stringArray(task.scope),
|
|
292
|
+
validationKeys: stringArray(task.validationKeys),
|
|
293
|
+
labels: stringArray(task.labels),
|
|
294
|
+
assignees: task.assignees ?? null,
|
|
295
|
+
assignedTo: task.assignedTo ?? null
|
|
63
296
|
};
|
|
64
297
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
298
|
+
function latestRunByTaskId(runs) {
|
|
299
|
+
const byTask = new Map;
|
|
300
|
+
const stamp = (run) => Date.parse(run.updatedAt ?? run.startedAt ?? "") || 0;
|
|
301
|
+
for (const run of runs) {
|
|
302
|
+
if (!run.taskId)
|
|
303
|
+
continue;
|
|
304
|
+
const current = byTask.get(run.taskId);
|
|
305
|
+
if (!current || stamp(run) >= stamp(current))
|
|
306
|
+
byTask.set(run.taskId, run);
|
|
307
|
+
}
|
|
308
|
+
return byTask;
|
|
67
309
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
310
|
+
var TASK_STATUSES;
|
|
311
|
+
var init_taskGraphPrimitives = __esm(() => {
|
|
312
|
+
TASK_STATUSES = new Set([
|
|
313
|
+
"draft",
|
|
314
|
+
"open",
|
|
315
|
+
"ready",
|
|
316
|
+
"queued",
|
|
317
|
+
"running",
|
|
318
|
+
"in_progress",
|
|
319
|
+
"under_review",
|
|
320
|
+
"blocked",
|
|
321
|
+
"unknown",
|
|
322
|
+
"completed",
|
|
323
|
+
"failed",
|
|
324
|
+
"cancelled",
|
|
325
|
+
"closed"
|
|
326
|
+
]);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// packages/supervisor-plugin/src/analysis/taskScore.ts
|
|
330
|
+
function finiteNumber(value, fallback) {
|
|
331
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
332
|
+
}
|
|
333
|
+
function scoreTask(input) {
|
|
334
|
+
const priority = finiteNumber(input.priority, 3);
|
|
335
|
+
const unblockCount = Math.max(0, finiteNumber(input.unblockCount, 0));
|
|
336
|
+
const roleWeight = input.role ? ROLE_WEIGHT[input.role] ?? 0 : 0;
|
|
337
|
+
const criticalityWeight = input.criticality ? CRITICALITY_WEIGHT[input.criticality] ?? 0 : 0;
|
|
338
|
+
const validationWeight = (input.validation ?? []).some((entry) => entry.includes("test-contract") || entry.includes("test-boundary")) ? 8 : 0;
|
|
339
|
+
const queueWeight = finiteNumber(input.queueWeight, 0);
|
|
340
|
+
return unblockCount * 100 + (10 - priority) + roleWeight + criticalityWeight + validationWeight + queueWeight;
|
|
341
|
+
}
|
|
342
|
+
function rankTasks(tasks, scoreInput, idOf) {
|
|
343
|
+
return tasks.map((task) => {
|
|
344
|
+
const input = scoreInput(task);
|
|
345
|
+
return {
|
|
346
|
+
task,
|
|
347
|
+
score: scoreTask(input),
|
|
348
|
+
priority: finiteNumber(input.priority, 3),
|
|
349
|
+
unblockCount: Math.max(0, finiteNumber(input.unblockCount, 0))
|
|
350
|
+
};
|
|
351
|
+
}).toSorted((left, right) => {
|
|
352
|
+
const scoreDelta = right.score - left.score;
|
|
353
|
+
if (scoreDelta !== 0)
|
|
354
|
+
return scoreDelta;
|
|
355
|
+
const unblockDelta = right.unblockCount - left.unblockCount;
|
|
356
|
+
if (unblockDelta !== 0)
|
|
357
|
+
return unblockDelta;
|
|
358
|
+
const priorityDelta = left.priority - right.priority;
|
|
359
|
+
if (priorityDelta !== 0)
|
|
360
|
+
return priorityDelta;
|
|
361
|
+
return idOf(left.task).localeCompare(idOf(right.task));
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
var ROLE_WEIGHT, CRITICALITY_WEIGHT;
|
|
365
|
+
var init_taskScore = __esm(() => {
|
|
366
|
+
ROLE_WEIGHT = {
|
|
367
|
+
architect: 25,
|
|
368
|
+
extractor: 10
|
|
82
369
|
};
|
|
370
|
+
CRITICALITY_WEIGHT = {
|
|
371
|
+
core: 20,
|
|
372
|
+
high: 10,
|
|
373
|
+
normal: 0
|
|
374
|
+
};
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// packages/supervisor-plugin/src/analysis/taskRanking.ts
|
|
378
|
+
function isObjectRecord2(value) {
|
|
379
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
83
380
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
381
|
+
function readStringList2(value) {
|
|
382
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
|
|
383
|
+
}
|
|
384
|
+
function readTaskRole(task) {
|
|
385
|
+
if (typeof task.role === "string" && task.role.trim())
|
|
386
|
+
return task.role.trim();
|
|
387
|
+
const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
|
|
388
|
+
return typeof metadata?.role === "string" && metadata.role.trim() ? metadata.role.trim() : null;
|
|
389
|
+
}
|
|
390
|
+
function readTaskCriticality(task) {
|
|
391
|
+
const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
|
|
392
|
+
return typeof metadata?.criticality === "string" && metadata.criticality.trim() ? metadata.criticality.trim() : null;
|
|
393
|
+
}
|
|
394
|
+
function readTaskValidationKeys(task) {
|
|
395
|
+
const taskRecord = task;
|
|
396
|
+
const topLevel = readStringList2(taskRecord.validationKeys);
|
|
397
|
+
if (topLevel.length > 0)
|
|
398
|
+
return topLevel;
|
|
399
|
+
const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
|
|
400
|
+
return readStringList2(metadata?.validation);
|
|
401
|
+
}
|
|
402
|
+
function readTaskQueueWeight(task) {
|
|
403
|
+
const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
|
|
404
|
+
const queueWeight = metadata?.queueWeight ?? metadata?.queue_weight;
|
|
405
|
+
return typeof queueWeight === "number" && Number.isFinite(queueWeight) ? queueWeight : 0;
|
|
406
|
+
}
|
|
407
|
+
function scoreInputForTask(task, unblockCount) {
|
|
408
|
+
return {
|
|
409
|
+
priority: task.priority,
|
|
410
|
+
unblockCount,
|
|
411
|
+
role: readTaskRole(task),
|
|
412
|
+
criticality: readTaskCriticality(task),
|
|
413
|
+
validation: readTaskValidationKeys(task),
|
|
414
|
+
queueWeight: readTaskQueueWeight(task)
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
function rankReadyTasks(tasks, options = {}) {
|
|
418
|
+
const excluded = new Set(options.excludeTaskIds ?? []);
|
|
419
|
+
const activeTaskIds = new Set(options.activeTaskIds ?? []);
|
|
420
|
+
const badges = computeTaskDependencyBadges(tasks);
|
|
421
|
+
const tasksById = new Map(tasks.map((task) => [String(task.id), task]));
|
|
422
|
+
const openBlockCount = (task) => (badges.get(task.id)?.blocks ?? []).filter((blockedTaskId) => {
|
|
423
|
+
const blockedTask = tasksById.get(blockedTaskId);
|
|
424
|
+
return blockedTask ? !isTaskTerminalStatus(blockedTask.status) : false;
|
|
425
|
+
}).length;
|
|
426
|
+
const readyTasks = tasks.filter((task) => !excluded.has(task.id)).filter((task) => !activeTaskIds.has(task.id)).filter((task) => options.filter?.(task) ?? true).filter((task) => badges.get(task.id)?.ready === true);
|
|
427
|
+
const maxUnblockCount = Math.max(0, ...readyTasks.map(openBlockCount));
|
|
428
|
+
const selectedByMode = readyTasks.filter((task) => {
|
|
429
|
+
const unblockCount = openBlockCount(task);
|
|
430
|
+
if (options.selection === "blocking-only")
|
|
431
|
+
return unblockCount > 0;
|
|
432
|
+
if (options.selection === "max-unblock")
|
|
433
|
+
return maxUnblockCount > 0 && unblockCount === maxUnblockCount;
|
|
434
|
+
return true;
|
|
435
|
+
});
|
|
436
|
+
return rankTasks(selectedByMode, (task) => scoreInputForTask(task, openBlockCount(task)), (task) => task.id).map((entry) => ({
|
|
437
|
+
task: entry.task,
|
|
438
|
+
score: entry.score,
|
|
439
|
+
priority: entry.priority,
|
|
440
|
+
unblockCount: entry.unblockCount,
|
|
441
|
+
scope: readTaskScope(entry.task)
|
|
442
|
+
}));
|
|
443
|
+
}
|
|
444
|
+
function selectRankedReadyTasks(tasks, options = {}) {
|
|
445
|
+
const ranked = rankReadyTasks(tasks, options);
|
|
446
|
+
if (options.requireDisjointScopes !== true && !options.disjointWithScopes) {
|
|
447
|
+
return ranked.slice(0, options.limit).map((entry) => entry.task);
|
|
448
|
+
}
|
|
449
|
+
const occupiedScopes = new Set(options.disjointWithScopes ?? []);
|
|
450
|
+
const selected = [];
|
|
451
|
+
for (const entry of ranked) {
|
|
452
|
+
if (entry.scope.some((scope) => occupiedScopes.has(scope)))
|
|
453
|
+
continue;
|
|
454
|
+
selected.push(entry.task);
|
|
455
|
+
for (const scope of entry.scope)
|
|
456
|
+
occupiedScopes.add(scope);
|
|
457
|
+
if (options.limit !== undefined && selected.length >= options.limit)
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
return selected;
|
|
461
|
+
}
|
|
462
|
+
var init_taskRanking = __esm(() => {
|
|
463
|
+
init_taskGraphPrimitives();
|
|
464
|
+
init_taskScore();
|
|
465
|
+
});
|
|
100
466
|
|
|
101
467
|
// packages/supervisor-plugin/src/journal.ts
|
|
102
468
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
@@ -105,8 +471,6 @@ import { Schema } from "effect";
|
|
|
105
471
|
import {
|
|
106
472
|
SupervisorEvent
|
|
107
473
|
} from "@rig/contracts";
|
|
108
|
-
var NEWLINE = `
|
|
109
|
-
`;
|
|
110
474
|
function reduceSupervisorJournal(events) {
|
|
111
475
|
let processed = 0;
|
|
112
476
|
let succeeded = 0;
|
|
@@ -227,9 +591,102 @@ function createSupervisorJournal(store) {
|
|
|
227
591
|
const readProjection = async () => reduceSupervisorJournal(await readEvents());
|
|
228
592
|
return { append, readEvents, readProjection };
|
|
229
593
|
}
|
|
594
|
+
var NEWLINE = `
|
|
595
|
+
`;
|
|
596
|
+
var init_journal = () => {};
|
|
597
|
+
|
|
598
|
+
// packages/supervisor-plugin/src/analysis/blockers.ts
|
|
599
|
+
function labelsFor(task) {
|
|
600
|
+
return readTaskMetadataStringList(task, "labels").map((label) => label.toLowerCase());
|
|
601
|
+
}
|
|
602
|
+
function configuredTier(task, labels) {
|
|
603
|
+
const metadata = task.metadata && typeof task.metadata === "object" && !Array.isArray(task.metadata) ? task.metadata : {};
|
|
604
|
+
const tier = metadata.riskTier ?? metadata.actionRiskTier;
|
|
605
|
+
if (tier === "t1-read" || tier === "t2-reversible" || tier === "t3-external" || tier === "t4-irreversible")
|
|
606
|
+
return tier;
|
|
607
|
+
if (labels.some((label) => /prod|deploy|migration|billing|delete|irreversible/.test(label)))
|
|
608
|
+
return "t4-irreversible";
|
|
609
|
+
if (labels.some((label) => /customer|vendor|external|secret|credential|approval|decision/.test(label)))
|
|
610
|
+
return "t3-external";
|
|
611
|
+
const scopes = task.scope ?? [];
|
|
612
|
+
if (scopes.some((scope) => /docs|readme|test|spec/.test(scope.toLowerCase())))
|
|
613
|
+
return "t1-read";
|
|
614
|
+
return "t2-reversible";
|
|
615
|
+
}
|
|
616
|
+
function tierOf(task, labels = []) {
|
|
617
|
+
const projection = "metadata" in task && "id" in task && typeof task.id === "string" ? toTaskDependencyProjection(task) : task;
|
|
618
|
+
return configuredTier(projection, labels.length > 0 ? labels : labelsFor(projection)) ?? "t2-reversible";
|
|
619
|
+
}
|
|
620
|
+
function baseClassification(task, blockerClass, source, rationale, labels) {
|
|
621
|
+
return {
|
|
622
|
+
taskId: task.id,
|
|
623
|
+
blockerClass,
|
|
624
|
+
actionRiskTier: tierOf(task, labels),
|
|
625
|
+
rationale,
|
|
626
|
+
source,
|
|
627
|
+
autoApplied: source === "llm"
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
function normalizeRunStatus(status) {
|
|
631
|
+
if (status === "waiting-approval" || status === "waiting-user-input" || status === "needs-attention" || status === "completed" || status === "failed" || status === "stopped" || status === "running")
|
|
632
|
+
return status;
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
function isHumanBlockerClass(blockerClass) {
|
|
636
|
+
return HUMAN_BLOCKERS.has(blockerClass);
|
|
637
|
+
}
|
|
638
|
+
function classifyBlocker(input) {
|
|
639
|
+
const labels = input.labels ?? labelsFor(input.task);
|
|
640
|
+
const runStatus = normalizeRunStatus(input.run?.status);
|
|
641
|
+
if (runStatus === "waiting-approval")
|
|
642
|
+
return baseClassification(input.task, "human-approval", "status", "run awaiting approval", labels);
|
|
643
|
+
if (runStatus === "waiting-user-input")
|
|
644
|
+
return baseClassification(input.task, "external-input", "status", "run awaiting user input", labels);
|
|
645
|
+
if (input.task.status !== "blocked")
|
|
646
|
+
return baseClassification(input.task, "not-blocked", "elimination", "task is not blocked", labels);
|
|
647
|
+
const incomplete = (input.badges.get(input.task.id)?.blockedBy ?? []).filter((dependencyId) => {
|
|
648
|
+
const dependency = input.tasksById.get(dependencyId);
|
|
649
|
+
return dependency ? !isTaskTerminalStatus(dependency.status) : false;
|
|
650
|
+
});
|
|
651
|
+
if (incomplete.length > 0)
|
|
652
|
+
return baseClassification(input.task, "task-blocked", "elimination", `blocked by ${incomplete.length} incomplete task(s)`, labels);
|
|
653
|
+
if (labels.includes("needs-decision"))
|
|
654
|
+
return baseClassification(input.task, "human-decision", "label", "needs-decision label", labels);
|
|
655
|
+
if (labels.some((label) => /^waiting-on-/.test(label)))
|
|
656
|
+
return baseClassification(input.task, "external-input", "label", "waiting-on-* label", labels);
|
|
657
|
+
if (input.config?.llm)
|
|
658
|
+
return baseClassification(input.task, "human-decision", "llm", "LLM residue classifier marked this as human-gated", labels);
|
|
659
|
+
return baseClassification(input.task, "human-decision", "elimination", "blocked, all task dependencies terminal; non-task gate remains", labels);
|
|
660
|
+
}
|
|
661
|
+
function classifyTasks(tasks, runs = [], options = {}) {
|
|
662
|
+
const projected = tasks.map(toTaskDependencyProjection);
|
|
663
|
+
const badges = computeTaskDependencyBadges(projected);
|
|
664
|
+
const tasksById = new Map(projected.map((task) => [task.id, task]));
|
|
665
|
+
const runByTask = latestRunByTaskId(runs);
|
|
666
|
+
const classifier = options.classifier ?? classifyBlocker;
|
|
667
|
+
const classifications = projected.map((task) => classifier({ task, badges, tasksById, run: runByTask.get(task.id) ?? null, labels: labelsFor(task) })).filter((classification) => options.humanOnly !== true || isHumanBlockerClass(classification.blockerClass));
|
|
668
|
+
return {
|
|
669
|
+
classifications,
|
|
670
|
+
byTaskId: new Map(classifications.map((classification) => [classification.taskId, classification])),
|
|
671
|
+
human: classifications.filter((classification) => isHumanBlockerClass(classification.blockerClass)),
|
|
672
|
+
machine: classifications.filter((classification) => !isHumanBlockerClass(classification.blockerClass)),
|
|
673
|
+
generatedAt: options.generatedAt ?? new Date().toISOString()
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
var HUMAN_BLOCKERS;
|
|
677
|
+
var init_blockers = __esm(() => {
|
|
678
|
+
init_taskGraphPrimitives();
|
|
679
|
+
HUMAN_BLOCKERS = new Set(["human-decision", "human-approval", "external-input"]);
|
|
680
|
+
});
|
|
230
681
|
|
|
231
682
|
// packages/supervisor-plugin/src/loop.ts
|
|
232
|
-
|
|
683
|
+
var exports_loop = {};
|
|
684
|
+
__export(exports_loop, {
|
|
685
|
+
unblockTask: () => unblockTask,
|
|
686
|
+
runSupervisorLoop: () => runSupervisorLoop,
|
|
687
|
+
planSupervisorLoop: () => planSupervisorLoop,
|
|
688
|
+
controlSupervisorRun: () => controlSupervisorRun
|
|
689
|
+
});
|
|
233
690
|
function selectionMode(policy) {
|
|
234
691
|
return policy === "blocking-only" || policy === "max-unblock" ? policy : "all-ready";
|
|
235
692
|
}
|
|
@@ -288,25 +745,7 @@ function failedOutcome(outcome) {
|
|
|
288
745
|
return outcome.status === "failed" || outcome.status === "stopped" || outcome.status === "needs-attention" || outcome.status === "waiting-approval" || outcome.status === "waiting-user-input";
|
|
289
746
|
}
|
|
290
747
|
function runStatus(value) {
|
|
291
|
-
|
|
292
|
-
case "created":
|
|
293
|
-
case "queued":
|
|
294
|
-
case "preparing":
|
|
295
|
-
case "running":
|
|
296
|
-
case "waiting-approval":
|
|
297
|
-
case "waiting-user-input":
|
|
298
|
-
case "paused":
|
|
299
|
-
case "validating":
|
|
300
|
-
case "reviewing":
|
|
301
|
-
case "closing-out":
|
|
302
|
-
case "needs-attention":
|
|
303
|
-
case "completed":
|
|
304
|
-
case "failed":
|
|
305
|
-
case "stopped":
|
|
306
|
-
return value;
|
|
307
|
-
default:
|
|
308
|
-
return "failed";
|
|
309
|
-
}
|
|
748
|
+
return coerceRunStatus(value, "failed");
|
|
310
749
|
}
|
|
311
750
|
async function runSupervisorLoop(projectRoot, deps, options = {}) {
|
|
312
751
|
const events = [{ kind: "supervisor.started", at: at(deps), options }];
|
|
@@ -403,10 +842,115 @@ async function unblockTask(projectRoot, taskId, deps, options = {}) {
|
|
|
403
842
|
const blockers = collectBlockingClosure(taskId, badges);
|
|
404
843
|
return runSupervisorLoop(projectRoot, deps, { ...options, candidateTaskIds: blockers, selectionPolicy: "rank", maxTasks: 1 });
|
|
405
844
|
}
|
|
845
|
+
var init_loop = __esm(() => {
|
|
846
|
+
init_taskGraphPrimitives();
|
|
847
|
+
init_taskRanking();
|
|
848
|
+
init_journal();
|
|
849
|
+
init_run_status();
|
|
850
|
+
init_blockers();
|
|
851
|
+
});
|
|
406
852
|
|
|
853
|
+
// packages/supervisor-plugin/src/awaiter.ts
|
|
854
|
+
var TERMINAL_RUN_STATUSES = {
|
|
855
|
+
created: false,
|
|
856
|
+
queued: false,
|
|
857
|
+
preparing: false,
|
|
858
|
+
running: false,
|
|
859
|
+
"waiting-approval": false,
|
|
860
|
+
"waiting-user-input": false,
|
|
861
|
+
paused: false,
|
|
862
|
+
validating: false,
|
|
863
|
+
reviewing: false,
|
|
864
|
+
"closing-out": false,
|
|
865
|
+
"needs-attention": true,
|
|
866
|
+
completed: true,
|
|
867
|
+
failed: true,
|
|
868
|
+
stopped: true
|
|
869
|
+
};
|
|
870
|
+
async function awaitTerminalRun(options) {
|
|
871
|
+
const startedAt = Date.now();
|
|
872
|
+
const pollMs = Math.max(0, Math.floor(options.pollMs ?? 5000));
|
|
873
|
+
while (true) {
|
|
874
|
+
const snapshot = await options.readStatus(options.runId);
|
|
875
|
+
if (snapshot && TERMINAL_RUN_STATUSES[snapshot.status]) {
|
|
876
|
+
return {
|
|
877
|
+
runId: options.runId,
|
|
878
|
+
status: snapshot.status,
|
|
879
|
+
failed: snapshot.status !== "completed"
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
if (options.timeoutMs !== undefined && Date.now() - startedAt >= options.timeoutMs) {
|
|
883
|
+
return { runId: options.runId, status: "needs-attention", failed: true };
|
|
884
|
+
}
|
|
885
|
+
await options.waitForChange(pollMs);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
// packages/supervisor-plugin/src/closureStage.ts
|
|
889
|
+
var SUPERVISOR_CLOSURE_STAGE_ID = "supervisor-closure-observer";
|
|
890
|
+
function createClosureStage(port) {
|
|
891
|
+
return async (ctx) => {
|
|
892
|
+
const summary = await port.summarize(ctx);
|
|
893
|
+
if (!summary)
|
|
894
|
+
return { kind: "continue", ctx };
|
|
895
|
+
await port.record?.(summary);
|
|
896
|
+
const metadata = { ...ctx.metadata ?? {}, supervisorClosure: summary };
|
|
897
|
+
return { kind: "continue", ctx: { ...ctx, metadata } };
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
function summarizeClosure(ctx) {
|
|
901
|
+
const state = (ctx.metadata ?? {}).closeoutState ?? null;
|
|
902
|
+
const taskId = typeof ctx.taskId === "string" ? ctx.taskId : null;
|
|
903
|
+
if (!state || !taskId)
|
|
904
|
+
return null;
|
|
905
|
+
return {
|
|
906
|
+
taskId,
|
|
907
|
+
runId: ctx.runId,
|
|
908
|
+
prUrl: state.prUrl ?? null,
|
|
909
|
+
mergeGate: state.mergeGate ?? null,
|
|
910
|
+
acceptanceChecked: [],
|
|
911
|
+
unblockedTaskIds: state.unblockedTaskIds ?? []
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
function createSupervisorClosureStage() {
|
|
915
|
+
return createClosureStage({ summarize: summarizeClosure });
|
|
916
|
+
}
|
|
917
|
+
function createDefaultSupervisorClosureStage() {
|
|
918
|
+
return async (ctx) => {
|
|
919
|
+
const summary = summarizeClosure(ctx);
|
|
920
|
+
const projectRoot = (ctx.metadata ?? {}).projectRoot;
|
|
921
|
+
if (summary && typeof projectRoot === "string") {
|
|
922
|
+
const { appendFileSync, mkdirSync } = await import("fs");
|
|
923
|
+
const { join } = await import("path");
|
|
924
|
+
try {
|
|
925
|
+
mkdirSync(join(projectRoot, ".rig"), { recursive: true });
|
|
926
|
+
appendFileSync(join(projectRoot, ".rig", "supervisor.jsonl"), `${JSON.stringify({ kind: "supervisor.outcome", at: new Date().toISOString(), taskId: summary.taskId, runId: summary.runId, status: summary.mergeGate === "passed" ? "completed" : "needs-attention", failed: false, unblockedTaskIds: summary.unblockedTaskIds, closure: summary })}
|
|
927
|
+
`);
|
|
928
|
+
} catch {}
|
|
929
|
+
}
|
|
930
|
+
return { kind: "continue", ctx };
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
var supervisorClosureStage = {
|
|
934
|
+
id: SUPERVISOR_CLOSURE_STAGE_ID,
|
|
935
|
+
kind: "observe",
|
|
936
|
+
after: ["source-closeout"],
|
|
937
|
+
before: ["journal-append"],
|
|
938
|
+
priority: 0,
|
|
939
|
+
protected: false
|
|
940
|
+
};
|
|
941
|
+
var supervisorClosureStageMutation = {
|
|
942
|
+
op: "insert",
|
|
943
|
+
contributedBy: "@rig/supervisor-plugin",
|
|
944
|
+
stage: supervisorClosureStage
|
|
945
|
+
};
|
|
407
946
|
// packages/supervisor-plugin/src/cli.ts
|
|
947
|
+
import { RUN_DISPATCH, RUN_READ_MODEL, TASK_IO_SERVICE_CAPABILITY } from "@rig/contracts";
|
|
948
|
+
import { defineCapability } from "@rig/core/capability";
|
|
949
|
+
import { requireCapabilityForRoot } from "@rig/core/capability-loaders";
|
|
950
|
+
init_run_status();
|
|
408
951
|
var SUPERVISOR_LOOP_CLI_ID = "supervisor.loop";
|
|
409
952
|
var SUPERVISOR_UNBLOCK_CLI_ID = "supervisor.unblock";
|
|
953
|
+
var RunReadModelCap = defineCapability(RUN_READ_MODEL);
|
|
410
954
|
function printJson(value) {
|
|
411
955
|
console.log(JSON.stringify(value, null, 2));
|
|
412
956
|
}
|
|
@@ -443,55 +987,36 @@ function parsePositiveInt(value, flag, fallback) {
|
|
|
443
987
|
}
|
|
444
988
|
return parsed;
|
|
445
989
|
}
|
|
446
|
-
function parseCsv(value) {
|
|
447
|
-
return value?.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0) ?? [];
|
|
448
|
-
}
|
|
449
990
|
function asRunStatus(value) {
|
|
450
|
-
|
|
451
|
-
case "created":
|
|
452
|
-
case "queued":
|
|
453
|
-
case "preparing":
|
|
454
|
-
case "running":
|
|
455
|
-
case "waiting-approval":
|
|
456
|
-
case "waiting-user-input":
|
|
457
|
-
case "paused":
|
|
458
|
-
case "validating":
|
|
459
|
-
case "reviewing":
|
|
460
|
-
case "closing-out":
|
|
461
|
-
case "needs-attention":
|
|
462
|
-
case "completed":
|
|
463
|
-
case "failed":
|
|
464
|
-
case "stopped":
|
|
465
|
-
return value;
|
|
466
|
-
default:
|
|
467
|
-
return "needs-attention";
|
|
468
|
-
}
|
|
991
|
+
return coerceRunStatus(value, "needs-attention");
|
|
469
992
|
}
|
|
470
993
|
function delay(ms) {
|
|
471
994
|
const { promise, resolve } = Promise.withResolvers();
|
|
472
995
|
setTimeout(resolve, ms);
|
|
473
996
|
return promise;
|
|
474
997
|
}
|
|
475
|
-
async function loadSupervisorClient() {
|
|
476
|
-
const [taskIo,
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
import("@rig/runtime/control-plane/dispatch")
|
|
998
|
+
async function loadSupervisorClient(projectRoot) {
|
|
999
|
+
const [taskIo, runReadModel] = await Promise.all([
|
|
1000
|
+
requireCapabilityForRoot(projectRoot, defineCapability(TASK_IO_SERVICE_CAPABILITY), "No task-sources plugin provides task IO for this project root."),
|
|
1001
|
+
requireCapabilityForRoot(projectRoot, RunReadModelCap, "No run-worker plugin provides run read-model for this project root.")
|
|
480
1002
|
]);
|
|
481
|
-
return { listTasks: taskIo.listTasks, listRuns:
|
|
1003
|
+
return { listTasks: taskIo.listTasks, listRuns: (root) => runReadModel.listRuns({ projectRoot: root }) };
|
|
482
1004
|
}
|
|
483
1005
|
function supervisorDeps(timeoutMs) {
|
|
484
1006
|
return {
|
|
485
|
-
listTasks: async (projectRoot) => (await loadSupervisorClient()).listTasks(projectRoot),
|
|
486
|
-
listRuns: async (projectRoot) => (await loadSupervisorClient()).listRuns(projectRoot),
|
|
487
|
-
dispatchRun: async (
|
|
1007
|
+
listTasks: async (projectRoot) => (await loadSupervisorClient(projectRoot)).listTasks(projectRoot),
|
|
1008
|
+
listRuns: async (projectRoot) => (await loadSupervisorClient(projectRoot)).listRuns(projectRoot),
|
|
1009
|
+
dispatchRun: async (input) => {
|
|
1010
|
+
const service = await requireCapabilityForRoot(input.projectRoot, defineCapability(RUN_DISPATCH), "No transport plugin provides run dispatch for this project root.");
|
|
1011
|
+
return service.dispatchRun(input);
|
|
1012
|
+
},
|
|
488
1013
|
awaitRunTerminal: async (projectRoot, runId) => {
|
|
489
1014
|
const awaitedRunId = runId;
|
|
490
1015
|
return awaitTerminalRun({
|
|
491
1016
|
runId: awaitedRunId,
|
|
492
1017
|
timeoutMs,
|
|
493
1018
|
readStatus: async (id) => {
|
|
494
|
-
const { listRuns } = await loadSupervisorClient();
|
|
1019
|
+
const { listRuns } = await loadSupervisorClient(projectRoot);
|
|
495
1020
|
const run = (await listRuns(projectRoot)).find((candidate) => candidate.runId === id);
|
|
496
1021
|
return run ? { runId: id, status: asRunStatus(run.status), ...run.errorSummary ? { diagnostic: run.errorSummary } : {} } : null;
|
|
497
1022
|
},
|
|
@@ -521,13 +1046,14 @@ async function executeLoop(context, args) {
|
|
|
521
1046
|
const stopWhen = takeOption(task.rest, "--stop-when");
|
|
522
1047
|
const timeout = takeOption(stopWhen.rest, "--timeout-ms");
|
|
523
1048
|
requireNoExtraArgs(timeout.rest, "rig loop [--task <id>] [--max-tasks <n>] [--concurrency <n>] [--stop-when <csv>] [--dry-run] [--json]");
|
|
524
|
-
const
|
|
1049
|
+
const { runSupervisorLoop: runSupervisorLoop2 } = await Promise.resolve().then(() => (init_loop(), exports_loop));
|
|
1050
|
+
const result = await runSupervisorLoop2(context.projectRoot, supervisorDeps(parsePositiveInt(timeout.value, "--timeout-ms", 1800000)), {
|
|
525
1051
|
maxTasks: parsePositiveInt(maxTasks.value, "--max-tasks", 1),
|
|
526
1052
|
concurrency: parsePositiveInt(concurrency.value, "--concurrency", 1),
|
|
527
1053
|
...task.value ? { candidateTaskIds: [task.value] } : {},
|
|
528
|
-
dryRun: dry.value || context.dryRun
|
|
1054
|
+
dryRun: Boolean(dry.value || context.dryRun)
|
|
529
1055
|
});
|
|
530
|
-
const details = { ...result, stopWhen:
|
|
1056
|
+
const details = { ...result, stopWhen: stopWhen.value?.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0) ?? [] };
|
|
531
1057
|
if (context.outputMode === "text") {
|
|
532
1058
|
if (json.value)
|
|
533
1059
|
printJson(details);
|
|
@@ -542,7 +1068,8 @@ async function executeUnblock(context, args) {
|
|
|
542
1068
|
const timeout = takeOption(json.rest, "--timeout-ms");
|
|
543
1069
|
const taskId = timeout.rest[0]?.startsWith("-") ? undefined : timeout.rest[0];
|
|
544
1070
|
requireNoExtraArgs(taskId ? timeout.rest.slice(1) : timeout.rest, "rig unblock [task-id] [--dry-run] [--json]");
|
|
545
|
-
const
|
|
1071
|
+
const { unblockTask: unblockTask2 } = await Promise.resolve().then(() => (init_loop(), exports_loop));
|
|
1072
|
+
const result = await unblockTask2(context.projectRoot, taskId ?? null, supervisorDeps(parsePositiveInt(timeout.value, "--timeout-ms", 1800000)), { dryRun: Boolean(dry.value || context.dryRun) });
|
|
546
1073
|
if (context.outputMode === "text") {
|
|
547
1074
|
if (json.value)
|
|
548
1075
|
printJson(result);
|
|
@@ -571,6 +1098,11 @@ var supervisorCliCommands = [
|
|
|
571
1098
|
run: executeUnblock
|
|
572
1099
|
}
|
|
573
1100
|
];
|
|
1101
|
+
|
|
1102
|
+
// packages/supervisor-plugin/src/index.ts
|
|
1103
|
+
init_loop();
|
|
1104
|
+
init_journal();
|
|
1105
|
+
|
|
574
1106
|
// packages/supervisor-plugin/src/plugin.ts
|
|
575
1107
|
import { RIG_CAPABILITY_PANEL_SLOT, RIG_SUPERVISOR_PANEL_ID } from "@rig/contracts";
|
|
576
1108
|
import { definePlugin } from "@rig/core/config";
|
|
@@ -626,13 +1158,8 @@ function createSupervisorPlugin() {
|
|
|
626
1158
|
return supervisorPlugin;
|
|
627
1159
|
}
|
|
628
1160
|
// packages/supervisor-plugin/src/supervisor.ts
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
disjointScope,
|
|
632
|
-
isTaskTerminalStatus,
|
|
633
|
-
readTaskScope
|
|
634
|
-
} from "@rig/contracts";
|
|
635
|
-
import { rankReadyTasks as rankReadyTasks2 } from "@rig/dependency-graph-plugin";
|
|
1161
|
+
init_taskGraphPrimitives();
|
|
1162
|
+
init_taskRanking();
|
|
636
1163
|
var DEFAULT_STOP_WHEN = new Set(["all-done", "all-human-blocked", "source-error"]);
|
|
637
1164
|
var HUMAN_BLOCKER_CLASS = {
|
|
638
1165
|
"not-blocked": false,
|
|
@@ -679,14 +1206,14 @@ function filterTask(task, filter) {
|
|
|
679
1206
|
}
|
|
680
1207
|
return true;
|
|
681
1208
|
}
|
|
682
|
-
function classifyEmptyReadySet(snapshot, badges,
|
|
1209
|
+
function classifyEmptyReadySet(snapshot, badges, classifyBlocker2) {
|
|
683
1210
|
if ((snapshot.diagnostics ?? []).length > 0)
|
|
684
1211
|
return "source-error";
|
|
685
1212
|
const openTasks = snapshot.tasks.filter((task) => !isTaskTerminalStatus(task.status));
|
|
686
1213
|
if (openTasks.length === 0)
|
|
687
1214
|
return "all-done";
|
|
688
|
-
if (
|
|
689
|
-
const allHumanBlocked = openTasks.every((task) => HUMAN_BLOCKER_CLASS[
|
|
1215
|
+
if (classifyBlocker2) {
|
|
1216
|
+
const allHumanBlocked = openTasks.every((task) => HUMAN_BLOCKER_CLASS[classifyBlocker2(task, badges).blockerClass]);
|
|
690
1217
|
if (allHumanBlocked)
|
|
691
1218
|
return "all-human-blocked";
|
|
692
1219
|
}
|
|
@@ -715,11 +1242,11 @@ async function runSupervisor(ctx, options = {}) {
|
|
|
715
1242
|
break;
|
|
716
1243
|
}
|
|
717
1244
|
const snapshot = await ctx.readTasks();
|
|
718
|
-
const badges =
|
|
1245
|
+
const badges = computeTaskDependencyBadges(snapshot.tasks);
|
|
719
1246
|
const activeRuns = snapshot.activeRuns ?? [];
|
|
720
1247
|
const activeTaskIds2 = activeRuns.map((run) => run.taskId);
|
|
721
1248
|
const remainingSlots = normalized.maxTasks === null ? normalized.concurrency : Math.min(normalized.concurrency, normalized.maxTasks - processed);
|
|
722
|
-
const ranked =
|
|
1249
|
+
const ranked = rankReadyTasks(snapshot.tasks, {
|
|
723
1250
|
excludeTaskIds: completedTaskIds,
|
|
724
1251
|
activeTaskIds: activeTaskIds2,
|
|
725
1252
|
filter: (task) => filterTask(task, normalized.filter),
|