@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/README.md +15 -2
- package/benchmarks/package-bench.mjs +39 -0
- package/dist/index.d.ts +918 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +974 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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();
|