@shapeshift-labs/frontier-swarm 0.5.22 → 0.5.24
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.d.ts +76 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +574 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createRunDashboardSnapshot, createRunEdgeEvent, createRunEvent, createRunNodeEvent, defineRunArtifact, defineRunAttempt, defineRunDecision, defineRunEvidence, defineRunLane, defineRunPatch, defineRunTask, defineRunVerification, linkRunNodes, replayRunEvents } from '@shapeshift-labs/frontier-run';
|
|
2
|
+
import { acquireSemanticLease, createSemanticLeaseState, defineSemanticLeaseScope, validateSemanticLeaseFence } from '@shapeshift-labs/frontier-lease';
|
|
2
3
|
export const FRONTIER_SWARM_MANIFEST_KIND = 'frontier.swarm.manifest';
|
|
3
4
|
export const FRONTIER_SWARM_MANIFEST_VERSION = 1;
|
|
4
5
|
export const FRONTIER_SWARM_TASK_KIND = 'frontier.swarm.task';
|
|
@@ -2294,6 +2295,45 @@ export function createRunProjectionFromSwarmRunEvents(events, options = {}) {
|
|
|
2294
2295
|
metadata: toJsonObject(options.metadata)
|
|
2295
2296
|
});
|
|
2296
2297
|
}
|
|
2298
|
+
export function createSwarmRunFromRunProjection(projection, options) {
|
|
2299
|
+
const plan = options.plan;
|
|
2300
|
+
const results = plan.jobs
|
|
2301
|
+
.map((job) => deriveSwarmResultFromRunProjection(projection, job))
|
|
2302
|
+
.filter((result) => !!result);
|
|
2303
|
+
const run = createSwarmRun({
|
|
2304
|
+
id: options.id ?? projection.run.id ?? plan.runId,
|
|
2305
|
+
plan,
|
|
2306
|
+
startedAt: options.startedAt ?? parseRunTimeMs(projection.run.createdAt, plan.createdAt),
|
|
2307
|
+
status: options.status ?? deriveSwarmRunStatusFromResults(plan.jobs, results),
|
|
2308
|
+
events: createSwarmEventsFromRunProjection(projection),
|
|
2309
|
+
results,
|
|
2310
|
+
metadata: mergeSwarmMetadata([
|
|
2311
|
+
projection.run.metadata,
|
|
2312
|
+
options.metadata,
|
|
2313
|
+
{ source: 'frontier-run.projection', frontierRunId: projection.run.id }
|
|
2314
|
+
])
|
|
2315
|
+
});
|
|
2316
|
+
const finishedAt = latestSwarmResultFinishedAt(run.results);
|
|
2317
|
+
return finishedAt === undefined ? run : { ...run, finishedAt };
|
|
2318
|
+
}
|
|
2319
|
+
export function createSwarmQueueOverlayFromRunProjection(projection, options) {
|
|
2320
|
+
const run = createSwarmRunFromRunProjection(projection, {
|
|
2321
|
+
plan: options.plan,
|
|
2322
|
+
id: projection.run.id,
|
|
2323
|
+
startedAt: parseRunTimeMs(projection.run.createdAt, options.plan.createdAt),
|
|
2324
|
+
metadata: options.metadata
|
|
2325
|
+
});
|
|
2326
|
+
return createSwarmQueueOverlay({
|
|
2327
|
+
id: options.id,
|
|
2328
|
+
runId: run.id,
|
|
2329
|
+
results: run.results,
|
|
2330
|
+
generatedAt: options.generatedAt,
|
|
2331
|
+
metadata: mergeSwarmMetadata([
|
|
2332
|
+
options.metadata,
|
|
2333
|
+
{ source: 'frontier-run.projection', frontierRunId: projection.run.id }
|
|
2334
|
+
])
|
|
2335
|
+
});
|
|
2336
|
+
}
|
|
2297
2337
|
export function createRunDashboardFromSwarmRun(input, options = {}) {
|
|
2298
2338
|
const events = isFrontierRunEventList(input) ? input : createRunEventsFromSwarmRun(input, options);
|
|
2299
2339
|
return createRunDashboardSnapshot(createRunProjectionFromSwarmRunEvents(events, options));
|
|
@@ -4075,6 +4115,11 @@ export function createSwarmHierarchicalMergeQueue(input) {
|
|
|
4075
4115
|
const admissionPressure = summarizeSwarmMergeAdmissionPressure(assignments);
|
|
4076
4116
|
const orderedScopes = Array.from(scopes.values()).sort((left, right) => (mergeQueueScopeRank(left.kind) - mergeQueueScopeRank(right.kind)
|
|
4077
4117
|
|| left.id.localeCompare(right.id)));
|
|
4118
|
+
const semanticLeaseScopeMap = createSwarmSemanticLeaseScopeMap(orderedScopes, {
|
|
4119
|
+
rootScopeId,
|
|
4120
|
+
...semanticLeaseScopeOptionsFromMetadata(input.metadata)
|
|
4121
|
+
});
|
|
4122
|
+
const scopesWithSemanticLeases = orderedScopes.map((scope) => attachSemanticLeaseScopeToMergeQueueScope(scope, semanticLeaseScopeMap));
|
|
4078
4123
|
const queueId = input.id ?? 'swarm-hierarchical-merge-queue:' + stableHash([input.index.id, input.admission?.id, orderedScopes, assignments, promotions, generatedAt]);
|
|
4079
4124
|
const linkedAssignments = assignments.map((assignment) => {
|
|
4080
4125
|
if (!coordinatorAgentDrainActionIsTerminal(assignment.action))
|
|
@@ -4084,19 +4129,19 @@ export function createSwarmHierarchicalMergeQueue(input) {
|
|
|
4084
4129
|
terminalDecisionId: hierarchicalQueueTerminalDecisionId(queueId, assignment),
|
|
4085
4130
|
terminalDecisionQueueItemIds: [...assignment.queueItemIds]
|
|
4086
4131
|
};
|
|
4087
|
-
});
|
|
4132
|
+
}).map((assignment) => attachSemanticLeaseScopesToAssignment(assignment, semanticLeaseScopeMap));
|
|
4088
4133
|
const leaseRecords = createHierarchicalQueueLeaseRecords({
|
|
4089
4134
|
queueId,
|
|
4090
4135
|
rootScopeId,
|
|
4091
4136
|
generatedAt,
|
|
4092
|
-
scopes:
|
|
4137
|
+
scopes: scopesWithSemanticLeases,
|
|
4093
4138
|
assignments: linkedAssignments,
|
|
4094
4139
|
promotions,
|
|
4095
4140
|
localLeader: input.localLeader,
|
|
4096
4141
|
localLeaders: input.localLeaders
|
|
4097
4142
|
});
|
|
4098
4143
|
const byLeaseKey = groupJobIdsByMany(linkedAssignments, (assignment) => assignment.requiredLeaseKeys ?? [assignment.leaseKey]);
|
|
4099
|
-
const scopeTree = createHierarchicalQueueScopeTree(
|
|
4144
|
+
const scopeTree = createHierarchicalQueueScopeTree(scopesWithSemanticLeases, rootScopeId);
|
|
4100
4145
|
return {
|
|
4101
4146
|
kind: FRONTIER_SWARM_HIERARCHICAL_MERGE_QUEUE_KIND,
|
|
4102
4147
|
version: FRONTIER_SWARM_HIERARCHICAL_MERGE_QUEUE_VERSION,
|
|
@@ -4106,13 +4151,14 @@ export function createSwarmHierarchicalMergeQueue(input) {
|
|
|
4106
4151
|
generatedAt,
|
|
4107
4152
|
rootScopeId,
|
|
4108
4153
|
scopeTree,
|
|
4109
|
-
scopes:
|
|
4154
|
+
scopes: scopesWithSemanticLeases,
|
|
4110
4155
|
leaseRecords,
|
|
4111
4156
|
assignments: linkedAssignments,
|
|
4112
4157
|
promotions,
|
|
4113
4158
|
byScope,
|
|
4114
4159
|
byLeaseKey,
|
|
4115
4160
|
byAction,
|
|
4161
|
+
semanticLeaseScopes: uniqueSemanticLeaseScopes(Array.from(semanticLeaseScopeMap.values())),
|
|
4116
4162
|
summary: {
|
|
4117
4163
|
scopeCount: orderedScopes.length,
|
|
4118
4164
|
assignmentCount: linkedAssignments.length,
|
|
@@ -4142,6 +4188,7 @@ export function createSwarmCoordinatorAgentDrainWork(input) {
|
|
|
4142
4188
|
title: scope.title,
|
|
4143
4189
|
leaseScope: scope.leaseKey,
|
|
4144
4190
|
leaseKey: scope.leaseKey,
|
|
4191
|
+
...(scope.semanticLeaseScope ? { semanticLeaseScope: scope.semanticLeaseScope } : {}),
|
|
4145
4192
|
...(scope.parentId ? { parentQueueId: scope.parentId } : {}),
|
|
4146
4193
|
...(scope.lane ? { lane: scope.lane } : {}),
|
|
4147
4194
|
changedPaths: [...scope.changedPaths],
|
|
@@ -4171,6 +4218,9 @@ export function createSwarmCoordinatorAgentDrainWork(input) {
|
|
|
4171
4218
|
const requiredLeaseKeys = assignmentRequiredLeaseKeys.length
|
|
4172
4219
|
? [...assignmentRequiredLeaseKeys]
|
|
4173
4220
|
: [leaseScope];
|
|
4221
|
+
const requiredSemanticLeaseScopes = requiredLeaseScopeIds
|
|
4222
|
+
.map((scopeId) => scopesById.get(scopeId)?.semanticLeaseScope)
|
|
4223
|
+
.filter((scope) => Boolean(scope));
|
|
4174
4224
|
const decision = coordinatorAgentDrainDecisionForAction(assignment.action);
|
|
4175
4225
|
const terminal = coordinatorAgentDrainActionIsTerminal(assignment.action);
|
|
4176
4226
|
const parentQueueId = assignment.action === 'promote'
|
|
@@ -4207,6 +4257,7 @@ export function createSwarmCoordinatorAgentDrainWork(input) {
|
|
|
4207
4257
|
conflictingJobIds: [...assignment.conflictingJobIds],
|
|
4208
4258
|
requiredLeaseScopeIds,
|
|
4209
4259
|
requiredLeaseKeys,
|
|
4260
|
+
...(requiredSemanticLeaseScopes.length ? { requiredSemanticLeaseScopes } : {}),
|
|
4210
4261
|
...(assignment.retrySlices?.length ? { retrySlices: cloneMergeQueueRetrySlices(assignment.retrySlices) } : {}),
|
|
4211
4262
|
...(assignment.semanticSliceScopeIds?.length ? { semanticSliceScopeIds: [...assignment.semanticSliceScopeIds] } : {}),
|
|
4212
4263
|
...(assignment.semanticSliceLeaseKeys?.length ? { semanticSliceLeaseKeys: [...assignment.semanticSliceLeaseKeys] } : {}),
|
|
@@ -4231,6 +4282,7 @@ export function createSwarmCoordinatorAgentDrainWork(input) {
|
|
|
4231
4282
|
reasons: [...assignment.reasons],
|
|
4232
4283
|
requiredLeaseScopeIds: [...(assignment.requiredLeaseScopeIds ?? [])],
|
|
4233
4284
|
requiredLeaseKeys: [...(assignment.requiredLeaseKeys ?? [])],
|
|
4285
|
+
...(assignment.requiredSemanticLeaseScopes?.length ? { requiredSemanticLeaseScopes: assignment.requiredSemanticLeaseScopes.map(cloneSemanticLeaseScope) } : {}),
|
|
4234
4286
|
...(assignment.retrySlices?.length ? { retrySlices: cloneMergeQueueRetrySlices(assignment.retrySlices) } : {}),
|
|
4235
4287
|
...(assignment.semanticSliceScopeIds?.length ? { semanticSliceScopeIds: [...assignment.semanticSliceScopeIds] } : {}),
|
|
4236
4288
|
...(assignment.semanticSliceLeaseKeys?.length ? { semanticSliceLeaseKeys: [...assignment.semanticSliceLeaseKeys] } : {}),
|
|
@@ -4257,6 +4309,7 @@ export function createSwarmCoordinatorAgentDrainWork(input) {
|
|
|
4257
4309
|
reasons: [...assignment.reasons],
|
|
4258
4310
|
requiredLeaseScopeIds: [...(assignment.requiredLeaseScopeIds ?? [])],
|
|
4259
4311
|
requiredLeaseKeys: [...(assignment.requiredLeaseKeys ?? [])],
|
|
4312
|
+
...(assignment.requiredSemanticLeaseScopes?.length ? { requiredSemanticLeaseScopes: assignment.requiredSemanticLeaseScopes.map(cloneSemanticLeaseScope) } : {}),
|
|
4260
4313
|
...(assignment.metadata ? { metadata: cloneJsonValue(assignment.metadata) } : {})
|
|
4261
4314
|
}));
|
|
4262
4315
|
const activeAssignments = assignments.filter((assignment) => !coordinatorAgentDrainAssignmentIsTerminal(assignment));
|
|
@@ -4314,6 +4367,120 @@ export function createSwarmCoordinatorAgentDrainWork(input) {
|
|
|
4314
4367
|
...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
|
|
4315
4368
|
};
|
|
4316
4369
|
}
|
|
4370
|
+
export function createSwarmSemanticLeaseScopeForMergeQueueScope(scope, input = {}) {
|
|
4371
|
+
const scopesById = new Map((input.scopes ?? []).map((entry) => [entry.id, entry]));
|
|
4372
|
+
const parentKeys = [];
|
|
4373
|
+
let parentId = scope.parentId;
|
|
4374
|
+
const visited = new Set([scope.id]);
|
|
4375
|
+
while (parentId && !visited.has(parentId)) {
|
|
4376
|
+
const parent = scopesById.get(parentId);
|
|
4377
|
+
if (!parent)
|
|
4378
|
+
break;
|
|
4379
|
+
parentKeys.push(parent.leaseKey);
|
|
4380
|
+
visited.add(parentId);
|
|
4381
|
+
parentId = parent.parentId;
|
|
4382
|
+
}
|
|
4383
|
+
const regionId = scope.changedRegions.length === 1 ? scope.changedRegions[0] : scope.id;
|
|
4384
|
+
const path = scope.changedPaths.length === 1 ? scope.changedPaths[0] : undefined;
|
|
4385
|
+
const metadata = mergeSwarmMetadata([
|
|
4386
|
+
scope.metadata,
|
|
4387
|
+
toJsonObject(input.metadata),
|
|
4388
|
+
{
|
|
4389
|
+
swarmQueueScopeId: scope.id,
|
|
4390
|
+
swarmQueueScopeKind: scope.kind,
|
|
4391
|
+
swarmLeaseKey: scope.leaseKey,
|
|
4392
|
+
changedPaths: scope.changedPaths,
|
|
4393
|
+
changedRegions: scope.changedRegions
|
|
4394
|
+
}
|
|
4395
|
+
]);
|
|
4396
|
+
return defineSemanticLeaseScope({
|
|
4397
|
+
kind: semanticLeaseScopeKindForMergeQueueScope(scope),
|
|
4398
|
+
key: scope.leaseKey,
|
|
4399
|
+
repository: input.repository,
|
|
4400
|
+
packageId: input.packageId,
|
|
4401
|
+
lane: scope.lane,
|
|
4402
|
+
path,
|
|
4403
|
+
regionId,
|
|
4404
|
+
name: semanticLeaseScopeNameForMergeQueueScope(scope),
|
|
4405
|
+
parentKeys,
|
|
4406
|
+
metadata
|
|
4407
|
+
});
|
|
4408
|
+
}
|
|
4409
|
+
export function createSwarmSemanticLeaseScopesForMergeQueue(queue, input = {}) {
|
|
4410
|
+
const existing = queue.scopes
|
|
4411
|
+
.map((scope) => scope.semanticLeaseScope)
|
|
4412
|
+
.filter((scope) => Boolean(scope));
|
|
4413
|
+
if (existing.length)
|
|
4414
|
+
return uniqueSemanticLeaseScopes(existing);
|
|
4415
|
+
return uniqueSemanticLeaseScopes(queue.scopes.map((scope) => (createSwarmSemanticLeaseScopeForMergeQueueScope(scope, {
|
|
4416
|
+
...input,
|
|
4417
|
+
rootScopeId: input.rootScopeId ?? queue.rootScopeId,
|
|
4418
|
+
scopes: queue.scopes
|
|
4419
|
+
}))));
|
|
4420
|
+
}
|
|
4421
|
+
export function createSwarmSemanticLeaseStateForMergeQueue(queue, input = {}) {
|
|
4422
|
+
const semanticLeaseScopes = createSwarmSemanticLeaseScopesForMergeQueue(queue, input);
|
|
4423
|
+
return createSemanticLeaseState({
|
|
4424
|
+
id: input.id ?? `frontier-swarm-merge-queue:${queue.id}`,
|
|
4425
|
+
defaultTtlMs: input.defaultTtlMs,
|
|
4426
|
+
metadata: mergeSwarmMetadata([
|
|
4427
|
+
toJsonObject(input.metadata),
|
|
4428
|
+
{
|
|
4429
|
+
swarmQueueId: queue.id,
|
|
4430
|
+
swarmMergeIndexId: queue.mergeIndexId,
|
|
4431
|
+
rootScopeId: queue.rootScopeId,
|
|
4432
|
+
semanticLeaseScopeCount: semanticLeaseScopes.length,
|
|
4433
|
+
generatedAt: input.now ?? queue.generatedAt
|
|
4434
|
+
}
|
|
4435
|
+
])
|
|
4436
|
+
});
|
|
4437
|
+
}
|
|
4438
|
+
export function acquireSwarmCoordinatorSemanticLease(input) {
|
|
4439
|
+
const scopes = semanticLeaseScopesForCoordinatorAssignment(input.queue, input.assignment, input);
|
|
4440
|
+
const requiredLeaseScopeIds = coordinatorAssignmentRequiredLeaseScopeIds(input.assignment);
|
|
4441
|
+
const requiredLeaseKeys = coordinatorAssignmentRequiredLeaseKeys(input.assignment, scopes);
|
|
4442
|
+
const state = input.state ?? createSwarmSemanticLeaseStateForMergeQueue(input.queue, input);
|
|
4443
|
+
const acquireInput = {
|
|
4444
|
+
ownerId: input.ownerId,
|
|
4445
|
+
holderId: input.holderId,
|
|
4446
|
+
now: input.now,
|
|
4447
|
+
ttlMs: input.ttlMs,
|
|
4448
|
+
purpose: input.purpose ?? `frontier swarm coordinator apply: ${input.assignment.jobId}`,
|
|
4449
|
+
reason: input.reason,
|
|
4450
|
+
scopes,
|
|
4451
|
+
metadata: mergeSwarmMetadata([
|
|
4452
|
+
toJsonObject(input.metadata),
|
|
4453
|
+
{
|
|
4454
|
+
swarmQueueId: input.queue.id,
|
|
4455
|
+
swarmJobId: input.assignment.jobId,
|
|
4456
|
+
swarmTaskId: input.assignment.taskId,
|
|
4457
|
+
requiredLeaseScopeIds,
|
|
4458
|
+
requiredLeaseKeys
|
|
4459
|
+
}
|
|
4460
|
+
])
|
|
4461
|
+
};
|
|
4462
|
+
const mutation = acquireSemanticLease(state, acquireInput);
|
|
4463
|
+
return {
|
|
4464
|
+
state: mutation.state,
|
|
4465
|
+
mutation,
|
|
4466
|
+
scopes,
|
|
4467
|
+
requiredLeaseScopeIds,
|
|
4468
|
+
requiredLeaseKeys,
|
|
4469
|
+
...(mutation.lease ? { lease: mutation.lease } : {})
|
|
4470
|
+
};
|
|
4471
|
+
}
|
|
4472
|
+
export function validateSwarmCoordinatorSemanticLeaseFence(input) {
|
|
4473
|
+
const requiredScopes = input.requiredSemanticLeaseScopes?.length
|
|
4474
|
+
? input.requiredSemanticLeaseScopes.map(cloneSemanticLeaseScope)
|
|
4475
|
+
: (input.assignment.requiredSemanticLeaseScopes ?? []).map(cloneSemanticLeaseScope);
|
|
4476
|
+
return validateSemanticLeaseFence(input.state, {
|
|
4477
|
+
leaseId: input.leaseId ?? input.lease?.id ?? '',
|
|
4478
|
+
token: input.token,
|
|
4479
|
+
fencingToken: input.fencingToken,
|
|
4480
|
+
now: input.now,
|
|
4481
|
+
scopes: requiredScopes.length ? requiredScopes : undefined
|
|
4482
|
+
});
|
|
4483
|
+
}
|
|
4317
4484
|
export function summarizeSwarmCoordinatorAgentDrainWork(work) {
|
|
4318
4485
|
const activeAssignments = work.assignments.filter((assignment) => !coordinatorAgentDrainAssignmentIsTerminal(assignment));
|
|
4319
4486
|
const terminalAssignments = work.assignments.filter((assignment) => coordinatorAgentDrainAssignmentIsTerminal(assignment));
|
|
@@ -5888,6 +6055,154 @@ function ensureMergeQueueScope(scopes, input) {
|
|
|
5888
6055
|
scopes.set(scope.id, scope);
|
|
5889
6056
|
return scope;
|
|
5890
6057
|
}
|
|
6058
|
+
function createSwarmSemanticLeaseScopeMap(scopes, input = {}) {
|
|
6059
|
+
const out = new Map();
|
|
6060
|
+
for (const scope of scopes) {
|
|
6061
|
+
out.set(scope.id, createSwarmSemanticLeaseScopeForMergeQueueScope(scope, { ...input, scopes }));
|
|
6062
|
+
}
|
|
6063
|
+
return out;
|
|
6064
|
+
}
|
|
6065
|
+
function attachSemanticLeaseScopeToMergeQueueScope(scope, semanticLeaseScopes) {
|
|
6066
|
+
const semanticLeaseScope = semanticLeaseScopes.get(scope.id);
|
|
6067
|
+
return {
|
|
6068
|
+
...scope,
|
|
6069
|
+
...(semanticLeaseScope ? { semanticLeaseScope: cloneSemanticLeaseScope(semanticLeaseScope) } : {})
|
|
6070
|
+
};
|
|
6071
|
+
}
|
|
6072
|
+
function attachSemanticLeaseScopesToAssignment(assignment, semanticLeaseScopes) {
|
|
6073
|
+
const requiredLeaseScopeIds = assignment.requiredLeaseScopeIds?.length ? assignment.requiredLeaseScopeIds : [assignment.scopeId];
|
|
6074
|
+
const requiredSemanticLeaseScopes = requiredLeaseScopeIds
|
|
6075
|
+
.map((scopeId) => semanticLeaseScopes.get(scopeId))
|
|
6076
|
+
.filter((scope) => Boolean(scope))
|
|
6077
|
+
.map(cloneSemanticLeaseScope);
|
|
6078
|
+
const retrySlices = assignment.retrySlices?.map((slice) => {
|
|
6079
|
+
const requiredScopeIds = slice.requiredLeaseScopeIds?.length ? slice.requiredLeaseScopeIds : [slice.scopeId];
|
|
6080
|
+
const requiredScopes = requiredScopeIds
|
|
6081
|
+
.map((scopeId) => semanticLeaseScopes.get(scopeId))
|
|
6082
|
+
.filter((scope) => Boolean(scope))
|
|
6083
|
+
.map(cloneSemanticLeaseScope);
|
|
6084
|
+
const semanticLeaseScope = semanticLeaseScopes.get(slice.scopeId);
|
|
6085
|
+
return {
|
|
6086
|
+
...slice,
|
|
6087
|
+
...(semanticLeaseScope ? { semanticLeaseScope: cloneSemanticLeaseScope(semanticLeaseScope) } : {}),
|
|
6088
|
+
...(requiredScopes.length ? { requiredSemanticLeaseScopes: requiredScopes } : {})
|
|
6089
|
+
};
|
|
6090
|
+
});
|
|
6091
|
+
return {
|
|
6092
|
+
...assignment,
|
|
6093
|
+
...(requiredSemanticLeaseScopes.length ? { requiredSemanticLeaseScopes } : {}),
|
|
6094
|
+
...(retrySlices?.length ? { retrySlices } : {})
|
|
6095
|
+
};
|
|
6096
|
+
}
|
|
6097
|
+
function semanticLeaseScopesForCoordinatorAssignment(queue, assignment, input = {}) {
|
|
6098
|
+
if (assignment.requiredSemanticLeaseScopes?.length) {
|
|
6099
|
+
return uniqueSemanticLeaseScopes(assignment.requiredSemanticLeaseScopes.map(cloneSemanticLeaseScope));
|
|
6100
|
+
}
|
|
6101
|
+
const queueScopes = new Map(queue.scopes.map((scope) => [scope.id, scope]));
|
|
6102
|
+
const semanticByKey = new Map(createSwarmSemanticLeaseScopesForMergeQueue(queue, input).map((scope) => [scope.key, scope]));
|
|
6103
|
+
const fromIds = coordinatorAssignmentRequiredLeaseScopeIds(assignment)
|
|
6104
|
+
.map((scopeId) => queueScopes.get(scopeId)?.semanticLeaseScope)
|
|
6105
|
+
.filter((scope) => Boolean(scope));
|
|
6106
|
+
if (fromIds.length)
|
|
6107
|
+
return uniqueSemanticLeaseScopes(fromIds);
|
|
6108
|
+
const fromKeys = coordinatorAssignmentRequiredLeaseKeys(assignment, [])
|
|
6109
|
+
.map((key) => semanticByKey.get(key))
|
|
6110
|
+
.filter((scope) => Boolean(scope));
|
|
6111
|
+
if (fromKeys.length)
|
|
6112
|
+
return uniqueSemanticLeaseScopes(fromKeys);
|
|
6113
|
+
const fallbackKey = 'leaseScope' in assignment ? assignment.leaseScope : assignment.leaseKey;
|
|
6114
|
+
return [defineSemanticLeaseScope({
|
|
6115
|
+
kind: 'custom',
|
|
6116
|
+
key: fallbackKey,
|
|
6117
|
+
repository: input.repository,
|
|
6118
|
+
packageId: input.packageId,
|
|
6119
|
+
metadata: mergeSwarmMetadata([
|
|
6120
|
+
toJsonObject(input.metadata),
|
|
6121
|
+
{
|
|
6122
|
+
swarmQueueId: queue.id,
|
|
6123
|
+
swarmJobId: assignment.jobId,
|
|
6124
|
+
fallbackLeaseKey: fallbackKey
|
|
6125
|
+
}
|
|
6126
|
+
])
|
|
6127
|
+
})];
|
|
6128
|
+
}
|
|
6129
|
+
function coordinatorAssignmentRequiredLeaseScopeIds(assignment) {
|
|
6130
|
+
const explicit = assignment.requiredLeaseScopeIds ?? [];
|
|
6131
|
+
if (explicit.length)
|
|
6132
|
+
return uniqueStrings(explicit);
|
|
6133
|
+
return uniqueStrings(['queueId' in assignment ? assignment.queueId : assignment.scopeId]);
|
|
6134
|
+
}
|
|
6135
|
+
function coordinatorAssignmentRequiredLeaseKeys(assignment, scopes) {
|
|
6136
|
+
const explicit = assignment.requiredLeaseKeys ?? [];
|
|
6137
|
+
if (explicit.length)
|
|
6138
|
+
return uniqueStrings(explicit);
|
|
6139
|
+
if (scopes.length)
|
|
6140
|
+
return uniqueStrings(scopes.map((scope) => scope.key));
|
|
6141
|
+
return uniqueStrings(['leaseScope' in assignment ? assignment.leaseScope : assignment.leaseKey]);
|
|
6142
|
+
}
|
|
6143
|
+
function semanticLeaseScopeOptionsFromMetadata(metadata) {
|
|
6144
|
+
const object = toJsonObject(metadata);
|
|
6145
|
+
if (!object)
|
|
6146
|
+
return {};
|
|
6147
|
+
return {
|
|
6148
|
+
...(typeof object.repository === 'string' ? { repository: object.repository } : {}),
|
|
6149
|
+
...(typeof object.packageId === 'string' ? { packageId: object.packageId } : {})
|
|
6150
|
+
};
|
|
6151
|
+
}
|
|
6152
|
+
function semanticLeaseScopeKindForMergeQueueScope(scope) {
|
|
6153
|
+
if (scope.kind === 'root')
|
|
6154
|
+
return 'repository';
|
|
6155
|
+
if (scope.kind === 'path')
|
|
6156
|
+
return 'path';
|
|
6157
|
+
if (scope.kind === 'lane')
|
|
6158
|
+
return 'lane';
|
|
6159
|
+
if (scope.kind === 'semantic-region' || scope.kind === 'semantic')
|
|
6160
|
+
return semanticLeaseScopeKindForRegion(scope.changedRegions[0]);
|
|
6161
|
+
if (scope.kind === 'package')
|
|
6162
|
+
return 'package';
|
|
6163
|
+
return 'custom';
|
|
6164
|
+
}
|
|
6165
|
+
function semanticLeaseScopeKindForRegion(region) {
|
|
6166
|
+
if (!region)
|
|
6167
|
+
return 'semantic-region';
|
|
6168
|
+
const normalized = region.toLowerCase();
|
|
6169
|
+
if (normalized.includes(FRONTIER_SWARM_SEMANTIC_OWNERSHIP_EXPORT_STABLE_KEY_KIND) || normalized.includes('named-export') || normalized.includes('default-export'))
|
|
6170
|
+
return 'export';
|
|
6171
|
+
if (normalized.includes(FRONTIER_SWARM_SEMANTIC_OWNERSHIP_TYPE_STABLE_KEY_KIND) || normalized.includes('interface') || normalized.includes('type-alias'))
|
|
6172
|
+
return 'type';
|
|
6173
|
+
if (normalized.includes(FRONTIER_SWARM_SEMANTIC_OWNERSHIP_CLI_COMMAND_STABLE_KEY_KIND))
|
|
6174
|
+
return 'cli-command';
|
|
6175
|
+
if (normalized.includes(FRONTIER_SWARM_SEMANTIC_OWNERSHIP_DOCS_SECTION_STABLE_KEY_KIND))
|
|
6176
|
+
return 'docs-section';
|
|
6177
|
+
if (normalized.includes(FRONTIER_SWARM_SEMANTIC_OWNERSHIP_FIXTURE_FAMILY_STABLE_KEY_KIND))
|
|
6178
|
+
return 'test-fixture';
|
|
6179
|
+
if (normalized.includes(FRONTIER_SWARM_SEMANTIC_OWNERSHIP_TEST_CASE_STABLE_KEY_KIND))
|
|
6180
|
+
return 'test-fixture';
|
|
6181
|
+
if (normalized.includes('function'))
|
|
6182
|
+
return 'function';
|
|
6183
|
+
if (normalized.includes('class'))
|
|
6184
|
+
return 'class';
|
|
6185
|
+
if (normalized.includes('member'))
|
|
6186
|
+
return 'member';
|
|
6187
|
+
return 'semantic-region';
|
|
6188
|
+
}
|
|
6189
|
+
function semanticLeaseScopeNameForMergeQueueScope(scope) {
|
|
6190
|
+
const region = scope.changedRegions[0];
|
|
6191
|
+
if (region)
|
|
6192
|
+
return region.split(':').filter(Boolean).at(-1);
|
|
6193
|
+
if (scope.changedPaths.length === 1)
|
|
6194
|
+
return scope.changedPaths[0].split('/').at(-1);
|
|
6195
|
+
return scope.title || scope.id;
|
|
6196
|
+
}
|
|
6197
|
+
function uniqueSemanticLeaseScopes(scopes) {
|
|
6198
|
+
const out = new Map();
|
|
6199
|
+
for (const scope of scopes)
|
|
6200
|
+
out.set(scope.key, cloneSemanticLeaseScope(scope));
|
|
6201
|
+
return Array.from(out.values()).sort((left, right) => left.key.localeCompare(right.key));
|
|
6202
|
+
}
|
|
6203
|
+
function cloneSemanticLeaseScope(scope) {
|
|
6204
|
+
return cloneJsonValue(scope);
|
|
6205
|
+
}
|
|
5891
6206
|
function mergeQueueRootLeaseKey(rootScopeId) {
|
|
5892
6207
|
return `merge:root:${rootScopeId}`;
|
|
5893
6208
|
}
|
|
@@ -6002,6 +6317,8 @@ function cloneMergeQueueRetrySlices(slices) {
|
|
|
6002
6317
|
leaseKey: slice.leaseKey,
|
|
6003
6318
|
...(slice.requiredLeaseScopeIds?.length ? { requiredLeaseScopeIds: [...slice.requiredLeaseScopeIds] } : {}),
|
|
6004
6319
|
...(slice.requiredLeaseKeys?.length ? { requiredLeaseKeys: [...slice.requiredLeaseKeys] } : {}),
|
|
6320
|
+
...(slice.semanticLeaseScope ? { semanticLeaseScope: cloneSemanticLeaseScope(slice.semanticLeaseScope) } : {}),
|
|
6321
|
+
...(slice.requiredSemanticLeaseScopes?.length ? { requiredSemanticLeaseScopes: slice.requiredSemanticLeaseScopes.map(cloneSemanticLeaseScope) } : {}),
|
|
6005
6322
|
...(slice.lane ? { lane: slice.lane } : {}),
|
|
6006
6323
|
changedPaths: [...slice.changedPaths],
|
|
6007
6324
|
changedRegions: [...slice.changedRegions],
|
|
@@ -6051,6 +6368,7 @@ function createHierarchicalQueueLeaseRecords(input) {
|
|
|
6051
6368
|
...(scope.lane ? { lane: scope.lane } : {}),
|
|
6052
6369
|
title: scope.title,
|
|
6053
6370
|
leaseKey: scope.leaseKey,
|
|
6371
|
+
...(scope.semanticLeaseScope ? { semanticLeaseScope: cloneSemanticLeaseScope(scope.semanticLeaseScope) } : {}),
|
|
6054
6372
|
...(hierarchicalQueueLocalLeaderForScope(input, scope) ? { localLeader: hierarchicalQueueLocalLeaderForScope(input, scope) } : {}),
|
|
6055
6373
|
promotion: {
|
|
6056
6374
|
state: promotionState,
|
|
@@ -10173,6 +10491,258 @@ function swarmRunEventTime(options, fallback) {
|
|
|
10173
10491
|
const value = options.now ?? fallback;
|
|
10174
10492
|
return new Date(Number.isFinite(value) ? value : Date.now()).toISOString();
|
|
10175
10493
|
}
|
|
10494
|
+
function createSwarmEventsFromRunProjection(projection) {
|
|
10495
|
+
return projection.events.map((event) => ({
|
|
10496
|
+
id: `frontier-run:${event.id}`,
|
|
10497
|
+
type: event.type,
|
|
10498
|
+
runId: projection.run.id,
|
|
10499
|
+
at: parseRunTimeMs(event.time, Date.now()),
|
|
10500
|
+
message: event.type,
|
|
10501
|
+
data: event.payload,
|
|
10502
|
+
metadata: pruneUndefinedJsonObject({
|
|
10503
|
+
source: 'frontier-run.event',
|
|
10504
|
+
eventId: event.id,
|
|
10505
|
+
actorId: event.actorId,
|
|
10506
|
+
actorSeq: event.actorSeq,
|
|
10507
|
+
parents: event.parents
|
|
10508
|
+
})
|
|
10509
|
+
}));
|
|
10510
|
+
}
|
|
10511
|
+
function deriveSwarmResultFromRunProjection(projection, job) {
|
|
10512
|
+
const graph = projection.run.graph;
|
|
10513
|
+
const nodes = Object.values(graph.nodes);
|
|
10514
|
+
const edges = Object.values(graph.edges);
|
|
10515
|
+
const attempt = nodes.find((node) => node.kind === 'attempt' && runNodeMatchesJob(node, job));
|
|
10516
|
+
const attemptId = attempt?.id ?? swarmRunAttemptNodeId(job.id);
|
|
10517
|
+
const patchNodes = nodes.filter((node) => node.kind === 'patch' && runNodeMatchesJob(node, job));
|
|
10518
|
+
const patchIds = new Set(patchNodes.map((node) => node.id));
|
|
10519
|
+
const verificationNodeIds = new Set(edges
|
|
10520
|
+
.filter((edge) => edge.from === attemptId && edge.type === 'verified-by')
|
|
10521
|
+
.map((edge) => edge.to));
|
|
10522
|
+
const verificationNodes = nodes.filter((node) => (node.kind === 'verification'
|
|
10523
|
+
&& (runNodeMatchesJob(node, job) || verificationNodeIds.has(node.id))));
|
|
10524
|
+
const decisionNodes = nodes.filter((node) => (node.kind === 'decision'
|
|
10525
|
+
&& (runNodeMatchesJob(node, job) || runDecisionSubjectsJob(node, job, patchIds))));
|
|
10526
|
+
const evidenceNodes = nodes.filter((node) => (node.kind === 'evidence'
|
|
10527
|
+
&& (runNodeMatchesJob(node, job) || runEvidenceLinkedToJob(node, attemptId, patchIds, edges))));
|
|
10528
|
+
const hasProjectionResult = !!attempt || patchNodes.length > 0 || verificationNodes.length > 0 || evidenceNodes.length > 0 || decisionNodes.length > 0;
|
|
10529
|
+
if (!hasProjectionResult)
|
|
10530
|
+
return undefined;
|
|
10531
|
+
const attemptMetadata = attempt ? runNodeMetadata(attempt) : undefined;
|
|
10532
|
+
const patchMetadata = patchNodes.map(runNodeMetadata);
|
|
10533
|
+
const decisionMetadata = decisionNodes.map(runNodeMetadata);
|
|
10534
|
+
const mergedMetadata = mergeSwarmMetadata([
|
|
10535
|
+
attemptMetadata,
|
|
10536
|
+
...patchMetadata,
|
|
10537
|
+
...decisionMetadata,
|
|
10538
|
+
{ source: 'frontier-run.projection', frontierRunId: projection.run.id }
|
|
10539
|
+
]);
|
|
10540
|
+
const changedPaths = uniqueStrings(patchNodes.flatMap((node) => node.kind === 'patch' ? node.changedPaths : []));
|
|
10541
|
+
const changedRegions = uniqueStrings(patchMetadata.flatMap((metadata) => jsonStringArray(metadata?.changedRegions)));
|
|
10542
|
+
const ownershipViolations = uniqueStrings(patchMetadata.flatMap((metadata) => jsonStringArray(metadata?.ownershipViolations)));
|
|
10543
|
+
const evidencePaths = deriveRunEvidencePaths(nodes, edges, attemptId, patchIds, evidenceNodes);
|
|
10544
|
+
const patchPath = deriveRunPatchPath(nodes, edges, patchNodes);
|
|
10545
|
+
const status = deriveSwarmResultStatusFromRunAttempt(attempt, verificationNodes, patchNodes);
|
|
10546
|
+
const error = status === 'failed' || status === 'blocked'
|
|
10547
|
+
? (runMetadataString(attemptMetadata, 'reason') ?? (attempt?.kind === 'attempt' ? attempt.reason : undefined))
|
|
10548
|
+
: undefined;
|
|
10549
|
+
const startedAt = attempt?.kind === 'attempt' ? parseOptionalRunTimeMs(attempt.startedAt) : undefined;
|
|
10550
|
+
const finishedAt = attempt?.kind === 'attempt' ? parseOptionalRunTimeMs(attempt.endedAt) : undefined;
|
|
10551
|
+
return {
|
|
10552
|
+
jobId: job.id,
|
|
10553
|
+
status,
|
|
10554
|
+
mergeReadiness: readSwarmMergeReadiness(attemptMetadata, patchMetadata, decisionMetadata),
|
|
10555
|
+
...(startedAt !== undefined ? { startedAt } : {}),
|
|
10556
|
+
...(finishedAt !== undefined ? { finishedAt } : {}),
|
|
10557
|
+
...(runMetadataNumber(attemptMetadata, 'exitCode') !== undefined ? { exitCode: runMetadataNumber(attemptMetadata, 'exitCode') } : {}),
|
|
10558
|
+
...(runMetadataString(attemptMetadata, 'signal') ? { signal: runMetadataString(attemptMetadata, 'signal') } : {}),
|
|
10559
|
+
changedPaths,
|
|
10560
|
+
changedRegions,
|
|
10561
|
+
ownershipViolations,
|
|
10562
|
+
evidencePaths,
|
|
10563
|
+
...(patchPath ? { patchPath } : {}),
|
|
10564
|
+
queueItemIds: uniqueStrings([
|
|
10565
|
+
...jsonStringArray(attemptMetadata?.queueItemIds),
|
|
10566
|
+
...patchMetadata.flatMap((metadata) => jsonStringArray(metadata?.queueItemIds)),
|
|
10567
|
+
...decisionMetadata.flatMap((metadata) => jsonStringArray(metadata?.queueItemIds))
|
|
10568
|
+
]),
|
|
10569
|
+
riskLevel: readSwarmRiskLevel(attemptMetadata, patchMetadata),
|
|
10570
|
+
mergeDisposition: readSwarmMergeDisposition(attemptMetadata, patchMetadata, decisionMetadata),
|
|
10571
|
+
verification: verificationNodes.map((node) => runVerificationNodeToSwarmResult(node)),
|
|
10572
|
+
...(attemptMetadata?.semanticImport !== undefined ? { semanticImport: cloneJsonValue(attemptMetadata.semanticImport) } : {}),
|
|
10573
|
+
...(patchNodes[0]?.kind === 'patch' && patchNodes[0].summary ? { lastMessage: patchNodes[0].summary } : {}),
|
|
10574
|
+
...(error ? { error } : {}),
|
|
10575
|
+
...(mergedMetadata ? { metadata: mergedMetadata } : {})
|
|
10576
|
+
};
|
|
10577
|
+
}
|
|
10578
|
+
function runNodeMatchesJob(node, job) {
|
|
10579
|
+
const metadata = runNodeMetadata(node);
|
|
10580
|
+
const metadataJobId = runMetadataString(metadata, 'jobId');
|
|
10581
|
+
if (metadataJobId === job.id)
|
|
10582
|
+
return true;
|
|
10583
|
+
if (node.kind === 'attempt' && node.taskId === swarmRunTaskNodeId(job.taskId))
|
|
10584
|
+
return true;
|
|
10585
|
+
if (node.kind === 'task' && node.id === swarmRunTaskNodeId(job.taskId))
|
|
10586
|
+
return true;
|
|
10587
|
+
if ((node.kind === 'attempt' || node.kind === 'patch') && stripRunNodePrefix(node.id, node.kind) === job.id)
|
|
10588
|
+
return true;
|
|
10589
|
+
return false;
|
|
10590
|
+
}
|
|
10591
|
+
function runDecisionSubjectsJob(node, job, patchIds) {
|
|
10592
|
+
if (node.kind !== 'decision')
|
|
10593
|
+
return false;
|
|
10594
|
+
const taskId = swarmRunTaskNodeId(job.taskId);
|
|
10595
|
+
return node.subjectIds.some((subjectId) => subjectId === taskId || patchIds.has(subjectId));
|
|
10596
|
+
}
|
|
10597
|
+
function runEvidenceLinkedToJob(node, attemptId, patchIds, edges) {
|
|
10598
|
+
if (node.kind !== 'evidence')
|
|
10599
|
+
return false;
|
|
10600
|
+
return edges.some((edge) => edge.to === node.id && (edge.from === attemptId || patchIds.has(edge.from)));
|
|
10601
|
+
}
|
|
10602
|
+
function deriveRunEvidencePaths(nodes, edges, attemptId, patchIds, evidenceNodes) {
|
|
10603
|
+
const artifacts = new Map(nodes
|
|
10604
|
+
.filter((node) => node.kind === 'artifact')
|
|
10605
|
+
.map((node) => [node.id, node]));
|
|
10606
|
+
const subjectIds = new Set([attemptId, ...patchIds]);
|
|
10607
|
+
const linkedArtifactIds = edges
|
|
10608
|
+
.filter((edge) => edge.type === 'produces-artifact' && subjectIds.has(edge.from))
|
|
10609
|
+
.map((edge) => edge.to);
|
|
10610
|
+
return uniqueStrings([
|
|
10611
|
+
...evidenceNodes.flatMap((node) => node.kind === 'evidence' ? (node.artifactIds ?? []) : [])
|
|
10612
|
+
.map((artifactId) => runArtifactPath(artifacts.get(artifactId)))
|
|
10613
|
+
.filter((file) => !!file),
|
|
10614
|
+
...linkedArtifactIds
|
|
10615
|
+
.map((artifactId) => artifacts.get(artifactId))
|
|
10616
|
+
.filter((node) => !!node && node.kind === 'artifact' && node.artifactType !== 'patch')
|
|
10617
|
+
.map(runArtifactPath)
|
|
10618
|
+
.filter((file) => !!file)
|
|
10619
|
+
]);
|
|
10620
|
+
}
|
|
10621
|
+
function deriveRunPatchPath(nodes, edges, patchNodes) {
|
|
10622
|
+
const artifacts = new Map(nodes
|
|
10623
|
+
.filter((node) => node.kind === 'artifact')
|
|
10624
|
+
.map((node) => [node.id, node]));
|
|
10625
|
+
for (const patch of patchNodes) {
|
|
10626
|
+
if (patch.kind !== 'patch')
|
|
10627
|
+
continue;
|
|
10628
|
+
const metadataPatchPath = runMetadataString(runNodeMetadata(patch), 'patchPath');
|
|
10629
|
+
if (metadataPatchPath)
|
|
10630
|
+
return metadataPatchPath;
|
|
10631
|
+
if (patch.artifactId) {
|
|
10632
|
+
const artifactPath = runArtifactPath(artifacts.get(patch.artifactId));
|
|
10633
|
+
if (artifactPath)
|
|
10634
|
+
return artifactPath;
|
|
10635
|
+
}
|
|
10636
|
+
const attached = edges
|
|
10637
|
+
.filter((edge) => edge.from === patch.id && edge.type === 'produces-artifact')
|
|
10638
|
+
.map((edge) => artifacts.get(edge.to))
|
|
10639
|
+
.find((node) => node?.kind === 'artifact' && node.artifactType === 'patch');
|
|
10640
|
+
const attachedPath = runArtifactPath(attached);
|
|
10641
|
+
if (attachedPath)
|
|
10642
|
+
return attachedPath;
|
|
10643
|
+
}
|
|
10644
|
+
return undefined;
|
|
10645
|
+
}
|
|
10646
|
+
function runVerificationNodeToSwarmResult(node) {
|
|
10647
|
+
if (node.kind !== 'verification')
|
|
10648
|
+
return { name: node.id };
|
|
10649
|
+
const metadata = runNodeMetadata(node);
|
|
10650
|
+
const command = node.command ? [node.command, ...(node.args ?? [])] : [];
|
|
10651
|
+
return {
|
|
10652
|
+
name: node.title ?? node.id,
|
|
10653
|
+
command,
|
|
10654
|
+
...(command.length ? { commandLine: command.join(' ') } : {}),
|
|
10655
|
+
...(node.cwd ? { cwd: node.cwd } : {}),
|
|
10656
|
+
...(node.exitCode !== undefined ? { status: node.exitCode } : { status: node.status === 'passed' ? 0 : node.status === 'failed' ? 1 : undefined }),
|
|
10657
|
+
...(runMetadataNumber(metadata, 'durationMs') !== undefined ? { durationMs: runMetadataNumber(metadata, 'durationMs') } : {}),
|
|
10658
|
+
stdoutTail: jsonStringArray(metadata?.stdoutTail),
|
|
10659
|
+
stderrTail: jsonStringArray(metadata?.stderrTail),
|
|
10660
|
+
required: node.required ?? true,
|
|
10661
|
+
...(runMetadataString(metadata, 'category') ? { category: runMetadataString(metadata, 'category') } : {}),
|
|
10662
|
+
...(metadata ? { metadata } : {})
|
|
10663
|
+
};
|
|
10664
|
+
}
|
|
10665
|
+
function deriveSwarmResultStatusFromRunAttempt(attempt, verificationNodes, patchNodes) {
|
|
10666
|
+
if (attempt?.kind === 'attempt') {
|
|
10667
|
+
if (attempt.status === 'completed')
|
|
10668
|
+
return 'completed';
|
|
10669
|
+
if (attempt.status === 'running')
|
|
10670
|
+
return 'running';
|
|
10671
|
+
if (attempt.status === 'queued')
|
|
10672
|
+
return 'scheduled';
|
|
10673
|
+
if (attempt.status === 'cancelled' || attempt.status === 'timed-out' || attempt.status === 'failed')
|
|
10674
|
+
return 'failed';
|
|
10675
|
+
}
|
|
10676
|
+
if (verificationNodes.some((node) => node.kind === 'verification' && node.required !== false && node.status === 'failed'))
|
|
10677
|
+
return 'failed';
|
|
10678
|
+
if (patchNodes.length > 0 || verificationNodes.length > 0)
|
|
10679
|
+
return 'completed';
|
|
10680
|
+
return 'planned';
|
|
10681
|
+
}
|
|
10682
|
+
function deriveSwarmRunStatusFromResults(jobs, results) {
|
|
10683
|
+
if (results.length === 0)
|
|
10684
|
+
return 'planned';
|
|
10685
|
+
if (results.some((result) => result.status === 'failed'))
|
|
10686
|
+
return 'failed';
|
|
10687
|
+
if (results.some((result) => result.status === 'blocked'))
|
|
10688
|
+
return 'blocked';
|
|
10689
|
+
if (results.some((result) => result.status === 'running'))
|
|
10690
|
+
return 'running';
|
|
10691
|
+
if (results.length >= jobs.length && results.every((result) => result.status === 'completed' || result.status === 'verified'))
|
|
10692
|
+
return 'completed';
|
|
10693
|
+
return 'running';
|
|
10694
|
+
}
|
|
10695
|
+
function latestSwarmResultFinishedAt(results) {
|
|
10696
|
+
const finishedAt = results
|
|
10697
|
+
.map((result) => result.finishedAt)
|
|
10698
|
+
.filter((value) => value !== undefined);
|
|
10699
|
+
return finishedAt.length ? Math.max(...finishedAt) : undefined;
|
|
10700
|
+
}
|
|
10701
|
+
function readSwarmMergeReadiness(attemptMetadata, patchMetadata, decisionMetadata) {
|
|
10702
|
+
return readFirstMetadataString('mergeReadiness', [attemptMetadata, ...patchMetadata, ...decisionMetadata]);
|
|
10703
|
+
}
|
|
10704
|
+
function readSwarmMergeDisposition(attemptMetadata, patchMetadata, decisionMetadata) {
|
|
10705
|
+
return readFirstMetadataString('mergeDisposition', [attemptMetadata])
|
|
10706
|
+
?? readFirstMetadataString('disposition', [...patchMetadata, ...decisionMetadata]);
|
|
10707
|
+
}
|
|
10708
|
+
function readSwarmRiskLevel(attemptMetadata, patchMetadata) {
|
|
10709
|
+
return readFirstMetadataString('riskLevel', [attemptMetadata, ...patchMetadata]);
|
|
10710
|
+
}
|
|
10711
|
+
function readFirstMetadataString(key, metadata) {
|
|
10712
|
+
for (const entry of metadata) {
|
|
10713
|
+
const value = runMetadataString(entry, key);
|
|
10714
|
+
if (value)
|
|
10715
|
+
return value;
|
|
10716
|
+
}
|
|
10717
|
+
return undefined;
|
|
10718
|
+
}
|
|
10719
|
+
function runNodeMetadata(node) {
|
|
10720
|
+
return toJsonObject(node.metadata);
|
|
10721
|
+
}
|
|
10722
|
+
function runMetadataString(metadata, key) {
|
|
10723
|
+
const value = metadata?.[key];
|
|
10724
|
+
return typeof value === 'string' && value ? value : undefined;
|
|
10725
|
+
}
|
|
10726
|
+
function runMetadataNumber(metadata, key) {
|
|
10727
|
+
const value = metadata?.[key];
|
|
10728
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
10729
|
+
}
|
|
10730
|
+
function runArtifactPath(node) {
|
|
10731
|
+
if (!node || node.kind !== 'artifact')
|
|
10732
|
+
return undefined;
|
|
10733
|
+
return node.path ?? node.uri ?? node.title;
|
|
10734
|
+
}
|
|
10735
|
+
function stripRunNodePrefix(id, prefix) {
|
|
10736
|
+
return id.startsWith(`${prefix}:`) ? id.slice(prefix.length + 1) : undefined;
|
|
10737
|
+
}
|
|
10738
|
+
function parseRunTimeMs(value, fallback) {
|
|
10739
|
+
const parsed = value ? Date.parse(value) : NaN;
|
|
10740
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
10741
|
+
}
|
|
10742
|
+
function parseOptionalRunTimeMs(value) {
|
|
10743
|
+
const parsed = value ? Date.parse(value) : NaN;
|
|
10744
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
10745
|
+
}
|
|
10176
10746
|
function isFrontierRunEventList(input) {
|
|
10177
10747
|
return Array.isArray(input);
|
|
10178
10748
|
}
|