@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/plugin.js
CHANGED
|
@@ -1,95 +1,456 @@
|
|
|
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
|
-
|
|
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
|
+
});
|
|
7
38
|
|
|
8
|
-
// packages/supervisor-plugin/src/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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 resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId) {
|
|
95
|
+
if (tasksById.has(ref))
|
|
96
|
+
return ref;
|
|
97
|
+
return taskIdBySourceIssueId.get(ref) ?? taskIdByExternalRef.get(ref) ?? null;
|
|
98
|
+
}
|
|
99
|
+
function buildTaskReferenceIndex(tasks) {
|
|
15
100
|
return {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
101
|
+
tasksById: new Map(tasks.map((task) => [task.id, task])),
|
|
102
|
+
taskIdByExternalRef: new Map(tasks.flatMap((task) => task.externalId ? [[task.externalId, task.id]] : [])),
|
|
103
|
+
taskIdBySourceIssueId: new Map(tasks.flatMap((task) => {
|
|
104
|
+
const sourceIssueId = readTaskSourceIssueId(task);
|
|
105
|
+
return sourceIssueId ? [[sourceIssueId, task.id]] : [];
|
|
106
|
+
}))
|
|
22
107
|
};
|
|
23
108
|
}
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
109
|
+
function computeTaskBlockingDepths(tasks) {
|
|
110
|
+
const { tasksById, taskIdByExternalRef, taskIdBySourceIssueId } = buildTaskReferenceIndex(tasks);
|
|
111
|
+
const memo = new Map;
|
|
112
|
+
const visit = (taskId, stack) => {
|
|
113
|
+
const cached = memo.get(taskId);
|
|
114
|
+
if (cached !== undefined)
|
|
115
|
+
return cached;
|
|
116
|
+
if (stack.has(taskId))
|
|
117
|
+
return 0;
|
|
118
|
+
const task = tasksById.get(taskId);
|
|
119
|
+
if (!task)
|
|
120
|
+
return 0;
|
|
121
|
+
stack.add(taskId);
|
|
122
|
+
const blockers = readTaskBlockingDependencyRefs(task).map((ref) => resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId)).filter((ref) => ref !== null && ref !== taskId);
|
|
123
|
+
const depth = blockers.length === 0 ? 0 : Math.max(...blockers.map((blockerId) => visit(blockerId, stack) + 1));
|
|
124
|
+
stack.delete(taskId);
|
|
125
|
+
memo.set(taskId, depth);
|
|
126
|
+
return depth;
|
|
38
127
|
};
|
|
128
|
+
for (const task of tasks) {
|
|
129
|
+
visit(task.id, new Set);
|
|
130
|
+
}
|
|
131
|
+
return memo;
|
|
39
132
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
133
|
+
function isTaskTerminalStatus(status) {
|
|
134
|
+
switch (status) {
|
|
135
|
+
case "closed":
|
|
136
|
+
case "completed":
|
|
137
|
+
case "done":
|
|
138
|
+
case "cancelled":
|
|
139
|
+
case "canceled":
|
|
140
|
+
return true;
|
|
141
|
+
default:
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function isTaskBlockedStatus(status) {
|
|
146
|
+
return status === "blocked";
|
|
147
|
+
}
|
|
148
|
+
function isTaskRunnableStatus(status) {
|
|
149
|
+
if (status === null || status === undefined || status === "")
|
|
150
|
+
return true;
|
|
151
|
+
if (isTaskTerminalStatus(status) || isTaskBlockedStatus(status))
|
|
152
|
+
return false;
|
|
153
|
+
switch (status) {
|
|
154
|
+
case "ready":
|
|
155
|
+
case "open":
|
|
156
|
+
case "failed":
|
|
157
|
+
return true;
|
|
158
|
+
default:
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function computeTaskDependencyBadges(tasks) {
|
|
163
|
+
const index = buildTaskReferenceIndex(tasks);
|
|
164
|
+
const blockingDepths = computeTaskBlockingDepths(tasks);
|
|
165
|
+
const dependencyIdsByTask = new Map;
|
|
166
|
+
const unresolvedRefsByTask = new Map;
|
|
167
|
+
const blocksByTask = new Map;
|
|
168
|
+
for (const task of tasks) {
|
|
169
|
+
const dependencyIds = [];
|
|
170
|
+
const unresolvedRefs = [];
|
|
171
|
+
for (const ref of readTaskBlockingDependencyRefs(task)) {
|
|
172
|
+
const dependencyId = resolveTaskReference(ref, index.tasksById, index.taskIdByExternalRef, index.taskIdBySourceIssueId);
|
|
173
|
+
if (dependencyId && dependencyId !== task.id) {
|
|
174
|
+
dependencyIds.push(dependencyId);
|
|
175
|
+
const blocks = blocksByTask.get(dependencyId);
|
|
176
|
+
if (blocks) {
|
|
177
|
+
blocks.push(task.id);
|
|
178
|
+
} else {
|
|
179
|
+
blocksByTask.set(dependencyId, [task.id]);
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
unresolvedRefs.push(ref);
|
|
183
|
+
}
|
|
82
184
|
}
|
|
83
|
-
|
|
84
|
-
|
|
185
|
+
dependencyIdsByTask.set(task.id, unique(dependencyIds));
|
|
186
|
+
unresolvedRefsByTask.set(task.id, unique(unresolvedRefs));
|
|
187
|
+
}
|
|
188
|
+
const summaries = new Map;
|
|
189
|
+
for (const task of tasks) {
|
|
190
|
+
const dependencyIds = dependencyIdsByTask.get(task.id) ?? [];
|
|
191
|
+
const unresolvedDependencyRefs = unresolvedRefsByTask.get(task.id) ?? [];
|
|
192
|
+
const blockedBy = dependencyIds.filter((dependencyId) => {
|
|
193
|
+
const dependency = index.tasksById.get(dependencyId);
|
|
194
|
+
return dependency ? !isTaskTerminalStatus(dependency.status) : false;
|
|
195
|
+
});
|
|
196
|
+
const blocks = unique(blocksByTask.get(task.id) ?? []);
|
|
197
|
+
const blocked = isTaskBlockedStatus(task.status) || blockedBy.length > 0;
|
|
198
|
+
const ready = isTaskRunnableStatus(task.status) && !blocked;
|
|
199
|
+
const badges = [];
|
|
200
|
+
if (blocked) {
|
|
201
|
+
badges.push({
|
|
202
|
+
kind: "blocked",
|
|
203
|
+
label: blockedBy.length > 0 ? `blocked \xD7${blockedBy.length}` : "blocked",
|
|
204
|
+
description: blockedBy.length > 0 ? `Waiting on ${blockedBy.join(", ")}.` : "Task source marks this task blocked.",
|
|
205
|
+
...blockedBy.length > 0 ? { count: blockedBy.length } : {},
|
|
206
|
+
taskIds: blockedBy
|
|
207
|
+
});
|
|
208
|
+
} else if (ready) {
|
|
209
|
+
badges.push({
|
|
210
|
+
kind: "ready",
|
|
211
|
+
label: "ready",
|
|
212
|
+
description: "No open dependencies block this task."
|
|
213
|
+
});
|
|
85
214
|
}
|
|
86
|
-
|
|
215
|
+
if (dependencyIds.length > 0 || blocks.length > 0 || unresolvedDependencyRefs.length > 0) {
|
|
216
|
+
badges.push({
|
|
217
|
+
kind: "dependency",
|
|
218
|
+
label: `deps ${dependencyIds.length}/${blocks.length}`,
|
|
219
|
+
description: [
|
|
220
|
+
dependencyIds.length > 0 ? `Depends on ${dependencyIds.join(", ")}.` : null,
|
|
221
|
+
blocks.length > 0 ? `Blocks ${blocks.join(", ")}.` : null,
|
|
222
|
+
unresolvedDependencyRefs.length > 0 ? `Unresolved refs: ${unresolvedDependencyRefs.join(", ")}.` : null
|
|
223
|
+
].filter((part) => part !== null).join(" "),
|
|
224
|
+
count: dependencyIds.length + blocks.length,
|
|
225
|
+
taskIds: unique([...dependencyIds, ...blocks])
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
summaries.set(task.id, {
|
|
229
|
+
taskId: task.id,
|
|
230
|
+
blockingDepth: blockingDepths.get(task.id) ?? 0,
|
|
231
|
+
dependencyIds,
|
|
232
|
+
unresolvedDependencyRefs,
|
|
233
|
+
blockedBy,
|
|
234
|
+
blocks,
|
|
235
|
+
blocked,
|
|
236
|
+
ready,
|
|
237
|
+
dependencyCount: dependencyIds.length,
|
|
238
|
+
dependentCount: blocks.length,
|
|
239
|
+
badges
|
|
240
|
+
});
|
|
87
241
|
}
|
|
242
|
+
return summaries;
|
|
243
|
+
}
|
|
244
|
+
function stringValue(value) {
|
|
245
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
246
|
+
}
|
|
247
|
+
function stringArray(value) {
|
|
248
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0).map((entry) => entry.trim()) : [];
|
|
88
249
|
}
|
|
250
|
+
function numberOrNull(value) {
|
|
251
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
252
|
+
}
|
|
253
|
+
function metadataOf(task) {
|
|
254
|
+
return isObjectRecord(task.metadata) ? task.metadata : {};
|
|
255
|
+
}
|
|
256
|
+
function normalizeTaskStatus(status) {
|
|
257
|
+
const token = typeof status === "string" ? status.trim().toLowerCase() : "";
|
|
258
|
+
if (token === "done")
|
|
259
|
+
return "completed";
|
|
260
|
+
if (token === "canceled")
|
|
261
|
+
return "cancelled";
|
|
262
|
+
return TASK_STATUSES.has(token) ? token : "unknown";
|
|
263
|
+
}
|
|
264
|
+
function toTaskDependencyProjection(task) {
|
|
265
|
+
const metadata = metadataOf(task);
|
|
266
|
+
return {
|
|
267
|
+
id: String(task.id),
|
|
268
|
+
title: stringValue(task.title),
|
|
269
|
+
status: normalizeTaskStatus(task.status),
|
|
270
|
+
priority: numberOrNull(task.priority),
|
|
271
|
+
metadata,
|
|
272
|
+
externalId: stringValue(task.externalId),
|
|
273
|
+
sourceIssueId: stringValue(task.sourceIssueId),
|
|
274
|
+
dependencies: stringArray(task.dependencies),
|
|
275
|
+
parentChildDeps: stringArray(task.parentChildDeps),
|
|
276
|
+
createdAt: stringValue(task.createdAt) ?? "",
|
|
277
|
+
updatedAt: stringValue(task.updatedAt) ?? "",
|
|
278
|
+
role: stringValue(task.role),
|
|
279
|
+
scope: stringArray(task.scope),
|
|
280
|
+
validationKeys: stringArray(task.validationKeys),
|
|
281
|
+
labels: stringArray(task.labels),
|
|
282
|
+
assignees: task.assignees ?? null,
|
|
283
|
+
assignedTo: task.assignedTo ?? null
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function latestRunByTaskId(runs) {
|
|
287
|
+
const byTask = new Map;
|
|
288
|
+
const stamp = (run) => Date.parse(run.updatedAt ?? run.startedAt ?? "") || 0;
|
|
289
|
+
for (const run of runs) {
|
|
290
|
+
if (!run.taskId)
|
|
291
|
+
continue;
|
|
292
|
+
const current = byTask.get(run.taskId);
|
|
293
|
+
if (!current || stamp(run) >= stamp(current))
|
|
294
|
+
byTask.set(run.taskId, run);
|
|
295
|
+
}
|
|
296
|
+
return byTask;
|
|
297
|
+
}
|
|
298
|
+
var TASK_STATUSES;
|
|
299
|
+
var init_taskGraphPrimitives = __esm(() => {
|
|
300
|
+
TASK_STATUSES = new Set([
|
|
301
|
+
"draft",
|
|
302
|
+
"open",
|
|
303
|
+
"ready",
|
|
304
|
+
"queued",
|
|
305
|
+
"running",
|
|
306
|
+
"in_progress",
|
|
307
|
+
"under_review",
|
|
308
|
+
"blocked",
|
|
309
|
+
"unknown",
|
|
310
|
+
"completed",
|
|
311
|
+
"failed",
|
|
312
|
+
"cancelled",
|
|
313
|
+
"closed"
|
|
314
|
+
]);
|
|
315
|
+
});
|
|
89
316
|
|
|
90
|
-
// packages/supervisor-plugin/src/
|
|
91
|
-
|
|
92
|
-
|
|
317
|
+
// packages/supervisor-plugin/src/analysis/taskScore.ts
|
|
318
|
+
function finiteNumber(value, fallback) {
|
|
319
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
320
|
+
}
|
|
321
|
+
function scoreTask(input) {
|
|
322
|
+
const priority = finiteNumber(input.priority, 3);
|
|
323
|
+
const unblockCount = Math.max(0, finiteNumber(input.unblockCount, 0));
|
|
324
|
+
const roleWeight = input.role ? ROLE_WEIGHT[input.role] ?? 0 : 0;
|
|
325
|
+
const criticalityWeight = input.criticality ? CRITICALITY_WEIGHT[input.criticality] ?? 0 : 0;
|
|
326
|
+
const validationWeight = (input.validation ?? []).some((entry) => entry.includes("test-contract") || entry.includes("test-boundary")) ? 8 : 0;
|
|
327
|
+
const queueWeight = finiteNumber(input.queueWeight, 0);
|
|
328
|
+
return unblockCount * 100 + (10 - priority) + roleWeight + criticalityWeight + validationWeight + queueWeight;
|
|
329
|
+
}
|
|
330
|
+
function rankTasks(tasks, scoreInput, idOf) {
|
|
331
|
+
return tasks.map((task) => {
|
|
332
|
+
const input = scoreInput(task);
|
|
333
|
+
return {
|
|
334
|
+
task,
|
|
335
|
+
score: scoreTask(input),
|
|
336
|
+
priority: finiteNumber(input.priority, 3),
|
|
337
|
+
unblockCount: Math.max(0, finiteNumber(input.unblockCount, 0))
|
|
338
|
+
};
|
|
339
|
+
}).toSorted((left, right) => {
|
|
340
|
+
const scoreDelta = right.score - left.score;
|
|
341
|
+
if (scoreDelta !== 0)
|
|
342
|
+
return scoreDelta;
|
|
343
|
+
const unblockDelta = right.unblockCount - left.unblockCount;
|
|
344
|
+
if (unblockDelta !== 0)
|
|
345
|
+
return unblockDelta;
|
|
346
|
+
const priorityDelta = left.priority - right.priority;
|
|
347
|
+
if (priorityDelta !== 0)
|
|
348
|
+
return priorityDelta;
|
|
349
|
+
return idOf(left.task).localeCompare(idOf(right.task));
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
var ROLE_WEIGHT, CRITICALITY_WEIGHT;
|
|
353
|
+
var init_taskScore = __esm(() => {
|
|
354
|
+
ROLE_WEIGHT = {
|
|
355
|
+
architect: 25,
|
|
356
|
+
extractor: 10
|
|
357
|
+
};
|
|
358
|
+
CRITICALITY_WEIGHT = {
|
|
359
|
+
core: 20,
|
|
360
|
+
high: 10,
|
|
361
|
+
normal: 0
|
|
362
|
+
};
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// packages/supervisor-plugin/src/analysis/taskRanking.ts
|
|
366
|
+
function isObjectRecord2(value) {
|
|
367
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
368
|
+
}
|
|
369
|
+
function readStringList2(value) {
|
|
370
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
|
|
371
|
+
}
|
|
372
|
+
function readTaskRole(task) {
|
|
373
|
+
if (typeof task.role === "string" && task.role.trim())
|
|
374
|
+
return task.role.trim();
|
|
375
|
+
const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
|
|
376
|
+
return typeof metadata?.role === "string" && metadata.role.trim() ? metadata.role.trim() : null;
|
|
377
|
+
}
|
|
378
|
+
function readTaskCriticality(task) {
|
|
379
|
+
const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
|
|
380
|
+
return typeof metadata?.criticality === "string" && metadata.criticality.trim() ? metadata.criticality.trim() : null;
|
|
381
|
+
}
|
|
382
|
+
function readTaskValidationKeys(task) {
|
|
383
|
+
const taskRecord = task;
|
|
384
|
+
const topLevel = readStringList2(taskRecord.validationKeys);
|
|
385
|
+
if (topLevel.length > 0)
|
|
386
|
+
return topLevel;
|
|
387
|
+
const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
|
|
388
|
+
return readStringList2(metadata?.validation);
|
|
389
|
+
}
|
|
390
|
+
function readTaskQueueWeight(task) {
|
|
391
|
+
const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
|
|
392
|
+
const queueWeight = metadata?.queueWeight ?? metadata?.queue_weight;
|
|
393
|
+
return typeof queueWeight === "number" && Number.isFinite(queueWeight) ? queueWeight : 0;
|
|
394
|
+
}
|
|
395
|
+
function scoreInputForTask(task, unblockCount) {
|
|
396
|
+
return {
|
|
397
|
+
priority: task.priority,
|
|
398
|
+
unblockCount,
|
|
399
|
+
role: readTaskRole(task),
|
|
400
|
+
criticality: readTaskCriticality(task),
|
|
401
|
+
validation: readTaskValidationKeys(task),
|
|
402
|
+
queueWeight: readTaskQueueWeight(task)
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
function rankReadyTasks(tasks, options = {}) {
|
|
406
|
+
const excluded = new Set(options.excludeTaskIds ?? []);
|
|
407
|
+
const activeTaskIds = new Set(options.activeTaskIds ?? []);
|
|
408
|
+
const badges = computeTaskDependencyBadges(tasks);
|
|
409
|
+
const tasksById = new Map(tasks.map((task) => [String(task.id), task]));
|
|
410
|
+
const openBlockCount = (task) => (badges.get(task.id)?.blocks ?? []).filter((blockedTaskId) => {
|
|
411
|
+
const blockedTask = tasksById.get(blockedTaskId);
|
|
412
|
+
return blockedTask ? !isTaskTerminalStatus(blockedTask.status) : false;
|
|
413
|
+
}).length;
|
|
414
|
+
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);
|
|
415
|
+
const maxUnblockCount = Math.max(0, ...readyTasks.map(openBlockCount));
|
|
416
|
+
const selectedByMode = readyTasks.filter((task) => {
|
|
417
|
+
const unblockCount = openBlockCount(task);
|
|
418
|
+
if (options.selection === "blocking-only")
|
|
419
|
+
return unblockCount > 0;
|
|
420
|
+
if (options.selection === "max-unblock")
|
|
421
|
+
return maxUnblockCount > 0 && unblockCount === maxUnblockCount;
|
|
422
|
+
return true;
|
|
423
|
+
});
|
|
424
|
+
return rankTasks(selectedByMode, (task) => scoreInputForTask(task, openBlockCount(task)), (task) => task.id).map((entry) => ({
|
|
425
|
+
task: entry.task,
|
|
426
|
+
score: entry.score,
|
|
427
|
+
priority: entry.priority,
|
|
428
|
+
unblockCount: entry.unblockCount,
|
|
429
|
+
scope: readTaskScope(entry.task)
|
|
430
|
+
}));
|
|
431
|
+
}
|
|
432
|
+
function selectRankedReadyTasks(tasks, options = {}) {
|
|
433
|
+
const ranked = rankReadyTasks(tasks, options);
|
|
434
|
+
if (options.requireDisjointScopes !== true && !options.disjointWithScopes) {
|
|
435
|
+
return ranked.slice(0, options.limit).map((entry) => entry.task);
|
|
436
|
+
}
|
|
437
|
+
const occupiedScopes = new Set(options.disjointWithScopes ?? []);
|
|
438
|
+
const selected = [];
|
|
439
|
+
for (const entry of ranked) {
|
|
440
|
+
if (entry.scope.some((scope) => occupiedScopes.has(scope)))
|
|
441
|
+
continue;
|
|
442
|
+
selected.push(entry.task);
|
|
443
|
+
for (const scope of entry.scope)
|
|
444
|
+
occupiedScopes.add(scope);
|
|
445
|
+
if (options.limit !== undefined && selected.length >= options.limit)
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
return selected;
|
|
449
|
+
}
|
|
450
|
+
var init_taskRanking = __esm(() => {
|
|
451
|
+
init_taskGraphPrimitives();
|
|
452
|
+
init_taskScore();
|
|
453
|
+
});
|
|
93
454
|
|
|
94
455
|
// packages/supervisor-plugin/src/journal.ts
|
|
95
456
|
import { Schema } from "effect";
|
|
@@ -169,9 +530,100 @@ function reduceSupervisorJournal(events) {
|
|
|
169
530
|
}
|
|
170
531
|
return { status, processed, succeeded, failed, skipped, current, plannedOrder, selectionPolicy, concurrency, idleReason, stopReason, closures, anomalies };
|
|
171
532
|
}
|
|
533
|
+
var init_journal = () => {};
|
|
534
|
+
|
|
535
|
+
// packages/supervisor-plugin/src/analysis/blockers.ts
|
|
536
|
+
function labelsFor(task) {
|
|
537
|
+
return readTaskMetadataStringList(task, "labels").map((label) => label.toLowerCase());
|
|
538
|
+
}
|
|
539
|
+
function configuredTier(task, labels) {
|
|
540
|
+
const metadata = task.metadata && typeof task.metadata === "object" && !Array.isArray(task.metadata) ? task.metadata : {};
|
|
541
|
+
const tier = metadata.riskTier ?? metadata.actionRiskTier;
|
|
542
|
+
if (tier === "t1-read" || tier === "t2-reversible" || tier === "t3-external" || tier === "t4-irreversible")
|
|
543
|
+
return tier;
|
|
544
|
+
if (labels.some((label) => /prod|deploy|migration|billing|delete|irreversible/.test(label)))
|
|
545
|
+
return "t4-irreversible";
|
|
546
|
+
if (labels.some((label) => /customer|vendor|external|secret|credential|approval|decision/.test(label)))
|
|
547
|
+
return "t3-external";
|
|
548
|
+
const scopes = task.scope ?? [];
|
|
549
|
+
if (scopes.some((scope) => /docs|readme|test|spec/.test(scope.toLowerCase())))
|
|
550
|
+
return "t1-read";
|
|
551
|
+
return "t2-reversible";
|
|
552
|
+
}
|
|
553
|
+
function tierOf(task, labels = []) {
|
|
554
|
+
const projection = "metadata" in task && "id" in task && typeof task.id === "string" ? toTaskDependencyProjection(task) : task;
|
|
555
|
+
return configuredTier(projection, labels.length > 0 ? labels : labelsFor(projection)) ?? "t2-reversible";
|
|
556
|
+
}
|
|
557
|
+
function baseClassification(task, blockerClass, source, rationale, labels) {
|
|
558
|
+
return {
|
|
559
|
+
taskId: task.id,
|
|
560
|
+
blockerClass,
|
|
561
|
+
actionRiskTier: tierOf(task, labels),
|
|
562
|
+
rationale,
|
|
563
|
+
source,
|
|
564
|
+
autoApplied: source === "llm"
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
function normalizeRunStatus(status) {
|
|
568
|
+
if (status === "waiting-approval" || status === "waiting-user-input" || status === "needs-attention" || status === "completed" || status === "failed" || status === "stopped" || status === "running")
|
|
569
|
+
return status;
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
function isHumanBlockerClass(blockerClass) {
|
|
573
|
+
return HUMAN_BLOCKERS.has(blockerClass);
|
|
574
|
+
}
|
|
575
|
+
function classifyBlocker(input) {
|
|
576
|
+
const labels = input.labels ?? labelsFor(input.task);
|
|
577
|
+
const runStatus = normalizeRunStatus(input.run?.status);
|
|
578
|
+
if (runStatus === "waiting-approval")
|
|
579
|
+
return baseClassification(input.task, "human-approval", "status", "run awaiting approval", labels);
|
|
580
|
+
if (runStatus === "waiting-user-input")
|
|
581
|
+
return baseClassification(input.task, "external-input", "status", "run awaiting user input", labels);
|
|
582
|
+
if (input.task.status !== "blocked")
|
|
583
|
+
return baseClassification(input.task, "not-blocked", "elimination", "task is not blocked", labels);
|
|
584
|
+
const incomplete = (input.badges.get(input.task.id)?.blockedBy ?? []).filter((dependencyId) => {
|
|
585
|
+
const dependency = input.tasksById.get(dependencyId);
|
|
586
|
+
return dependency ? !isTaskTerminalStatus(dependency.status) : false;
|
|
587
|
+
});
|
|
588
|
+
if (incomplete.length > 0)
|
|
589
|
+
return baseClassification(input.task, "task-blocked", "elimination", `blocked by ${incomplete.length} incomplete task(s)`, labels);
|
|
590
|
+
if (labels.includes("needs-decision"))
|
|
591
|
+
return baseClassification(input.task, "human-decision", "label", "needs-decision label", labels);
|
|
592
|
+
if (labels.some((label) => /^waiting-on-/.test(label)))
|
|
593
|
+
return baseClassification(input.task, "external-input", "label", "waiting-on-* label", labels);
|
|
594
|
+
if (input.config?.llm)
|
|
595
|
+
return baseClassification(input.task, "human-decision", "llm", "LLM residue classifier marked this as human-gated", labels);
|
|
596
|
+
return baseClassification(input.task, "human-decision", "elimination", "blocked, all task dependencies terminal; non-task gate remains", labels);
|
|
597
|
+
}
|
|
598
|
+
function classifyTasks(tasks, runs = [], options = {}) {
|
|
599
|
+
const projected = tasks.map(toTaskDependencyProjection);
|
|
600
|
+
const badges = computeTaskDependencyBadges(projected);
|
|
601
|
+
const tasksById = new Map(projected.map((task) => [task.id, task]));
|
|
602
|
+
const runByTask = latestRunByTaskId(runs);
|
|
603
|
+
const classifier = options.classifier ?? classifyBlocker;
|
|
604
|
+
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));
|
|
605
|
+
return {
|
|
606
|
+
classifications,
|
|
607
|
+
byTaskId: new Map(classifications.map((classification) => [classification.taskId, classification])),
|
|
608
|
+
human: classifications.filter((classification) => isHumanBlockerClass(classification.blockerClass)),
|
|
609
|
+
machine: classifications.filter((classification) => !isHumanBlockerClass(classification.blockerClass)),
|
|
610
|
+
generatedAt: options.generatedAt ?? new Date().toISOString()
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
var HUMAN_BLOCKERS;
|
|
614
|
+
var init_blockers = __esm(() => {
|
|
615
|
+
init_taskGraphPrimitives();
|
|
616
|
+
HUMAN_BLOCKERS = new Set(["human-decision", "human-approval", "external-input"]);
|
|
617
|
+
});
|
|
172
618
|
|
|
173
619
|
// packages/supervisor-plugin/src/loop.ts
|
|
174
|
-
|
|
620
|
+
var exports_loop = {};
|
|
621
|
+
__export(exports_loop, {
|
|
622
|
+
unblockTask: () => unblockTask,
|
|
623
|
+
runSupervisorLoop: () => runSupervisorLoop,
|
|
624
|
+
planSupervisorLoop: () => planSupervisorLoop,
|
|
625
|
+
controlSupervisorRun: () => controlSupervisorRun
|
|
626
|
+
});
|
|
175
627
|
function selectionMode(policy) {
|
|
176
628
|
return policy === "blocking-only" || policy === "max-unblock" ? policy : "all-ready";
|
|
177
629
|
}
|
|
@@ -230,25 +682,7 @@ function failedOutcome(outcome) {
|
|
|
230
682
|
return outcome.status === "failed" || outcome.status === "stopped" || outcome.status === "needs-attention" || outcome.status === "waiting-approval" || outcome.status === "waiting-user-input";
|
|
231
683
|
}
|
|
232
684
|
function runStatus(value) {
|
|
233
|
-
|
|
234
|
-
case "created":
|
|
235
|
-
case "queued":
|
|
236
|
-
case "preparing":
|
|
237
|
-
case "running":
|
|
238
|
-
case "waiting-approval":
|
|
239
|
-
case "waiting-user-input":
|
|
240
|
-
case "paused":
|
|
241
|
-
case "validating":
|
|
242
|
-
case "reviewing":
|
|
243
|
-
case "closing-out":
|
|
244
|
-
case "needs-attention":
|
|
245
|
-
case "completed":
|
|
246
|
-
case "failed":
|
|
247
|
-
case "stopped":
|
|
248
|
-
return value;
|
|
249
|
-
default:
|
|
250
|
-
return "failed";
|
|
251
|
-
}
|
|
685
|
+
return coerceRunStatus(value, "failed");
|
|
252
686
|
}
|
|
253
687
|
async function runSupervisorLoop(projectRoot, deps, options = {}) {
|
|
254
688
|
const events = [{ kind: "supervisor.started", at: at(deps), options }];
|
|
@@ -319,6 +753,10 @@ async function runSupervisorLoop(projectRoot, deps, options = {}) {
|
|
|
319
753
|
const projection = reduceSupervisorJournal(events);
|
|
320
754
|
return { ok: failed === 0, dryRun: options.dryRun === true, plannedOrder: plannedAll, events, projection };
|
|
321
755
|
}
|
|
756
|
+
async function controlSupervisorRun(projectRoot, runId, control, deps) {
|
|
757
|
+
await deps.deliverRunControl(projectRoot, runId, control);
|
|
758
|
+
return { ok: true, runId, control: control.kind };
|
|
759
|
+
}
|
|
322
760
|
function collectBlockingClosure(taskId, badges) {
|
|
323
761
|
const closure = new Set;
|
|
324
762
|
const visit = (currentTaskId) => {
|
|
@@ -341,10 +779,110 @@ async function unblockTask(projectRoot, taskId, deps, options = {}) {
|
|
|
341
779
|
const blockers = collectBlockingClosure(taskId, badges);
|
|
342
780
|
return runSupervisorLoop(projectRoot, deps, { ...options, candidateTaskIds: blockers, selectionPolicy: "rank", maxTasks: 1 });
|
|
343
781
|
}
|
|
782
|
+
var init_loop = __esm(() => {
|
|
783
|
+
init_taskGraphPrimitives();
|
|
784
|
+
init_taskRanking();
|
|
785
|
+
init_journal();
|
|
786
|
+
init_run_status();
|
|
787
|
+
init_blockers();
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
// packages/supervisor-plugin/src/plugin.ts
|
|
791
|
+
import { RIG_CAPABILITY_PANEL_SLOT, RIG_SUPERVISOR_PANEL_ID } from "@rig/contracts";
|
|
792
|
+
import { definePlugin } from "@rig/core/config";
|
|
793
|
+
|
|
794
|
+
// packages/supervisor-plugin/src/closureStage.ts
|
|
795
|
+
var SUPERVISOR_CLOSURE_STAGE_ID = "supervisor-closure-observer";
|
|
796
|
+
function summarizeClosure(ctx) {
|
|
797
|
+
const state = (ctx.metadata ?? {}).closeoutState ?? null;
|
|
798
|
+
const taskId = typeof ctx.taskId === "string" ? ctx.taskId : null;
|
|
799
|
+
if (!state || !taskId)
|
|
800
|
+
return null;
|
|
801
|
+
return {
|
|
802
|
+
taskId,
|
|
803
|
+
runId: ctx.runId,
|
|
804
|
+
prUrl: state.prUrl ?? null,
|
|
805
|
+
mergeGate: state.mergeGate ?? null,
|
|
806
|
+
acceptanceChecked: [],
|
|
807
|
+
unblockedTaskIds: state.unblockedTaskIds ?? []
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
function createDefaultSupervisorClosureStage() {
|
|
811
|
+
return async (ctx) => {
|
|
812
|
+
const summary = summarizeClosure(ctx);
|
|
813
|
+
const projectRoot = (ctx.metadata ?? {}).projectRoot;
|
|
814
|
+
if (summary && typeof projectRoot === "string") {
|
|
815
|
+
const { appendFileSync, mkdirSync } = await import("fs");
|
|
816
|
+
const { join } = await import("path");
|
|
817
|
+
try {
|
|
818
|
+
mkdirSync(join(projectRoot, ".rig"), { recursive: true });
|
|
819
|
+
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 })}
|
|
820
|
+
`);
|
|
821
|
+
} catch {}
|
|
822
|
+
}
|
|
823
|
+
return { kind: "continue", ctx };
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
var supervisorClosureStage = {
|
|
827
|
+
id: SUPERVISOR_CLOSURE_STAGE_ID,
|
|
828
|
+
kind: "observe",
|
|
829
|
+
after: ["source-closeout"],
|
|
830
|
+
before: ["journal-append"],
|
|
831
|
+
priority: 0,
|
|
832
|
+
protected: false
|
|
833
|
+
};
|
|
834
|
+
var supervisorClosureStageMutation = {
|
|
835
|
+
op: "insert",
|
|
836
|
+
contributedBy: "@rig/supervisor-plugin",
|
|
837
|
+
stage: supervisorClosureStage
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
// packages/supervisor-plugin/src/cli.ts
|
|
841
|
+
import { RUN_DISPATCH, RUN_READ_MODEL, TASK_IO_SERVICE_CAPABILITY } from "@rig/contracts";
|
|
842
|
+
import { defineCapability } from "@rig/core/capability";
|
|
843
|
+
import { requireCapabilityForRoot } from "@rig/core/capability-loaders";
|
|
844
|
+
|
|
845
|
+
// packages/supervisor-plugin/src/awaiter.ts
|
|
846
|
+
var TERMINAL_RUN_STATUSES = {
|
|
847
|
+
created: false,
|
|
848
|
+
queued: false,
|
|
849
|
+
preparing: false,
|
|
850
|
+
running: false,
|
|
851
|
+
"waiting-approval": false,
|
|
852
|
+
"waiting-user-input": false,
|
|
853
|
+
paused: false,
|
|
854
|
+
validating: false,
|
|
855
|
+
reviewing: false,
|
|
856
|
+
"closing-out": false,
|
|
857
|
+
"needs-attention": true,
|
|
858
|
+
completed: true,
|
|
859
|
+
failed: true,
|
|
860
|
+
stopped: true
|
|
861
|
+
};
|
|
862
|
+
async function awaitTerminalRun(options) {
|
|
863
|
+
const startedAt = Date.now();
|
|
864
|
+
const pollMs = Math.max(0, Math.floor(options.pollMs ?? 5000));
|
|
865
|
+
while (true) {
|
|
866
|
+
const snapshot = await options.readStatus(options.runId);
|
|
867
|
+
if (snapshot && TERMINAL_RUN_STATUSES[snapshot.status]) {
|
|
868
|
+
return {
|
|
869
|
+
runId: options.runId,
|
|
870
|
+
status: snapshot.status,
|
|
871
|
+
failed: snapshot.status !== "completed"
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
if (options.timeoutMs !== undefined && Date.now() - startedAt >= options.timeoutMs) {
|
|
875
|
+
return { runId: options.runId, status: "needs-attention", failed: true };
|
|
876
|
+
}
|
|
877
|
+
await options.waitForChange(pollMs);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
344
880
|
|
|
345
881
|
// packages/supervisor-plugin/src/cli.ts
|
|
882
|
+
init_run_status();
|
|
346
883
|
var SUPERVISOR_LOOP_CLI_ID = "supervisor.loop";
|
|
347
884
|
var SUPERVISOR_UNBLOCK_CLI_ID = "supervisor.unblock";
|
|
885
|
+
var RunReadModelCap = defineCapability(RUN_READ_MODEL);
|
|
348
886
|
function printJson(value) {
|
|
349
887
|
console.log(JSON.stringify(value, null, 2));
|
|
350
888
|
}
|
|
@@ -381,55 +919,36 @@ function parsePositiveInt(value, flag, fallback) {
|
|
|
381
919
|
}
|
|
382
920
|
return parsed;
|
|
383
921
|
}
|
|
384
|
-
function parseCsv(value) {
|
|
385
|
-
return value?.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0) ?? [];
|
|
386
|
-
}
|
|
387
922
|
function asRunStatus(value) {
|
|
388
|
-
|
|
389
|
-
case "created":
|
|
390
|
-
case "queued":
|
|
391
|
-
case "preparing":
|
|
392
|
-
case "running":
|
|
393
|
-
case "waiting-approval":
|
|
394
|
-
case "waiting-user-input":
|
|
395
|
-
case "paused":
|
|
396
|
-
case "validating":
|
|
397
|
-
case "reviewing":
|
|
398
|
-
case "closing-out":
|
|
399
|
-
case "needs-attention":
|
|
400
|
-
case "completed":
|
|
401
|
-
case "failed":
|
|
402
|
-
case "stopped":
|
|
403
|
-
return value;
|
|
404
|
-
default:
|
|
405
|
-
return "needs-attention";
|
|
406
|
-
}
|
|
923
|
+
return coerceRunStatus(value, "needs-attention");
|
|
407
924
|
}
|
|
408
925
|
function delay(ms) {
|
|
409
926
|
const { promise, resolve } = Promise.withResolvers();
|
|
410
927
|
setTimeout(resolve, ms);
|
|
411
928
|
return promise;
|
|
412
929
|
}
|
|
413
|
-
async function loadSupervisorClient() {
|
|
414
|
-
const [taskIo,
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
import("@rig/runtime/control-plane/dispatch")
|
|
930
|
+
async function loadSupervisorClient(projectRoot) {
|
|
931
|
+
const [taskIo, runReadModel] = await Promise.all([
|
|
932
|
+
requireCapabilityForRoot(projectRoot, defineCapability(TASK_IO_SERVICE_CAPABILITY), "No task-sources plugin provides task IO for this project root."),
|
|
933
|
+
requireCapabilityForRoot(projectRoot, RunReadModelCap, "No run-worker plugin provides run read-model for this project root.")
|
|
418
934
|
]);
|
|
419
|
-
return { listTasks: taskIo.listTasks, listRuns:
|
|
935
|
+
return { listTasks: taskIo.listTasks, listRuns: (root) => runReadModel.listRuns({ projectRoot: root }) };
|
|
420
936
|
}
|
|
421
937
|
function supervisorDeps(timeoutMs) {
|
|
422
938
|
return {
|
|
423
|
-
listTasks: async (projectRoot) => (await loadSupervisorClient()).listTasks(projectRoot),
|
|
424
|
-
listRuns: async (projectRoot) => (await loadSupervisorClient()).listRuns(projectRoot),
|
|
425
|
-
dispatchRun: async (
|
|
939
|
+
listTasks: async (projectRoot) => (await loadSupervisorClient(projectRoot)).listTasks(projectRoot),
|
|
940
|
+
listRuns: async (projectRoot) => (await loadSupervisorClient(projectRoot)).listRuns(projectRoot),
|
|
941
|
+
dispatchRun: async (input) => {
|
|
942
|
+
const service = await requireCapabilityForRoot(input.projectRoot, defineCapability(RUN_DISPATCH), "No transport plugin provides run dispatch for this project root.");
|
|
943
|
+
return service.dispatchRun(input);
|
|
944
|
+
},
|
|
426
945
|
awaitRunTerminal: async (projectRoot, runId) => {
|
|
427
946
|
const awaitedRunId = runId;
|
|
428
947
|
return awaitTerminalRun({
|
|
429
948
|
runId: awaitedRunId,
|
|
430
949
|
timeoutMs,
|
|
431
950
|
readStatus: async (id) => {
|
|
432
|
-
const { listRuns } = await loadSupervisorClient();
|
|
951
|
+
const { listRuns } = await loadSupervisorClient(projectRoot);
|
|
433
952
|
const run = (await listRuns(projectRoot)).find((candidate) => candidate.runId === id);
|
|
434
953
|
return run ? { runId: id, status: asRunStatus(run.status), ...run.errorSummary ? { diagnostic: run.errorSummary } : {} } : null;
|
|
435
954
|
},
|
|
@@ -459,13 +978,14 @@ async function executeLoop(context, args) {
|
|
|
459
978
|
const stopWhen = takeOption(task.rest, "--stop-when");
|
|
460
979
|
const timeout = takeOption(stopWhen.rest, "--timeout-ms");
|
|
461
980
|
requireNoExtraArgs(timeout.rest, "rig loop [--task <id>] [--max-tasks <n>] [--concurrency <n>] [--stop-when <csv>] [--dry-run] [--json]");
|
|
462
|
-
const
|
|
981
|
+
const { runSupervisorLoop: runSupervisorLoop2 } = await Promise.resolve().then(() => (init_loop(), exports_loop));
|
|
982
|
+
const result = await runSupervisorLoop2(context.projectRoot, supervisorDeps(parsePositiveInt(timeout.value, "--timeout-ms", 1800000)), {
|
|
463
983
|
maxTasks: parsePositiveInt(maxTasks.value, "--max-tasks", 1),
|
|
464
984
|
concurrency: parsePositiveInt(concurrency.value, "--concurrency", 1),
|
|
465
985
|
...task.value ? { candidateTaskIds: [task.value] } : {},
|
|
466
|
-
dryRun: dry.value || context.dryRun
|
|
986
|
+
dryRun: Boolean(dry.value || context.dryRun)
|
|
467
987
|
});
|
|
468
|
-
const details = { ...result, stopWhen:
|
|
988
|
+
const details = { ...result, stopWhen: stopWhen.value?.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0) ?? [] };
|
|
469
989
|
if (context.outputMode === "text") {
|
|
470
990
|
if (json.value)
|
|
471
991
|
printJson(details);
|
|
@@ -480,7 +1000,8 @@ async function executeUnblock(context, args) {
|
|
|
480
1000
|
const timeout = takeOption(json.rest, "--timeout-ms");
|
|
481
1001
|
const taskId = timeout.rest[0]?.startsWith("-") ? undefined : timeout.rest[0];
|
|
482
1002
|
requireNoExtraArgs(taskId ? timeout.rest.slice(1) : timeout.rest, "rig unblock [task-id] [--dry-run] [--json]");
|
|
483
|
-
const
|
|
1003
|
+
const { unblockTask: unblockTask2 } = await Promise.resolve().then(() => (init_loop(), exports_loop));
|
|
1004
|
+
const result = await unblockTask2(context.projectRoot, taskId ?? null, supervisorDeps(parsePositiveInt(timeout.value, "--timeout-ms", 1800000)), { dryRun: Boolean(dry.value || context.dryRun) });
|
|
484
1005
|
if (context.outputMode === "text") {
|
|
485
1006
|
if (json.value)
|
|
486
1007
|
printJson(result);
|