@h-rig/contracts 0.0.6-alpha.154 → 0.0.6-alpha.156
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/index.cjs +407 -91
- package/dist/index.mjs +407 -91
- package/dist/src/browser.d.ts +50 -0
- package/dist/src/browser.js +6 -0
- package/dist/src/config.d.ts +4 -0
- package/dist/src/config.js +6 -14
- package/dist/src/control-plane-types.d.ts +283 -0
- package/dist/src/control-plane-types.js +29 -0
- package/dist/src/doctor.d.ts +27 -0
- package/dist/src/doctor.js +14 -0
- package/dist/src/github.d.ts +103 -0
- package/dist/src/github.js +6 -0
- package/dist/src/help-catalog.js +1 -1
- package/dist/src/index.d.ts +11 -0
- package/dist/src/index.js +407 -91
- package/dist/src/isolation.d.ts +21 -0
- package/dist/src/isolation.js +6 -0
- package/dist/src/kernel.d.ts +3 -35
- package/dist/src/kernel.js +0 -15
- package/dist/src/lifecycle-capabilities.d.ts +29 -0
- package/dist/src/lifecycle-capabilities.js +8 -0
- package/dist/src/managed-repos.d.ts +73 -0
- package/dist/src/managed-repos.js +6 -0
- package/dist/src/memory.d.ts +151 -0
- package/dist/src/memory.js +8 -0
- package/dist/src/panel-protocol.d.ts +17 -0
- package/dist/src/panel-protocol.js +10 -0
- package/dist/src/plugin-hooks.js +6 -14
- package/dist/src/plugin.d.ts +13 -0
- package/dist/src/plugin.js +7 -14
- package/dist/src/provider-instructions.d.ts +19 -0
- package/dist/src/provider-instructions.js +6 -0
- package/dist/src/run-journal.d.ts +1 -1
- package/dist/src/run-record.d.ts +11 -0
- package/dist/src/run-record.js +16 -0
- package/dist/src/supervisor-journal.d.ts +0 -1
- package/dist/src/supervisor-journal.js +0 -74
- package/dist/src/task-graph-primitives.d.ts +68 -0
- package/dist/src/task-graph-primitives.js +319 -0
- package/package.json +1 -1
|
@@ -1152,81 +1152,7 @@ var SupervisorProjection = Schema5.Struct({
|
|
|
1152
1152
|
closures: Schema5.Array(TaskClosureSummary),
|
|
1153
1153
|
anomalies: Schema5.Array(Schema5.String)
|
|
1154
1154
|
});
|
|
1155
|
-
function reduceSupervisorJournal(events) {
|
|
1156
|
-
let processed = 0;
|
|
1157
|
-
let succeeded = 0;
|
|
1158
|
-
let failed = 0;
|
|
1159
|
-
let skipped = 0;
|
|
1160
|
-
let current = null;
|
|
1161
|
-
let idleReason = null;
|
|
1162
|
-
let stopReason = null;
|
|
1163
|
-
let status = "running";
|
|
1164
|
-
let plannedOrder = [];
|
|
1165
|
-
let selectionPolicy = null;
|
|
1166
|
-
const concurrency = null;
|
|
1167
|
-
const closures = [];
|
|
1168
|
-
const anomalies = [];
|
|
1169
|
-
for (const event of events) {
|
|
1170
|
-
switch (event.kind) {
|
|
1171
|
-
case "supervisor.started":
|
|
1172
|
-
status = "running";
|
|
1173
|
-
break;
|
|
1174
|
-
case "supervisor.selection-planned":
|
|
1175
|
-
plannedOrder = [...event.taskIds];
|
|
1176
|
-
selectionPolicy = event.policy;
|
|
1177
|
-
break;
|
|
1178
|
-
case "supervisor.dispatch-started":
|
|
1179
|
-
break;
|
|
1180
|
-
case "supervisor.dispatch-confirmed":
|
|
1181
|
-
current = { taskId: event.taskId, runId: event.runId };
|
|
1182
|
-
break;
|
|
1183
|
-
case "supervisor.dispatch":
|
|
1184
|
-
current = { taskId: event.taskId, runId: event.runId };
|
|
1185
|
-
break;
|
|
1186
|
-
case "supervisor.outcome":
|
|
1187
|
-
processed += 1;
|
|
1188
|
-
if (event.failed) {
|
|
1189
|
-
failed += 1;
|
|
1190
|
-
} else {
|
|
1191
|
-
succeeded += 1;
|
|
1192
|
-
}
|
|
1193
|
-
if (event.closure) {
|
|
1194
|
-
closures.push(event.closure);
|
|
1195
|
-
}
|
|
1196
|
-
if (current?.runId === event.runId) {
|
|
1197
|
-
current = null;
|
|
1198
|
-
} else if (current !== null) {
|
|
1199
|
-
anomalies.push(`outcome for ${event.runId} did not match current ${current.runId}`);
|
|
1200
|
-
}
|
|
1201
|
-
break;
|
|
1202
|
-
case "supervisor.skipped":
|
|
1203
|
-
processed += 1;
|
|
1204
|
-
skipped += 1;
|
|
1205
|
-
break;
|
|
1206
|
-
case "supervisor.idle":
|
|
1207
|
-
status = "idle";
|
|
1208
|
-
idleReason = event.reason;
|
|
1209
|
-
break;
|
|
1210
|
-
case "supervisor.stopped":
|
|
1211
|
-
status = "stopped";
|
|
1212
|
-
stopReason = event.reason;
|
|
1213
|
-
current = null;
|
|
1214
|
-
break;
|
|
1215
|
-
case "supervisor.finished":
|
|
1216
|
-
status = "finished";
|
|
1217
|
-
processed = event.processed;
|
|
1218
|
-
succeeded = event.succeeded;
|
|
1219
|
-
failed = event.failed;
|
|
1220
|
-
skipped = event.skipped ?? skipped;
|
|
1221
|
-
idleReason = event.idleReason;
|
|
1222
|
-
current = null;
|
|
1223
|
-
break;
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
return { status, processed, succeeded, failed, skipped, current, plannedOrder, selectionPolicy, concurrency, idleReason, stopReason, closures, anomalies };
|
|
1227
|
-
}
|
|
1228
1155
|
export {
|
|
1229
|
-
reduceSupervisorJournal,
|
|
1230
1156
|
TaskClosureSummary,
|
|
1231
1157
|
SupervisorStoppedEvent,
|
|
1232
1158
|
SupervisorStopReason,
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { TaskSummary } from "./graph";
|
|
2
|
+
export type TaskDependencyProjection = Pick<TaskSummary, "id" | "status" | "priority" | "metadata"> & Partial<Pick<TaskSummary, "externalId" | "sourceIssueId" | "dependencies" | "parentChildDeps" | "createdAt" | "updatedAt" | "role" | "scope" | "validationKeys">> & {
|
|
3
|
+
readonly title?: string | null;
|
|
4
|
+
};
|
|
5
|
+
export type ClientTaskProjection = TaskDependencyProjection & {
|
|
6
|
+
readonly title?: string | null;
|
|
7
|
+
readonly assignees?: readonly string[] | null;
|
|
8
|
+
readonly assignedTo?: string | readonly string[] | null;
|
|
9
|
+
readonly labels?: readonly string[] | null;
|
|
10
|
+
};
|
|
11
|
+
export type TaskProjectionInput = {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly title?: string | null;
|
|
14
|
+
readonly status?: string | null;
|
|
15
|
+
readonly source?: string | null;
|
|
16
|
+
readonly url?: string | null;
|
|
17
|
+
readonly body?: string | null;
|
|
18
|
+
readonly priority?: number | string | null;
|
|
19
|
+
readonly assignedTo?: string | readonly string[] | null;
|
|
20
|
+
readonly assignees?: readonly string[] | null;
|
|
21
|
+
readonly dependencies?: readonly string[] | null;
|
|
22
|
+
readonly parentChildDeps?: readonly string[] | null;
|
|
23
|
+
readonly metadata?: Record<string, unknown> | null;
|
|
24
|
+
readonly [key: string]: unknown;
|
|
25
|
+
};
|
|
26
|
+
export type TaskDependencyBadgeKind = "blocked" | "ready" | "dependency";
|
|
27
|
+
export interface TaskDependencyBadge {
|
|
28
|
+
readonly kind: TaskDependencyBadgeKind;
|
|
29
|
+
readonly label: string;
|
|
30
|
+
readonly description: string;
|
|
31
|
+
readonly count?: number;
|
|
32
|
+
readonly taskIds?: readonly string[];
|
|
33
|
+
}
|
|
34
|
+
export interface TaskDependencyBadgeSummary {
|
|
35
|
+
readonly taskId: string;
|
|
36
|
+
readonly blockingDepth: number;
|
|
37
|
+
readonly dependencyIds: readonly string[];
|
|
38
|
+
readonly unresolvedDependencyRefs: readonly string[];
|
|
39
|
+
readonly blockedBy: readonly string[];
|
|
40
|
+
readonly blocks: readonly string[];
|
|
41
|
+
readonly blocked: boolean;
|
|
42
|
+
readonly ready: boolean;
|
|
43
|
+
readonly dependencyCount: number;
|
|
44
|
+
readonly dependentCount: number;
|
|
45
|
+
readonly badges: readonly TaskDependencyBadge[];
|
|
46
|
+
}
|
|
47
|
+
export declare function readTaskMetadataStringList(task: TaskDependencyProjection, key: "dependencies" | "parentChildDeps" | "labels"): string[];
|
|
48
|
+
export declare function readTaskBlockingDependencyRefs(task: TaskDependencyProjection): string[];
|
|
49
|
+
export declare function readTaskDependencyRefs(task: TaskDependencyProjection): string[];
|
|
50
|
+
export declare function readTaskSourceIssueId(task: TaskDependencyProjection): string | null;
|
|
51
|
+
export declare function readTaskScope(task: TaskDependencyProjection): string[];
|
|
52
|
+
export type TaskScopeInput = readonly string[] | TaskDependencyProjection;
|
|
53
|
+
export declare function disjointScope(left: TaskScopeInput, right: TaskScopeInput): boolean;
|
|
54
|
+
export declare function resolveTaskReference(ref: string, tasksById: ReadonlyMap<string, TaskDependencyProjection>, taskIdByExternalRef: ReadonlyMap<string, string>, taskIdBySourceIssueId: ReadonlyMap<string, string>): string | null;
|
|
55
|
+
export declare function buildTaskReferenceIndex<T extends TaskDependencyProjection>(tasks: readonly T[]): {
|
|
56
|
+
readonly tasksById: Map<string, T>;
|
|
57
|
+
readonly taskIdByExternalRef: Map<string, string>;
|
|
58
|
+
readonly taskIdBySourceIssueId: Map<string, string>;
|
|
59
|
+
};
|
|
60
|
+
export declare function computeTaskBlockingDepths<T extends TaskDependencyProjection>(tasks: readonly T[]): Map<string, number>;
|
|
61
|
+
export declare function isTaskTerminalStatus(status: string | null | undefined): boolean;
|
|
62
|
+
export declare function computeTaskDependencyBadges(tasks: readonly TaskDependencyProjection[]): Map<string, TaskDependencyBadgeSummary>;
|
|
63
|
+
export declare function normalizeTaskStatus(status: unknown): TaskSummary["status"];
|
|
64
|
+
export declare function toTaskDependencyProjection(task: TaskProjectionInput): ClientTaskProjection;
|
|
65
|
+
export declare function toTaskSummary(task: TaskProjectionInput, defaults?: {
|
|
66
|
+
readonly workspaceId?: string;
|
|
67
|
+
readonly graphId?: string | null;
|
|
68
|
+
}): TaskSummary;
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/contracts/src/task-graph-primitives.ts
|
|
3
|
+
function isObjectRecord(value) {
|
|
4
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
function readStringList(value) {
|
|
7
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
|
|
8
|
+
}
|
|
9
|
+
function unique(values) {
|
|
10
|
+
return Array.from(new Set(values));
|
|
11
|
+
}
|
|
12
|
+
function readTaskMetadataStringList(task, key) {
|
|
13
|
+
const taskRecord = task;
|
|
14
|
+
const topLevel = readStringList(taskRecord[key]);
|
|
15
|
+
if (topLevel.length > 0)
|
|
16
|
+
return topLevel;
|
|
17
|
+
const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
|
|
18
|
+
const metadataList = readStringList(metadata?.[key]);
|
|
19
|
+
if (metadataList.length > 0)
|
|
20
|
+
return metadataList;
|
|
21
|
+
if (key === "dependencies") {
|
|
22
|
+
return readStringList(metadata?.deps);
|
|
23
|
+
}
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
function readTaskBlockingDependencyRefs(task) {
|
|
27
|
+
return readTaskMetadataStringList(task, "dependencies");
|
|
28
|
+
}
|
|
29
|
+
function readTaskDependencyRefs(task) {
|
|
30
|
+
return unique([
|
|
31
|
+
...readTaskMetadataStringList(task, "dependencies"),
|
|
32
|
+
...readTaskMetadataStringList(task, "parentChildDeps")
|
|
33
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
function readTaskSourceIssueId(task) {
|
|
36
|
+
if (typeof task.sourceIssueId === "string" && task.sourceIssueId.length > 0) {
|
|
37
|
+
return task.sourceIssueId;
|
|
38
|
+
}
|
|
39
|
+
const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
|
|
40
|
+
if (typeof metadata?.sourceIssueId === "string" && metadata.sourceIssueId.length > 0) {
|
|
41
|
+
return metadata.sourceIssueId;
|
|
42
|
+
}
|
|
43
|
+
const rigMetadata = isObjectRecord(metadata?._rig) ? metadata._rig : null;
|
|
44
|
+
return typeof rigMetadata?.sourceIssueId === "string" && rigMetadata.sourceIssueId.length > 0 ? rigMetadata.sourceIssueId : null;
|
|
45
|
+
}
|
|
46
|
+
function readTaskScope(task) {
|
|
47
|
+
const taskRecord = task;
|
|
48
|
+
const topLevel = readStringList(taskRecord.scope);
|
|
49
|
+
if (topLevel.length > 0)
|
|
50
|
+
return unique(topLevel.map((entry) => entry.trim()).filter((entry) => entry.length > 0));
|
|
51
|
+
const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
|
|
52
|
+
const metadataScope = readStringList(metadata?.scope);
|
|
53
|
+
if (metadataScope.length > 0)
|
|
54
|
+
return unique(metadataScope.map((entry) => entry.trim()).filter((entry) => entry.length > 0));
|
|
55
|
+
return unique([
|
|
56
|
+
...readStringList(metadata?.files),
|
|
57
|
+
...readStringList(metadata?.paths)
|
|
58
|
+
].map((entry) => entry.trim()).filter((entry) => entry.length > 0));
|
|
59
|
+
}
|
|
60
|
+
function isScopeList(input) {
|
|
61
|
+
return Array.isArray(input);
|
|
62
|
+
}
|
|
63
|
+
function normalizeScopeInput(input) {
|
|
64
|
+
return isScopeList(input) ? unique(input.map((entry) => entry.trim()).filter((entry) => entry.length > 0)) : readTaskScope(input);
|
|
65
|
+
}
|
|
66
|
+
function disjointScope(left, right) {
|
|
67
|
+
const leftScope = new Set(normalizeScopeInput(left));
|
|
68
|
+
if (leftScope.size === 0)
|
|
69
|
+
return true;
|
|
70
|
+
return normalizeScopeInput(right).every((entry) => !leftScope.has(entry));
|
|
71
|
+
}
|
|
72
|
+
function resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId) {
|
|
73
|
+
if (tasksById.has(ref))
|
|
74
|
+
return ref;
|
|
75
|
+
return taskIdBySourceIssueId.get(ref) ?? taskIdByExternalRef.get(ref) ?? null;
|
|
76
|
+
}
|
|
77
|
+
function buildTaskReferenceIndex(tasks) {
|
|
78
|
+
return {
|
|
79
|
+
tasksById: new Map(tasks.map((task) => [task.id, task])),
|
|
80
|
+
taskIdByExternalRef: new Map(tasks.flatMap((task) => task.externalId ? [[task.externalId, task.id]] : [])),
|
|
81
|
+
taskIdBySourceIssueId: new Map(tasks.flatMap((task) => {
|
|
82
|
+
const sourceIssueId = readTaskSourceIssueId(task);
|
|
83
|
+
return sourceIssueId ? [[sourceIssueId, task.id]] : [];
|
|
84
|
+
}))
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function computeTaskBlockingDepths(tasks) {
|
|
88
|
+
const { tasksById, taskIdByExternalRef, taskIdBySourceIssueId } = buildTaskReferenceIndex(tasks);
|
|
89
|
+
const memo = new Map;
|
|
90
|
+
const visit = (taskId, stack) => {
|
|
91
|
+
const cached = memo.get(taskId);
|
|
92
|
+
if (cached !== undefined)
|
|
93
|
+
return cached;
|
|
94
|
+
if (stack.has(taskId))
|
|
95
|
+
return 0;
|
|
96
|
+
const task = tasksById.get(taskId);
|
|
97
|
+
if (!task)
|
|
98
|
+
return 0;
|
|
99
|
+
stack.add(taskId);
|
|
100
|
+
const blockers = readTaskBlockingDependencyRefs(task).map((ref) => resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId)).filter((ref) => ref !== null && ref !== taskId);
|
|
101
|
+
const depth = blockers.length === 0 ? 0 : Math.max(...blockers.map((blockerId) => visit(blockerId, stack) + 1));
|
|
102
|
+
stack.delete(taskId);
|
|
103
|
+
memo.set(taskId, depth);
|
|
104
|
+
return depth;
|
|
105
|
+
};
|
|
106
|
+
for (const task of tasks) {
|
|
107
|
+
visit(task.id, new Set);
|
|
108
|
+
}
|
|
109
|
+
return memo;
|
|
110
|
+
}
|
|
111
|
+
function isTaskTerminalStatus(status) {
|
|
112
|
+
switch (status) {
|
|
113
|
+
case "closed":
|
|
114
|
+
case "completed":
|
|
115
|
+
case "done":
|
|
116
|
+
case "cancelled":
|
|
117
|
+
case "canceled":
|
|
118
|
+
return true;
|
|
119
|
+
default:
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function isTaskBlockedStatus(status) {
|
|
124
|
+
return status === "blocked";
|
|
125
|
+
}
|
|
126
|
+
function isTaskRunnableStatus(status) {
|
|
127
|
+
if (status === null || status === undefined || status === "")
|
|
128
|
+
return true;
|
|
129
|
+
if (isTaskTerminalStatus(status) || isTaskBlockedStatus(status))
|
|
130
|
+
return false;
|
|
131
|
+
switch (status) {
|
|
132
|
+
case "ready":
|
|
133
|
+
case "open":
|
|
134
|
+
case "failed":
|
|
135
|
+
return true;
|
|
136
|
+
default:
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function computeTaskDependencyBadges(tasks) {
|
|
141
|
+
const index = buildTaskReferenceIndex(tasks);
|
|
142
|
+
const blockingDepths = computeTaskBlockingDepths(tasks);
|
|
143
|
+
const dependencyIdsByTask = new Map;
|
|
144
|
+
const unresolvedRefsByTask = new Map;
|
|
145
|
+
const blocksByTask = new Map;
|
|
146
|
+
for (const task of tasks) {
|
|
147
|
+
const dependencyIds = [];
|
|
148
|
+
const unresolvedRefs = [];
|
|
149
|
+
for (const ref of readTaskBlockingDependencyRefs(task)) {
|
|
150
|
+
const dependencyId = resolveTaskReference(ref, index.tasksById, index.taskIdByExternalRef, index.taskIdBySourceIssueId);
|
|
151
|
+
if (dependencyId && dependencyId !== task.id) {
|
|
152
|
+
dependencyIds.push(dependencyId);
|
|
153
|
+
const blocks = blocksByTask.get(dependencyId);
|
|
154
|
+
if (blocks) {
|
|
155
|
+
blocks.push(task.id);
|
|
156
|
+
} else {
|
|
157
|
+
blocksByTask.set(dependencyId, [task.id]);
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
unresolvedRefs.push(ref);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
dependencyIdsByTask.set(task.id, unique(dependencyIds));
|
|
164
|
+
unresolvedRefsByTask.set(task.id, unique(unresolvedRefs));
|
|
165
|
+
}
|
|
166
|
+
const summaries = new Map;
|
|
167
|
+
for (const task of tasks) {
|
|
168
|
+
const dependencyIds = dependencyIdsByTask.get(task.id) ?? [];
|
|
169
|
+
const unresolvedDependencyRefs = unresolvedRefsByTask.get(task.id) ?? [];
|
|
170
|
+
const blockedBy = dependencyIds.filter((dependencyId) => {
|
|
171
|
+
const dependency = index.tasksById.get(dependencyId);
|
|
172
|
+
return dependency ? !isTaskTerminalStatus(dependency.status) : false;
|
|
173
|
+
});
|
|
174
|
+
const blocks = unique(blocksByTask.get(task.id) ?? []);
|
|
175
|
+
const blocked = isTaskBlockedStatus(task.status) || blockedBy.length > 0;
|
|
176
|
+
const ready = isTaskRunnableStatus(task.status) && !blocked;
|
|
177
|
+
const badges = [];
|
|
178
|
+
if (blocked) {
|
|
179
|
+
badges.push({
|
|
180
|
+
kind: "blocked",
|
|
181
|
+
label: blockedBy.length > 0 ? `blocked \xD7${blockedBy.length}` : "blocked",
|
|
182
|
+
description: blockedBy.length > 0 ? `Waiting on ${blockedBy.join(", ")}.` : "Task source marks this task blocked.",
|
|
183
|
+
...blockedBy.length > 0 ? { count: blockedBy.length } : {},
|
|
184
|
+
taskIds: blockedBy
|
|
185
|
+
});
|
|
186
|
+
} else if (ready) {
|
|
187
|
+
badges.push({
|
|
188
|
+
kind: "ready",
|
|
189
|
+
label: "ready",
|
|
190
|
+
description: "No open dependencies block this task."
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
if (dependencyIds.length > 0 || blocks.length > 0 || unresolvedDependencyRefs.length > 0) {
|
|
194
|
+
badges.push({
|
|
195
|
+
kind: "dependency",
|
|
196
|
+
label: `deps ${dependencyIds.length}/${blocks.length}`,
|
|
197
|
+
description: [
|
|
198
|
+
dependencyIds.length > 0 ? `Depends on ${dependencyIds.join(", ")}.` : null,
|
|
199
|
+
blocks.length > 0 ? `Blocks ${blocks.join(", ")}.` : null,
|
|
200
|
+
unresolvedDependencyRefs.length > 0 ? `Unresolved refs: ${unresolvedDependencyRefs.join(", ")}.` : null
|
|
201
|
+
].filter((part) => part !== null).join(" "),
|
|
202
|
+
count: dependencyIds.length + blocks.length,
|
|
203
|
+
taskIds: unique([...dependencyIds, ...blocks])
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
summaries.set(task.id, {
|
|
207
|
+
taskId: task.id,
|
|
208
|
+
blockingDepth: blockingDepths.get(task.id) ?? 0,
|
|
209
|
+
dependencyIds,
|
|
210
|
+
unresolvedDependencyRefs,
|
|
211
|
+
blockedBy,
|
|
212
|
+
blocks,
|
|
213
|
+
blocked,
|
|
214
|
+
ready,
|
|
215
|
+
dependencyCount: dependencyIds.length,
|
|
216
|
+
dependentCount: blocks.length,
|
|
217
|
+
badges
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
return summaries;
|
|
221
|
+
}
|
|
222
|
+
var TASK_STATUSES = new Set([
|
|
223
|
+
"draft",
|
|
224
|
+
"open",
|
|
225
|
+
"ready",
|
|
226
|
+
"queued",
|
|
227
|
+
"running",
|
|
228
|
+
"in_progress",
|
|
229
|
+
"under_review",
|
|
230
|
+
"blocked",
|
|
231
|
+
"unknown",
|
|
232
|
+
"completed",
|
|
233
|
+
"failed",
|
|
234
|
+
"cancelled",
|
|
235
|
+
"closed"
|
|
236
|
+
]);
|
|
237
|
+
function stringValue(value) {
|
|
238
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
239
|
+
}
|
|
240
|
+
function stringArray(value) {
|
|
241
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0).map((entry) => entry.trim()) : [];
|
|
242
|
+
}
|
|
243
|
+
function numberOrNull(value) {
|
|
244
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
245
|
+
}
|
|
246
|
+
function metadataOf(task) {
|
|
247
|
+
return isObjectRecord(task.metadata) ? task.metadata : {};
|
|
248
|
+
}
|
|
249
|
+
function normalizeTaskStatus(status) {
|
|
250
|
+
const token = typeof status === "string" ? status.trim().toLowerCase() : "";
|
|
251
|
+
if (token === "done")
|
|
252
|
+
return "completed";
|
|
253
|
+
if (token === "canceled")
|
|
254
|
+
return "cancelled";
|
|
255
|
+
return TASK_STATUSES.has(token) ? token : "unknown";
|
|
256
|
+
}
|
|
257
|
+
function toTaskDependencyProjection(task) {
|
|
258
|
+
const metadata = metadataOf(task);
|
|
259
|
+
return {
|
|
260
|
+
id: String(task.id),
|
|
261
|
+
title: stringValue(task.title),
|
|
262
|
+
status: normalizeTaskStatus(task.status),
|
|
263
|
+
priority: numberOrNull(task.priority),
|
|
264
|
+
metadata,
|
|
265
|
+
externalId: stringValue(task.externalId),
|
|
266
|
+
sourceIssueId: stringValue(task.sourceIssueId),
|
|
267
|
+
dependencies: stringArray(task.dependencies),
|
|
268
|
+
parentChildDeps: stringArray(task.parentChildDeps),
|
|
269
|
+
createdAt: stringValue(task.createdAt) ?? "",
|
|
270
|
+
updatedAt: stringValue(task.updatedAt) ?? "",
|
|
271
|
+
role: stringValue(task.role),
|
|
272
|
+
scope: stringArray(task.scope),
|
|
273
|
+
validationKeys: stringArray(task.validationKeys),
|
|
274
|
+
labels: stringArray(task.labels),
|
|
275
|
+
assignees: task.assignees,
|
|
276
|
+
assignedTo: task.assignedTo
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function toTaskSummary(task, defaults = {}) {
|
|
280
|
+
const projection = toTaskDependencyProjection(task);
|
|
281
|
+
const metadata = metadataOf(task);
|
|
282
|
+
const createdAt = stringValue(task.createdAt) ?? "1970-01-01T00:00:00.000Z";
|
|
283
|
+
const updatedAt = stringValue(task.updatedAt) ?? createdAt;
|
|
284
|
+
return {
|
|
285
|
+
id: projection.id,
|
|
286
|
+
workspaceId: stringValue(task.workspaceId) ?? defaults.workspaceId ?? "workspace",
|
|
287
|
+
graphId: stringValue(task.graphId) ?? defaults.graphId ?? null,
|
|
288
|
+
externalId: projection.externalId,
|
|
289
|
+
title: projection.title ?? projection.id,
|
|
290
|
+
description: stringValue(task.description) ?? stringValue(task.body) ?? "",
|
|
291
|
+
status: projection.status,
|
|
292
|
+
priority: numberOrNull(task.priority),
|
|
293
|
+
role: projection.role,
|
|
294
|
+
scope: [...projection.scope ?? []],
|
|
295
|
+
validationKeys: [...projection.validationKeys ?? []],
|
|
296
|
+
...projection.sourceIssueId ? { sourceIssueId: projection.sourceIssueId } : {},
|
|
297
|
+
dependencies: [...projection.dependencies ?? []],
|
|
298
|
+
parentChildDeps: [...projection.parentChildDeps ?? []],
|
|
299
|
+
metadata,
|
|
300
|
+
createdAt,
|
|
301
|
+
updatedAt
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
export {
|
|
305
|
+
toTaskSummary,
|
|
306
|
+
toTaskDependencyProjection,
|
|
307
|
+
resolveTaskReference,
|
|
308
|
+
readTaskSourceIssueId,
|
|
309
|
+
readTaskScope,
|
|
310
|
+
readTaskMetadataStringList,
|
|
311
|
+
readTaskDependencyRefs,
|
|
312
|
+
readTaskBlockingDependencyRefs,
|
|
313
|
+
normalizeTaskStatus,
|
|
314
|
+
isTaskTerminalStatus,
|
|
315
|
+
disjointScope,
|
|
316
|
+
computeTaskDependencyBadges,
|
|
317
|
+
computeTaskBlockingDepths,
|
|
318
|
+
buildTaskReferenceIndex
|
|
319
|
+
};
|