@shapeshift-labs/frontier-swarm 0.4.0 → 0.5.0

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.js CHANGED
@@ -46,6 +46,44 @@ export const FRONTIER_SWARM_CONTEXT_PACK_KIND = 'frontier.swarm.context-pack';
46
46
  export const FRONTIER_SWARM_CONTEXT_PACK_VERSION = 1;
47
47
  export const FRONTIER_SWARM_ORACLE_CORPUS_KIND = 'frontier.swarm.oracle-corpus';
48
48
  export const FRONTIER_SWARM_ORACLE_CORPUS_VERSION = 1;
49
+ export const FRONTIER_SWARM_REPLAY_BUNDLE_KIND = 'frontier.swarm.replay-bundle';
50
+ export const FRONTIER_SWARM_REPLAY_BUNDLE_VERSION = 1;
51
+ export const FRONTIER_SWARM_PARITY_ORACLE_KIND = 'frontier.swarm.parity-oracle';
52
+ export const FRONTIER_SWARM_PARITY_ORACLE_VERSION = 1;
53
+ export const FRONTIER_SWARM_DIVERGENCE_REPORT_KIND = 'frontier.swarm.divergence-report';
54
+ export const FRONTIER_SWARM_DIVERGENCE_REPORT_VERSION = 1;
55
+ export const FRONTIER_SWARM_OBSERVABILITY_POINT_KIND = 'frontier.swarm.observability-point';
56
+ export const FRONTIER_SWARM_OBSERVABILITY_POINT_VERSION = 1;
57
+ export const FRONTIER_SWARM_WATCHPOINT_PLAN_KIND = 'frontier.swarm.watchpoint-plan';
58
+ export const FRONTIER_SWARM_WATCHPOINT_PLAN_VERSION = 1;
59
+ export const FRONTIER_SWARM_DEBUG_HANDOFF_KIND = 'frontier.swarm.debug-handoff';
60
+ export const FRONTIER_SWARM_DEBUG_HANDOFF_VERSION = 1;
61
+ export const FRONTIER_SWARM_INSTRUMENTATION_BUDGET_KIND = 'frontier.swarm.instrumentation-budget';
62
+ export const FRONTIER_SWARM_INSTRUMENTATION_BUDGET_VERSION = 1;
63
+ export const FRONTIER_SWARM_BOTTLENECK_REPORT_KIND = 'frontier.swarm.bottleneck-report';
64
+ export const FRONTIER_SWARM_BOTTLENECK_REPORT_VERSION = 1;
65
+ export const FRONTIER_SWARM_EVIDENCE_INDEX_KIND = 'frontier.swarm.evidence-index';
66
+ export const FRONTIER_SWARM_EVIDENCE_INDEX_VERSION = 1;
67
+ export const FRONTIER_SWARM_BLACKBOARD_KIND = 'frontier.swarm.blackboard';
68
+ export const FRONTIER_SWARM_BLACKBOARD_VERSION = 1;
69
+ export const FRONTIER_SWARM_REFERENCE_ORACLE_PLAN_KIND = 'frontier.swarm.reference-oracle-plan';
70
+ export const FRONTIER_SWARM_REFERENCE_ORACLE_PLAN_VERSION = 1;
71
+ export const FRONTIER_SWARM_REFERENCE_ORACLE_RESPONSE_KIND = 'frontier.swarm.reference-oracle-response';
72
+ export const FRONTIER_SWARM_REFERENCE_ORACLE_RESPONSE_VERSION = 1;
73
+ export const FRONTIER_SWARM_ARTIFACT_ROUTING_PLAN_KIND = 'frontier.swarm.artifact-routing-plan';
74
+ export const FRONTIER_SWARM_ARTIFACT_ROUTING_PLAN_VERSION = 1;
75
+ export const FRONTIER_SWARM_SCHEDULER_RECOMMENDATIONS_KIND = 'frontier.swarm.scheduler-recommendations';
76
+ export const FRONTIER_SWARM_SCHEDULER_RECOMMENDATIONS_VERSION = 1;
77
+ export const FRONTIER_SWARM_FIXTURE_CATALOG_KIND = 'frontier.swarm.fixture-catalog';
78
+ export const FRONTIER_SWARM_FIXTURE_CATALOG_VERSION = 1;
79
+ export const FRONTIER_SWARM_PROGRESS_MODEL_KIND = 'frontier.swarm.progress-model';
80
+ export const FRONTIER_SWARM_PROGRESS_MODEL_VERSION = 1;
81
+ export const FRONTIER_SWARM_AUTO_REVIEW_REPORT_KIND = 'frontier.swarm.auto-review-report';
82
+ export const FRONTIER_SWARM_AUTO_REVIEW_REPORT_VERSION = 1;
83
+ export const FRONTIER_SWARM_REBASE_REPORT_KIND = 'frontier.swarm.rebase-report';
84
+ export const FRONTIER_SWARM_REBASE_REPORT_VERSION = 1;
85
+ export const FRONTIER_SWARM_USAGE_GOVERNOR_KIND = 'frontier.swarm.usage-governor';
86
+ export const FRONTIER_SWARM_USAGE_GOVERNOR_VERSION = 1;
49
87
  export const FRONTIER_SWARM_LANE_PLAYBOOK_KIND = 'frontier.swarm.lane-playbook';
50
88
  export const FRONTIER_SWARM_LANE_PLAYBOOK_VERSION = 1;
51
89
  export const FRONTIER_SWARM_PATCH_STACK_PLAN_KIND = 'frontier.swarm.patch-stack-plan';
@@ -949,6 +987,551 @@ export function createSwarmOracleCorpus(input = {}) {
949
987
  ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
950
988
  };
951
989
  }
990
+ export function createSwarmObservabilityPoint(input = {}) {
991
+ const eventRefs = normalizeNamedRefs(input.eventRefs ?? [], 'event');
992
+ return {
993
+ kind: FRONTIER_SWARM_OBSERVABILITY_POINT_KIND,
994
+ version: FRONTIER_SWARM_OBSERVABILITY_POINT_VERSION,
995
+ id: input.id ?? 'swarm-observability-point:' + stableHash([input.subject, input.scope, input.operationIndex, input.at, input.path, input.selector, eventRefs]),
996
+ ...(input.subject ? { subject: input.subject } : {}),
997
+ ...(input.scope ? { scope: input.scope } : {}),
998
+ ...(input.operationIndex !== undefined ? { operationIndex: Math.max(0, Math.floor(input.operationIndex)) } : {}),
999
+ ...(input.at !== undefined ? { at: input.at } : {}),
1000
+ ...(input.path ? { path: input.path } : {}),
1001
+ ...(input.selector ? { selector: input.selector } : {}),
1002
+ ...(input.before !== undefined ? { before: toJsonValue(input.before) } : {}),
1003
+ ...(input.after !== undefined ? { after: toJsonValue(input.after) } : {}),
1004
+ eventRefs,
1005
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1006
+ };
1007
+ }
1008
+ export function createSwarmReplayBundle(input = {}) {
1009
+ const generatedAt = input.generatedAt ?? Date.now();
1010
+ const commands = normalizeCommands(input.commands ?? []);
1011
+ const inputs = normalizeNamedRefs(input.inputs ?? [], 'input');
1012
+ const artifacts = normalizeNamedRefs(input.artifacts ?? [], 'artifact');
1013
+ const sourceRefs = normalizeNamedRefs(input.sourceRefs ?? [], 'source');
1014
+ const seeds = normalizeSeedRefs(input.seeds ?? []);
1015
+ const expectedEvidence = uniqueStrings(input.expectedEvidence ?? []);
1016
+ const title = input.title ?? titleFromId(input.id ?? input.subject ?? 'replay bundle');
1017
+ return {
1018
+ kind: FRONTIER_SWARM_REPLAY_BUNDLE_KIND,
1019
+ version: FRONTIER_SWARM_REPLAY_BUNDLE_VERSION,
1020
+ id: input.id ?? 'swarm-replay-bundle:' + stableHash([title, input.subject, commands, inputs, artifacts, sourceRefs, seeds, expectedEvidence, generatedAt]),
1021
+ title,
1022
+ ...(input.subject ? { subject: input.subject } : {}),
1023
+ generatedAt,
1024
+ commands,
1025
+ inputs,
1026
+ artifacts,
1027
+ sourceRefs,
1028
+ seeds,
1029
+ ...(toJsonObject(input.environment) ? { environment: toJsonObject(input.environment) } : {}),
1030
+ expectedEvidence,
1031
+ summary: {
1032
+ commandCount: commands.length,
1033
+ inputCount: inputs.length,
1034
+ artifactCount: artifacts.length,
1035
+ sourceRefCount: sourceRefs.length
1036
+ },
1037
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1038
+ };
1039
+ }
1040
+ export function createSwarmParityOracle(input = {}) {
1041
+ const generatedAt = input.generatedAt ?? Date.now();
1042
+ const referenceCommands = normalizeCommands(input.referenceCommands ?? []);
1043
+ const testCommands = normalizeCommands(input.testCommands ?? []);
1044
+ const comparators = (input.comparators ?? []).map(normalizeParityComparator);
1045
+ const artifacts = normalizeNamedRefs(input.artifacts ?? [], 'parity-artifact');
1046
+ const status = input.status ?? inferParityStatus(comparators);
1047
+ return {
1048
+ kind: FRONTIER_SWARM_PARITY_ORACLE_KIND,
1049
+ version: FRONTIER_SWARM_PARITY_ORACLE_VERSION,
1050
+ id: input.id ?? 'swarm-parity-oracle:' + stableHash([input.title, input.subject, referenceCommands, testCommands, comparators, artifacts, generatedAt]),
1051
+ title: input.title ?? titleFromId(input.id ?? input.subject ?? 'parity oracle'),
1052
+ status,
1053
+ ...(input.subject ? { subject: input.subject } : {}),
1054
+ generatedAt,
1055
+ referenceCommands,
1056
+ testCommands,
1057
+ comparators,
1058
+ artifacts,
1059
+ replayBundleIds: uniqueStrings(input.replayBundleIds ?? []),
1060
+ summary: {
1061
+ comparatorCount: comparators.length,
1062
+ passedCount: comparators.filter((comparator) => comparator.status === 'passed').length,
1063
+ failedCount: comparators.filter((comparator) => comparator.status === 'failed').length,
1064
+ blockedCount: comparators.filter((comparator) => comparator.status === 'blocked').length
1065
+ },
1066
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1067
+ };
1068
+ }
1069
+ export function createSwarmDivergenceReport(input = {}) {
1070
+ const generatedAt = input.generatedAt ?? Date.now();
1071
+ const observabilityPoints = (input.observabilityPoints ?? []).map((point) => isSwarmObservabilityPoint(point) ? point : createSwarmObservabilityPoint(point));
1072
+ const earliest = observabilityPoints
1073
+ .filter((point) => point.operationIndex !== undefined)
1074
+ .sort((left, right) => left.operationIndex - right.operationIndex)[0];
1075
+ const divergesAt = input.divergesAt ?? earliest?.path;
1076
+ const operationIndex = input.operationIndex ?? earliest?.operationIndex;
1077
+ return {
1078
+ kind: FRONTIER_SWARM_DIVERGENCE_REPORT_KIND,
1079
+ version: FRONTIER_SWARM_DIVERGENCE_REPORT_VERSION,
1080
+ id: input.id ?? 'swarm-divergence-report:' + stableHash([input.subject, input.divergesAt, input.operationIndex, observabilityPoints, generatedAt]),
1081
+ title: input.title ?? titleFromId(input.id ?? input.subject ?? 'divergence report'),
1082
+ status: input.status ?? 'failed',
1083
+ severity: input.severity ?? 'error',
1084
+ ...(input.subject ? { subject: input.subject } : {}),
1085
+ confidence: input.confidence ?? 'medium',
1086
+ ...(divergesAt ? { divergesAt } : {}),
1087
+ ...(operationIndex !== undefined ? { operationIndex } : {}),
1088
+ ...(input.expected !== undefined ? { expected: toJsonValue(input.expected) } : {}),
1089
+ ...(input.actual !== undefined ? { actual: toJsonValue(input.actual) } : {}),
1090
+ observabilityPoints,
1091
+ traceRefs: normalizeNamedRefs(input.traceRefs ?? [], 'trace'),
1092
+ replayBundleIds: uniqueStrings(input.replayBundleIds ?? []),
1093
+ evidenceRefs: normalizeNamedRefs(input.evidenceRefs ?? [], 'evidence'),
1094
+ generatedAt,
1095
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1096
+ };
1097
+ }
1098
+ export function createSwarmWatchpointPlan(input = {}) {
1099
+ const generatedAt = input.generatedAt ?? Date.now();
1100
+ const watchpoints = (input.watchpoints ?? []).map(normalizeWatchpoint);
1101
+ const commands = normalizeCommands(input.commands ?? []);
1102
+ return {
1103
+ kind: FRONTIER_SWARM_WATCHPOINT_PLAN_KIND,
1104
+ version: FRONTIER_SWARM_WATCHPOINT_PLAN_VERSION,
1105
+ id: input.id ?? 'swarm-watchpoint-plan:' + stableHash([input.subject, watchpoints, commands, generatedAt]),
1106
+ title: input.title ?? titleFromId(input.id ?? input.subject ?? 'watchpoint plan'),
1107
+ ...(input.subject ? { subject: input.subject } : {}),
1108
+ matchMode: input.matchMode ?? 'all',
1109
+ generatedAt,
1110
+ watchpoints,
1111
+ commands,
1112
+ replayBundleIds: uniqueStrings(input.replayBundleIds ?? []),
1113
+ divergenceReportIds: uniqueStrings(input.divergenceReportIds ?? []),
1114
+ summary: {
1115
+ watchpointCount: watchpoints.length,
1116
+ commandCount: commands.length
1117
+ },
1118
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1119
+ };
1120
+ }
1121
+ export function createSwarmDebugHandoff(input = {}) {
1122
+ const generatedAt = input.generatedAt ?? Date.now();
1123
+ const focus = input.focus ? (isSwarmObservabilityPoint(input.focus) ? input.focus : createSwarmObservabilityPoint(input.focus)) : undefined;
1124
+ return {
1125
+ kind: FRONTIER_SWARM_DEBUG_HANDOFF_KIND,
1126
+ version: FRONTIER_SWARM_DEBUG_HANDOFF_VERSION,
1127
+ id: input.id ?? 'swarm-debug-handoff:' + stableHash([input.subject, focus, input.replayBundleIds, input.divergenceReportIds, input.watchpointPlanIds, generatedAt]),
1128
+ title: input.title ?? titleFromId(input.id ?? input.subject ?? 'debug handoff'),
1129
+ status: input.status ?? 'ready',
1130
+ ...(input.subject ? { subject: input.subject } : {}),
1131
+ ...(focus ? { focus } : {}),
1132
+ replayBundleIds: uniqueStrings(input.replayBundleIds ?? []),
1133
+ divergenceReportIds: uniqueStrings(input.divergenceReportIds ?? []),
1134
+ watchpointPlanIds: uniqueStrings(input.watchpointPlanIds ?? []),
1135
+ commands: normalizeCommands(input.commands ?? []),
1136
+ files: normalizeNamedRefs(input.files ?? [], 'file'),
1137
+ artifacts: normalizeNamedRefs(input.artifacts ?? [], 'artifact'),
1138
+ comparisons: (input.comparisons ?? []).map(normalizeParityComparator),
1139
+ ...(toJsonObject(input.environment) ? { environment: toJsonObject(input.environment) } : {}),
1140
+ generatedAt,
1141
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1142
+ };
1143
+ }
1144
+ export function createSwarmInstrumentationBudget(input = {}) {
1145
+ const generatedAt = input.generatedAt ?? Date.now();
1146
+ return {
1147
+ kind: FRONTIER_SWARM_INSTRUMENTATION_BUDGET_KIND,
1148
+ version: FRONTIER_SWARM_INSTRUMENTATION_BUDGET_VERSION,
1149
+ id: input.id ?? 'swarm-instrumentation-budget:' + stableHash([input.lane, input.maxEvents, input.maxBytes, input.maxDurationMs, input.maxOverheadRatio, generatedAt]),
1150
+ title: input.title ?? titleFromId(input.id ?? input.lane ?? 'instrumentation budget'),
1151
+ ...(input.lane ? { lane: input.lane } : {}),
1152
+ generatedAt,
1153
+ ...(positiveNumber(input.maxEvents) ? { maxEvents: Math.floor(input.maxEvents) } : {}),
1154
+ ...(positiveNumber(input.maxBytes) ? { maxBytes: Math.floor(input.maxBytes) } : {}),
1155
+ ...(positiveNumber(input.maxDurationMs) ? { maxDurationMs: Math.floor(input.maxDurationMs) } : {}),
1156
+ ...(positiveNumber(input.maxOverheadRatio) ? { maxOverheadRatio: input.maxOverheadRatio } : {}),
1157
+ captureKinds: uniqueStrings(input.captureKinds ?? []),
1158
+ sampling: {
1159
+ mode: input.sampling?.mode ?? 'adaptive',
1160
+ ...(positiveNumber(input.sampling?.rate) ? { rate: input.sampling?.rate } : {}),
1161
+ ...(toJsonObject(input.sampling?.metadata) ? { metadata: toJsonObject(input.sampling?.metadata) } : {})
1162
+ },
1163
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1164
+ };
1165
+ }
1166
+ export function checkSwarmInstrumentationBudget(budgetInput, usageInput = {}) {
1167
+ const budget = isSwarmInstrumentationBudget(budgetInput) ? budgetInput : createSwarmInstrumentationBudget(budgetInput);
1168
+ const usage = {
1169
+ events: Math.max(0, Math.floor(usageInput.events ?? 0)),
1170
+ bytes: Math.max(0, Math.floor(usageInput.bytes ?? 0)),
1171
+ durationMs: Math.max(0, Math.floor(usageInput.durationMs ?? 0)),
1172
+ overheadRatio: Math.max(0, usageInput.overheadRatio ?? 0),
1173
+ captureKinds: uniqueStrings(usageInput.captureKinds ?? []),
1174
+ ...(toJsonObject(usageInput.metadata) ? { metadata: toJsonObject(usageInput.metadata) } : {})
1175
+ };
1176
+ const violations = [];
1177
+ if (budget.maxEvents !== undefined && usage.events > budget.maxEvents)
1178
+ violations.push('max-events');
1179
+ if (budget.maxBytes !== undefined && usage.bytes > budget.maxBytes)
1180
+ violations.push('max-bytes');
1181
+ if (budget.maxDurationMs !== undefined && usage.durationMs > budget.maxDurationMs)
1182
+ violations.push('max-duration-ms');
1183
+ if (budget.maxOverheadRatio !== undefined && usage.overheadRatio > budget.maxOverheadRatio)
1184
+ violations.push('max-overhead-ratio');
1185
+ for (const kind of usage.captureKinds.filter((kind) => budget.captureKinds.length > 0 && !budget.captureKinds.includes(kind))) {
1186
+ violations.push(`capture-kind:${kind}`);
1187
+ }
1188
+ return { ok: violations.length === 0, budgetId: budget.id, usage, violations: uniqueStrings(violations) };
1189
+ }
1190
+ export function classifySwarmBottleneck(input) {
1191
+ const source = normalizeBottleneckSource(input);
1192
+ const text = [source.text, source.status, ...(source.reasons ?? []), ...(source.evidencePaths ?? []), ...(source.changedPaths ?? [])].join(' ').toLowerCase();
1193
+ const verification = source.verification ?? [];
1194
+ let kind = 'queue';
1195
+ let confidence = 'medium';
1196
+ if (/missing.*oracle|no oracle|needs-fixture|fixture/.test(text))
1197
+ kind = 'missing-oracle';
1198
+ else if (/flaky|timeout|browser|playwright|chrome|port/.test(text))
1199
+ kind = 'flaky-harness';
1200
+ else if (/instrument|logging|trace|telemetry|overhead/.test(text))
1201
+ kind = 'instrumentation-overhead';
1202
+ else if (/merge|conflict|review|needs-port|ownership/.test(text))
1203
+ kind = 'merge-review';
1204
+ else if (/dependency|depends|blocked/.test(text))
1205
+ kind = 'blocked-dependency';
1206
+ else if (/perf|slow|latency|throughput|cpu|memory|resource-capacity/.test(text))
1207
+ kind = 'performance';
1208
+ else if (/diverg|correct|parity|oracle failed|regression/.test(text))
1209
+ kind = 'correctness';
1210
+ if (verification.some((entry) => entry.status !== undefined && entry.status !== 0 && entry.required !== false))
1211
+ confidence = 'high';
1212
+ if ((source.evidencePaths?.length ?? 0) === 0 && (source.changedPaths?.length ?? 0) > 0)
1213
+ confidence = 'low';
1214
+ return {
1215
+ kind,
1216
+ confidence,
1217
+ reasons: uniqueStrings([kind, ...(source.reasons ?? []), ...verification.filter((entry) => entry.status !== undefined && entry.status !== 0).map((entry) => entry.name ?? 'failed-verification')]),
1218
+ route: routeForBottleneck(kind, source.lane)
1219
+ };
1220
+ }
1221
+ export function createSwarmBottleneckReport(input = {}) {
1222
+ const generatedAt = input.generatedAt ?? Date.now();
1223
+ const classifications = (input.sources ?? []).map(classifySwarmBottleneck);
1224
+ const byKind = groupObjects(classifications, (classification) => classification.kind);
1225
+ return {
1226
+ kind: FRONTIER_SWARM_BOTTLENECK_REPORT_KIND,
1227
+ version: FRONTIER_SWARM_BOTTLENECK_REPORT_VERSION,
1228
+ id: input.id ?? 'swarm-bottleneck-report:' + stableHash([classifications, generatedAt]),
1229
+ generatedAt,
1230
+ classifications,
1231
+ byKind,
1232
+ summary: {
1233
+ sourceCount: input.sources?.length ?? 0,
1234
+ kindCount: Object.keys(byKind).length
1235
+ },
1236
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1237
+ };
1238
+ }
1239
+ export function createSwarmEvidenceIndex(input = {}) {
1240
+ const source = isSwarmRun(input) ? { run: input } : input;
1241
+ const generatedAt = source.generatedAt ?? Date.now();
1242
+ const runEntries = source.run?.results.flatMap((result) => result.evidencePaths.map((path) => normalizeEvidenceIndexEntry({
1243
+ jobId: result.jobId,
1244
+ queueItemId: result.queueItemIds[0],
1245
+ path,
1246
+ kind: evidenceKindFromPath(path),
1247
+ status: result.status,
1248
+ confidence: result.status === 'verified' || result.mergeReadiness === 'verified-patch' ? 1 : 0.65,
1249
+ generatedAt
1250
+ }))) ?? [];
1251
+ const entries = [...runEntries, ...(source.entries ?? []).map((entry) => normalizeEvidenceIndexEntry({ ...entry, generatedAt: entry.generatedAt ?? generatedAt }))];
1252
+ const byJobId = groupObjects(entries.filter((entry) => entry.jobId), (entry) => entry.jobId);
1253
+ const byTopic = groupObjects(entries.filter((entry) => entry.topic), (entry) => entry.topic);
1254
+ const byPath = groupObjects(entries.filter((entry) => entry.path), (entry) => entry.path);
1255
+ return {
1256
+ kind: FRONTIER_SWARM_EVIDENCE_INDEX_KIND,
1257
+ version: FRONTIER_SWARM_EVIDENCE_INDEX_VERSION,
1258
+ id: source.id ?? 'swarm-evidence-index:' + stableHash([source.run?.id, entries, generatedAt]),
1259
+ ...(source.run?.id ? { runId: source.run.id } : {}),
1260
+ generatedAt,
1261
+ entries,
1262
+ byJobId,
1263
+ byTopic,
1264
+ byPath,
1265
+ summary: {
1266
+ entryCount: entries.length,
1267
+ jobCount: Object.keys(byJobId).length,
1268
+ topicCount: Object.keys(byTopic).length,
1269
+ pathCount: Object.keys(byPath).length
1270
+ },
1271
+ ...(toJsonObject(source.metadata) ? { metadata: toJsonObject(source.metadata) } : {})
1272
+ };
1273
+ }
1274
+ export function querySwarmEvidenceIndex(index, query = {}) {
1275
+ const entries = index.entries.filter((entry) => ((query.jobId === undefined || entry.jobId === query.jobId)
1276
+ && (query.lane === undefined || entry.lane === query.lane)
1277
+ && (query.topic === undefined || entry.topic === query.topic)
1278
+ && (query.pathIncludes === undefined || (entry.path ?? '').includes(query.pathIncludes))
1279
+ && (query.kind === undefined || entry.kind === query.kind)
1280
+ && (query.status === undefined || entry.status === query.status)
1281
+ && (query.tag === undefined || entry.tags.includes(query.tag))
1282
+ && (query.minConfidence === undefined || entry.confidence >= query.minConfidence)
1283
+ && matchesFacetQuery(entry.facets, query.facet)));
1284
+ return { entries, summary: { entryCount: entries.length } };
1285
+ }
1286
+ export function createSwarmBlackboard(input = {}) {
1287
+ const generatedAt = input.generatedAt ?? Date.now();
1288
+ const entries = (input.entries ?? []).map((entry) => normalizeBlackboardEntry({ ...entry, generatedAt: entry.generatedAt ?? generatedAt }));
1289
+ const byTopic = groupObjects(entries, (entry) => entry.topic);
1290
+ const byKind = groupObjects(entries, (entry) => entry.kind);
1291
+ return {
1292
+ kind: FRONTIER_SWARM_BLACKBOARD_KIND,
1293
+ version: FRONTIER_SWARM_BLACKBOARD_VERSION,
1294
+ id: input.id ?? 'swarm-blackboard:' + stableHash([input.runId, entries, generatedAt]),
1295
+ ...(input.runId ? { runId: input.runId } : {}),
1296
+ generatedAt,
1297
+ entries,
1298
+ byTopic,
1299
+ byKind,
1300
+ summary: {
1301
+ entryCount: entries.length,
1302
+ topicCount: Object.keys(byTopic).length,
1303
+ kindCount: Object.keys(byKind).length
1304
+ },
1305
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1306
+ };
1307
+ }
1308
+ export function querySwarmBlackboard(blackboard, query = {}) {
1309
+ const textIncludes = query.textIncludes?.toLowerCase();
1310
+ const entries = blackboard.entries.filter((entry) => ((query.kind === undefined || entry.kind === query.kind)
1311
+ && (query.topic === undefined || entry.topic === query.topic)
1312
+ && (query.status === undefined || entry.status === query.status)
1313
+ && (query.lane === undefined || entry.lane === query.lane)
1314
+ && (query.jobId === undefined || entry.jobId === query.jobId)
1315
+ && (query.owner === undefined || entry.owner === query.owner)
1316
+ && (query.tag === undefined || entry.tags.includes(query.tag))
1317
+ && (textIncludes === undefined || entry.text.toLowerCase().includes(textIncludes))));
1318
+ return { entries, summary: { entryCount: entries.length } };
1319
+ }
1320
+ export function createSwarmReferenceOraclePlan(input = {}) {
1321
+ const generatedAt = input.generatedAt ?? Date.now();
1322
+ const targets = (input.targets ?? []).map((target) => ({
1323
+ id: target.id,
1324
+ role: target.role ?? 'candidate',
1325
+ ...(target.command ? { command: normalizeCommand(target.command) } : {}),
1326
+ ...(toJsonObject(target.metadata) ? { metadata: toJsonObject(target.metadata) } : {})
1327
+ }));
1328
+ return {
1329
+ kind: FRONTIER_SWARM_REFERENCE_ORACLE_PLAN_KIND,
1330
+ version: FRONTIER_SWARM_REFERENCE_ORACLE_PLAN_VERSION,
1331
+ id: input.id ?? 'swarm-reference-oracle-plan:' + stableHash([input.serviceId, input.subject, input.fixtureId, targets, input.window, generatedAt]),
1332
+ ...(input.serviceId ? { serviceId: input.serviceId } : {}),
1333
+ ...(input.subject ? { subject: input.subject } : {}),
1334
+ ...(input.fixtureId ? { fixtureId: input.fixtureId } : {}),
1335
+ generatedAt,
1336
+ targets,
1337
+ ...(input.window ? { window: normalizeReferenceWindow(input.window) } : {}),
1338
+ watchpoints: (input.watchpoints ?? []).map(normalizeWatchpoint),
1339
+ artifactKinds: uniqueStrings(input.artifactKinds ?? []),
1340
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1341
+ };
1342
+ }
1343
+ export function createSwarmReferenceOracleResponse(input = {}) {
1344
+ const generatedAt = input.generatedAt ?? Date.now();
1345
+ return {
1346
+ kind: FRONTIER_SWARM_REFERENCE_ORACLE_RESPONSE_KIND,
1347
+ version: FRONTIER_SWARM_REFERENCE_ORACLE_RESPONSE_VERSION,
1348
+ id: input.id ?? 'swarm-reference-oracle-response:' + stableHash([input.planId, input.status, input.targetResults, input.divergence, generatedAt]),
1349
+ ...(input.planId ? { planId: input.planId } : {}),
1350
+ status: input.status ?? (input.divergence ? 'failed' : 'pending'),
1351
+ ...(input.subject ? { subject: input.subject } : {}),
1352
+ generatedAt,
1353
+ targetResults: (input.targetResults ?? []).map((target) => ({
1354
+ targetId: target.targetId,
1355
+ status: target.status ?? 'pending',
1356
+ artifacts: normalizeNamedRefs(target.artifacts ?? [], 'reference-oracle-artifact'),
1357
+ ...(toJsonObject(target.metadata) ? { metadata: toJsonObject(target.metadata) } : {})
1358
+ })),
1359
+ ...(input.divergence ? { divergence: createSwarmDivergenceReport(input.divergence) } : {}),
1360
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1361
+ };
1362
+ }
1363
+ export function createSwarmArtifactRoutingPlan(input = {}) {
1364
+ const generatedAt = input.generatedAt ?? Date.now();
1365
+ const artifacts = [
1366
+ ...normalizeNamedRefs(input.artifacts ?? [], 'artifact'),
1367
+ ...(input.bundles ?? []).flatMap((bundle) => bundle.evidencePaths.map((path) => normalizeNamedRef({ path, kind: evidenceKindFromPath(path), role: bundle.disposition }, 'evidence')))
1368
+ ];
1369
+ const hints = (input.hints ?? []).map(normalizeRoutingHint);
1370
+ const routes = artifacts.map((artifact) => {
1371
+ const matched = hints.filter((hint) => ((hint.artifactKind === undefined || hint.artifactKind === artifact.kind)
1372
+ && (hint.pathPattern === undefined || (artifact.path ?? artifact.uri ?? '').includes(hint.pathPattern))));
1373
+ const bucket = matched[0]?.bucket ?? defaultArtifactBucket(artifact);
1374
+ return {
1375
+ artifact,
1376
+ bucket,
1377
+ ...(matched[0]?.lane ? { lane: matched[0].lane } : {}),
1378
+ reasons: uniqueStrings(matched.map((hint) => hint.reason))
1379
+ };
1380
+ });
1381
+ const byBucket = groupIds(routes.map((route) => ({ id: route.artifact.id, bucket: route.bucket })), (route) => route.bucket);
1382
+ return {
1383
+ kind: FRONTIER_SWARM_ARTIFACT_ROUTING_PLAN_KIND,
1384
+ version: FRONTIER_SWARM_ARTIFACT_ROUTING_PLAN_VERSION,
1385
+ id: input.id ?? 'swarm-artifact-routing-plan:' + stableHash([routes, generatedAt]),
1386
+ generatedAt,
1387
+ routes,
1388
+ byBucket,
1389
+ summary: {
1390
+ routeCount: routes.length,
1391
+ bucketCount: Object.keys(byBucket).length
1392
+ },
1393
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1394
+ };
1395
+ }
1396
+ export function createSwarmFixtureCatalog(input = {}) {
1397
+ const generatedAt = input.generatedAt ?? Date.now();
1398
+ const fixtures = (input.fixtures ?? []).map(normalizeFixture);
1399
+ const byTag = {};
1400
+ for (const fixture of fixtures) {
1401
+ for (const tag of fixture.tags)
1402
+ byTag[tag] = uniqueStrings([...(byTag[tag] ?? []), fixture.id]);
1403
+ }
1404
+ return {
1405
+ kind: FRONTIER_SWARM_FIXTURE_CATALOG_KIND,
1406
+ version: FRONTIER_SWARM_FIXTURE_CATALOG_VERSION,
1407
+ id: input.id ?? 'swarm-fixture-catalog:' + stableHash([fixtures, generatedAt]),
1408
+ generatedAt,
1409
+ fixtures,
1410
+ byTag,
1411
+ summary: {
1412
+ fixtureCount: fixtures.length,
1413
+ tagCount: Object.keys(byTag).length
1414
+ },
1415
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1416
+ };
1417
+ }
1418
+ export function createSwarmProgressModel(input = {}) {
1419
+ const generatedAt = input.generatedAt ?? Date.now();
1420
+ const items = (input.items ?? []).map((item) => ({
1421
+ id: item.id,
1422
+ ...(item.surface ? { surface: item.surface } : {}),
1423
+ status: item.status ?? 'not-started',
1424
+ evidencePaths: uniqueStrings(item.evidencePaths ?? []),
1425
+ blockers: uniqueStrings(item.blockers ?? []),
1426
+ ...(toJsonObject(item.metadata) ? { metadata: toJsonObject(item.metadata) } : {})
1427
+ }));
1428
+ const byStatus = groupIds(items, (item) => item.status);
1429
+ return {
1430
+ kind: FRONTIER_SWARM_PROGRESS_MODEL_KIND,
1431
+ version: FRONTIER_SWARM_PROGRESS_MODEL_VERSION,
1432
+ id: input.id ?? 'swarm-progress-model:' + stableHash([items, generatedAt]),
1433
+ generatedAt,
1434
+ items,
1435
+ byStatus,
1436
+ summary: {
1437
+ itemCount: items.length,
1438
+ acceptedCount: byStatus.accepted?.length ?? 0,
1439
+ blockedCount: byStatus.blocked?.length ?? 0
1440
+ },
1441
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1442
+ };
1443
+ }
1444
+ export function createSwarmAutoReviewReport(input = {}) {
1445
+ const generatedAt = input.generatedAt ?? Date.now();
1446
+ const derived = (input.bundles ?? []).flatMap((bundle) => deriveAutoReviewFindings(bundle, generatedAt));
1447
+ const findings = [...derived, ...(input.findings ?? []).map((finding) => normalizeAutoReviewFinding(finding, generatedAt))];
1448
+ const byKind = groupObjects(findings, (finding) => finding.kind);
1449
+ return {
1450
+ kind: FRONTIER_SWARM_AUTO_REVIEW_REPORT_KIND,
1451
+ version: FRONTIER_SWARM_AUTO_REVIEW_REPORT_VERSION,
1452
+ id: input.id ?? 'swarm-auto-review-report:' + stableHash([findings, generatedAt]),
1453
+ generatedAt,
1454
+ findings,
1455
+ byKind,
1456
+ summary: {
1457
+ findingCount: findings.length,
1458
+ highSeverityCount: findings.filter((finding) => finding.severity === 'error' || finding.severity === 'critical').length
1459
+ },
1460
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1461
+ };
1462
+ }
1463
+ export function createSwarmRebaseReport(input = {}) {
1464
+ const generatedAt = input.generatedAt ?? Date.now();
1465
+ const fromIndex = input.mergeIndex?.entries.map((entry) => ({
1466
+ jobId: entry.jobId,
1467
+ status: entry.staleAgainstHead ? 'stale-evidence' : entry.conflictingJobIds.length ? 'semantic-overlap' : 'clean-apply',
1468
+ reasons: uniqueStrings([...entry.reasons, ...entry.conflictKeys])
1469
+ })) ?? [];
1470
+ const fromBundles = !input.mergeIndex && input.bundles ? input.bundles.map((bundle) => ({
1471
+ jobId: bundle.jobId,
1472
+ status: bundle.staleAgainstHead ? 'stale-evidence' : 'clean-apply',
1473
+ reasons: [...bundle.reasons]
1474
+ })) : [];
1475
+ const entries = [...fromIndex, ...fromBundles, ...(input.entries ?? [])].map((entry) => ({
1476
+ jobId: entry.jobId,
1477
+ status: entry.status ?? 'clean-apply',
1478
+ reasons: uniqueStrings(entry.reasons ?? []),
1479
+ ...('metadata' in entry && toJsonObject(entry.metadata) ? { metadata: toJsonObject(entry.metadata) } : {})
1480
+ }));
1481
+ const byStatus = groupIds(entries, (entry) => entry.status);
1482
+ return {
1483
+ kind: FRONTIER_SWARM_REBASE_REPORT_KIND,
1484
+ version: FRONTIER_SWARM_REBASE_REPORT_VERSION,
1485
+ id: input.id ?? 'swarm-rebase-report:' + stableHash([input.currentHead, entries, generatedAt]),
1486
+ ...(input.currentHead ? { currentHead: input.currentHead } : {}),
1487
+ generatedAt,
1488
+ entries,
1489
+ byStatus,
1490
+ summary: {
1491
+ entryCount: entries.length,
1492
+ cleanCount: byStatus['clean-apply']?.length ?? 0,
1493
+ conflictCount: (byStatus['textual-conflict']?.length ?? 0) + (byStatus['semantic-overlap']?.length ?? 0),
1494
+ staleCount: byStatus['stale-evidence']?.length ?? 0
1495
+ },
1496
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1497
+ };
1498
+ }
1499
+ export function createSwarmUsageGovernor(input = {}) {
1500
+ const generatedAt = input.generatedAt ?? Date.now();
1501
+ return {
1502
+ kind: FRONTIER_SWARM_USAGE_GOVERNOR_KIND,
1503
+ version: FRONTIER_SWARM_USAGE_GOVERNOR_VERSION,
1504
+ id: input.id ?? 'swarm-usage-governor:' + stableHash([input.maxWorkers, input.maxTokensByLane, input.maxCostUsd, input.retryBudget, generatedAt]),
1505
+ generatedAt,
1506
+ ...(positiveNumber(input.maxWorkers) ? { maxWorkers: Math.floor(input.maxWorkers) } : {}),
1507
+ maxTokensByLane: { ...(input.maxTokensByLane ?? {}) },
1508
+ ...(positiveNumber(input.maxCostUsd) ? { maxCostUsd: input.maxCostUsd } : {}),
1509
+ retryBudget: Math.max(0, Math.floor(input.retryBudget ?? 0)),
1510
+ stopConditions: uniqueStrings(input.stopConditions ?? []),
1511
+ preferStaticWhenLowBudget: input.preferStaticWhenLowBudget ?? true,
1512
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1513
+ };
1514
+ }
1515
+ export function checkSwarmUsageGovernor(governorInput, usage = {}) {
1516
+ const governor = isSwarmUsageGovernor(governorInput) ? governorInput : createSwarmUsageGovernor(governorInput);
1517
+ const reasons = [];
1518
+ if (governor.maxWorkers !== undefined && (usage.activeWorkers ?? 0) > governor.maxWorkers)
1519
+ reasons.push('max-workers');
1520
+ if (governor.maxCostUsd !== undefined && (usage.costUsd ?? 0) > governor.maxCostUsd)
1521
+ reasons.push('max-cost-usd');
1522
+ if ((usage.retriesUsed ?? 0) > governor.retryBudget)
1523
+ reasons.push('retry-budget');
1524
+ for (const [lane, maxTokens] of Object.entries(governor.maxTokensByLane)) {
1525
+ if ((usage.tokensByLane?.[lane] ?? 0) > maxTokens)
1526
+ reasons.push(`max-tokens:${lane}`);
1527
+ }
1528
+ return {
1529
+ ok: reasons.length === 0,
1530
+ reasons,
1531
+ ...(governor.maxWorkers !== undefined ? { recommendedMaxWorkers: Math.max(1, governor.maxWorkers - (reasons.length ? 1 : 0)) } : {}),
1532
+ preferStatic: governor.preferStaticWhenLowBudget && reasons.some((reason) => reason.startsWith('max-tokens') || reason === 'max-cost-usd')
1533
+ };
1534
+ }
952
1535
  export function createSwarmLanePlaybook(input) {
953
1536
  const generatedAt = input.generatedAt ?? Date.now();
954
1537
  const successful = (input.successfulBundles ?? []).filter((bundle) => bundle.status === 'completed' || bundle.status === 'verified' || bundle.autoMergeable);
@@ -1064,6 +1647,7 @@ export function createSwarmSchedule(input) {
1064
1647
  const runningByLane = countBy(runningJobs.map((job) => job.lane));
1065
1648
  const runningByKey = countBy(runningJobs.map((job) => job.concurrencyKey));
1066
1649
  const runningByCompute = countBy(runningJobs.map((job) => job.compute));
1650
+ const runningByResource = resourceUsageFromScheduled(runningJobs);
1067
1651
  const ready = [];
1068
1652
  const blocked = [];
1069
1653
  const sortedJobs = [...plan.jobs].sort((left, right) => left.priority - right.priority || left.id.localeCompare(right.id));
@@ -1085,6 +1669,9 @@ export function createSwarmSchedule(input) {
1085
1669
  if ((runningByCompute[job.compute.id] ?? 0) >= computeMax)
1086
1670
  reasons.push('compute-capacity');
1087
1671
  const scheduled = scheduleJob(job, dependencyIds);
1672
+ for (const resource of resourceQuotaViolations(scheduled, runningByResource, limits.resourceQuotas)) {
1673
+ reasons.push(`resource-capacity:${resource}`);
1674
+ }
1088
1675
  if (reasons.length) {
1089
1676
  blocked.push({ ...scheduled, reasons, waitingFor });
1090
1677
  continue;
@@ -1097,6 +1684,7 @@ export function createSwarmSchedule(input) {
1097
1684
  runningByLane[job.lane] = (runningByLane[job.lane] ?? 0) + 1;
1098
1685
  runningByKey[job.concurrencyKey] = (runningByKey[job.concurrencyKey] ?? 0) + 1;
1099
1686
  runningByCompute[job.compute.id] = (runningByCompute[job.compute.id] ?? 0) + 1;
1687
+ addResourceUsage(runningByResource, scheduled);
1100
1688
  }
1101
1689
  return {
1102
1690
  kind: FRONTIER_SWARM_SCHEDULE_KIND,
@@ -1120,6 +1708,46 @@ export function createSwarmSchedule(input) {
1120
1708
  }
1121
1709
  };
1122
1710
  }
1711
+ export function createSwarmSchedulerRecommendations(input) {
1712
+ const generatedAt = input.generatedAt ?? Date.now();
1713
+ const recommendations = [];
1714
+ const blockedByReason = new Map();
1715
+ for (const job of input.schedule.blocked) {
1716
+ for (const reason of job.reasons)
1717
+ blockedByReason.set(reason, [...(blockedByReason.get(reason) ?? []), job]);
1718
+ }
1719
+ for (const [reason, jobs] of blockedByReason) {
1720
+ const resource = reason.startsWith('resource-capacity:') ? reason.slice('resource-capacity:'.length) : undefined;
1721
+ recommendations.push({
1722
+ id: 'swarm-scheduler-recommendation:' + stableHash([input.schedule.id, reason, jobs.map((job) => job.jobId), generatedAt]),
1723
+ reason,
1724
+ jobIds: jobs.map((job) => job.jobId).sort(),
1725
+ ...(resource ? { resource } : {}),
1726
+ ...(jobs[0]?.lane ? { lane: jobs[0].lane } : {}),
1727
+ action: schedulerActionForReason(reason),
1728
+ priority: schedulerPriorityForReason(reason)
1729
+ });
1730
+ }
1731
+ for (const conflict of input.mergeIndex?.conflicts ?? []) {
1732
+ recommendations.push({
1733
+ id: 'swarm-scheduler-recommendation:' + stableHash([input.schedule.id, conflict.key, conflict.jobIds, generatedAt]),
1734
+ reason: `merge-conflict:${conflict.kind}`,
1735
+ jobIds: [...conflict.jobIds],
1736
+ action: 'serialize-conflicting-surface-or-split-ownership',
1737
+ priority: 15
1738
+ });
1739
+ }
1740
+ return {
1741
+ kind: FRONTIER_SWARM_SCHEDULER_RECOMMENDATIONS_KIND,
1742
+ version: FRONTIER_SWARM_SCHEDULER_RECOMMENDATIONS_VERSION,
1743
+ id: input.id ?? 'swarm-scheduler-recommendations:' + stableHash([input.schedule.id, recommendations, generatedAt]),
1744
+ scheduleId: input.schedule.id,
1745
+ generatedAt,
1746
+ recommendations: recommendations.sort((left, right) => left.priority - right.priority || left.id.localeCompare(right.id)),
1747
+ summary: { recommendationCount: recommendations.length },
1748
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1749
+ };
1750
+ }
1123
1751
  export function createSwarmLeases(input) {
1124
1752
  const now = input.now ?? Date.now();
1125
1753
  const leaseMs = Math.max(1, Math.floor(input.leaseMs ?? 900000));
@@ -1403,7 +2031,8 @@ function normalizeScheduleLimits(manifest, options) {
1403
2031
  ...(positiveNumber(options.maxReadyJobs) ? { maxReadyJobs: Math.floor(options.maxReadyJobs) } : {}),
1404
2032
  maxLaneConcurrency: { ...maxLaneConcurrency, ...(options.maxLaneConcurrency ?? {}) },
1405
2033
  maxConcurrencyKeyConcurrency: { ...(options.maxConcurrencyKeyConcurrency ?? {}) },
1406
- maxComputeConcurrency: { ...(options.maxComputeConcurrency ?? {}) }
2034
+ maxComputeConcurrency: { ...(options.maxComputeConcurrency ?? {}) },
2035
+ resourceQuotas: normalizeResourceQuotas(options.resourceQuotas ?? {})
1407
2036
  };
1408
2037
  }
1409
2038
  function mergeScheduleLimits(base, override) {
@@ -1411,7 +2040,8 @@ function mergeScheduleLimits(base, override) {
1411
2040
  maxReadyJobs: positiveNumber(override.maxReadyJobs) ? Math.floor(override.maxReadyJobs) : base.maxReadyJobs,
1412
2041
  maxLaneConcurrency: { ...base.maxLaneConcurrency, ...(override.maxLaneConcurrency ?? {}) },
1413
2042
  maxConcurrencyKeyConcurrency: { ...base.maxConcurrencyKeyConcurrency, ...(override.maxConcurrencyKeyConcurrency ?? {}) },
1414
- maxComputeConcurrency: { ...base.maxComputeConcurrency, ...(override.maxComputeConcurrency ?? {}) }
2043
+ maxComputeConcurrency: { ...base.maxComputeConcurrency, ...(override.maxComputeConcurrency ?? {}) },
2044
+ resourceQuotas: { ...base.resourceQuotas, ...normalizeResourceQuotas(override.resourceQuotas ?? {}) }
1415
2045
  };
1416
2046
  }
1417
2047
  function scheduleJob(job, dependsOn = job.dependsOn) {
@@ -1427,6 +2057,63 @@ function scheduleJob(job, dependsOn = job.dependsOn) {
1427
2057
  ...(job.resourceRequirements ? { resourceRequirements: cloneJsonValue(job.resourceRequirements) } : {})
1428
2058
  };
1429
2059
  }
2060
+ function normalizeResourceQuotas(input) {
2061
+ const quotas = {};
2062
+ for (const [resource, value] of Object.entries(input)) {
2063
+ if (positiveNumber(value))
2064
+ quotas[resource] = value;
2065
+ }
2066
+ return quotas;
2067
+ }
2068
+ function resourceUsageFromScheduled(jobs) {
2069
+ const usage = {};
2070
+ for (const job of jobs)
2071
+ addResourceUsage(usage, job);
2072
+ return usage;
2073
+ }
2074
+ function addResourceUsage(usage, job) {
2075
+ for (const [resource, amount] of Object.entries(job.resourceRequirements?.resources ?? {})) {
2076
+ usage[resource] = (usage[resource] ?? 0) + amount;
2077
+ }
2078
+ if (job.resourceRequirements?.browser?.required) {
2079
+ if (job.resourceRequirements.resources.browser === undefined)
2080
+ usage.browser = (usage.browser ?? 0) + 1;
2081
+ if (job.resourceRequirements.resources['browser-port'] === undefined)
2082
+ usage['browser-port'] = (usage['browser-port'] ?? 0) + 1;
2083
+ }
2084
+ }
2085
+ function resourceQuotaViolations(job, usage, quotas) {
2086
+ const next = { ...usage };
2087
+ addResourceUsage(next, job);
2088
+ return Object.entries(quotas)
2089
+ .filter(([resource, quota]) => (next[resource] ?? 0) > quota)
2090
+ .map(([resource]) => resource)
2091
+ .sort();
2092
+ }
2093
+ function schedulerActionForReason(reason) {
2094
+ if (reason.startsWith('resource-capacity:'))
2095
+ return 'lower-concurrency-or-add-resource-pool';
2096
+ if (reason === 'lane-capacity')
2097
+ return 'increase-lane-capacity-or-split-lane';
2098
+ if (reason === 'compute-capacity')
2099
+ return 'increase-compute-capacity-or-use-another-compute';
2100
+ if (reason === 'concurrency-key-capacity')
2101
+ return 'serialize-or-split-concurrency-key';
2102
+ if (reason === 'waiting-for-dependencies')
2103
+ return 'prioritize-dependency-chain';
2104
+ if (reason === 'ready-capacity')
2105
+ return 'raise-ready-window-or-drain-ready-jobs';
2106
+ return 'review-scheduler-blocker';
2107
+ }
2108
+ function schedulerPriorityForReason(reason) {
2109
+ if (reason.startsWith('resource-capacity:'))
2110
+ return 10;
2111
+ if (reason === 'waiting-for-dependencies')
2112
+ return 20;
2113
+ if (reason === 'ready-capacity')
2114
+ return 25;
2115
+ return 30;
2116
+ }
1430
2117
  function normalizeBudget(input = {}) {
1431
2118
  return {
1432
2119
  ...(positiveNumber(input.maxCostUsd) ? { maxCostUsd: input.maxCostUsd } : {}),
@@ -1647,6 +2334,287 @@ function normalizeOracleArtifact(input) {
1647
2334
  ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1648
2335
  };
1649
2336
  }
2337
+ function normalizeCommand(input) {
2338
+ return normalizeCommands([input])[0];
2339
+ }
2340
+ function normalizeNamedRef(input, fallbackKind) {
2341
+ if (typeof input === 'string' || typeof input === 'number') {
2342
+ const value = String(input);
2343
+ return { id: value, kind: fallbackKind, path: value, tags: [] };
2344
+ }
2345
+ const path = input.path ?? input.uri;
2346
+ const id = input.id ?? path ?? stableHash(input);
2347
+ return {
2348
+ id,
2349
+ kind: input.kind ?? fallbackKind,
2350
+ ...(input.path ? { path: input.path } : {}),
2351
+ ...(input.uri ? { uri: input.uri } : {}),
2352
+ ...(input.role ? { role: input.role } : {}),
2353
+ ...(input.hash ? { hash: input.hash } : {}),
2354
+ ...(positiveNumber(input.bytes) ? { bytes: Math.floor(input.bytes) } : {}),
2355
+ tags: uniqueStrings(input.tags ?? []),
2356
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2357
+ };
2358
+ }
2359
+ function normalizeNamedRefs(input, fallbackKind) {
2360
+ return input.map((entry) => normalizeNamedRef(entry, fallbackKind)).sort((left, right) => left.id.localeCompare(right.id));
2361
+ }
2362
+ function normalizeSeedRefs(input) {
2363
+ return input.map((entry) => normalizeNamedRef(entry, 'seed')).sort((left, right) => left.id.localeCompare(right.id));
2364
+ }
2365
+ function normalizeParityComparator(input) {
2366
+ const title = input.title ?? titleFromId(input.id ?? input.path ?? 'comparator');
2367
+ return {
2368
+ id: input.id ?? 'swarm-parity-comparator:' + stableHash([title, input.status, input.expected, input.actual, input.path, input.operationIndex]),
2369
+ title,
2370
+ status: input.status ?? (input.expected !== undefined && input.actual !== undefined && stableStringify(input.expected) === stableStringify(input.actual) ? 'passed' : 'unknown'),
2371
+ ...(input.expected !== undefined ? { expected: toJsonValue(input.expected) } : {}),
2372
+ ...(input.actual !== undefined ? { actual: toJsonValue(input.actual) } : {}),
2373
+ ...(input.path ? { path: input.path } : {}),
2374
+ ...(input.operationIndex !== undefined ? { operationIndex: Math.max(0, Math.floor(input.operationIndex)) } : {}),
2375
+ evidenceRefs: normalizeNamedRefs(input.evidenceRefs ?? [], 'evidence'),
2376
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2377
+ };
2378
+ }
2379
+ function inferParityStatus(comparators) {
2380
+ if (comparators.some((comparator) => comparator.status === 'failed'))
2381
+ return 'failed';
2382
+ if (comparators.some((comparator) => comparator.status === 'blocked'))
2383
+ return 'blocked';
2384
+ if (comparators.length > 0 && comparators.every((comparator) => comparator.status === 'passed'))
2385
+ return 'passed';
2386
+ return 'pending';
2387
+ }
2388
+ function normalizeWatchpoint(input) {
2389
+ const title = input.title ?? titleFromId(input.id ?? input.path ?? input.selector ?? input.target ?? 'watchpoint');
2390
+ return {
2391
+ id: input.id ?? 'swarm-watchpoint:' + stableHash([input.target, input.path, input.selector, input.operator, input.value, input.action]),
2392
+ title,
2393
+ ...(input.target ? { target: input.target } : {}),
2394
+ ...(input.path ? { path: input.path } : {}),
2395
+ ...(input.selector ? { selector: input.selector } : {}),
2396
+ operator: input.operator ?? 'changed',
2397
+ ...(input.value !== undefined ? { value: toJsonValue(input.value) } : {}),
2398
+ action: input.action ?? 'capture',
2399
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2400
+ };
2401
+ }
2402
+ function normalizeBottleneckSource(input) {
2403
+ if (input.kind === FRONTIER_SWARM_MERGE_BUNDLE_KIND) {
2404
+ const bundle = input;
2405
+ return {
2406
+ jobId: bundle.jobId,
2407
+ lane: bundle.lane,
2408
+ status: bundle.status,
2409
+ reasons: bundle.reasons,
2410
+ evidencePaths: bundle.evidencePaths,
2411
+ changedPaths: bundle.changedPaths,
2412
+ text: bundle.title,
2413
+ metadata: bundle.metadata
2414
+ };
2415
+ }
2416
+ if ('text' in input || 'reasons' in input || 'lane' in input)
2417
+ return input;
2418
+ const result = input;
2419
+ if (result.jobId) {
2420
+ return {
2421
+ jobId: result.jobId,
2422
+ status: result.status,
2423
+ reasons: result.error !== undefined ? [stringifyError(result.error)] : [],
2424
+ verification: result.verification,
2425
+ evidencePaths: result.evidencePaths,
2426
+ changedPaths: result.changedPaths,
2427
+ text: result.lastMessage,
2428
+ metadata: result.metadata
2429
+ };
2430
+ }
2431
+ return input;
2432
+ }
2433
+ function routeForBottleneck(kind, lane) {
2434
+ if (kind === 'missing-oracle')
2435
+ return { lane: lane ?? 'verification', workKind: 'oracle', priority: 20 };
2436
+ if (kind === 'flaky-harness')
2437
+ return { lane: lane ?? 'evidence', workKind: 'harness', priority: 25 };
2438
+ if (kind === 'merge-review')
2439
+ return { lane: lane ?? 'review', workKind: 'review', priority: 10 };
2440
+ if (kind === 'instrumentation-overhead')
2441
+ return { lane: lane ?? 'diagnostics', workKind: 'instrumentation', priority: 30 };
2442
+ if (kind === 'performance')
2443
+ return { lane: lane ?? 'performance', workKind: 'benchmark', priority: 35 };
2444
+ if (kind === 'correctness')
2445
+ return { lane: lane ?? 'implementation', workKind: 'debug', priority: 15 };
2446
+ return { ...(lane ? { lane } : {}), workKind: 'triage', priority: 50 };
2447
+ }
2448
+ function evidenceKindFromPath(path) {
2449
+ const lower = path.toLowerCase();
2450
+ if (lower.endsWith('.patch') || lower.endsWith('.diff'))
2451
+ return 'patch';
2452
+ if (lower.endsWith('.jsonl'))
2453
+ return 'jsonl';
2454
+ if (lower.endsWith('.json'))
2455
+ return 'json';
2456
+ if (lower.includes('trace'))
2457
+ return 'trace';
2458
+ if (lower.includes('screenshot'))
2459
+ return 'screenshot';
2460
+ if (lower.includes('last-message'))
2461
+ return 'handoff';
2462
+ return 'evidence';
2463
+ }
2464
+ function normalizeEvidenceIndexEntry(input) {
2465
+ const generatedAt = input.generatedAt ?? Date.now();
2466
+ return {
2467
+ id: input.id ?? 'swarm-evidence-entry:' + stableHash([input.jobId, input.queueItemId, input.path, input.topic, input.kind, generatedAt]),
2468
+ ...(input.jobId ? { jobId: input.jobId } : {}),
2469
+ ...(input.queueItemId ? { queueItemId: input.queueItemId } : {}),
2470
+ ...(input.lane ? { lane: input.lane } : {}),
2471
+ ...(input.topic ? { topic: input.topic } : {}),
2472
+ ...(input.path ? { path: input.path } : {}),
2473
+ kind: input.kind ?? (input.path ? evidenceKindFromPath(input.path) : 'evidence'),
2474
+ status: input.status ?? 'unknown',
2475
+ confidence: clamp01(input.confidence ?? 0.5),
2476
+ tags: uniqueStrings(input.tags ?? []),
2477
+ facets: normalizeFacets(input.facets ?? {}),
2478
+ generatedAt,
2479
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2480
+ };
2481
+ }
2482
+ function matchesFacetQuery(facets, query) {
2483
+ if (!query)
2484
+ return true;
2485
+ return Object.entries(query).every(([key, value]) => facets[key] === value);
2486
+ }
2487
+ function normalizeFacets(input) {
2488
+ return Object.fromEntries(Object.entries(input).filter(([, value]) => ['string', 'number', 'boolean'].includes(typeof value)));
2489
+ }
2490
+ function normalizeBlackboardEntry(input) {
2491
+ const generatedAt = input.generatedAt ?? Date.now();
2492
+ const topic = input.topic ?? input.kind ?? 'general';
2493
+ return {
2494
+ id: input.id ?? 'swarm-blackboard-entry:' + stableHash([input.kind, topic, input.text, input.sourceIds, generatedAt]),
2495
+ kind: input.kind ?? 'fact',
2496
+ topic,
2497
+ status: input.status ?? 'fresh',
2498
+ text: input.text ?? '',
2499
+ ...(input.lane ? { lane: input.lane } : {}),
2500
+ ...(input.jobId ? { jobId: input.jobId } : {}),
2501
+ ...(input.owner ? { owner: input.owner } : {}),
2502
+ confidence: input.confidence ?? 'medium',
2503
+ sourceIds: uniqueStrings(input.sourceIds ?? []),
2504
+ paths: uniqueStrings(input.paths ?? []),
2505
+ tags: uniqueStrings(input.tags ?? []),
2506
+ supersedes: uniqueStrings(input.supersedes ?? []),
2507
+ generatedAt,
2508
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2509
+ };
2510
+ }
2511
+ function normalizeReferenceWindow(input) {
2512
+ return {
2513
+ ...(input.start !== undefined ? { start: Math.max(0, Math.floor(input.start)) } : {}),
2514
+ ...(input.end !== undefined ? { end: Math.max(0, Math.floor(input.end)) } : {}),
2515
+ ...(input.focus ? { focus: input.focus } : {}),
2516
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2517
+ };
2518
+ }
2519
+ function normalizeRoutingHint(input) {
2520
+ return {
2521
+ ...(input.artifactKind ? { artifactKind: input.artifactKind } : {}),
2522
+ ...(input.pathPattern ? { pathPattern: input.pathPattern } : {}),
2523
+ ...(input.lane ? { lane: input.lane } : {}),
2524
+ bucket: input.bucket ?? 'needs-human-port',
2525
+ reason: input.reason ?? 'matched-routing-hint',
2526
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2527
+ };
2528
+ }
2529
+ function defaultArtifactBucket(artifact) {
2530
+ if (artifact.kind === 'patch')
2531
+ return 'ready-to-apply';
2532
+ if (artifact.kind === 'handoff' || artifact.kind === 'trace' || artifact.kind === 'jsonl')
2533
+ return 'discovery-only';
2534
+ return 'needs-human-port';
2535
+ }
2536
+ function normalizeFixture(input) {
2537
+ return {
2538
+ id: normalizeId(input.id, 'fixture id'),
2539
+ title: input.title ?? titleFromId(input.id),
2540
+ ...(input.description ? { description: input.description } : {}),
2541
+ ...(input.state !== undefined ? { state: toJsonValue(input.state) } : {}),
2542
+ artifacts: normalizeNamedRefs(input.artifacts ?? [], 'fixture-artifact'),
2543
+ setupCommands: normalizeCommands(input.setupCommands ?? []),
2544
+ tags: uniqueStrings(input.tags ?? []),
2545
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2546
+ };
2547
+ }
2548
+ function deriveAutoReviewFindings(bundle, generatedAt) {
2549
+ const findings = [];
2550
+ if (bundle.ownershipViolations.length) {
2551
+ findings.push(normalizeAutoReviewFinding({
2552
+ jobId: bundle.jobId,
2553
+ kind: 'ownership-violation',
2554
+ severity: 'error',
2555
+ message: 'Bundle changed paths outside its ownership lease.',
2556
+ paths: bundle.ownershipViolations,
2557
+ evidencePaths: bundle.evidencePaths
2558
+ }, generatedAt));
2559
+ }
2560
+ if (bundle.evidencePaths.length === 0 && bundle.changedPaths.length > 0) {
2561
+ findings.push(normalizeAutoReviewFinding({
2562
+ jobId: bundle.jobId,
2563
+ kind: 'missing-evidence',
2564
+ severity: 'warning',
2565
+ message: 'Patch bundle has changed paths but no evidence paths.',
2566
+ paths: bundle.changedPaths
2567
+ }, generatedAt));
2568
+ }
2569
+ if (bundle.changedPaths.length > 12) {
2570
+ findings.push(normalizeAutoReviewFinding({
2571
+ jobId: bundle.jobId,
2572
+ kind: 'overlarge-patch',
2573
+ severity: 'warning',
2574
+ message: 'Patch bundle touches many files and should be split or reviewed manually.',
2575
+ paths: bundle.changedPaths,
2576
+ evidencePaths: bundle.evidencePaths
2577
+ }, generatedAt));
2578
+ }
2579
+ return findings;
2580
+ }
2581
+ function normalizeAutoReviewFinding(input, generatedAt) {
2582
+ return {
2583
+ id: input.id ?? 'swarm-auto-review-finding:' + stableHash([input.jobId, input.kind, input.message, input.paths, generatedAt]),
2584
+ ...(input.jobId ? { jobId: input.jobId } : {}),
2585
+ kind: input.kind ?? 'weak-evidence',
2586
+ severity: input.severity ?? 'warning',
2587
+ message: input.message,
2588
+ paths: uniqueStrings(input.paths ?? []),
2589
+ evidencePaths: uniqueStrings(input.evidencePaths ?? []),
2590
+ ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2591
+ };
2592
+ }
2593
+ function isSwarmObservabilityPoint(value) {
2594
+ return !!value && typeof value === 'object' && value.kind === FRONTIER_SWARM_OBSERVABILITY_POINT_KIND;
2595
+ }
2596
+ function isSwarmInstrumentationBudget(value) {
2597
+ return !!value && typeof value === 'object' && value.kind === FRONTIER_SWARM_INSTRUMENTATION_BUDGET_KIND;
2598
+ }
2599
+ function isSwarmUsageGovernor(value) {
2600
+ return !!value && typeof value === 'object' && value.kind === FRONTIER_SWARM_USAGE_GOVERNOR_KIND;
2601
+ }
2602
+ function isSwarmRun(value) {
2603
+ return !!value && typeof value === 'object' && value.kind === FRONTIER_SWARM_RUN_KIND;
2604
+ }
2605
+ function groupObjects(items, key) {
2606
+ const out = {};
2607
+ for (const item of items) {
2608
+ const group = key(item);
2609
+ out[group] = [...(out[group] ?? []), item];
2610
+ }
2611
+ return out;
2612
+ }
2613
+ function clamp01(value) {
2614
+ if (!Number.isFinite(value))
2615
+ return 0;
2616
+ return Math.max(0, Math.min(1, value));
2617
+ }
1650
2618
  function groupArtifactIdsBy(artifacts, key) {
1651
2619
  const out = {};
1652
2620
  for (const artifact of artifacts) {
@@ -1696,7 +2664,10 @@ function groupIds(items, key) {
1696
2664
  const out = {};
1697
2665
  for (const item of items) {
1698
2666
  const group = key(item);
1699
- out[group] = [...(out[group] ?? []), item.jobId];
2667
+ const id = item.jobId ?? item.id;
2668
+ if (!id)
2669
+ continue;
2670
+ out[group] = [...(out[group] ?? []), id];
1700
2671
  }
1701
2672
  for (const ids of Object.values(out))
1702
2673
  ids.sort();