@h-rig/core 0.0.6-alpha.15 → 0.0.6-alpha.151

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.
@@ -1,16 +1,118 @@
1
1
  // @bun
2
+ // packages/core/src/taskScore.ts
3
+ var ROLE_WEIGHT = {
4
+ architect: 25,
5
+ extractor: 10
6
+ };
7
+ var CRITICALITY_WEIGHT = {
8
+ core: 20,
9
+ high: 10,
10
+ normal: 0
11
+ };
12
+ function finiteNumber(value, fallback) {
13
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
14
+ }
15
+ function scoreTask(input) {
16
+ const priority = finiteNumber(input.priority, 3);
17
+ const unblockCount = Math.max(0, finiteNumber(input.unblockCount, 0));
18
+ const roleWeight = input.role ? ROLE_WEIGHT[input.role] ?? 0 : 0;
19
+ const criticalityWeight = input.criticality ? CRITICALITY_WEIGHT[input.criticality] ?? 0 : 0;
20
+ const validationWeight = (input.validation ?? []).some((entry) => entry.includes("test-contract") || entry.includes("test-boundary")) ? 8 : 0;
21
+ const queueWeight = finiteNumber(input.queueWeight, 0);
22
+ return unblockCount * 100 + (10 - priority) + roleWeight + criticalityWeight + validationWeight + queueWeight;
23
+ }
24
+ function rankTasks(tasks, scoreInput, idOf) {
25
+ return tasks.map((task) => {
26
+ const input = scoreInput(task);
27
+ return {
28
+ task,
29
+ score: scoreTask(input),
30
+ priority: finiteNumber(input.priority, 3),
31
+ unblockCount: Math.max(0, finiteNumber(input.unblockCount, 0))
32
+ };
33
+ }).toSorted((left, right) => {
34
+ const scoreDelta = right.score - left.score;
35
+ if (scoreDelta !== 0)
36
+ return scoreDelta;
37
+ const unblockDelta = right.unblockCount - left.unblockCount;
38
+ if (unblockDelta !== 0)
39
+ return unblockDelta;
40
+ const priorityDelta = left.priority - right.priority;
41
+ if (priorityDelta !== 0)
42
+ return priorityDelta;
43
+ return idOf(left.task).localeCompare(idOf(right.task));
44
+ });
45
+ }
46
+
2
47
  // packages/core/src/taskGraph.ts
3
48
  function isObjectRecord(value) {
4
49
  return typeof value === "object" && value !== null && !Array.isArray(value);
5
50
  }
51
+ function readStringList(value) {
52
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
53
+ }
54
+ function unique(values) {
55
+ return Array.from(new Set(values));
56
+ }
6
57
  function readTaskMetadataStringList(task, key) {
58
+ const taskRecord = task;
59
+ const topLevel = readStringList(taskRecord[key]);
60
+ if (topLevel.length > 0)
61
+ return topLevel;
7
62
  const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
8
- const value = metadata?.[key];
9
- return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
63
+ const metadataList = readStringList(metadata?.[key]);
64
+ if (metadataList.length > 0)
65
+ return metadataList;
66
+ if (key === "dependencies") {
67
+ return readStringList(metadata?.deps);
68
+ }
69
+ return [];
70
+ }
71
+ function readTaskBlockingDependencyRefs(task) {
72
+ return readTaskMetadataStringList(task, "dependencies");
73
+ }
74
+ function readTaskDependencyRefs(task) {
75
+ return unique([
76
+ ...readTaskMetadataStringList(task, "dependencies"),
77
+ ...readTaskMetadataStringList(task, "parentChildDeps")
78
+ ]);
10
79
  }
11
80
  function readTaskSourceIssueId(task) {
81
+ if (typeof task.sourceIssueId === "string" && task.sourceIssueId.length > 0) {
82
+ return task.sourceIssueId;
83
+ }
84
+ const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
85
+ if (typeof metadata?.sourceIssueId === "string" && metadata.sourceIssueId.length > 0) {
86
+ return metadata.sourceIssueId;
87
+ }
88
+ const rigMetadata = isObjectRecord(metadata?._rig) ? metadata._rig : null;
89
+ return typeof rigMetadata?.sourceIssueId === "string" && rigMetadata.sourceIssueId.length > 0 ? rigMetadata.sourceIssueId : null;
90
+ }
91
+ function readTaskScope(task) {
92
+ const taskRecord = task;
93
+ const topLevel = readStringList(taskRecord.scope);
94
+ if (topLevel.length > 0)
95
+ return unique(topLevel.map((entry) => entry.trim()).filter((entry) => entry.length > 0));
12
96
  const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
13
- return typeof metadata?.sourceIssueId === "string" && metadata.sourceIssueId.length > 0 ? metadata.sourceIssueId : null;
97
+ const metadataScope = readStringList(metadata?.scope);
98
+ if (metadataScope.length > 0)
99
+ return unique(metadataScope.map((entry) => entry.trim()).filter((entry) => entry.length > 0));
100
+ return unique([
101
+ ...readStringList(metadata?.files),
102
+ ...readStringList(metadata?.paths)
103
+ ].map((entry) => entry.trim()).filter((entry) => entry.length > 0));
104
+ }
105
+ function isScopeList(input) {
106
+ return Array.isArray(input);
107
+ }
108
+ function normalizeScopeInput(input) {
109
+ return isScopeList(input) ? unique(input.map((entry) => entry.trim()).filter((entry) => entry.length > 0)) : readTaskScope(input);
110
+ }
111
+ function disjointScope(left, right) {
112
+ const leftScope = new Set(normalizeScopeInput(left));
113
+ if (leftScope.size === 0)
114
+ return true;
115
+ return normalizeScopeInput(right).every((entry) => !leftScope.has(entry));
14
116
  }
15
117
  function resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId) {
16
118
  if (tasksById.has(ref))
@@ -40,11 +142,7 @@ function computeTaskBlockingDepths(tasks) {
40
142
  if (!task)
41
143
  return 0;
42
144
  stack.add(taskId);
43
- const refs = [
44
- ...readTaskMetadataStringList(task, "dependencies"),
45
- ...readTaskMetadataStringList(task, "parentChildDeps")
46
- ];
47
- const blockers = refs.map((ref) => resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId)).filter((ref) => ref !== null && ref !== taskId);
145
+ const blockers = readTaskBlockingDependencyRefs(task).map((ref) => resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId)).filter((ref) => ref !== null && ref !== taskId);
48
146
  const depth = blockers.length === 0 ? 0 : Math.max(...blockers.map((blockerId) => visit(blockerId, stack) + 1));
49
147
  stack.delete(taskId);
50
148
  memo.set(taskId, depth);
@@ -55,10 +153,225 @@ function computeTaskBlockingDepths(tasks) {
55
153
  }
56
154
  return memo;
57
155
  }
156
+ function isTaskTerminalStatus(status) {
157
+ switch (status) {
158
+ case "closed":
159
+ case "completed":
160
+ case "done":
161
+ case "cancelled":
162
+ case "canceled":
163
+ return true;
164
+ default:
165
+ return false;
166
+ }
167
+ }
168
+ function isTaskBlockedStatus(status) {
169
+ return status === "blocked";
170
+ }
171
+ function isTaskRunnableStatus(status) {
172
+ if (status === null || status === undefined || status === "")
173
+ return true;
174
+ if (isTaskTerminalStatus(status) || isTaskBlockedStatus(status))
175
+ return false;
176
+ switch (status) {
177
+ case "ready":
178
+ case "open":
179
+ case "failed":
180
+ return true;
181
+ default:
182
+ return false;
183
+ }
184
+ }
185
+ function priorityValue(task) {
186
+ return typeof task.priority === "number" && Number.isFinite(task.priority) ? task.priority : Number.MAX_SAFE_INTEGER;
187
+ }
188
+ function readTaskRole(task) {
189
+ if (typeof task.role === "string" && task.role.trim())
190
+ return task.role.trim();
191
+ const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
192
+ return typeof metadata?.role === "string" && metadata.role.trim() ? metadata.role.trim() : null;
193
+ }
194
+ function readTaskCriticality(task) {
195
+ const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
196
+ return typeof metadata?.criticality === "string" && metadata.criticality.trim() ? metadata.criticality.trim() : null;
197
+ }
198
+ function readTaskValidationKeys(task) {
199
+ const taskRecord = task;
200
+ const topLevel = readStringList(taskRecord.validationKeys);
201
+ if (topLevel.length > 0)
202
+ return topLevel;
203
+ const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
204
+ return readStringList(metadata?.validation);
205
+ }
206
+ function readTaskQueueWeight(task) {
207
+ const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
208
+ const queueWeight = metadata?.queueWeight ?? metadata?.queue_weight;
209
+ return typeof queueWeight === "number" && Number.isFinite(queueWeight) ? queueWeight : 0;
210
+ }
211
+ function scoreInputForTask(task, unblockCount) {
212
+ return {
213
+ priority: task.priority,
214
+ unblockCount,
215
+ role: readTaskRole(task),
216
+ criticality: readTaskCriticality(task),
217
+ validation: readTaskValidationKeys(task),
218
+ queueWeight: readTaskQueueWeight(task)
219
+ };
220
+ }
221
+ function computeTaskDependencyBadges(tasks) {
222
+ const index = buildTaskReferenceIndex(tasks);
223
+ const blockingDepths = computeTaskBlockingDepths(tasks);
224
+ const dependencyIdsByTask = new Map;
225
+ const unresolvedRefsByTask = new Map;
226
+ const blocksByTask = new Map;
227
+ for (const task of tasks) {
228
+ const dependencyIds = [];
229
+ const unresolvedRefs = [];
230
+ for (const ref of readTaskBlockingDependencyRefs(task)) {
231
+ const dependencyId = resolveTaskReference(ref, index.tasksById, index.taskIdByExternalRef, index.taskIdBySourceIssueId);
232
+ if (dependencyId && dependencyId !== task.id) {
233
+ dependencyIds.push(dependencyId);
234
+ const blocks = blocksByTask.get(dependencyId);
235
+ if (blocks) {
236
+ blocks.push(task.id);
237
+ } else {
238
+ blocksByTask.set(dependencyId, [task.id]);
239
+ }
240
+ } else {
241
+ unresolvedRefs.push(ref);
242
+ }
243
+ }
244
+ dependencyIdsByTask.set(task.id, unique(dependencyIds));
245
+ unresolvedRefsByTask.set(task.id, unique(unresolvedRefs));
246
+ }
247
+ const summaries = new Map;
248
+ for (const task of tasks) {
249
+ const dependencyIds = dependencyIdsByTask.get(task.id) ?? [];
250
+ const unresolvedDependencyRefs = unresolvedRefsByTask.get(task.id) ?? [];
251
+ const blockedBy = dependencyIds.filter((dependencyId) => {
252
+ const dependency = index.tasksById.get(dependencyId);
253
+ return dependency ? !isTaskTerminalStatus(dependency.status) : false;
254
+ });
255
+ const blocks = unique(blocksByTask.get(task.id) ?? []);
256
+ const blocked = isTaskBlockedStatus(task.status) || blockedBy.length > 0;
257
+ const ready = isTaskRunnableStatus(task.status) && !blocked;
258
+ const badges = [];
259
+ if (blocked) {
260
+ badges.push({
261
+ kind: "blocked",
262
+ label: blockedBy.length > 0 ? `blocked \xD7${blockedBy.length}` : "blocked",
263
+ description: blockedBy.length > 0 ? `Waiting on ${blockedBy.join(", ")}.` : "Task source marks this task blocked.",
264
+ ...blockedBy.length > 0 ? { count: blockedBy.length } : {},
265
+ taskIds: blockedBy
266
+ });
267
+ } else if (ready) {
268
+ badges.push({
269
+ kind: "ready",
270
+ label: "ready",
271
+ description: "No open dependencies block this task."
272
+ });
273
+ }
274
+ if (dependencyIds.length > 0 || blocks.length > 0 || unresolvedDependencyRefs.length > 0) {
275
+ badges.push({
276
+ kind: "dependency",
277
+ label: `deps ${dependencyIds.length}/${blocks.length}`,
278
+ description: [
279
+ dependencyIds.length > 0 ? `Depends on ${dependencyIds.join(", ")}.` : null,
280
+ blocks.length > 0 ? `Blocks ${blocks.join(", ")}.` : null,
281
+ unresolvedDependencyRefs.length > 0 ? `Unresolved refs: ${unresolvedDependencyRefs.join(", ")}.` : null
282
+ ].filter((part) => part !== null).join(" "),
283
+ count: dependencyIds.length + blocks.length,
284
+ taskIds: unique([...dependencyIds, ...blocks])
285
+ });
286
+ }
287
+ summaries.set(task.id, {
288
+ taskId: task.id,
289
+ blockingDepth: blockingDepths.get(task.id) ?? 0,
290
+ dependencyIds,
291
+ unresolvedDependencyRefs,
292
+ blockedBy,
293
+ blocks,
294
+ blocked,
295
+ ready,
296
+ dependencyCount: dependencyIds.length,
297
+ dependentCount: blocks.length,
298
+ badges
299
+ });
300
+ }
301
+ return summaries;
302
+ }
303
+ function selectNextReadyTaskByPriority(tasks, options = {}) {
304
+ const excluded = new Set(options.excludeTaskIds ?? []);
305
+ const badges = computeTaskDependencyBadges(tasks);
306
+ const candidates = tasks.filter((task) => !excluded.has(task.id)).filter((task) => options.filter?.(task) ?? true).filter((task) => badges.get(task.id)?.ready === true).toSorted((left, right) => {
307
+ const priorityDelta = priorityValue(left) - priorityValue(right);
308
+ if (priorityDelta !== 0)
309
+ return priorityDelta;
310
+ const createdDelta = (left.createdAt ?? "").localeCompare(right.createdAt ?? "");
311
+ if (createdDelta !== 0)
312
+ return createdDelta;
313
+ return left.id.localeCompare(right.id);
314
+ });
315
+ return candidates[0] ?? null;
316
+ }
317
+ function rankReadyTasks(tasks, options = {}) {
318
+ const excluded = new Set(options.excludeTaskIds ?? []);
319
+ const activeTaskIds = new Set(options.activeTaskIds ?? []);
320
+ const badges = computeTaskDependencyBadges(tasks);
321
+ const tasksById = new Map(tasks.map((task) => [String(task.id), task]));
322
+ const openBlockCount = (task) => (badges.get(task.id)?.blocks ?? []).filter((blockedTaskId) => {
323
+ const blockedTask = tasksById.get(blockedTaskId);
324
+ return blockedTask ? !isTaskTerminalStatus(blockedTask.status) : false;
325
+ }).length;
326
+ 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);
327
+ const maxUnblockCount = Math.max(0, ...readyTasks.map(openBlockCount));
328
+ const selectedByMode = readyTasks.filter((task) => {
329
+ const unblockCount = openBlockCount(task);
330
+ if (options.selection === "blocking-only")
331
+ return unblockCount > 0;
332
+ if (options.selection === "max-unblock")
333
+ return maxUnblockCount > 0 && unblockCount === maxUnblockCount;
334
+ return true;
335
+ });
336
+ return rankTasks(selectedByMode, (task) => scoreInputForTask(task, openBlockCount(task)), (task) => task.id).map((entry) => ({
337
+ task: entry.task,
338
+ score: entry.score,
339
+ priority: entry.priority,
340
+ unblockCount: entry.unblockCount,
341
+ scope: readTaskScope(entry.task)
342
+ }));
343
+ }
344
+ function selectRankedReadyTasks(tasks, options = {}) {
345
+ const ranked = rankReadyTasks(tasks, options);
346
+ if (options.requireDisjointScopes !== true && !options.disjointWithScopes) {
347
+ return ranked.slice(0, options.limit).map((entry) => entry.task);
348
+ }
349
+ const occupiedScopes = new Set(options.disjointWithScopes ?? []);
350
+ const selected = [];
351
+ for (const entry of ranked) {
352
+ if (entry.scope.some((scope) => occupiedScopes.has(scope)))
353
+ continue;
354
+ selected.push(entry.task);
355
+ for (const scope of entry.scope)
356
+ occupiedScopes.add(scope);
357
+ if (options.limit !== undefined && selected.length >= options.limit)
358
+ break;
359
+ }
360
+ return selected;
361
+ }
58
362
  export {
363
+ selectRankedReadyTasks,
364
+ selectNextReadyTaskByPriority,
59
365
  resolveTaskReference,
60
366
  readTaskSourceIssueId,
367
+ readTaskScope,
61
368
  readTaskMetadataStringList,
369
+ readTaskDependencyRefs,
370
+ readTaskBlockingDependencyRefs,
371
+ rankReadyTasks,
372
+ isTaskTerminalStatus,
373
+ disjointScope,
374
+ computeTaskDependencyBadges,
62
375
  computeTaskBlockingDepths,
63
376
  buildTaskReferenceIndex
64
377
  };
@@ -0,0 +1,3 @@
1
+ export declare function extractTaskCode(title: string): string | null;
2
+ export declare function extractTaskGroupKey(title: string): string | null;
3
+ export declare function stripTaskCode(label: string): string;
@@ -0,0 +1,61 @@
1
+ import type { EngineReadModel, TaskSummary } from "@rig/contracts";
2
+ export type TaskGraphLane = Readonly<{
3
+ key: string;
4
+ label: string;
5
+ rowIndex: number;
6
+ x: number;
7
+ y: number;
8
+ width: number;
9
+ height: number;
10
+ color: string;
11
+ taskCount: number;
12
+ }>;
13
+ export type TaskGraphStage = Readonly<{
14
+ index: number;
15
+ label: string;
16
+ x: number;
17
+ width: number;
18
+ }>;
19
+ export type TaskGraphNode = Readonly<{
20
+ id: string;
21
+ taskId: string;
22
+ task: TaskSummary;
23
+ rowKey: string;
24
+ rowLabel: string;
25
+ rowIndex: number;
26
+ stage: number;
27
+ x: number;
28
+ y: number;
29
+ width: number;
30
+ height: number;
31
+ color: string;
32
+ taskCode: string | null;
33
+ strippedTitle: string;
34
+ depsIn: number;
35
+ depsOut: number;
36
+ runCount: number;
37
+ hasApprovals: boolean;
38
+ hasPendingUserInput: boolean;
39
+ hasRejectedReview: boolean;
40
+ hasFailedValidations: boolean;
41
+ artifactCount: number;
42
+ }>;
43
+ export type TaskGraphEdge = Readonly<{
44
+ id: string;
45
+ sourceId: string;
46
+ targetId: string;
47
+ color: string;
48
+ kind: "blocking" | "parent-child";
49
+ }>;
50
+ export type TaskGraphLayout = Readonly<{
51
+ lanes: readonly TaskGraphLane[];
52
+ stages: readonly TaskGraphStage[];
53
+ nodes: readonly TaskGraphNode[];
54
+ edges: readonly TaskGraphEdge[];
55
+ totalWidth: number;
56
+ totalHeight: number;
57
+ taskCount: number;
58
+ }>;
59
+ export declare function buildTaskGraphLayout(snapshot: EngineReadModel | null, tasks: readonly TaskSummary[], options?: {
60
+ showParentChild?: boolean;
61
+ }): TaskGraphLayout;
@@ -39,14 +39,33 @@ function stripTaskCode(label) {
39
39
  function isObjectRecord(value) {
40
40
  return typeof value === "object" && value !== null && !Array.isArray(value);
41
41
  }
42
+ function readStringList(value) {
43
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
44
+ }
42
45
  function readTaskMetadataStringList(task, key) {
46
+ const taskRecord = task;
47
+ const topLevel = readStringList(taskRecord[key]);
48
+ if (topLevel.length > 0)
49
+ return topLevel;
43
50
  const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
44
- const value = metadata?.[key];
45
- return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
51
+ const metadataList = readStringList(metadata?.[key]);
52
+ if (metadataList.length > 0)
53
+ return metadataList;
54
+ if (key === "dependencies") {
55
+ return readStringList(metadata?.deps);
56
+ }
57
+ return [];
46
58
  }
47
59
  function readTaskSourceIssueId(task) {
60
+ if (typeof task.sourceIssueId === "string" && task.sourceIssueId.length > 0) {
61
+ return task.sourceIssueId;
62
+ }
48
63
  const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
49
- return typeof metadata?.sourceIssueId === "string" && metadata.sourceIssueId.length > 0 ? metadata.sourceIssueId : null;
64
+ if (typeof metadata?.sourceIssueId === "string" && metadata.sourceIssueId.length > 0) {
65
+ return metadata.sourceIssueId;
66
+ }
67
+ const rigMetadata = isObjectRecord(metadata?._rig) ? metadata._rig : null;
68
+ return typeof rigMetadata?.sourceIssueId === "string" && rigMetadata.sourceIssueId.length > 0 ? rigMetadata.sourceIssueId : null;
50
69
  }
51
70
  function resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId) {
52
71
  if (tasksById.has(ref))
@@ -88,7 +107,10 @@ function isObjectRecord2(value) {
88
107
  }
89
108
  function readIssueType(task) {
90
109
  const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
91
- return typeof metadata?.issueType === "string" ? metadata.issueType : null;
110
+ if (typeof metadata?.issueType === "string")
111
+ return metadata.issueType;
112
+ const raw = isObjectRecord2(metadata?.raw) ? metadata.raw : null;
113
+ return typeof raw?.issueType === "string" ? raw.issueType : null;
92
114
  }
93
115
  function isGraphTask(task) {
94
116
  return readIssueType(task) !== "epic";
@@ -335,6 +357,7 @@ function buildTaskGraphLayout(snapshot, tasks, options) {
335
357
  const artifactCount = (snapshot?.artifacts ?? []).filter((artifact) => runIds.has(artifact.runId)).length;
336
358
  nodes.push({
337
359
  id: task.id,
360
+ taskId: task.id,
338
361
  task,
339
362
  rowKey,
340
363
  rowLabel: rowLabelByKey.get(rowKey) ?? rowKey,
@@ -0,0 +1,17 @@
1
+ export type TaskCriticality = "core" | "high" | "normal" | string;
2
+ export interface TaskScoreInput {
3
+ readonly priority?: number | null;
4
+ readonly unblockCount?: number | null;
5
+ readonly role?: string | null;
6
+ readonly criticality?: TaskCriticality | null;
7
+ readonly validation?: readonly string[] | null;
8
+ readonly queueWeight?: number | null;
9
+ }
10
+ export interface RankedTask<T> {
11
+ readonly task: T;
12
+ readonly score: number;
13
+ readonly priority: number;
14
+ readonly unblockCount: number;
15
+ }
16
+ export declare function scoreTask(input: TaskScoreInput): number;
17
+ export declare function rankTasks<T>(tasks: readonly T[], scoreInput: (task: T) => TaskScoreInput, idOf: (task: T) => string): readonly RankedTask<T>[];
@@ -0,0 +1,49 @@
1
+ // @bun
2
+ // packages/core/src/taskScore.ts
3
+ var ROLE_WEIGHT = {
4
+ architect: 25,
5
+ extractor: 10
6
+ };
7
+ var CRITICALITY_WEIGHT = {
8
+ core: 20,
9
+ high: 10,
10
+ normal: 0
11
+ };
12
+ function finiteNumber(value, fallback) {
13
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
14
+ }
15
+ function scoreTask(input) {
16
+ const priority = finiteNumber(input.priority, 3);
17
+ const unblockCount = Math.max(0, finiteNumber(input.unblockCount, 0));
18
+ const roleWeight = input.role ? ROLE_WEIGHT[input.role] ?? 0 : 0;
19
+ const criticalityWeight = input.criticality ? CRITICALITY_WEIGHT[input.criticality] ?? 0 : 0;
20
+ const validationWeight = (input.validation ?? []).some((entry) => entry.includes("test-contract") || entry.includes("test-boundary")) ? 8 : 0;
21
+ const queueWeight = finiteNumber(input.queueWeight, 0);
22
+ return unblockCount * 100 + (10 - priority) + roleWeight + criticalityWeight + validationWeight + queueWeight;
23
+ }
24
+ function rankTasks(tasks, scoreInput, idOf) {
25
+ return tasks.map((task) => {
26
+ const input = scoreInput(task);
27
+ return {
28
+ task,
29
+ score: scoreTask(input),
30
+ priority: finiteNumber(input.priority, 3),
31
+ unblockCount: Math.max(0, finiteNumber(input.unblockCount, 0))
32
+ };
33
+ }).toSorted((left, right) => {
34
+ const scoreDelta = right.score - left.score;
35
+ if (scoreDelta !== 0)
36
+ return scoreDelta;
37
+ const unblockDelta = right.unblockCount - left.unblockCount;
38
+ if (unblockDelta !== 0)
39
+ return unblockDelta;
40
+ const priorityDelta = left.priority - right.priority;
41
+ if (priorityDelta !== 0)
42
+ return priorityDelta;
43
+ return idOf(left.task).localeCompare(idOf(right.task));
44
+ });
45
+ }
46
+ export {
47
+ scoreTask,
48
+ rankTasks
49
+ };
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@h-rig/core",
3
- "version": "0.0.6-alpha.15",
3
+ "version": "0.0.6-alpha.151",
4
4
  "type": "module",
5
- "description": "Rig package",
5
+ "description": "Config and plugin composition library for Rig's OMP extension ecosystem; not a product host/runtime.",
6
6
  "license": "UNLICENSED",
7
7
  "files": [
8
8
  "dist",
@@ -10,19 +10,36 @@
10
10
  ],
11
11
  "exports": {
12
12
  ".": {
13
+ "types": "./dist/src/index.d.ts",
13
14
  "import": "./dist/src/index.js"
14
15
  },
15
16
  "./load-config": {
17
+ "types": "./dist/src/load-config.d.ts",
16
18
  "import": "./dist/src/load-config.js"
17
19
  },
18
- "./engineReadModelReducer": {
19
- "import": "./dist/src/engineReadModelReducer.js"
20
+ "./config": {
21
+ "types": "./dist/src/config.d.ts",
22
+ "import": "./dist/src/config.js"
20
23
  },
21
- "./rigSelectors": {
22
- "import": "./dist/src/rigSelectors.js"
23
- },
24
- "./taskGraph": {
24
+ "./task-graph": {
25
+ "types": "./dist/src/taskGraph.d.ts",
25
26
  "import": "./dist/src/taskGraph.js"
27
+ },
28
+ "./stage-resolve": {
29
+ "types": "./dist/src/stageResolve.d.ts",
30
+ "import": "./dist/src/stageResolve.js"
31
+ },
32
+ "./task-score": {
33
+ "types": "./dist/src/taskScore.d.ts",
34
+ "import": "./dist/src/taskScore.js"
35
+ },
36
+ "./dependency-graph": {
37
+ "types": "./dist/src/dependencyGraph.d.ts",
38
+ "import": "./dist/src/dependencyGraph.js"
39
+ },
40
+ "./rollups": {
41
+ "types": "./dist/src/rollups.d.ts",
42
+ "import": "./dist/src/rollups.js"
26
43
  }
27
44
  },
28
45
  "engines": {
@@ -30,8 +47,9 @@
30
47
  },
31
48
  "main": "./dist/src/index.js",
32
49
  "module": "./dist/src/index.js",
50
+ "types": "./dist/src/index.d.ts",
33
51
  "dependencies": {
34
- "@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.15",
35
- "effect": "4.0.0-beta.78"
52
+ "@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.151",
53
+ "effect": "4.0.0-beta.90"
36
54
  }
37
55
  }