@shapeshift-labs/frontier-swarm 0.5.9 → 0.5.11

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.
Files changed (302) hide show
  1. package/README.md +62 -3
  2. package/benchmarks/package-bench-fixtures.mjs +95 -0
  3. package/benchmarks/package-bench-rows.mjs +190 -0
  4. package/benchmarks/package-bench-runtime.mjs +94 -0
  5. package/benchmarks/package-bench.mjs +20 -307
  6. package/dist/adaptive-load-decisions.d.ts +6 -0
  7. package/dist/adaptive-load-decisions.d.ts.map +1 -0
  8. package/dist/adaptive-load-decisions.js +130 -0
  9. package/dist/adaptive-load-decisions.js.map +1 -0
  10. package/dist/adaptive-load-limits.d.ts +8 -0
  11. package/dist/adaptive-load-limits.d.ts.map +1 -0
  12. package/dist/adaptive-load-limits.js +105 -0
  13. package/dist/adaptive-load-limits.js.map +1 -0
  14. package/dist/adaptive-load-observations.d.ts +16 -0
  15. package/dist/adaptive-load-observations.d.ts.map +1 -0
  16. package/dist/adaptive-load-observations.js +248 -0
  17. package/dist/adaptive-load-observations.js.map +1 -0
  18. package/dist/adaptive-load-types.d.ts +108 -0
  19. package/dist/adaptive-load-types.d.ts.map +1 -0
  20. package/dist/adaptive-load-types.js +3 -0
  21. package/dist/adaptive-load-types.js.map +1 -0
  22. package/dist/adaptive-load.d.ts +10 -0
  23. package/dist/adaptive-load.d.ts.map +1 -0
  24. package/dist/adaptive-load.js +87 -0
  25. package/dist/adaptive-load.js.map +1 -0
  26. package/dist/adaptive-types.d.ts +118 -0
  27. package/dist/adaptive-types.d.ts.map +1 -0
  28. package/dist/adaptive-types.js +2 -0
  29. package/dist/adaptive-types.js.map +1 -0
  30. package/dist/artifact-routing-runtime.d.ts +3 -0
  31. package/dist/artifact-routing-runtime.d.ts.map +1 -0
  32. package/dist/artifact-routing-runtime.js +72 -0
  33. package/dist/artifact-routing-runtime.js.map +1 -0
  34. package/dist/auto-review.d.ts +47 -0
  35. package/dist/auto-review.d.ts.map +1 -0
  36. package/dist/auto-review.js +69 -0
  37. package/dist/auto-review.js.map +1 -0
  38. package/dist/blackboard.d.ts +81 -0
  39. package/dist/blackboard.d.ts.map +1 -0
  40. package/dist/blackboard.js +60 -0
  41. package/dist/blackboard.js.map +1 -0
  42. package/dist/coercion.d.ts +12 -0
  43. package/dist/coercion.d.ts.map +1 -0
  44. package/dist/coercion.js +48 -0
  45. package/dist/coercion.js.map +1 -0
  46. package/dist/constants.d.ts +94 -0
  47. package/dist/constants.d.ts.map +1 -0
  48. package/dist/constants.js +94 -0
  49. package/dist/constants.js.map +1 -0
  50. package/dist/context-pack-runtime.d.ts +3 -0
  51. package/dist/context-pack-runtime.d.ts.map +1 -0
  52. package/dist/context-pack-runtime.js +77 -0
  53. package/dist/context-pack-runtime.js.map +1 -0
  54. package/dist/context-types.d.ts +188 -0
  55. package/dist/context-types.d.ts.map +1 -0
  56. package/dist/context-types.js +2 -0
  57. package/dist/context-types.js.map +1 -0
  58. package/dist/coordinator-dashboard-helpers.d.ts +14 -0
  59. package/dist/coordinator-dashboard-helpers.d.ts.map +1 -0
  60. package/dist/coordinator-dashboard-helpers.js +106 -0
  61. package/dist/coordinator-dashboard-helpers.js.map +1 -0
  62. package/dist/coordinator-dashboard-types.d.ts +161 -0
  63. package/dist/coordinator-dashboard-types.d.ts.map +1 -0
  64. package/dist/coordinator-dashboard-types.js +3 -0
  65. package/dist/coordinator-dashboard-types.js.map +1 -0
  66. package/dist/coordinator-dashboard.d.ts +4 -0
  67. package/dist/coordinator-dashboard.d.ts.map +1 -0
  68. package/dist/coordinator-dashboard.js +161 -0
  69. package/dist/coordinator-dashboard.js.map +1 -0
  70. package/dist/coordinator-scoring.d.ts +8 -0
  71. package/dist/coordinator-scoring.d.ts.map +1 -0
  72. package/dist/coordinator-scoring.js +134 -0
  73. package/dist/coordinator-scoring.js.map +1 -0
  74. package/dist/coordinator-types.d.ts +164 -0
  75. package/dist/coordinator-types.d.ts.map +1 -0
  76. package/dist/coordinator-types.js +2 -0
  77. package/dist/coordinator-types.js.map +1 -0
  78. package/dist/debug-runtime.d.ts +4 -0
  79. package/dist/debug-runtime.d.ts.map +1 -0
  80. package/dist/debug-runtime.js +93 -0
  81. package/dist/debug-runtime.js.map +1 -0
  82. package/dist/debug-types.d.ts +194 -0
  83. package/dist/debug-types.d.ts.map +1 -0
  84. package/dist/debug-types.js +2 -0
  85. package/dist/debug-types.js.map +1 -0
  86. package/dist/diagnostic-helpers.d.ts +5 -0
  87. package/dist/diagnostic-helpers.d.ts.map +1 -0
  88. package/dist/diagnostic-helpers.js +54 -0
  89. package/dist/diagnostic-helpers.js.map +1 -0
  90. package/dist/evidence-index-helpers.d.ts +10 -0
  91. package/dist/evidence-index-helpers.d.ts.map +1 -0
  92. package/dist/evidence-index-helpers.js +78 -0
  93. package/dist/evidence-index-helpers.js.map +1 -0
  94. package/dist/evidence-index.d.ts +4 -0
  95. package/dist/evidence-index.d.ts.map +1 -0
  96. package/dist/evidence-index.js +59 -0
  97. package/dist/evidence-index.js.map +1 -0
  98. package/dist/evidence-types.d.ts +151 -0
  99. package/dist/evidence-types.d.ts.map +1 -0
  100. package/dist/evidence-types.js +2 -0
  101. package/dist/evidence-types.js.map +1 -0
  102. package/dist/fixtures.d.ts +45 -0
  103. package/dist/fixtures.d.ts.map +1 -0
  104. package/dist/fixtures.js +39 -0
  105. package/dist/fixtures.js.map +1 -0
  106. package/dist/governance-types.d.ts +172 -0
  107. package/dist/governance-types.d.ts.map +1 -0
  108. package/dist/governance-types.js +2 -0
  109. package/dist/governance-types.js.map +1 -0
  110. package/dist/index.d.ts +35 -2933
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +34 -4526
  113. package/dist/index.js.map +1 -1
  114. package/dist/instrumentation-runtime.d.ts +6 -0
  115. package/dist/instrumentation-runtime.d.ts.map +1 -0
  116. package/dist/instrumentation-runtime.js +111 -0
  117. package/dist/instrumentation-runtime.js.map +1 -0
  118. package/dist/internal.d.ts +11 -0
  119. package/dist/internal.d.ts.map +1 -0
  120. package/dist/internal.js +49 -0
  121. package/dist/internal.js.map +1 -0
  122. package/dist/leases.d.ts +32 -0
  123. package/dist/leases.d.ts.map +1 -0
  124. package/dist/leases.js +36 -0
  125. package/dist/leases.js.map +1 -0
  126. package/dist/manifest-normalization.d.ts +8 -0
  127. package/dist/manifest-normalization.d.ts.map +1 -0
  128. package/dist/manifest-normalization.js +191 -0
  129. package/dist/manifest-normalization.js.map +1 -0
  130. package/dist/manifest-types.d.ts +227 -0
  131. package/dist/manifest-types.d.ts.map +1 -0
  132. package/dist/manifest-types.js +2 -0
  133. package/dist/manifest-types.js.map +1 -0
  134. package/dist/manifest.d.ts +7 -0
  135. package/dist/manifest.d.ts.map +1 -0
  136. package/dist/manifest.js +126 -0
  137. package/dist/manifest.js.map +1 -0
  138. package/dist/merge-admission.d.ts +3 -0
  139. package/dist/merge-admission.d.ts.map +1 -0
  140. package/dist/merge-admission.js +71 -0
  141. package/dist/merge-admission.js.map +1 -0
  142. package/dist/merge-bundles.d.ts +13 -0
  143. package/dist/merge-bundles.d.ts.map +1 -0
  144. package/dist/merge-bundles.js +144 -0
  145. package/dist/merge-bundles.js.map +1 -0
  146. package/dist/merge-classification.d.ts +6 -0
  147. package/dist/merge-classification.d.ts.map +1 -0
  148. package/dist/merge-classification.js +36 -0
  149. package/dist/merge-classification.js.map +1 -0
  150. package/dist/merge-index.d.ts +3 -0
  151. package/dist/merge-index.d.ts.map +1 -0
  152. package/dist/merge-index.js +130 -0
  153. package/dist/merge-index.js.map +1 -0
  154. package/dist/merge-plan-helpers.d.ts +11 -0
  155. package/dist/merge-plan-helpers.d.ts.map +1 -0
  156. package/dist/merge-plan-helpers.js +97 -0
  157. package/dist/merge-plan-helpers.js.map +1 -0
  158. package/dist/merge-plans-runtime.d.ts +8 -0
  159. package/dist/merge-plans-runtime.d.ts.map +1 -0
  160. package/dist/merge-plans-runtime.js +106 -0
  161. package/dist/merge-plans-runtime.js.map +1 -0
  162. package/dist/merge-review-types.d.ts +76 -0
  163. package/dist/merge-review-types.d.ts.map +1 -0
  164. package/dist/merge-review-types.js +2 -0
  165. package/dist/merge-review-types.js.map +1 -0
  166. package/dist/merge-review.d.ts +7 -0
  167. package/dist/merge-review.d.ts.map +1 -0
  168. package/dist/merge-review.js +142 -0
  169. package/dist/merge-review.js.map +1 -0
  170. package/dist/merge-types.d.ts +196 -0
  171. package/dist/merge-types.d.ts.map +1 -0
  172. package/dist/merge-types.js +2 -0
  173. package/dist/merge-types.js.map +1 -0
  174. package/dist/merge-wrappers.d.ts +5 -0
  175. package/dist/merge-wrappers.d.ts.map +1 -0
  176. package/dist/merge-wrappers.js +198 -0
  177. package/dist/merge-wrappers.js.map +1 -0
  178. package/dist/observability-types.d.ts +164 -0
  179. package/dist/observability-types.d.ts.map +1 -0
  180. package/dist/observability-types.js +2 -0
  181. package/dist/observability-types.js.map +1 -0
  182. package/dist/oracle-helpers.d.ts +6 -0
  183. package/dist/oracle-helpers.d.ts.map +1 -0
  184. package/dist/oracle-helpers.js +73 -0
  185. package/dist/oracle-helpers.js.map +1 -0
  186. package/dist/oracle-runtime.d.ts +5 -0
  187. package/dist/oracle-runtime.d.ts.map +1 -0
  188. package/dist/oracle-runtime.js +154 -0
  189. package/dist/oracle-runtime.js.map +1 -0
  190. package/dist/ownership-runtime.d.ts +6 -0
  191. package/dist/ownership-runtime.d.ts.map +1 -0
  192. package/dist/ownership-runtime.js +49 -0
  193. package/dist/ownership-runtime.js.map +1 -0
  194. package/dist/patch-stack-runtime.d.ts +4 -0
  195. package/dist/patch-stack-runtime.d.ts.map +1 -0
  196. package/dist/patch-stack-runtime.js +97 -0
  197. package/dist/patch-stack-runtime.js.map +1 -0
  198. package/dist/plan-helpers.d.ts +13 -0
  199. package/dist/plan-helpers.d.ts.map +1 -0
  200. package/dist/plan-helpers.js +236 -0
  201. package/dist/plan-helpers.js.map +1 -0
  202. package/dist/plan-selection.d.ts +7 -0
  203. package/dist/plan-selection.d.ts.map +1 -0
  204. package/dist/plan-selection.js +86 -0
  205. package/dist/plan-selection.js.map +1 -0
  206. package/dist/plan.d.ts +126 -0
  207. package/dist/plan.d.ts.map +1 -0
  208. package/dist/plan.js +67 -0
  209. package/dist/plan.js.map +1 -0
  210. package/dist/progress.d.ts +42 -0
  211. package/dist/progress.d.ts.map +1 -0
  212. package/dist/progress.js +31 -0
  213. package/dist/progress.js.map +1 -0
  214. package/dist/proof-runtime.d.ts +16 -0
  215. package/dist/proof-runtime.d.ts.map +1 -0
  216. package/dist/proof-runtime.js +143 -0
  217. package/dist/proof-runtime.js.map +1 -0
  218. package/dist/queue-overlays.d.ts +7 -0
  219. package/dist/queue-overlays.d.ts.map +1 -0
  220. package/dist/queue-overlays.js +184 -0
  221. package/dist/queue-overlays.js.map +1 -0
  222. package/dist/queue-types.d.ts +194 -0
  223. package/dist/queue-types.d.ts.map +1 -0
  224. package/dist/queue-types.js +2 -0
  225. package/dist/queue-types.js.map +1 -0
  226. package/dist/rebase-report.d.ts +3 -0
  227. package/dist/rebase-report.d.ts.map +1 -0
  228. package/dist/rebase-report.js +52 -0
  229. package/dist/rebase-report.js.map +1 -0
  230. package/dist/record-helpers.d.ts +15 -0
  231. package/dist/record-helpers.d.ts.map +1 -0
  232. package/dist/record-helpers.js +78 -0
  233. package/dist/record-helpers.js.map +1 -0
  234. package/dist/reference-oracle-runtime.d.ts +4 -0
  235. package/dist/reference-oracle-runtime.d.ts.map +1 -0
  236. package/dist/reference-oracle-runtime.js +89 -0
  237. package/dist/reference-oracle-runtime.js.map +1 -0
  238. package/dist/reference-types.d.ts +204 -0
  239. package/dist/reference-types.d.ts.map +1 -0
  240. package/dist/reference-types.js +2 -0
  241. package/dist/reference-types.js.map +1 -0
  242. package/dist/result-types.d.ts +75 -0
  243. package/dist/result-types.d.ts.map +1 -0
  244. package/dist/result-types.js +2 -0
  245. package/dist/result-types.js.map +1 -0
  246. package/dist/run-normalization.d.ts +9 -0
  247. package/dist/run-normalization.d.ts.map +1 -0
  248. package/dist/run-normalization.js +126 -0
  249. package/dist/run-normalization.js.map +1 -0
  250. package/dist/run-records.d.ts +8 -0
  251. package/dist/run-records.d.ts.map +1 -0
  252. package/dist/run-records.js +145 -0
  253. package/dist/run-records.js.map +1 -0
  254. package/dist/run-store-shards-runtime.d.ts +3 -0
  255. package/dist/run-store-shards-runtime.d.ts.map +1 -0
  256. package/dist/run-store-shards-runtime.js +54 -0
  257. package/dist/run-store-shards-runtime.js.map +1 -0
  258. package/dist/run-types.d.ts +112 -0
  259. package/dist/run-types.d.ts.map +1 -0
  260. package/dist/run-types.js +2 -0
  261. package/dist/run-types.js.map +1 -0
  262. package/dist/scheduler.d.ts +94 -0
  263. package/dist/scheduler.d.ts.map +1 -0
  264. package/dist/scheduler.js +213 -0
  265. package/dist/scheduler.js.map +1 -0
  266. package/dist/semantic-normalization.d.ts +3 -0
  267. package/dist/semantic-normalization.d.ts.map +1 -0
  268. package/dist/semantic-normalization.js +162 -0
  269. package/dist/semantic-normalization.js.map +1 -0
  270. package/dist/semantic-types.d.ts +229 -0
  271. package/dist/semantic-types.d.ts.map +1 -0
  272. package/dist/semantic-types.js +2 -0
  273. package/dist/semantic-types.js.map +1 -0
  274. package/dist/status-types.d.ts +28 -0
  275. package/dist/status-types.d.ts.map +1 -0
  276. package/dist/status-types.js +2 -0
  277. package/dist/status-types.js.map +1 -0
  278. package/dist/task-types.d.ts +165 -0
  279. package/dist/task-types.d.ts.map +1 -0
  280. package/dist/task-types.js +2 -0
  281. package/dist/task-types.js.map +1 -0
  282. package/dist/trace-helpers.d.ts +22 -0
  283. package/dist/trace-helpers.d.ts.map +1 -0
  284. package/dist/trace-helpers.js +181 -0
  285. package/dist/trace-helpers.js.map +1 -0
  286. package/dist/trace-runtime.d.ts +7 -0
  287. package/dist/trace-runtime.d.ts.map +1 -0
  288. package/dist/trace-runtime.js +196 -0
  289. package/dist/trace-runtime.js.map +1 -0
  290. package/dist/trace-types.d.ts +168 -0
  291. package/dist/trace-types.d.ts.map +1 -0
  292. package/dist/trace-types.js +2 -0
  293. package/dist/trace-types.js.map +1 -0
  294. package/dist/types.d.ts +19 -0
  295. package/dist/types.d.ts.map +1 -0
  296. package/dist/types.js +2 -0
  297. package/dist/types.js.map +1 -0
  298. package/dist/usage-governor.d.ts +41 -0
  299. package/dist/usage-governor.d.ts.map +1 -0
  300. package/dist/usage-governor.js +43 -0
  301. package/dist/usage-governor.js.map +1 -0
  302. package/package.json +6 -4
package/dist/index.js CHANGED
@@ -1,4527 +1,35 @@
1
- export const FRONTIER_SWARM_MANIFEST_KIND = 'frontier.swarm.manifest';
2
- export const FRONTIER_SWARM_MANIFEST_VERSION = 1;
3
- export const FRONTIER_SWARM_TASK_KIND = 'frontier.swarm.task';
4
- export const FRONTIER_SWARM_TASK_VERSION = 1;
5
- export const FRONTIER_SWARM_PLAN_KIND = 'frontier.swarm.plan';
6
- export const FRONTIER_SWARM_PLAN_VERSION = 1;
7
- export const FRONTIER_SWARM_RUN_KIND = 'frontier.swarm.run';
8
- export const FRONTIER_SWARM_RUN_VERSION = 1;
9
- export const FRONTIER_SWARM_EVENT_KIND = 'frontier.swarm.event';
10
- export const FRONTIER_SWARM_EVENT_VERSION = 1;
11
- export const FRONTIER_SWARM_EVENT_STREAM_KIND = 'frontier.swarm.event-stream';
12
- export const FRONTIER_SWARM_EVENT_STREAM_VERSION = 1;
13
- export const FRONTIER_SWARM_MAILBOX_KIND = 'frontier.swarm.mailbox';
14
- export const FRONTIER_SWARM_MAILBOX_VERSION = 1;
15
- export const FRONTIER_SWARM_PROOF_KIND = 'frontier.swarm.proof';
16
- export const FRONTIER_SWARM_PROOF_VERSION = 1;
17
- export const FRONTIER_SWARM_SCHEDULE_KIND = 'frontier.swarm.schedule';
18
- export const FRONTIER_SWARM_SCHEDULE_VERSION = 1;
19
- export const FRONTIER_SWARM_LEASE_KIND = 'frontier.swarm.lease';
20
- export const FRONTIER_SWARM_LEASE_VERSION = 1;
21
- export const FRONTIER_SWARM_QUEUE_SNAPSHOT_KIND = 'frontier.swarm.queue-snapshot';
22
- export const FRONTIER_SWARM_QUEUE_SNAPSHOT_VERSION = 1;
23
- export const FRONTIER_SWARM_RUN_CHECKPOINT_KIND = 'frontier.swarm.run-checkpoint';
24
- export const FRONTIER_SWARM_RUN_CHECKPOINT_VERSION = 1;
25
- export const FRONTIER_SWARM_ARTIFACT_INDEX_KIND = 'frontier.swarm.artifact-index';
26
- export const FRONTIER_SWARM_ARTIFACT_INDEX_VERSION = 1;
27
- export const FRONTIER_SWARM_REVIEW_PLAN_KIND = 'frontier.swarm.review-plan';
28
- export const FRONTIER_SWARM_REVIEW_PLAN_VERSION = 1;
29
- export const FRONTIER_SWARM_MERGE_PLAN_KIND = 'frontier.swarm.merge-plan';
30
- export const FRONTIER_SWARM_MERGE_PLAN_VERSION = 1;
31
- export const FRONTIER_SWARM_MERGE_BUNDLE_KIND = 'frontier.swarm.merge-bundle';
32
- export const FRONTIER_SWARM_MERGE_BUNDLE_VERSION = 1;
33
- export const FRONTIER_SWARM_QUEUE_OVERLAY_KIND = 'frontier.swarm.queue-overlay';
34
- export const FRONTIER_SWARM_QUEUE_OVERLAY_VERSION = 1;
35
- export const FRONTIER_SWARM_MERGE_INDEX_KIND = 'frontier.swarm.merge-index';
36
- export const FRONTIER_SWARM_MERGE_INDEX_VERSION = 1;
37
- export const FRONTIER_SWARM_HOTSPOT_REPORT_KIND = 'frontier.swarm.hotspot-report';
38
- export const FRONTIER_SWARM_HOTSPOT_REPORT_VERSION = 1;
39
- export const FRONTIER_SWARM_REVIEWER_LANE_PLAN_KIND = 'frontier.swarm.reviewer-lane-plan';
40
- export const FRONTIER_SWARM_REVIEWER_LANE_PLAN_VERSION = 1;
41
- export const FRONTIER_SWARM_RUN_STORE_SHARDS_KIND = 'frontier.swarm.run-store-shards';
42
- export const FRONTIER_SWARM_RUN_STORE_SHARDS_VERSION = 1;
43
- export const FRONTIER_SWARM_MERGE_ADMISSION_KIND = 'frontier.swarm.merge-admission';
44
- export const FRONTIER_SWARM_MERGE_ADMISSION_VERSION = 1;
45
- export const FRONTIER_SWARM_CONTEXT_PACK_KIND = 'frontier.swarm.context-pack';
46
- export const FRONTIER_SWARM_CONTEXT_PACK_VERSION = 1;
47
- export const FRONTIER_SWARM_ORACLE_CORPUS_KIND = 'frontier.swarm.oracle-corpus';
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;
87
- export const FRONTIER_SWARM_LANE_PLAYBOOK_KIND = 'frontier.swarm.lane-playbook';
88
- export const FRONTIER_SWARM_LANE_PLAYBOOK_VERSION = 1;
89
- export const FRONTIER_SWARM_PATCH_STACK_PLAN_KIND = 'frontier.swarm.patch-stack-plan';
90
- export const FRONTIER_SWARM_PATCH_STACK_PLAN_VERSION = 1;
91
- export const FRONTIER_SWARM_COORDINATOR_DASHBOARD_KIND = 'frontier.swarm.coordinator-dashboard';
92
- export const FRONTIER_SWARM_COORDINATOR_DASHBOARD_VERSION = 1;
93
- export const FRONTIER_SWARM_ADAPTIVE_LOAD_PLAN_KIND = 'frontier.swarm.adaptive-load-plan';
94
- export const FRONTIER_SWARM_ADAPTIVE_LOAD_PLAN_VERSION = 1;
95
- export const FRONTIER_SWARM_DEFAULT_CODEX_COMPUTE_ID = 'codex.gpt-5.5.xhigh';
96
- export const FRONTIER_SWARM_DEFAULT_MODEL = 'gpt-5.5';
97
- export const FRONTIER_SWARM_DEFAULT_REASONING_EFFORT = 'xhigh';
98
- const DEFAULT_COMPLETED_STATUSES = ['completed', 'verified', 'done', 'verified-local-harness'];
99
- const DEFAULT_SWARM_EVENT_TYPES = [
100
- 'swarm.started',
101
- 'swarm.finished',
102
- 'agent.scheduled',
103
- 'agent.finished',
104
- 'agent.handoff',
105
- 'agent.blocked',
106
- 'agent.ownership-request',
107
- 'agent.evidence',
108
- 'review.requested',
109
- 'review.completed',
110
- 'merge.proposed'
111
- ];
112
- export function defineSwarmManifest(input = {}) {
113
- return createSwarmManifest(input);
114
- }
115
- export function createSwarmManifest(input = {}) {
116
- const compute = normalizeComputeList(input.compute);
117
- const defaultCompute = input.policy?.defaultCompute ?? compute[0]?.id ?? FRONTIER_SWARM_DEFAULT_CODEX_COMPUTE_ID;
118
- const layers = (input.layers ?? []).map(normalizeLayer);
119
- const lanes = (input.lanes ?? []).map(normalizeLane);
120
- const policy = normalizePolicy(input.policy, defaultCompute);
121
- return {
122
- kind: FRONTIER_SWARM_MANIFEST_KIND,
123
- version: FRONTIER_SWARM_MANIFEST_VERSION,
124
- id: normalizeId(input.id ?? 'frontier-swarm', 'manifest id'),
125
- title: input.title ?? titleFromId(input.id ?? 'frontier swarm'),
126
- ...(input.description ? { description: input.description } : {}),
127
- ...(input.package ? { package: input.package } : {}),
128
- ...(input.feature ? { feature: input.feature } : {}),
129
- ...(input.owner ? { owner: input.owner } : {}),
130
- compute,
131
- layers,
132
- lanes,
133
- policy,
134
- resources: uniqueStrings(input.resources ?? []),
135
- tags: uniqueStrings(input.tags ?? []),
136
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {}),
137
- summary: {
138
- computeCount: compute.length,
139
- layerCount: layers.length,
140
- laneCount: lanes.length
141
- }
142
- };
143
- }
144
- export function defineSwarmTasks(input = []) {
145
- const raw = Array.isArray(input) ? input : (input.tasks ?? input.items ?? []);
146
- return raw.map(normalizeTask);
147
- }
148
- export function compileSwarm(input) {
149
- const manifest = isSwarmManifest(input) ? cloneJsonValue(input) : createSwarmManifest(input);
150
- const computeById = new Map(manifest.compute.map((compute) => [compute.id, compute]));
151
- const layersById = new Map(manifest.layers.map((layer) => [layer.id, layer]));
152
- const lanesById = new Map(manifest.lanes.map((lane) => [lane.id, lane]));
153
- return {
154
- manifest,
155
- computeById,
156
- layersById,
157
- lanesById,
158
- validation: validateSwarmManifest(manifest)
159
- };
160
- }
161
- export function validateSwarmManifest(input) {
162
- const manifest = isSwarmManifest(input) ? input : createSwarmManifest(input);
163
- const issues = [];
164
- const computeIds = new Set();
165
- const layerIds = new Set();
166
- const laneIds = new Set();
167
- for (const compute of manifest.compute) {
168
- if (computeIds.has(compute.id))
169
- addIssue(issues, 'duplicate-compute', 'error', `compute.${compute.id}`, `Duplicate compute id: ${compute.id}`);
170
- computeIds.add(compute.id);
171
- }
172
- if (!computeIds.has(manifest.policy.defaultCompute)) {
173
- addIssue(issues, 'missing-default-compute', 'error', 'policy.defaultCompute', `Default compute is not declared: ${manifest.policy.defaultCompute}`);
174
- }
175
- for (const layer of manifest.layers) {
176
- if (layerIds.has(layer.id))
177
- addIssue(issues, 'duplicate-layer', 'error', `layers.${layer.id}`, `Duplicate layer id: ${layer.id}`);
178
- layerIds.add(layer.id);
179
- }
180
- for (const layer of manifest.layers) {
181
- if (layer.parentId && !layerIds.has(layer.parentId)) {
182
- addIssue(issues, 'missing-parent-layer', 'error', `layers.${layer.id}.parentId`, `Layer parent is not declared: ${layer.parentId}`);
183
- }
184
- for (const [childLayer, compute] of Object.entries(layer.childCompute)) {
185
- if (childLayer !== '*' && !layerIds.has(childLayer)) {
186
- addIssue(issues, 'missing-child-layer', 'error', `layers.${layer.id}.childCompute.${childLayer}`, `Child layer is not declared: ${childLayer}`);
187
- }
188
- if (!computeIds.has(compute)) {
189
- addIssue(issues, 'missing-child-compute', 'error', `layers.${layer.id}.childCompute.${childLayer}`, `Child compute is not declared: ${compute}`);
190
- }
191
- }
192
- for (const field of ['compute', 'defaultCompute']) {
193
- const compute = layer[field];
194
- if (compute && !computeIds.has(compute)) {
195
- addIssue(issues, 'missing-layer-compute', 'error', `layers.${layer.id}.${field}`, `Layer compute is not declared: ${compute}`);
196
- }
197
- }
198
- if (hasLayerCycle(layer.id, manifest.layers)) {
199
- addIssue(issues, 'layer-cycle', 'error', `layers.${layer.id}`, `Layer parent chain contains a cycle at ${layer.id}`);
200
- }
201
- }
202
- for (const lane of manifest.lanes) {
203
- if (laneIds.has(lane.id))
204
- addIssue(issues, 'duplicate-lane', 'error', `lanes.${lane.id}`, `Duplicate lane id: ${lane.id}`);
205
- laneIds.add(lane.id);
206
- if (lane.layer && !layerIds.has(lane.layer)) {
207
- addIssue(issues, 'missing-lane-layer', 'error', `lanes.${lane.id}.layer`, `Lane layer is not declared: ${lane.layer}`);
208
- }
209
- if (lane.compute && !computeIds.has(lane.compute)) {
210
- addIssue(issues, 'missing-lane-compute', 'error', `lanes.${lane.id}.compute`, `Lane compute is not declared: ${lane.compute}`);
211
- }
212
- }
213
- return { valid: issues.every((issue) => issue.severity !== 'error'), issues };
214
- }
215
- export function createSwarmPlan(manifestInput, taskInput, options = {}) {
216
- const compiled = compileSwarm(manifestInput);
217
- const tasks = normalizeTaskList(taskInput);
218
- const jobs = selectSwarmTasks(compiled.manifest, tasks, options).map((task) => createJob(compiled, task, options));
219
- const id = options.id ?? 'swarm-plan:' + stableHash([compiled.manifest.id, jobs.map((job) => job.id), options]);
220
- const graph = createSwarmJobGraph(jobs);
221
- const validation = validateTasksForManifest(compiled, tasks, graph);
222
- return {
223
- kind: FRONTIER_SWARM_PLAN_KIND,
224
- version: FRONTIER_SWARM_PLAN_VERSION,
225
- id,
226
- runId: options.runId ?? id.replace(/^swarm-plan:/, 'swarm-run:'),
227
- manifestId: compiled.manifest.id,
228
- createdAt: options.now ?? Date.now(),
229
- filters: {
230
- lanes: options.lanes ? [...options.lanes] : undefined,
231
- layers: options.layers ? [...options.layers] : undefined,
232
- statuses: options.statuses ? [...options.statuses] : undefined,
233
- selectors: options.selectors ? [...options.selectors] : undefined,
234
- includeCompleted: options.includeCompleted,
235
- limit: options.limit,
236
- compute: options.compute
237
- },
238
- limits: normalizeScheduleLimits(compiled.manifest, options),
239
- validation,
240
- jobs,
241
- graph,
242
- summary: summarizeJobs(jobs),
243
- ...(toJsonObject(options.metadata) ? { metadata: toJsonObject(options.metadata) } : {})
244
- };
245
- }
246
- export function createSwarmTaskSelection(manifestInput, taskInput, options = {}) {
247
- const manifest = compileSwarm(manifestInput).manifest;
248
- const tasks = normalizeTaskList(taskInput);
249
- const lanes = new Set(options.lanes ?? []);
250
- const layers = new Set(options.layers ?? []);
251
- const statuses = new Set(options.statuses ?? []);
252
- const workKinds = new Set(options.workKinds ?? []);
253
- const selectors = (options.selectors ?? []).map((selector) => selector.toLowerCase());
254
- const completed = new Set(manifest.policy.completedStatuses);
255
- const limit = options.limit === undefined ? tasks.length : Math.max(0, Math.floor(options.limit));
256
- const candidates = tasks
257
- .filter((task) => !task.lane || manifest.lanes.some((lane) => lane.id === task.lane))
258
- .filter((task) => lanes.size === 0 || (task.lane !== undefined && lanes.has(task.lane)))
259
- .filter((task) => layers.size === 0 || taskLayer(manifest, task) !== undefined && layers.has(taskLayer(manifest, task)))
260
- .filter((task) => statuses.size === 0 || statuses.has(task.status))
261
- .filter((task) => workKinds.size === 0 || workKinds.has(task.workKind))
262
- .filter((task) => options.includeCompleted || !completed.has(task.status))
263
- .filter((task) => selectors.length === 0 || selectors.some((selector) => searchableTask(task).includes(selector)))
264
- .map((task) => createSelectionEntry(manifest, task, options.priority))
265
- .filter((entry) => options.includeOwnershipWarnings || entry.ownershipWarnings.length === 0)
266
- .sort((left, right) => (left.selectionPriority - right.selectionPriority
267
- || left.task.priority - right.task.priority
268
- || left.task.id.localeCompare(right.task.id)));
269
- const ordered = options.spreadLanes ? roundRobinSelectionByLane(candidates) : candidates;
270
- const entries = ordered.slice(0, limit).map((entry, index) => {
271
- if (!options.assignSelectionPriority)
272
- return entry;
273
- return { ...entry, task: { ...entry.task, priority: index } };
274
- });
275
- return {
276
- tasks: entries.map((entry) => entry.task),
277
- entries,
278
- summary: summarizeTaskSelection(entries)
279
- };
280
- }
281
- export function createSwarmRun(input) {
282
- const results = (input.results ?? []).map(normalizeResult);
283
- const events = (input.events ?? []).map((event) => normalizeEvent({ ...event, runId: event.runId ?? input.id ?? input.plan.runId }));
284
- const run = {
285
- kind: FRONTIER_SWARM_RUN_KIND,
286
- version: FRONTIER_SWARM_RUN_VERSION,
287
- id: input.id ?? input.plan.runId,
288
- planId: input.plan.id,
289
- manifestId: input.plan.manifestId,
290
- startedAt: input.startedAt ?? Date.now(),
291
- status: input.status ?? 'planned',
292
- jobs: input.plan.jobs.map((job) => cloneJsonValue(job)),
293
- events,
294
- results,
295
- summary: summarizeRun(input.plan.jobs, results),
296
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
297
- };
298
- return run;
299
- }
300
- export function recordSwarmEvent(runInput, eventInput) {
301
- const run = cloneJsonValue(runInput);
302
- run.events = run.events.concat(normalizeEvent({ ...eventInput, runId: eventInput.runId ?? run.id }));
303
- return run;
304
- }
305
- export function createSwarmMailbox(input = {}) {
306
- const scope = input.scope ?? (input.lane ? 'lane' : input.jobId ? 'job' : 'global');
307
- const eventTypes = uniqueStrings(input.eventTypes ?? DEFAULT_SWARM_EVENT_TYPES);
308
- return {
309
- kind: FRONTIER_SWARM_MAILBOX_KIND,
310
- version: FRONTIER_SWARM_MAILBOX_VERSION,
311
- id: input.id ?? 'swarm-mailbox:' + stableHash([input.runId, scope, input.lane, input.jobId, input.path, eventTypes]),
312
- ...(input.runId ? { runId: input.runId } : {}),
313
- scope,
314
- ...(input.lane ? { lane: input.lane } : {}),
315
- ...(input.jobId ? { jobId: input.jobId } : {}),
316
- ...(input.path ? { path: input.path } : {}),
317
- eventTypes,
318
- appendOnly: input.appendOnly ?? true,
319
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
320
- };
321
- }
322
- export function createSwarmEventStream(input = {}) {
323
- const laneIds = uniqueStrings((input.lanes ?? []).map(readLaneId));
324
- const eventTypes = uniqueStrings(input.eventTypes ?? DEFAULT_SWARM_EVENT_TYPES);
325
- const appendOnly = input.appendOnly ?? true;
326
- const global = createSwarmMailbox({
327
- runId: input.runId,
328
- scope: 'global',
329
- path: input.root ? joinPathParts(input.root, 'global.jsonl') : undefined,
330
- eventTypes,
331
- appendOnly
332
- });
333
- const lanes = Object.fromEntries(laneIds.map((lane) => [lane, createSwarmMailbox({
334
- runId: input.runId,
335
- scope: 'lane',
336
- lane,
337
- path: input.root ? joinPathParts(input.root, 'lanes', `${lane}.jsonl`) : undefined,
338
- eventTypes,
339
- appendOnly
340
- })]));
341
- return {
342
- kind: FRONTIER_SWARM_EVENT_STREAM_KIND,
343
- version: FRONTIER_SWARM_EVENT_STREAM_VERSION,
344
- id: input.id ?? 'swarm-event-stream:' + stableHash([input.runId, input.root, laneIds, eventTypes]),
345
- ...(input.runId ? { runId: input.runId } : {}),
346
- ...(input.root ? { root: input.root } : {}),
347
- appendOnly,
348
- global,
349
- lanes,
350
- eventTypes,
351
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {}),
352
- summary: {
353
- mailboxCount: 1 + laneIds.length,
354
- laneCount: laneIds.length,
355
- eventTypeCount: eventTypes.length
356
- }
357
- };
358
- }
359
- export function routeSwarmEventToMailboxes(stream, eventInput) {
360
- const event = isSwarmEvent(eventInput) ? eventInput : normalizeEvent(eventInput);
361
- const mailboxes = [stream.global];
362
- if (event.lane && stream.lanes[event.lane])
363
- mailboxes.push(stream.lanes[event.lane]);
364
- return mailboxes;
365
- }
366
- export function completeSwarmJob(runInput, resultInput) {
367
- const run = cloneJsonValue(runInput);
368
- const result = normalizeResult(resultInput);
369
- const resultIndex = run.results.findIndex((entry) => entry.jobId === result.jobId);
370
- if (resultIndex >= 0)
371
- run.results[resultIndex] = result;
372
- else
373
- run.results.push(result);
374
- run.jobs = run.jobs.map((job) => job.id === result.jobId ? { ...job, status: result.status } : job);
375
- run.summary = summarizeRun(run.jobs, run.results);
376
- if (run.results.length >= run.jobs.length && run.summary.failedCount === 0 && run.summary.blockedCount === 0) {
377
- run.status = 'completed';
378
- run.finishedAt = result.finishedAt ?? Date.now();
379
- }
380
- else if (run.summary.failedCount && run.summary.failedCount > 0) {
381
- run.status = 'failed';
382
- }
383
- return run;
384
- }
385
- export function checkSwarmOwnership(job, changedPaths) {
386
- const changed = uniqueStrings(changedPaths);
387
- const violations = changed.filter((file) => !job.allowedWrites.some((glob) => matchesGlob(file, glob)));
388
- return {
389
- ok: violations.length === 0,
390
- changedPaths: changed,
391
- allowedWrites: [...job.allowedWrites],
392
- violations
393
- };
394
- }
395
- export function resolveSwarmChangedRegions(job, changedPaths) {
396
- const changed = uniqueStrings(changedPaths);
397
- const regions = new Set(job.changedRegions);
398
- for (const region of job.ownershipRegions) {
399
- if (region.globs.some((glob) => changed.some((file) => matchesGlob(file, glob))))
400
- regions.add(region.id);
401
- for (const selector of region.selectors) {
402
- if (changed.includes(selector))
403
- regions.add(region.id);
404
- }
405
- }
406
- return Array.from(regions).sort();
407
- }
408
- export function classifySwarmMergeReadiness(result) {
409
- if (result.mergeReadiness)
410
- return result.mergeReadiness;
411
- if (result.status === 'blocked')
412
- return 'blocked';
413
- if (result.status === 'failed' || result.exitCode !== undefined && result.exitCode !== 0)
414
- return 'rejected';
415
- const changedPaths = result.changedPaths ?? [];
416
- if (changedPaths.length === 0)
417
- return 'discovery-only';
418
- const ownershipViolations = result.ownershipViolations ?? [];
419
- if (ownershipViolations.length)
420
- return 'rejected';
421
- const verification = result.verification ?? [];
422
- const failedRequired = verification.some((entry) => entry.required !== false && entry.status !== 0);
423
- if (failedRequired)
424
- return 'patch-candidate';
425
- return verification.length > 0 || result.status === 'verified' ? 'verified-patch' : 'patch-candidate';
426
- }
427
- export function classifySwarmMergeDisposition(result, input = {}) {
428
- if (result.mergeDisposition)
429
- return result.mergeDisposition;
430
- if (input.staleAgainstHead)
431
- return 'stale-against-head';
432
- const readiness = classifySwarmMergeReadiness(result);
433
- if (readiness === 'discovery-only')
434
- return 'discovery-only';
435
- if (readiness === 'blocked')
436
- return 'blocked';
437
- if (readiness === 'rejected')
438
- return 'rejected';
439
- if (readiness === 'verified-patch')
440
- return 'auto-mergeable';
441
- return 'needs-port';
442
- }
443
- export function createSwarmMergeBundle(input) {
444
- const generatedAt = input.generatedAt ?? Date.now();
445
- const result = isSwarmJobResult(input.result) ? cloneJsonValue(input.result) : normalizeResult(input.result);
446
- const job = input.job;
447
- const inputMetadata = toJsonObject(input.metadata);
448
- const semanticImport = normalizeSemanticImportSummary(input.semanticImport ?? result.semanticImport ?? inputMetadata?.semanticImport);
449
- const changedPaths = uniqueStrings(result.changedPaths);
450
- const changedRegions = uniqueStrings([
451
- ...result.changedRegions,
452
- ...(job ? resolveSwarmChangedRegions(job, changedPaths) : [])
453
- ]);
454
- const evidencePaths = uniqueStrings([...(result.evidencePaths ?? []), ...(input.evidencePaths ?? [])]);
455
- const queueItemIds = uniqueStrings([...(result.queueItemIds ?? []), ...(input.queueItemIds ?? []), ...(job ? [job.taskId] : [])]);
456
- const disposition = input.disposition ?? classifySwarmMergeDisposition(result, { staleAgainstHead: input.staleAgainstHead });
457
- const commandsPassed = result.verification.filter((entry) => entry.status === 0 || entry.required === false && entry.status === undefined);
458
- const commandsFailed = result.verification.filter((entry) => entry.status !== undefined && entry.status !== 0 && entry.required !== false);
459
- const ownedFilesTouched = job ? changedPaths.filter((file) => job.allowedWrites.some((glob) => matchesGlob(file, glob))) : changedPaths;
460
- const reasons = mergeBundleReasons(result, disposition, input.staleAgainstHead ?? false);
461
- return {
462
- kind: FRONTIER_SWARM_MERGE_BUNDLE_KIND,
463
- version: FRONTIER_SWARM_MERGE_BUNDLE_VERSION,
464
- id: input.id ?? 'swarm-merge-bundle:' + stableHash([input.runId, input.planId, result.jobId, changedPaths, changedRegions, disposition, generatedAt]),
465
- ...(input.runId ? { runId: input.runId } : {}),
466
- ...(input.planId ? { planId: input.planId } : {}),
467
- jobId: result.jobId,
468
- ...(job ? { taskId: job.taskId, lane: job.lane, title: job.title } : {}),
469
- generatedAt,
470
- status: result.status,
471
- mergeReadiness: result.mergeReadiness,
472
- disposition,
473
- riskLevel: input.riskLevel ?? result.riskLevel ?? inferMergeRisk(result, disposition),
474
- autoMergeable: disposition === 'auto-mergeable' && reasons.length === 0,
475
- changedPaths,
476
- changedRegions,
477
- ownedFilesTouched,
478
- allowedWrites: job ? [...job.allowedWrites] : [],
479
- ownershipViolations: [...result.ownershipViolations],
480
- ...(input.patchPath ?? result.patchPath ? { patchPath: input.patchPath ?? result.patchPath } : {}),
481
- ...(input.patchHash ? { patchHash: input.patchHash } : {}),
482
- evidencePaths,
483
- commandsPassed,
484
- commandsFailed,
485
- queueItemIds,
486
- ...(input.branchName ? { branchName: input.branchName } : {}),
487
- ...(input.commit ? { commit: input.commit } : {}),
488
- staleAgainstHead: input.staleAgainstHead ?? false,
489
- reasons,
490
- ...(semanticImport ? { semanticImport } : {}),
491
- ...(inputMetadata ? { metadata: inputMetadata } : {})
492
- };
493
- }
494
- export function createSwarmQueueOverlay(input = {}) {
495
- const generatedAt = input.generatedAt ?? Date.now();
496
- const entries = [];
497
- for (const bundle of input.bundles ?? []) {
498
- const status = queueOverlayStatusFromBundle(bundle);
499
- const queueItemIds = bundle.queueItemIds.length ? bundle.queueItemIds : [bundle.taskId ?? bundle.jobId];
500
- for (const queueItemId of queueItemIds) {
501
- entries.push({
502
- queueItemId,
503
- jobId: bundle.jobId,
504
- status,
505
- mergeReadiness: bundle.mergeReadiness,
506
- disposition: bundle.disposition,
507
- riskLevel: bundle.riskLevel,
508
- ...(bundle.patchPath ? { patchPath: bundle.patchPath } : {}),
509
- evidencePaths: [...bundle.evidencePaths],
510
- changedPaths: [...bundle.changedPaths],
511
- changedRegions: [...bundle.changedRegions],
512
- reasons: [...bundle.reasons],
513
- ...(bundle.semanticImport ? { semanticImport: cloneJsonValue(bundle.semanticImport) } : {}),
514
- generatedAt: bundle.generatedAt
515
- });
516
- }
517
- }
518
- for (const raw of input.results ?? []) {
519
- const result = isSwarmJobResult(raw) ? cloneJsonValue(raw) : normalizeResult(raw);
520
- const queueItemIds = result.queueItemIds.length ? result.queueItemIds : [result.jobId];
521
- for (const queueItemId of queueItemIds) {
522
- entries.push({
523
- queueItemId,
524
- jobId: result.jobId,
525
- status: queueOverlayStatusFromResult(result),
526
- mergeReadiness: result.mergeReadiness,
527
- disposition: result.mergeDisposition,
528
- riskLevel: result.riskLevel,
529
- ...(result.patchPath ? { patchPath: result.patchPath } : {}),
530
- evidencePaths: [...result.evidencePaths],
531
- changedPaths: [...result.changedPaths],
532
- changedRegions: [...result.changedRegions],
533
- reasons: result.error ? [result.error] : [],
534
- ...(result.semanticImport ? { semanticImport: cloneJsonValue(result.semanticImport) } : {}),
535
- generatedAt
536
- });
537
- }
538
- }
539
- const byQueueItemId = groupOverlayEntries(entries);
540
- return {
541
- kind: FRONTIER_SWARM_QUEUE_OVERLAY_KIND,
542
- version: FRONTIER_SWARM_QUEUE_OVERLAY_VERSION,
543
- id: input.id ?? 'swarm-queue-overlay:' + stableHash([input.runId, entries, generatedAt]),
544
- ...(input.runId ? { runId: input.runId } : {}),
545
- generatedAt,
546
- entries,
547
- byQueueItemId,
548
- summary: {
549
- entryCount: entries.length,
550
- queueItemCount: Object.keys(byQueueItemId).length,
551
- readyToApplyCount: entries.filter((entry) => entry.status === 'ready-to-apply').length,
552
- needsHumanPortCount: entries.filter((entry) => entry.status === 'needs-human-port').length,
553
- failedEvidenceCount: entries.filter((entry) => entry.status === 'failed-evidence').length,
554
- staleAgainstHeadCount: entries.filter((entry) => entry.status === 'stale-against-head').length,
555
- discoveryOnlyCount: entries.filter((entry) => entry.status === 'discovery-only').length
556
- },
557
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
558
- };
559
- }
560
- export function deriveSwarmQueueStatus(input) {
561
- const generatedAt = input.generatedAt ?? Date.now();
562
- const latestByQueueItem = new Map();
563
- for (const overlay of input.overlays ?? []) {
564
- for (const entry of overlay.entries) {
565
- const existing = latestByQueueItem.get(entry.queueItemId);
566
- if (!existing || entry.generatedAt >= existing.generatedAt)
567
- latestByQueueItem.set(entry.queueItemId, entry);
568
- }
569
- }
570
- const jobs = input.snapshot.jobs.map((job) => {
571
- const overlay = latestByQueueItem.get(job.taskId ?? job.jobId) ?? latestByQueueItem.get(job.jobId);
572
- if (!overlay)
573
- return cloneJsonValue(job);
574
- return {
575
- ...cloneJsonValue(job),
576
- status: queueJobStatusFromOverlay(overlay),
577
- lastError: overlay.status === 'failed-evidence' || overlay.status === 'stale-against-head' ? overlay.reasons.join(', ') : job.lastError,
578
- metadata: toJsonObject({
579
- ...(job.metadata ?? {}),
580
- overlayStatus: overlay.status,
581
- mergeDisposition: overlay.disposition,
582
- mergeReadiness: overlay.mergeReadiness,
583
- ...(overlay.semanticImport ? { semanticImport: overlay.semanticImport } : {}),
584
- evidencePaths: overlay.evidencePaths
585
- })
586
- };
587
- });
588
- const byStatus = groupIds(jobs, (job) => job.status);
589
- return {
590
- generatedAt,
591
- jobs,
592
- byStatus,
593
- summary: {
594
- jobCount: jobs.length,
595
- leaseCount: input.snapshot.leases.length,
596
- readyCount: byStatus.ready?.length ?? 0,
597
- leasedCount: byStatus.leased?.length ?? 0,
598
- completedCount: byStatus.completed?.length ?? 0,
599
- failedCount: byStatus.failed?.length ?? 0,
600
- deadLetterCount: byStatus['dead-letter']?.length ?? 0
601
- }
602
- };
603
- }
604
- export function createSwarmMergeIndex(input) {
605
- const generatedAt = input.generatedAt ?? Date.now();
606
- const entries = input.bundles.map((bundle) => {
607
- const patchStatus = input.patchStatuses?.[bundle.jobId] ?? (bundle.staleAgainstHead ? 'stale' : bundle.patchPath ? 'unknown' : 'missing');
608
- const staleAgainstHead = bundle.staleAgainstHead || patchStatus === 'stale' || patchStatus === 'failed-check';
609
- return {
610
- jobId: bundle.jobId,
611
- ...(bundle.taskId ? { taskId: bundle.taskId } : {}),
612
- ...(bundle.lane ? { lane: bundle.lane } : {}),
613
- ...(bundle.title ? { title: bundle.title } : {}),
614
- status: bundle.status,
615
- mergeReadiness: bundle.mergeReadiness,
616
- disposition: staleAgainstHead ? 'stale-against-head' : bundle.disposition,
617
- riskLevel: bundle.riskLevel,
618
- patchStatus,
619
- staleAgainstHead,
620
- autoMergeable: bundle.autoMergeable && !staleAgainstHead,
621
- changedPaths: [...bundle.changedPaths],
622
- changedRegions: [...bundle.changedRegions],
623
- conflictKeys: mergeIndexConflictKeys(bundle),
624
- conflictingJobIds: [],
625
- ownedFilesTouched: [...bundle.ownedFilesTouched],
626
- ownershipViolations: [...bundle.ownershipViolations],
627
- ...(bundle.patchPath ? { patchPath: bundle.patchPath } : {}),
628
- ...(bundle.patchHash ? { patchHash: bundle.patchHash } : {}),
629
- evidencePaths: [...bundle.evidencePaths],
630
- queueItemIds: [...bundle.queueItemIds],
631
- reasons: uniqueStrings([...bundle.reasons, ...(staleAgainstHead ? ['stale-against-head'] : [])]),
632
- ...(bundle.semanticImport ? { semanticImport: cloneJsonValue(bundle.semanticImport) } : {}),
633
- generatedAt: bundle.generatedAt
634
- };
635
- });
636
- const conflicts = createMergeIndexConflicts(entries);
637
- const conflictsByJob = new Map();
638
- for (const conflict of conflicts) {
639
- for (const jobId of conflict.jobIds) {
640
- const set = conflictsByJob.get(jobId) ?? new Set();
641
- for (const other of conflict.jobIds)
642
- if (other !== jobId)
643
- set.add(other);
644
- conflictsByJob.set(jobId, set);
645
- }
646
- }
647
- const indexed = entries.map((entry) => ({
648
- ...entry,
649
- conflictingJobIds: Array.from(conflictsByJob.get(entry.jobId) ?? []).sort()
650
- }));
651
- const byDisposition = groupJobIdsBy(indexed, (entry) => entry.disposition);
652
- const byPath = groupJobIdsByMany(indexed, (entry) => entry.changedPaths);
653
- const byRegion = groupJobIdsByMany(indexed, (entry) => entry.changedRegions);
654
- return {
655
- kind: FRONTIER_SWARM_MERGE_INDEX_KIND,
656
- version: FRONTIER_SWARM_MERGE_INDEX_VERSION,
657
- id: input.id ?? 'swarm-merge-index:' + stableHash([input.runId, input.planId, indexed, conflicts, generatedAt]),
658
- ...(input.runId ? { runId: input.runId } : {}),
659
- ...(input.planId ? { planId: input.planId } : {}),
660
- generatedAt,
661
- entries: indexed,
662
- conflicts,
663
- byDisposition,
664
- byPath,
665
- byRegion,
666
- summary: {
667
- entryCount: indexed.length,
668
- readyToApplyCount: indexed.filter((entry) => entry.disposition === 'auto-mergeable' && entry.autoMergeable && !entry.conflictingJobIds.length).length,
669
- needsHumanPortCount: indexed.filter((entry) => entry.disposition === 'needs-port').length,
670
- failedEvidenceCount: indexed.filter((entry) => entry.disposition === 'rejected' || entry.disposition === 'blocked' || entry.ownershipViolations.length > 0).length,
671
- staleAgainstHeadCount: indexed.filter((entry) => entry.staleAgainstHead || entry.disposition === 'stale-against-head').length,
672
- discoveryOnlyCount: indexed.filter((entry) => entry.disposition === 'discovery-only').length,
673
- conflictCount: conflicts.length,
674
- conflictedJobCount: conflictsByJob.size
675
- },
676
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
677
- };
678
- }
679
- export function checkSwarmRegionOwnership(job, input = {}) {
680
- const changedPaths = uniqueStrings(input.changedPaths ?? []);
681
- const resolvedRegions = resolveSwarmChangedRegions(job, changedPaths);
682
- const changedRegions = uniqueStrings([...(input.changedRegions ?? []), ...resolvedRegions]);
683
- const ownedRegions = new Set(job.ownedRegions);
684
- const regionViolations = changedRegions.filter((region) => !ownedRegions.has(region));
685
- const classifiedPaths = new Set();
686
- for (const region of job.ownershipRegions) {
687
- for (const file of changedPaths) {
688
- if (region.globs.some((glob) => matchesGlob(file, glob)))
689
- classifiedPaths.add(file);
690
- }
691
- }
692
- const unclassifiedChangedPaths = changedPaths.filter((file) => !classifiedPaths.has(file));
693
- return {
694
- ok: regionViolations.length === 0 && (job.ownershipRegions.length === 0 || unclassifiedChangedPaths.length === 0),
695
- jobId: job.id,
696
- changedPaths,
697
- changedRegions,
698
- ownedRegions: [...job.ownedRegions],
699
- regionViolations,
700
- unclassifiedChangedPaths
701
- };
702
- }
703
- export function createSwarmHotspotReport(input = {}) {
704
- const generatedAt = input.generatedAt ?? Date.now();
705
- const threshold = Math.max(2, Math.floor(input.threshold ?? 3));
706
- const byPath = new Map();
707
- for (const bundle of input.bundles ?? []) {
708
- for (const file of bundle.changedPaths) {
709
- const current = byPath.get(file) ?? {
710
- path: file,
711
- touchCount: 0,
712
- jobIds: [],
713
- regions: [],
714
- dispositions: [],
715
- riskLevels: []
716
- };
717
- current.touchCount += 1;
718
- current.jobIds = uniqueStrings([...current.jobIds, bundle.jobId]);
719
- current.regions = uniqueStrings([...current.regions, ...bundle.changedRegions]);
720
- current.dispositions = uniqueStrings([...current.dispositions, bundle.disposition]);
721
- current.riskLevels = uniqueStrings([...current.riskLevels, bundle.riskLevel]);
722
- byPath.set(file, current);
723
- }
724
- }
725
- for (const raw of input.results ?? []) {
726
- const result = isSwarmJobResult(raw) ? raw : normalizeResult(raw);
727
- for (const file of result.changedPaths) {
728
- const current = byPath.get(file) ?? {
729
- path: file,
730
- touchCount: 0,
731
- jobIds: [],
732
- regions: [],
733
- dispositions: [],
734
- riskLevels: []
735
- };
736
- current.touchCount += 1;
737
- current.jobIds = uniqueStrings([...current.jobIds, result.jobId]);
738
- current.regions = uniqueStrings([...current.regions, ...result.changedRegions]);
739
- current.dispositions = uniqueStrings([...current.dispositions, result.mergeDisposition]);
740
- current.riskLevels = uniqueStrings([...current.riskLevels, result.riskLevel]);
741
- byPath.set(file, current);
742
- }
743
- }
744
- const entries = Array.from(byPath.values()).sort((left, right) => right.touchCount - left.touchCount || left.path.localeCompare(right.path));
745
- const recommendations = entries
746
- .filter((entry) => entry.touchCount >= threshold || entry.regions.length > 1)
747
- .map((entry) => ({
748
- path: entry.path,
749
- reason: entry.regions.length > 1 ? 'region-overlap' : 'hot-file',
750
- suggestedModuleId: suggestedModuleId(entry.path),
751
- suggestedOwnershipRegions: entry.regions.length ? entry.regions : [`${suggestedModuleId(entry.path)}.*`],
752
- jobIds: [...entry.jobIds]
753
- }));
754
- return {
755
- kind: FRONTIER_SWARM_HOTSPOT_REPORT_KIND,
756
- version: FRONTIER_SWARM_HOTSPOT_REPORT_VERSION,
757
- id: input.id ?? 'swarm-hotspot-report:' + stableHash([entries, threshold, generatedAt]),
758
- generatedAt,
759
- threshold,
760
- entries,
761
- recommendations,
762
- summary: {
763
- pathCount: entries.length,
764
- hotspotCount: entries.filter((entry) => entry.touchCount >= threshold).length,
765
- recommendationCount: recommendations.length
766
- },
767
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
768
- };
769
- }
770
- export function createSwarmReviewerLanePlan(input) {
771
- const generatedAt = input.generatedAt ?? Date.now();
772
- const reviewerLane = input.reviewerLane ?? 'review';
773
- const reviewers = uniqueStrings(input.reviewers ?? []);
774
- const deferralsByJob = new Map((input.admission?.deferred ?? []).map((entry) => [entry.jobId, entry.reasons]));
775
- const candidates = input.index.entries.filter((entry) => input.includeAutoMergeable
776
- || deferralsByJob.has(entry.jobId)
777
- || entry.conflictingJobIds.length > 0
778
- || entry.riskLevel === 'high'
779
- || entry.disposition !== 'auto-mergeable'
780
- || !entry.autoMergeable);
781
- const assignments = candidates.map((entry) => ({
782
- jobId: entry.jobId,
783
- reviewers: selectReviewers(reviewers, reviewers.length ? 1 : 0, entry.jobId),
784
- required: deferralsByJob.has(entry.jobId) || entry.conflictingJobIds.length > 0 || entry.riskLevel === 'high' || entry.disposition !== 'auto-mergeable',
785
- reasons: uniqueStrings([...reviewerLaneReasons(entry), ...(deferralsByJob.get(entry.jobId) ?? [])])
786
- }));
787
- const tasks = candidates.map((entry) => ({
788
- id: `review-${slug(entry.jobId)}`,
789
- lane: reviewerLane,
790
- kind: 'review',
791
- title: `Review ${entry.title ?? entry.jobId}`,
792
- objective: `Review swarm merge bundle ${entry.jobId}.`,
793
- sourceRefs: entry.evidencePaths,
794
- targetRefs: entry.changedPaths,
795
- ownedRegions: entry.changedRegions,
796
- acceptance: [
797
- 'Review evidence, patch applicability, ownership, conflicts, and risk.',
798
- `Merge disposition: ${entry.disposition}.`
799
- ],
800
- metadata: {
801
- mergeJobId: entry.jobId,
802
- conflictingJobIds: entry.conflictingJobIds,
803
- reasons: uniqueStrings([...reviewerLaneReasons(entry), ...(deferralsByJob.get(entry.jobId) ?? [])])
804
- }
805
- }));
806
- return {
807
- kind: FRONTIER_SWARM_REVIEWER_LANE_PLAN_KIND,
808
- version: FRONTIER_SWARM_REVIEWER_LANE_PLAN_VERSION,
809
- id: input.id ?? 'swarm-reviewer-lane-plan:' + stableHash([input.index.id, assignments, generatedAt]),
810
- mergeIndexId: input.index.id,
811
- generatedAt,
812
- reviewerLane,
813
- assignments,
814
- tasks,
815
- summary: {
816
- assignmentCount: assignments.length,
817
- taskCount: tasks.length
818
- },
819
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
820
- };
821
- }
822
- export function createSwarmRunStoreShards(input = {}) {
823
- const generatedAt = input.generatedAt ?? Date.now();
824
- const root = input.root ?? 'agent-runs/shards';
825
- const shardSize = Math.max(1, Math.floor(input.shardSize ?? 100));
826
- const groupBy = input.groupBy ?? 'lane';
827
- const jobs = input.run?.jobs ?? input.plan?.jobs ?? [];
828
- const groups = new Map();
829
- for (const job of jobs) {
830
- const key = groupBy === 'none' ? 'all' : groupBy === 'hash' ? String(hashBucket(job.id, shardSize)) : job.lane;
831
- groups.set(key, [...(groups.get(key) ?? []), job]);
832
- }
833
- const shards = [];
834
- for (const [group, groupJobs] of Array.from(groups.entries()).sort((left, right) => left[0].localeCompare(right[0]))) {
835
- for (let index = 0; index < groupJobs.length; index += shardSize) {
836
- const slice = groupJobs.slice(index, index + shardSize);
837
- const suffix = `${slug(group)}-${Math.floor(index / shardSize)}`;
838
- const shardRoot = joinPathParts(root, suffix);
839
- shards.push({
840
- id: 'swarm-run-store-shard:' + stableHash([input.run?.id, input.plan?.id, group, index, slice.map((job) => job.id)]),
841
- ...(groupBy === 'lane' ? { lane: group } : {}),
842
- path: shardRoot,
843
- eventPath: joinPathParts(shardRoot, 'events.jsonl'),
844
- resultPath: joinPathParts(shardRoot, 'results.jsonl'),
845
- checkpointPath: joinPathParts(shardRoot, 'checkpoint.json'),
846
- jobIds: slice.map((job) => job.id)
847
- });
848
- }
849
- }
850
- return {
851
- kind: FRONTIER_SWARM_RUN_STORE_SHARDS_KIND,
852
- version: FRONTIER_SWARM_RUN_STORE_SHARDS_VERSION,
853
- id: input.id ?? 'swarm-run-store-shards:' + stableHash([input.run?.id, input.plan?.id, root, shardSize, groupBy, shards, generatedAt]),
854
- ...(input.run ? { runId: input.run.id } : {}),
855
- ...(input.plan ? { planId: input.plan.id } : {}),
856
- root,
857
- generatedAt,
858
- groupBy,
859
- shardSize,
860
- shards,
861
- summary: {
862
- shardCount: shards.length,
863
- jobCount: jobs.length
864
- },
865
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
866
- };
867
- }
868
- export function createSwarmMergeAdmission(input) {
869
- const generatedAt = input.generatedAt ?? Date.now();
870
- const maxReady = Math.max(0, Math.floor(input.maxReady ?? input.index.entries.length));
871
- const maxChangedPaths = input.maxChangedPaths === undefined ? undefined : Math.max(0, Math.floor(input.maxChangedPaths));
872
- const maxChangedRegions = input.maxChangedRegions === undefined ? undefined : Math.max(0, Math.floor(input.maxChangedRegions));
873
- const maxHighRisk = input.maxHighRisk === undefined ? undefined : Math.max(0, Math.floor(input.maxHighRisk));
874
- const allowRisks = uniqueStrings(input.allowRisks ?? ['low', 'medium']);
875
- const admitted = [];
876
- const deferred = [];
877
- const usedPaths = new Set();
878
- const usedRegions = new Set();
879
- let highRiskCount = 0;
880
- for (const entry of input.index.entries) {
881
- const reasons = [];
882
- if (entry.disposition !== 'auto-mergeable' || !entry.autoMergeable)
883
- reasons.push('not-auto-mergeable');
884
- if (entry.staleAgainstHead)
885
- reasons.push('stale-against-head');
886
- if (entry.conflictingJobIds.length)
887
- reasons.push('conflicting-changes');
888
- if (!allowRisks.includes(entry.riskLevel))
889
- reasons.push('risk-not-admitted');
890
- if (admitted.length >= maxReady)
891
- reasons.push('max-ready');
892
- const nextPaths = new Set([...usedPaths, ...entry.changedPaths]);
893
- const nextRegions = new Set([...usedRegions, ...entry.changedRegions]);
894
- const nextHighRiskCount = highRiskCount + (entry.riskLevel === 'high' ? 1 : 0);
895
- if (maxChangedPaths !== undefined && nextPaths.size > maxChangedPaths)
896
- reasons.push('max-changed-paths');
897
- if (maxChangedRegions !== undefined && nextRegions.size > maxChangedRegions)
898
- reasons.push('max-changed-regions');
899
- if (maxHighRisk !== undefined && nextHighRiskCount > maxHighRisk)
900
- reasons.push('max-high-risk');
901
- if (reasons.length) {
902
- deferred.push({ jobId: entry.jobId, reasons: uniqueStrings(reasons) });
903
- continue;
904
- }
905
- admitted.push(entry.jobId);
906
- for (const file of entry.changedPaths)
907
- usedPaths.add(file);
908
- for (const region of entry.changedRegions)
909
- usedRegions.add(region);
910
- highRiskCount = nextHighRiskCount;
911
- }
912
- return {
913
- kind: FRONTIER_SWARM_MERGE_ADMISSION_KIND,
914
- version: FRONTIER_SWARM_MERGE_ADMISSION_VERSION,
915
- id: input.id ?? 'swarm-merge-admission:' + stableHash([input.index.id, admitted, deferred, generatedAt]),
916
- mergeIndexId: input.index.id,
917
- generatedAt,
918
- admitted,
919
- deferred,
920
- budget: {
921
- maxReady,
922
- ...(maxChangedPaths !== undefined ? { maxChangedPaths } : {}),
923
- ...(maxChangedRegions !== undefined ? { maxChangedRegions } : {}),
924
- ...(maxHighRisk !== undefined ? { maxHighRisk } : {}),
925
- allowRisks
926
- },
927
- summary: {
928
- admittedCount: admitted.length,
929
- deferredCount: deferred.length,
930
- changedPathCount: usedPaths.size,
931
- changedRegionCount: usedRegions.size,
932
- highRiskCount
933
- },
934
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
935
- };
936
- }
937
- export function createSwarmContextPack(input = {}) {
938
- const generatedAt = input.generatedAt ?? Date.now();
939
- const task = input.job?.task ?? (input.task ? isSwarmTask(input.task) ? input.task : normalizeTask(input.task) : undefined);
940
- const files = uniqueStrings([
941
- ...(input.files ?? []),
942
- ...(input.job?.task.sourceRefs ?? []),
943
- ...(input.job?.task.targetRefs ?? []),
944
- ...(task?.sourceRefs ?? []),
945
- ...(task?.targetRefs ?? [])
946
- ]);
947
- const apiMap = Object.fromEntries(Object.entries(input.apiMap ?? {}).map(([key, values]) => [key, uniqueStrings(values)]));
948
- const commands = normalizeCommands([
949
- ...(input.commands ?? []),
950
- ...(input.oracleCommands ?? []),
951
- ...(input.job?.verification ?? [])
952
- ]);
953
- const expectedEvidence = uniqueStrings([
954
- ...(input.expectedEvidence ?? []),
955
- ...(input.job?.evidencePrefix ? [joinPathParts(input.job.evidencePrefix, 'evidence.json')] : [])
956
- ]);
957
- return {
958
- kind: FRONTIER_SWARM_CONTEXT_PACK_KIND,
959
- version: FRONTIER_SWARM_CONTEXT_PACK_VERSION,
960
- id: input.id ?? 'swarm-context-pack:' + stableHash([input.job?.id, task?.id, files, apiMap, generatedAt]),
961
- ...(input.job ? { jobId: input.job.id } : {}),
962
- ...(task ? { taskId: task.id } : {}),
963
- ...(input.job?.lane ?? task?.lane ? { lane: input.job?.lane ?? task?.lane } : {}),
964
- title: input.title ?? input.job?.title ?? task?.title ?? 'Swarm Context Pack',
965
- generatedAt,
966
- files,
967
- apiMap,
968
- knownFailures: uniqueStrings(input.knownFailures ?? []),
969
- commands,
970
- oracleCommands: commands,
971
- ...(input.evidenceSchema !== undefined ? { evidenceSchema: toJsonValue(input.evidenceSchema) } : {}),
972
- expectedEvidence,
973
- exclusions: uniqueStrings(input.exclusions ?? []),
974
- avoidInvestigating: uniqueStrings(input.avoidInvestigating ?? []),
975
- playbookIds: uniqueStrings(input.playbookIds ?? []),
976
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
977
- };
978
- }
979
- export function createSwarmOracleCorpus(input = {}) {
980
- const generatedAt = input.generatedAt ?? Date.now();
981
- const artifacts = (input.artifacts ?? []).map(normalizeOracleArtifact).sort((left, right) => left.id.localeCompare(right.id));
982
- const byKind = groupArtifactIdsBy(artifacts, (artifact) => [artifact.kind]);
983
- const byTag = groupArtifactIdsBy(artifacts, (artifact) => artifact.tags);
984
- return {
985
- kind: FRONTIER_SWARM_ORACLE_CORPUS_KIND,
986
- version: FRONTIER_SWARM_ORACLE_CORPUS_VERSION,
987
- id: input.id ?? 'swarm-oracle-corpus:' + stableHash([artifacts, generatedAt]),
988
- title: input.title ?? titleFromId(input.id ?? 'oracle corpus'),
989
- generatedAt,
990
- artifacts,
991
- byKind,
992
- byTag,
993
- summary: {
994
- artifactCount: artifacts.length,
995
- kindCount: Object.keys(byKind).length,
996
- tagCount: Object.keys(byTag).length
997
- },
998
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
999
- };
1000
- }
1001
- export function createSwarmObservabilityPoint(input = {}) {
1002
- const eventRefs = normalizeNamedRefs(input.eventRefs ?? [], 'event');
1003
- return {
1004
- kind: FRONTIER_SWARM_OBSERVABILITY_POINT_KIND,
1005
- version: FRONTIER_SWARM_OBSERVABILITY_POINT_VERSION,
1006
- id: input.id ?? 'swarm-observability-point:' + stableHash([input.subject, input.scope, input.operationIndex, input.at, input.path, input.selector, eventRefs]),
1007
- ...(input.subject ? { subject: input.subject } : {}),
1008
- ...(input.scope ? { scope: input.scope } : {}),
1009
- ...(input.operationIndex !== undefined ? { operationIndex: Math.max(0, Math.floor(input.operationIndex)) } : {}),
1010
- ...(input.at !== undefined ? { at: input.at } : {}),
1011
- ...(input.path ? { path: input.path } : {}),
1012
- ...(input.selector ? { selector: input.selector } : {}),
1013
- ...(input.before !== undefined ? { before: toJsonValue(input.before) } : {}),
1014
- ...(input.after !== undefined ? { after: toJsonValue(input.after) } : {}),
1015
- eventRefs,
1016
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1017
- };
1018
- }
1019
- export function createSwarmReplayBundle(input = {}) {
1020
- const generatedAt = input.generatedAt ?? Date.now();
1021
- const commands = normalizeCommands(input.commands ?? []);
1022
- const inputs = normalizeNamedRefs(input.inputs ?? [], 'input');
1023
- const artifacts = normalizeNamedRefs(input.artifacts ?? [], 'artifact');
1024
- const sourceRefs = normalizeNamedRefs(input.sourceRefs ?? [], 'source');
1025
- const seeds = normalizeSeedRefs(input.seeds ?? []);
1026
- const expectedEvidence = uniqueStrings(input.expectedEvidence ?? []);
1027
- const title = input.title ?? titleFromId(input.id ?? input.subject ?? 'replay bundle');
1028
- return {
1029
- kind: FRONTIER_SWARM_REPLAY_BUNDLE_KIND,
1030
- version: FRONTIER_SWARM_REPLAY_BUNDLE_VERSION,
1031
- id: input.id ?? 'swarm-replay-bundle:' + stableHash([title, input.subject, commands, inputs, artifacts, sourceRefs, seeds, expectedEvidence, generatedAt]),
1032
- title,
1033
- ...(input.subject ? { subject: input.subject } : {}),
1034
- generatedAt,
1035
- commands,
1036
- inputs,
1037
- artifacts,
1038
- sourceRefs,
1039
- seeds,
1040
- ...(toJsonObject(input.environment) ? { environment: toJsonObject(input.environment) } : {}),
1041
- expectedEvidence,
1042
- summary: {
1043
- commandCount: commands.length,
1044
- inputCount: inputs.length,
1045
- artifactCount: artifacts.length,
1046
- sourceRefCount: sourceRefs.length
1047
- },
1048
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1049
- };
1050
- }
1051
- export function createSwarmParityOracle(input = {}) {
1052
- const generatedAt = input.generatedAt ?? Date.now();
1053
- const referenceCommands = normalizeCommands(input.referenceCommands ?? []);
1054
- const testCommands = normalizeCommands(input.testCommands ?? []);
1055
- const comparators = (input.comparators ?? []).map(normalizeParityComparator);
1056
- const artifacts = normalizeNamedRefs(input.artifacts ?? [], 'parity-artifact');
1057
- const status = input.status ?? inferParityStatus(comparators);
1058
- return {
1059
- kind: FRONTIER_SWARM_PARITY_ORACLE_KIND,
1060
- version: FRONTIER_SWARM_PARITY_ORACLE_VERSION,
1061
- id: input.id ?? 'swarm-parity-oracle:' + stableHash([input.title, input.subject, referenceCommands, testCommands, comparators, artifacts, generatedAt]),
1062
- title: input.title ?? titleFromId(input.id ?? input.subject ?? 'parity oracle'),
1063
- status,
1064
- ...(input.subject ? { subject: input.subject } : {}),
1065
- generatedAt,
1066
- referenceCommands,
1067
- testCommands,
1068
- comparators,
1069
- artifacts,
1070
- replayBundleIds: uniqueStrings(input.replayBundleIds ?? []),
1071
- summary: {
1072
- comparatorCount: comparators.length,
1073
- passedCount: comparators.filter((comparator) => comparator.status === 'passed').length,
1074
- failedCount: comparators.filter((comparator) => comparator.status === 'failed').length,
1075
- blockedCount: comparators.filter((comparator) => comparator.status === 'blocked').length
1076
- },
1077
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1078
- };
1079
- }
1080
- export function createSwarmDivergenceReport(input = {}) {
1081
- const generatedAt = input.generatedAt ?? Date.now();
1082
- const observabilityPoints = (input.observabilityPoints ?? []).map((point) => isSwarmObservabilityPoint(point) ? point : createSwarmObservabilityPoint(point));
1083
- const earliest = observabilityPoints
1084
- .filter((point) => point.operationIndex !== undefined)
1085
- .sort((left, right) => left.operationIndex - right.operationIndex)[0];
1086
- const divergesAt = input.divergesAt ?? earliest?.path;
1087
- const operationIndex = input.operationIndex ?? earliest?.operationIndex;
1088
- return {
1089
- kind: FRONTIER_SWARM_DIVERGENCE_REPORT_KIND,
1090
- version: FRONTIER_SWARM_DIVERGENCE_REPORT_VERSION,
1091
- id: input.id ?? 'swarm-divergence-report:' + stableHash([input.subject, input.divergesAt, input.operationIndex, observabilityPoints, generatedAt]),
1092
- title: input.title ?? titleFromId(input.id ?? input.subject ?? 'divergence report'),
1093
- status: input.status ?? 'failed',
1094
- severity: input.severity ?? 'error',
1095
- ...(input.subject ? { subject: input.subject } : {}),
1096
- confidence: input.confidence ?? 'medium',
1097
- ...(divergesAt ? { divergesAt } : {}),
1098
- ...(operationIndex !== undefined ? { operationIndex } : {}),
1099
- ...(input.expected !== undefined ? { expected: toJsonValue(input.expected) } : {}),
1100
- ...(input.actual !== undefined ? { actual: toJsonValue(input.actual) } : {}),
1101
- observabilityPoints,
1102
- traceRefs: normalizeNamedRefs(input.traceRefs ?? [], 'trace'),
1103
- replayBundleIds: uniqueStrings(input.replayBundleIds ?? []),
1104
- evidenceRefs: normalizeNamedRefs(input.evidenceRefs ?? [], 'evidence'),
1105
- generatedAt,
1106
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1107
- };
1108
- }
1109
- export function createSwarmWatchpointPlan(input = {}) {
1110
- const generatedAt = input.generatedAt ?? Date.now();
1111
- const watchpoints = (input.watchpoints ?? []).map(normalizeWatchpoint);
1112
- const commands = normalizeCommands(input.commands ?? []);
1113
- return {
1114
- kind: FRONTIER_SWARM_WATCHPOINT_PLAN_KIND,
1115
- version: FRONTIER_SWARM_WATCHPOINT_PLAN_VERSION,
1116
- id: input.id ?? 'swarm-watchpoint-plan:' + stableHash([input.subject, watchpoints, commands, generatedAt]),
1117
- title: input.title ?? titleFromId(input.id ?? input.subject ?? 'watchpoint plan'),
1118
- ...(input.subject ? { subject: input.subject } : {}),
1119
- matchMode: input.matchMode ?? 'all',
1120
- generatedAt,
1121
- watchpoints,
1122
- commands,
1123
- replayBundleIds: uniqueStrings(input.replayBundleIds ?? []),
1124
- divergenceReportIds: uniqueStrings(input.divergenceReportIds ?? []),
1125
- summary: {
1126
- watchpointCount: watchpoints.length,
1127
- commandCount: commands.length
1128
- },
1129
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1130
- };
1131
- }
1132
- export function createSwarmDebugHandoff(input = {}) {
1133
- const generatedAt = input.generatedAt ?? Date.now();
1134
- const focus = input.focus ? (isSwarmObservabilityPoint(input.focus) ? input.focus : createSwarmObservabilityPoint(input.focus)) : undefined;
1135
- return {
1136
- kind: FRONTIER_SWARM_DEBUG_HANDOFF_KIND,
1137
- version: FRONTIER_SWARM_DEBUG_HANDOFF_VERSION,
1138
- id: input.id ?? 'swarm-debug-handoff:' + stableHash([input.subject, focus, input.replayBundleIds, input.divergenceReportIds, input.watchpointPlanIds, generatedAt]),
1139
- title: input.title ?? titleFromId(input.id ?? input.subject ?? 'debug handoff'),
1140
- status: input.status ?? 'ready',
1141
- ...(input.subject ? { subject: input.subject } : {}),
1142
- ...(focus ? { focus } : {}),
1143
- replayBundleIds: uniqueStrings(input.replayBundleIds ?? []),
1144
- divergenceReportIds: uniqueStrings(input.divergenceReportIds ?? []),
1145
- watchpointPlanIds: uniqueStrings(input.watchpointPlanIds ?? []),
1146
- commands: normalizeCommands(input.commands ?? []),
1147
- files: normalizeNamedRefs(input.files ?? [], 'file'),
1148
- artifacts: normalizeNamedRefs(input.artifacts ?? [], 'artifact'),
1149
- comparisons: (input.comparisons ?? []).map(normalizeParityComparator),
1150
- ...(toJsonObject(input.environment) ? { environment: toJsonObject(input.environment) } : {}),
1151
- generatedAt,
1152
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1153
- };
1154
- }
1155
- export function createSwarmInstrumentationBudget(input = {}) {
1156
- const generatedAt = input.generatedAt ?? Date.now();
1157
- return {
1158
- kind: FRONTIER_SWARM_INSTRUMENTATION_BUDGET_KIND,
1159
- version: FRONTIER_SWARM_INSTRUMENTATION_BUDGET_VERSION,
1160
- id: input.id ?? 'swarm-instrumentation-budget:' + stableHash([input.lane, input.maxEvents, input.maxBytes, input.maxDurationMs, input.maxOverheadRatio, generatedAt]),
1161
- title: input.title ?? titleFromId(input.id ?? input.lane ?? 'instrumentation budget'),
1162
- ...(input.lane ? { lane: input.lane } : {}),
1163
- generatedAt,
1164
- ...(positiveNumber(input.maxEvents) ? { maxEvents: Math.floor(input.maxEvents) } : {}),
1165
- ...(positiveNumber(input.maxBytes) ? { maxBytes: Math.floor(input.maxBytes) } : {}),
1166
- ...(positiveNumber(input.maxDurationMs) ? { maxDurationMs: Math.floor(input.maxDurationMs) } : {}),
1167
- ...(positiveNumber(input.maxOverheadRatio) ? { maxOverheadRatio: input.maxOverheadRatio } : {}),
1168
- captureKinds: uniqueStrings(input.captureKinds ?? []),
1169
- sampling: {
1170
- mode: input.sampling?.mode ?? 'adaptive',
1171
- ...(positiveNumber(input.sampling?.rate) ? { rate: input.sampling?.rate } : {}),
1172
- ...(toJsonObject(input.sampling?.metadata) ? { metadata: toJsonObject(input.sampling?.metadata) } : {})
1173
- },
1174
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1175
- };
1176
- }
1177
- export function checkSwarmInstrumentationBudget(budgetInput, usageInput = {}) {
1178
- const budget = isSwarmInstrumentationBudget(budgetInput) ? budgetInput : createSwarmInstrumentationBudget(budgetInput);
1179
- const usage = {
1180
- events: Math.max(0, Math.floor(usageInput.events ?? 0)),
1181
- bytes: Math.max(0, Math.floor(usageInput.bytes ?? 0)),
1182
- durationMs: Math.max(0, Math.floor(usageInput.durationMs ?? 0)),
1183
- overheadRatio: Math.max(0, usageInput.overheadRatio ?? 0),
1184
- captureKinds: uniqueStrings(usageInput.captureKinds ?? []),
1185
- ...(toJsonObject(usageInput.metadata) ? { metadata: toJsonObject(usageInput.metadata) } : {})
1186
- };
1187
- const violations = [];
1188
- if (budget.maxEvents !== undefined && usage.events > budget.maxEvents)
1189
- violations.push('max-events');
1190
- if (budget.maxBytes !== undefined && usage.bytes > budget.maxBytes)
1191
- violations.push('max-bytes');
1192
- if (budget.maxDurationMs !== undefined && usage.durationMs > budget.maxDurationMs)
1193
- violations.push('max-duration-ms');
1194
- if (budget.maxOverheadRatio !== undefined && usage.overheadRatio > budget.maxOverheadRatio)
1195
- violations.push('max-overhead-ratio');
1196
- for (const kind of usage.captureKinds.filter((kind) => budget.captureKinds.length > 0 && !budget.captureKinds.includes(kind))) {
1197
- violations.push(`capture-kind:${kind}`);
1198
- }
1199
- return { ok: violations.length === 0, budgetId: budget.id, usage, violations: uniqueStrings(violations) };
1200
- }
1201
- export function classifySwarmBottleneck(input) {
1202
- const source = normalizeBottleneckSource(input);
1203
- const text = [source.text, source.status, ...(source.reasons ?? []), ...(source.evidencePaths ?? []), ...(source.changedPaths ?? [])].join(' ').toLowerCase();
1204
- const verification = source.verification ?? [];
1205
- let kind = 'queue';
1206
- let confidence = 'medium';
1207
- if (/missing.*oracle|no oracle|needs-fixture|fixture/.test(text))
1208
- kind = 'missing-oracle';
1209
- else if (/flaky|timeout|browser|playwright|chrome|port/.test(text))
1210
- kind = 'flaky-harness';
1211
- else if (/instrument|logging|trace|telemetry|overhead/.test(text))
1212
- kind = 'instrumentation-overhead';
1213
- else if (/merge|conflict|review|needs-port|ownership/.test(text))
1214
- kind = 'merge-review';
1215
- else if (/dependency|depends|blocked/.test(text))
1216
- kind = 'blocked-dependency';
1217
- else if (/perf|slow|latency|throughput|cpu|memory|resource-capacity/.test(text))
1218
- kind = 'performance';
1219
- else if (/diverg|correct|parity|oracle failed|regression/.test(text))
1220
- kind = 'correctness';
1221
- if (verification.some((entry) => entry.status !== undefined && entry.status !== 0 && entry.required !== false))
1222
- confidence = 'high';
1223
- if ((source.evidencePaths?.length ?? 0) === 0 && (source.changedPaths?.length ?? 0) > 0)
1224
- confidence = 'low';
1225
- return {
1226
- kind,
1227
- confidence,
1228
- reasons: uniqueStrings([kind, ...(source.reasons ?? []), ...verification.filter((entry) => entry.status !== undefined && entry.status !== 0).map((entry) => entry.name ?? 'failed-verification')]),
1229
- route: routeForBottleneck(kind, source.lane)
1230
- };
1231
- }
1232
- export function createSwarmBottleneckReport(input = {}) {
1233
- const generatedAt = input.generatedAt ?? Date.now();
1234
- const classifications = (input.sources ?? []).map(classifySwarmBottleneck);
1235
- const byKind = groupObjects(classifications, (classification) => classification.kind);
1236
- return {
1237
- kind: FRONTIER_SWARM_BOTTLENECK_REPORT_KIND,
1238
- version: FRONTIER_SWARM_BOTTLENECK_REPORT_VERSION,
1239
- id: input.id ?? 'swarm-bottleneck-report:' + stableHash([classifications, generatedAt]),
1240
- generatedAt,
1241
- classifications,
1242
- byKind,
1243
- summary: {
1244
- sourceCount: input.sources?.length ?? 0,
1245
- kindCount: Object.keys(byKind).length
1246
- },
1247
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1248
- };
1249
- }
1250
- export function createSwarmEvidenceIndex(input = {}) {
1251
- const source = isSwarmRun(input) ? { run: input } : input;
1252
- const generatedAt = source.generatedAt ?? Date.now();
1253
- const runEntries = source.run?.results.flatMap((result) => result.evidencePaths.map((path) => normalizeEvidenceIndexEntry({
1254
- jobId: result.jobId,
1255
- queueItemId: result.queueItemIds[0],
1256
- path,
1257
- kind: evidenceKindFromPath(path),
1258
- status: result.status,
1259
- confidence: result.status === 'verified' || result.mergeReadiness === 'verified-patch' ? 1 : 0.65,
1260
- generatedAt
1261
- }))) ?? [];
1262
- const entries = [...runEntries, ...(source.entries ?? []).map((entry) => normalizeEvidenceIndexEntry({ ...entry, generatedAt: entry.generatedAt ?? generatedAt }))];
1263
- const byJobId = groupObjects(entries.filter((entry) => entry.jobId), (entry) => entry.jobId);
1264
- const byTopic = groupObjects(entries.filter((entry) => entry.topic), (entry) => entry.topic);
1265
- const byPath = groupObjects(entries.filter((entry) => entry.path), (entry) => entry.path);
1266
- return {
1267
- kind: FRONTIER_SWARM_EVIDENCE_INDEX_KIND,
1268
- version: FRONTIER_SWARM_EVIDENCE_INDEX_VERSION,
1269
- id: source.id ?? 'swarm-evidence-index:' + stableHash([source.run?.id, entries, generatedAt]),
1270
- ...(source.run?.id ? { runId: source.run.id } : {}),
1271
- generatedAt,
1272
- entries,
1273
- byJobId,
1274
- byTopic,
1275
- byPath,
1276
- summary: {
1277
- entryCount: entries.length,
1278
- jobCount: Object.keys(byJobId).length,
1279
- topicCount: Object.keys(byTopic).length,
1280
- pathCount: Object.keys(byPath).length
1281
- },
1282
- ...(toJsonObject(source.metadata) ? { metadata: toJsonObject(source.metadata) } : {})
1283
- };
1284
- }
1285
- export function querySwarmEvidenceIndex(index, query = {}) {
1286
- const entries = index.entries.filter((entry) => ((query.jobId === undefined || entry.jobId === query.jobId)
1287
- && (query.lane === undefined || entry.lane === query.lane)
1288
- && (query.topic === undefined || entry.topic === query.topic)
1289
- && (query.pathIncludes === undefined || (entry.path ?? '').includes(query.pathIncludes))
1290
- && (query.kind === undefined || entry.kind === query.kind)
1291
- && (query.status === undefined || entry.status === query.status)
1292
- && (query.tag === undefined || entry.tags.includes(query.tag))
1293
- && (query.minConfidence === undefined || entry.confidence >= query.minConfidence)
1294
- && matchesFacetQuery(entry.facets, query.facet)));
1295
- return { entries, summary: { entryCount: entries.length } };
1296
- }
1297
- export function createSwarmBlackboard(input = {}) {
1298
- const generatedAt = input.generatedAt ?? Date.now();
1299
- const entries = (input.entries ?? []).map((entry) => normalizeBlackboardEntry({ ...entry, generatedAt: entry.generatedAt ?? generatedAt }));
1300
- const byTopic = groupObjects(entries, (entry) => entry.topic);
1301
- const byKind = groupObjects(entries, (entry) => entry.kind);
1302
- return {
1303
- kind: FRONTIER_SWARM_BLACKBOARD_KIND,
1304
- version: FRONTIER_SWARM_BLACKBOARD_VERSION,
1305
- id: input.id ?? 'swarm-blackboard:' + stableHash([input.runId, entries, generatedAt]),
1306
- ...(input.runId ? { runId: input.runId } : {}),
1307
- generatedAt,
1308
- entries,
1309
- byTopic,
1310
- byKind,
1311
- summary: {
1312
- entryCount: entries.length,
1313
- topicCount: Object.keys(byTopic).length,
1314
- kindCount: Object.keys(byKind).length
1315
- },
1316
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1317
- };
1318
- }
1319
- export function querySwarmBlackboard(blackboard, query = {}) {
1320
- const textIncludes = query.textIncludes?.toLowerCase();
1321
- const entries = blackboard.entries.filter((entry) => ((query.kind === undefined || entry.kind === query.kind)
1322
- && (query.topic === undefined || entry.topic === query.topic)
1323
- && (query.status === undefined || entry.status === query.status)
1324
- && (query.lane === undefined || entry.lane === query.lane)
1325
- && (query.jobId === undefined || entry.jobId === query.jobId)
1326
- && (query.owner === undefined || entry.owner === query.owner)
1327
- && (query.tag === undefined || entry.tags.includes(query.tag))
1328
- && (textIncludes === undefined || entry.text.toLowerCase().includes(textIncludes))));
1329
- return { entries, summary: { entryCount: entries.length } };
1330
- }
1331
- export function createSwarmReferenceOraclePlan(input = {}) {
1332
- const generatedAt = input.generatedAt ?? Date.now();
1333
- const targets = (input.targets ?? []).map((target) => ({
1334
- id: target.id,
1335
- role: target.role ?? 'candidate',
1336
- ...(target.command ? { command: normalizeCommand(target.command) } : {}),
1337
- ...(toJsonObject(target.metadata) ? { metadata: toJsonObject(target.metadata) } : {})
1338
- }));
1339
- return {
1340
- kind: FRONTIER_SWARM_REFERENCE_ORACLE_PLAN_KIND,
1341
- version: FRONTIER_SWARM_REFERENCE_ORACLE_PLAN_VERSION,
1342
- id: input.id ?? 'swarm-reference-oracle-plan:' + stableHash([input.serviceId, input.subject, input.fixtureId, targets, input.window, generatedAt]),
1343
- ...(input.serviceId ? { serviceId: input.serviceId } : {}),
1344
- ...(input.subject ? { subject: input.subject } : {}),
1345
- ...(input.fixtureId ? { fixtureId: input.fixtureId } : {}),
1346
- generatedAt,
1347
- targets,
1348
- ...(input.window ? { window: normalizeReferenceWindow(input.window) } : {}),
1349
- watchpoints: (input.watchpoints ?? []).map(normalizeWatchpoint),
1350
- artifactKinds: uniqueStrings(input.artifactKinds ?? []),
1351
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1352
- };
1353
- }
1354
- export function createSwarmReferenceOracleResponse(input = {}) {
1355
- const generatedAt = input.generatedAt ?? Date.now();
1356
- return {
1357
- kind: FRONTIER_SWARM_REFERENCE_ORACLE_RESPONSE_KIND,
1358
- version: FRONTIER_SWARM_REFERENCE_ORACLE_RESPONSE_VERSION,
1359
- id: input.id ?? 'swarm-reference-oracle-response:' + stableHash([input.planId, input.status, input.targetResults, input.divergence, generatedAt]),
1360
- ...(input.planId ? { planId: input.planId } : {}),
1361
- status: input.status ?? (input.divergence ? 'failed' : 'pending'),
1362
- ...(input.subject ? { subject: input.subject } : {}),
1363
- generatedAt,
1364
- targetResults: (input.targetResults ?? []).map((target) => ({
1365
- targetId: target.targetId,
1366
- status: target.status ?? 'pending',
1367
- artifacts: normalizeNamedRefs(target.artifacts ?? [], 'reference-oracle-artifact'),
1368
- ...(toJsonObject(target.metadata) ? { metadata: toJsonObject(target.metadata) } : {})
1369
- })),
1370
- ...(input.divergence ? { divergence: createSwarmDivergenceReport(input.divergence) } : {}),
1371
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1372
- };
1373
- }
1374
- export function createSwarmArtifactRoutingPlan(input = {}) {
1375
- const generatedAt = input.generatedAt ?? Date.now();
1376
- const artifacts = [
1377
- ...normalizeNamedRefs(input.artifacts ?? [], 'artifact'),
1378
- ...(input.bundles ?? []).flatMap((bundle) => bundle.evidencePaths.map((path) => normalizeNamedRef({ path, kind: evidenceKindFromPath(path), role: bundle.disposition }, 'evidence')))
1379
- ];
1380
- const hints = (input.hints ?? []).map(normalizeRoutingHint);
1381
- const routes = artifacts.map((artifact) => {
1382
- const matched = hints.filter((hint) => ((hint.artifactKind === undefined || hint.artifactKind === artifact.kind)
1383
- && (hint.pathPattern === undefined || (artifact.path ?? artifact.uri ?? '').includes(hint.pathPattern))));
1384
- const bucket = matched[0]?.bucket ?? defaultArtifactBucket(artifact);
1385
- return {
1386
- artifact,
1387
- bucket,
1388
- ...(matched[0]?.lane ? { lane: matched[0].lane } : {}),
1389
- reasons: uniqueStrings(matched.map((hint) => hint.reason))
1390
- };
1391
- });
1392
- const byBucket = groupIds(routes.map((route) => ({ id: route.artifact.id, bucket: route.bucket })), (route) => route.bucket);
1393
- return {
1394
- kind: FRONTIER_SWARM_ARTIFACT_ROUTING_PLAN_KIND,
1395
- version: FRONTIER_SWARM_ARTIFACT_ROUTING_PLAN_VERSION,
1396
- id: input.id ?? 'swarm-artifact-routing-plan:' + stableHash([routes, generatedAt]),
1397
- generatedAt,
1398
- routes,
1399
- byBucket,
1400
- summary: {
1401
- routeCount: routes.length,
1402
- bucketCount: Object.keys(byBucket).length
1403
- },
1404
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1405
- };
1406
- }
1407
- export function createSwarmFixtureCatalog(input = {}) {
1408
- const generatedAt = input.generatedAt ?? Date.now();
1409
- const fixtures = (input.fixtures ?? []).map(normalizeFixture);
1410
- const byTag = {};
1411
- for (const fixture of fixtures) {
1412
- for (const tag of fixture.tags)
1413
- byTag[tag] = uniqueStrings([...(byTag[tag] ?? []), fixture.id]);
1414
- }
1415
- return {
1416
- kind: FRONTIER_SWARM_FIXTURE_CATALOG_KIND,
1417
- version: FRONTIER_SWARM_FIXTURE_CATALOG_VERSION,
1418
- id: input.id ?? 'swarm-fixture-catalog:' + stableHash([fixtures, generatedAt]),
1419
- generatedAt,
1420
- fixtures,
1421
- byTag,
1422
- summary: {
1423
- fixtureCount: fixtures.length,
1424
- tagCount: Object.keys(byTag).length
1425
- },
1426
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1427
- };
1428
- }
1429
- export function createSwarmProgressModel(input = {}) {
1430
- const generatedAt = input.generatedAt ?? Date.now();
1431
- const items = (input.items ?? []).map((item) => ({
1432
- id: item.id,
1433
- ...(item.surface ? { surface: item.surface } : {}),
1434
- status: item.status ?? 'not-started',
1435
- evidencePaths: uniqueStrings(item.evidencePaths ?? []),
1436
- blockers: uniqueStrings(item.blockers ?? []),
1437
- ...(toJsonObject(item.metadata) ? { metadata: toJsonObject(item.metadata) } : {})
1438
- }));
1439
- const byStatus = groupIds(items, (item) => item.status);
1440
- return {
1441
- kind: FRONTIER_SWARM_PROGRESS_MODEL_KIND,
1442
- version: FRONTIER_SWARM_PROGRESS_MODEL_VERSION,
1443
- id: input.id ?? 'swarm-progress-model:' + stableHash([items, generatedAt]),
1444
- generatedAt,
1445
- items,
1446
- byStatus,
1447
- summary: {
1448
- itemCount: items.length,
1449
- acceptedCount: byStatus.accepted?.length ?? 0,
1450
- blockedCount: byStatus.blocked?.length ?? 0
1451
- },
1452
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1453
- };
1454
- }
1455
- export function createSwarmAutoReviewReport(input = {}) {
1456
- const generatedAt = input.generatedAt ?? Date.now();
1457
- const derived = (input.bundles ?? []).flatMap((bundle) => deriveAutoReviewFindings(bundle, generatedAt));
1458
- const findings = [...derived, ...(input.findings ?? []).map((finding) => normalizeAutoReviewFinding(finding, generatedAt))];
1459
- const byKind = groupObjects(findings, (finding) => finding.kind);
1460
- return {
1461
- kind: FRONTIER_SWARM_AUTO_REVIEW_REPORT_KIND,
1462
- version: FRONTIER_SWARM_AUTO_REVIEW_REPORT_VERSION,
1463
- id: input.id ?? 'swarm-auto-review-report:' + stableHash([findings, generatedAt]),
1464
- generatedAt,
1465
- findings,
1466
- byKind,
1467
- summary: {
1468
- findingCount: findings.length,
1469
- highSeverityCount: findings.filter((finding) => finding.severity === 'error' || finding.severity === 'critical').length
1470
- },
1471
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1472
- };
1473
- }
1474
- export function createSwarmRebaseReport(input = {}) {
1475
- const generatedAt = input.generatedAt ?? Date.now();
1476
- const fromIndex = input.mergeIndex?.entries.map((entry) => ({
1477
- jobId: entry.jobId,
1478
- status: entry.staleAgainstHead ? 'stale-evidence' : entry.conflictingJobIds.length ? 'semantic-overlap' : 'clean-apply',
1479
- reasons: uniqueStrings([...entry.reasons, ...entry.conflictKeys])
1480
- })) ?? [];
1481
- const fromBundles = !input.mergeIndex && input.bundles ? input.bundles.map((bundle) => ({
1482
- jobId: bundle.jobId,
1483
- status: bundle.staleAgainstHead ? 'stale-evidence' : 'clean-apply',
1484
- reasons: [...bundle.reasons]
1485
- })) : [];
1486
- const entries = [...fromIndex, ...fromBundles, ...(input.entries ?? [])].map((entry) => ({
1487
- jobId: entry.jobId,
1488
- status: entry.status ?? 'clean-apply',
1489
- reasons: uniqueStrings(entry.reasons ?? []),
1490
- ...('metadata' in entry && toJsonObject(entry.metadata) ? { metadata: toJsonObject(entry.metadata) } : {})
1491
- }));
1492
- const byStatus = groupIds(entries, (entry) => entry.status);
1493
- return {
1494
- kind: FRONTIER_SWARM_REBASE_REPORT_KIND,
1495
- version: FRONTIER_SWARM_REBASE_REPORT_VERSION,
1496
- id: input.id ?? 'swarm-rebase-report:' + stableHash([input.currentHead, entries, generatedAt]),
1497
- ...(input.currentHead ? { currentHead: input.currentHead } : {}),
1498
- generatedAt,
1499
- entries,
1500
- byStatus,
1501
- summary: {
1502
- entryCount: entries.length,
1503
- cleanCount: byStatus['clean-apply']?.length ?? 0,
1504
- conflictCount: (byStatus['textual-conflict']?.length ?? 0) + (byStatus['semantic-overlap']?.length ?? 0),
1505
- staleCount: byStatus['stale-evidence']?.length ?? 0
1506
- },
1507
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1508
- };
1509
- }
1510
- export function createSwarmUsageGovernor(input = {}) {
1511
- const generatedAt = input.generatedAt ?? Date.now();
1512
- return {
1513
- kind: FRONTIER_SWARM_USAGE_GOVERNOR_KIND,
1514
- version: FRONTIER_SWARM_USAGE_GOVERNOR_VERSION,
1515
- id: input.id ?? 'swarm-usage-governor:' + stableHash([input.maxWorkers, input.maxTokensByLane, input.maxCostUsd, input.retryBudget, generatedAt]),
1516
- generatedAt,
1517
- ...(positiveNumber(input.maxWorkers) ? { maxWorkers: Math.floor(input.maxWorkers) } : {}),
1518
- maxTokensByLane: { ...(input.maxTokensByLane ?? {}) },
1519
- ...(positiveNumber(input.maxCostUsd) ? { maxCostUsd: input.maxCostUsd } : {}),
1520
- retryBudget: Math.max(0, Math.floor(input.retryBudget ?? 0)),
1521
- stopConditions: uniqueStrings(input.stopConditions ?? []),
1522
- preferStaticWhenLowBudget: input.preferStaticWhenLowBudget ?? true,
1523
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1524
- };
1525
- }
1526
- export function checkSwarmUsageGovernor(governorInput, usage = {}) {
1527
- const governor = isSwarmUsageGovernor(governorInput) ? governorInput : createSwarmUsageGovernor(governorInput);
1528
- const reasons = [];
1529
- if (governor.maxWorkers !== undefined && (usage.activeWorkers ?? 0) > governor.maxWorkers)
1530
- reasons.push('max-workers');
1531
- if (governor.maxCostUsd !== undefined && (usage.costUsd ?? 0) > governor.maxCostUsd)
1532
- reasons.push('max-cost-usd');
1533
- if ((usage.retriesUsed ?? 0) > governor.retryBudget)
1534
- reasons.push('retry-budget');
1535
- for (const [lane, maxTokens] of Object.entries(governor.maxTokensByLane)) {
1536
- if ((usage.tokensByLane?.[lane] ?? 0) > maxTokens)
1537
- reasons.push(`max-tokens:${lane}`);
1538
- }
1539
- return {
1540
- ok: reasons.length === 0,
1541
- reasons,
1542
- ...(governor.maxWorkers !== undefined ? { recommendedMaxWorkers: Math.max(1, governor.maxWorkers - (reasons.length ? 1 : 0)) } : {}),
1543
- preferStatic: governor.preferStaticWhenLowBudget && reasons.some((reason) => reason.startsWith('max-tokens') || reason === 'max-cost-usd')
1544
- };
1545
- }
1546
- export function createSwarmLanePlaybook(input) {
1547
- const generatedAt = input.generatedAt ?? Date.now();
1548
- const successful = (input.successfulBundles ?? []).filter((bundle) => bundle.status === 'completed' || bundle.status === 'verified' || bundle.autoMergeable);
1549
- const hotPaths = createSwarmHotspotReport({ bundles: successful, threshold: 2, generatedAt }).entries
1550
- .filter((entry) => entry.touchCount >= 2)
1551
- .map((entry) => entry.path);
1552
- return {
1553
- kind: FRONTIER_SWARM_LANE_PLAYBOOK_KIND,
1554
- version: FRONTIER_SWARM_LANE_PLAYBOOK_VERSION,
1555
- id: input.id ?? 'swarm-lane-playbook:' + stableHash([input.lane, successful.map((bundle) => bundle.jobId), input.notes, generatedAt]),
1556
- lane: normalizeId(input.lane, 'playbook lane'),
1557
- title: input.title ?? `${titleFromId(input.lane)} Playbook`,
1558
- generatedAt,
1559
- notes: uniqueStrings(input.notes ?? []),
1560
- commands: normalizeCommands(input.commands ?? []),
1561
- avoidInvestigating: uniqueStrings(input.avoidInvestigating ?? []),
1562
- evidencePatterns: uniqueStrings(input.evidencePatterns ?? successful.flatMap((bundle) => bundle.evidencePaths)),
1563
- successfulJobIds: uniqueStrings(successful.map((bundle) => bundle.jobId)),
1564
- hotPaths,
1565
- changedRegions: uniqueStrings(successful.flatMap((bundle) => bundle.changedRegions)),
1566
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1567
- };
1568
- }
1569
- export function createSwarmPatchStackPlan(input) {
1570
- const generatedAt = input.generatedAt ?? Date.now();
1571
- const maxStackSize = Math.max(1, Math.floor(input.maxStackSize ?? 8));
1572
- const groups = new Map();
1573
- for (const entry of input.index.entries) {
1574
- const key = patchStackKey(entry);
1575
- groups.set(key, [...(groups.get(key) ?? []), entry]);
1576
- }
1577
- const stacks = [];
1578
- for (const [key, entries] of Array.from(groups.entries()).sort((left, right) => left[0].localeCompare(right[0]))) {
1579
- const sorted = [...entries].sort((left, right) => riskRank(left.riskLevel) - riskRank(right.riskLevel) || left.jobId.localeCompare(right.jobId));
1580
- for (let index = 0; index < sorted.length; index += maxStackSize) {
1581
- const slice = sorted.slice(index, index + maxStackSize);
1582
- const jobIds = slice.map((entry) => entry.jobId);
1583
- const conflicts = input.index.conflicts.filter((conflict) => conflict.jobIds.some((jobId) => jobIds.includes(jobId)));
1584
- stacks.push({
1585
- id: 'swarm-patch-stack:' + stableHash([input.index.id, key, index, jobIds]),
1586
- title: titleFromId(key),
1587
- ...(slice[0]?.lane ? { lane: slice[0].lane } : {}),
1588
- jobIds,
1589
- changedPaths: uniqueStrings(slice.flatMap((entry) => entry.changedPaths)),
1590
- changedRegions: uniqueStrings(slice.flatMap((entry) => entry.changedRegions)),
1591
- riskLevels: uniqueStrings(slice.map((entry) => entry.riskLevel)),
1592
- dispositions: uniqueStrings(slice.map((entry) => entry.disposition)),
1593
- conflicts,
1594
- gateHints: uniqueStrings(slice.flatMap((entry) => entry.evidencePaths.filter((file) => file.endsWith('.json') || file.endsWith('.jsonl'))))
1595
- });
1596
- }
1597
- }
1598
- return {
1599
- kind: FRONTIER_SWARM_PATCH_STACK_PLAN_KIND,
1600
- version: FRONTIER_SWARM_PATCH_STACK_PLAN_VERSION,
1601
- id: input.id ?? 'swarm-patch-stack-plan:' + stableHash([input.index.id, stacks, generatedAt]),
1602
- mergeIndexId: input.index.id,
1603
- generatedAt,
1604
- stacks,
1605
- summary: {
1606
- stackCount: stacks.length,
1607
- jobCount: input.index.entries.length,
1608
- conflictedStackCount: stacks.filter((stack) => stack.conflicts.length > 0).length
1609
- },
1610
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1611
- };
1612
- }
1613
- export function createSwarmCoordinatorDashboard(input = {}) {
1614
- const generatedAt = input.generatedAt ?? Date.now();
1615
- const runId = input.run?.id ?? input.mergeIndex?.runId ?? input.queueOverlay?.runId;
1616
- const planId = input.plan?.id ?? input.mergeIndex?.planId;
1617
- const bundles = input.bundles ?? input.run?.results.map((result) => createSwarmMergeBundle({
1618
- runId,
1619
- planId,
1620
- job: input.plan?.jobs.find((job) => job.id === result.jobId),
1621
- result,
1622
- generatedAt
1623
- })) ?? [];
1624
- const mergeIndex = input.mergeIndex ?? (bundles.length ? createSwarmMergeIndex({ runId, planId, bundles, generatedAt }) : undefined);
1625
- const queueOverlay = input.queueOverlay ?? (bundles.length ? createSwarmQueueOverlay({ runId, bundles, generatedAt }) : undefined);
1626
- const evidenceIndex = input.evidenceIndex ?? (input.run ? createSwarmEvidenceIndex({ run: input.run, generatedAt }) : undefined);
1627
- const admission = input.admission;
1628
- const jobsById = new Map((input.plan?.jobs ?? []).map((job) => [job.id, job]));
1629
- const resultsById = new Map((input.run?.results ?? []).map((result) => [result.jobId, result]));
1630
- const entriesById = new Map((mergeIndex?.entries ?? []).map((entry) => [entry.jobId, entry]));
1631
- const bundlesById = new Map(bundles.map((bundle) => [bundle.jobId, bundle]));
1632
- const processes = normalizeCoordinatorProcesses(input.processes ?? []);
1633
- const processesByJob = groupObjects(processes.filter((process) => process.jobId), (process) => process.jobId);
1634
- const duplicateGroups = createCoordinatorDuplicateGroups(mergeIndex?.entries ?? []);
1635
- const duplicateByJob = new Map();
1636
- for (const group of duplicateGroups) {
1637
- for (const jobId of group.jobIds)
1638
- duplicateByJob.set(jobId, group);
1639
- }
1640
- const admissionDeferred = new Map((admission?.deferred ?? []).map((entry) => [entry.jobId, entry.reasons]));
1641
- const admitted = new Set(admission?.admitted ?? []);
1642
- const jobIds = uniqueStrings([
1643
- ...(input.plan?.jobs ?? []).map((job) => job.id),
1644
- ...(input.run?.results ?? []).map((result) => result.jobId),
1645
- ...(mergeIndex?.entries ?? []).map((entry) => entry.jobId),
1646
- ...processes.map((process) => process.jobId)
1647
- ]).sort();
1648
- const jobs = jobIds.map((jobId) => {
1649
- const planJob = jobsById.get(jobId);
1650
- const result = resultsById.get(jobId);
1651
- const entry = entriesById.get(jobId);
1652
- const bundle = bundlesById.get(jobId);
1653
- const processList = processesByJob[jobId] ?? [];
1654
- const duplicateGroup = duplicateByJob.get(jobId);
1655
- const admissionReasons = admissionDeferred.get(jobId) ?? [];
1656
- const admissionStatus = admitted.has(jobId)
1657
- ? 'admitted'
1658
- : admissionReasons.length
1659
- ? 'deferred'
1660
- : entry?.autoMergeable
1661
- ? 'not-admissible'
1662
- : 'unknown';
1663
- const score = scoreCoordinatorMergeJob(entry, bundle, evidenceIndex?.byJobId[jobId]?.length ?? 0, duplicateGroup, admissionStatus, admissionReasons);
1664
- const evidencePaths = uniqueStrings([
1665
- ...(entry?.evidencePaths ?? []),
1666
- ...(result?.evidencePaths ?? []),
1667
- ...(bundle?.evidencePaths ?? [])
1668
- ]);
1669
- const changedRegions = uniqueStrings([...(entry?.changedRegions ?? []), ...(result?.changedRegions ?? []), ...(bundle?.changedRegions ?? [])]);
1670
- return {
1671
- jobId,
1672
- ...(entry?.taskId ?? result?.queueItemIds[0] ?? planJob?.taskId ? { taskId: entry?.taskId ?? result?.queueItemIds[0] ?? planJob?.taskId } : {}),
1673
- ...(entry?.lane ?? planJob?.lane ? { lane: entry?.lane ?? planJob?.lane } : {}),
1674
- ...(entry?.title ?? planJob?.title ? { title: entry?.title ?? planJob?.title } : {}),
1675
- status: entry?.status ?? result?.status ?? planJob?.status ?? 'planned',
1676
- liveness: coordinatorJobLiveness(result, entry, processList),
1677
- mergeReadiness: entry?.mergeReadiness ?? result?.mergeReadiness ?? 'blocked',
1678
- disposition: entry?.disposition ?? result?.mergeDisposition ?? 'blocked',
1679
- riskLevel: entry?.riskLevel ?? result?.riskLevel ?? 'unknown',
1680
- mergeScore: score.score,
1681
- mergeScoreReasons: score.reasons,
1682
- admissionStatus,
1683
- admissionReasons,
1684
- staleAgainstHead: Boolean(entry?.staleAgainstHead || bundle?.staleAgainstHead),
1685
- ...(duplicateGroup ? { duplicateGroupId: duplicateGroup.id, ...(duplicateGroup.jobIds[0] !== jobId ? { duplicateOf: duplicateGroup.jobIds[0] } : {}) } : {}),
1686
- changedPaths: uniqueStrings([...(entry?.changedPaths ?? []), ...(result?.changedPaths ?? []), ...(bundle?.changedPaths ?? [])]),
1687
- changedRegions,
1688
- semanticRegions: changedRegions,
1689
- ownershipViolations: uniqueStrings([...(entry?.ownershipViolations ?? []), ...(result?.ownershipViolations ?? []), ...(bundle?.ownershipViolations ?? [])]),
1690
- ...(entry?.patchPath ?? result?.patchPath ?? bundle?.patchPath ? { patchPath: entry?.patchPath ?? result?.patchPath ?? bundle?.patchPath } : {}),
1691
- evidencePaths,
1692
- ...(primaryEvidencePath(evidencePaths) ? { primaryEvidencePath: primaryEvidencePath(evidencePaths) } : {}),
1693
- sourceCitations: createCoordinatorSourceCitations(entry, evidenceIndex),
1694
- tests: {
1695
- passed: bundle?.commandsPassed.length ?? result?.verification.filter((test) => test.status === 0).length ?? 0,
1696
- failed: bundle?.commandsFailed.length ?? result?.verification.filter((test) => test.status !== undefined && test.status !== 0).length ?? 0,
1697
- requiredFailed: bundle?.commandsFailed.length ?? result?.verification.filter((test) => test.required !== false && test.status !== undefined && test.status !== 0).length ?? 0
1698
- },
1699
- ...(entry?.semanticImport ?? result?.semanticImport ?? bundle?.semanticImport ? {
1700
- semanticImport: cloneJsonValue(entry?.semanticImport ?? result?.semanticImport ?? bundle?.semanticImport)
1701
- } : {}),
1702
- generatedAt
1703
- };
1704
- }).sort((left, right) => right.mergeScore - left.mergeScore || left.jobId.localeCompare(right.jobId));
1705
- const byLane = groupIds(jobs, (job) => job.lane ?? 'unassigned');
1706
- const byDisposition = groupIds(jobs, (job) => job.disposition);
1707
- const byLiveness = groupIds(jobs, (job) => job.liveness);
1708
- return {
1709
- kind: FRONTIER_SWARM_COORDINATOR_DASHBOARD_KIND,
1710
- version: FRONTIER_SWARM_COORDINATOR_DASHBOARD_VERSION,
1711
- id: input.id ?? 'swarm-coordinator-dashboard:' + stableHash([runId, planId, jobs, duplicateGroups, generatedAt]),
1712
- ...(runId ? { runId } : {}),
1713
- ...(planId ? { planId } : {}),
1714
- generatedAt,
1715
- jobs,
1716
- duplicateGroups,
1717
- processes,
1718
- byLane,
1719
- byDisposition,
1720
- byLiveness,
1721
- ...(mergeIndex ? { mergeIndex } : {}),
1722
- ...(queueOverlay ? { queueOverlay } : {}),
1723
- ...(evidenceIndex ? { evidenceIndex } : {}),
1724
- ...(admission ? { admission } : {}),
1725
- summary: {
1726
- jobCount: jobs.length,
1727
- readyToApplyCount: jobs.filter((job) => job.disposition === 'auto-mergeable' && job.admissionStatus !== 'deferred').length,
1728
- needsHumanPortCount: jobs.filter((job) => job.disposition === 'needs-port').length,
1729
- failedEvidenceCount: jobs.filter((job) => job.disposition === 'rejected' || job.disposition === 'blocked' || job.tests.requiredFailed > 0).length,
1730
- staleAgainstHeadCount: jobs.filter((job) => job.staleAgainstHead).length,
1731
- duplicateGroupCount: duplicateGroups.length,
1732
- semanticSidecarCount: jobs.filter((job) => job.semanticImport && job.semanticImport.total > 0).length,
1733
- semanticRegionCount: jobs.reduce((total, job) => total + job.semanticRegions.length, 0),
1734
- averageMergeScore: averageScore(jobs.map((job) => job.mergeScore))
1735
- },
1736
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1737
- };
1738
- }
1739
- export function querySwarmCoordinatorDashboard(dashboard, query = {}) {
1740
- const jobs = dashboard.jobs.filter((job) => ((query.jobId === undefined || job.jobId === query.jobId)
1741
- && (query.lane === undefined || job.lane === query.lane)
1742
- && (query.disposition === undefined || job.disposition === query.disposition)
1743
- && (query.liveness === undefined || job.liveness === query.liveness)
1744
- && (query.admissionStatus === undefined || job.admissionStatus === query.admissionStatus)
1745
- && (query.pathIncludes === undefined || job.changedPaths.concat(job.evidencePaths).some((entry) => entry.includes(query.pathIncludes)))
1746
- && (query.region === undefined || job.changedRegions.includes(query.region) || job.semanticRegions.includes(query.region))
1747
- && (query.hasSemanticImport === undefined || Boolean(job.semanticImport && job.semanticImport.total > 0) === query.hasSemanticImport)
1748
- && (query.hasSemanticRegions === undefined || (job.semanticRegions.length > 0) === query.hasSemanticRegions)
1749
- && (query.staleAgainstHead === undefined || job.staleAgainstHead === query.staleAgainstHead)
1750
- && (query.duplicateOnly !== true || Boolean(job.duplicateGroupId))
1751
- && (query.minMergeScore === undefined || job.mergeScore >= query.minMergeScore)
1752
- && (query.maxMergeScore === undefined || job.mergeScore <= query.maxMergeScore)));
1753
- return {
1754
- jobs,
1755
- summary: {
1756
- jobCount: jobs.length,
1757
- averageMergeScore: averageScore(jobs.map((job) => job.mergeScore))
1758
- }
1759
- };
1760
- }
1761
- export function createSwarmAdaptiveLoadPlan(input = {}) {
1762
- const generatedAt = input.generatedAt ?? Date.now();
1763
- const mode = input.mode ?? 'balanced';
1764
- const maxLimits = createAdaptiveMaxLimits(input);
1765
- const minLimits = createAdaptiveMinLimits(input.minLimits, maxLimits);
1766
- const currentLimits = clampAdaptiveLimits(createAdaptiveCurrentLimits(input.currentLimits, maxLimits), minLimits, maxLimits);
1767
- const effectiveLimits = mode === 'off'
1768
- ? cloneJsonValue(maxLimits)
1769
- : cloneJsonValue(currentLimits);
1770
- const schedule = input.schedule ?? (input.plan ? createSwarmSchedule({ plan: input.plan, run: input.run }) : undefined);
1771
- const observations = normalizeAdaptiveObservations([
1772
- ...deriveAdaptiveScheduleObservations(schedule, generatedAt),
1773
- ...deriveAdaptiveRunObservations(input.run, generatedAt),
1774
- ...deriveAdaptiveMergeIndexObservations(input.mergeIndex, generatedAt),
1775
- ...deriveAdaptiveDashboardObservations(input.dashboard, generatedAt),
1776
- ...deriveAdaptiveAdmissionObservations(input.admission, generatedAt),
1777
- ...(input.observations ?? [])
1778
- ], generatedAt);
1779
- const decisions = [];
1780
- const bottlenecks = observations.filter((observation) => adaptiveObservationIsBottleneck(observation));
1781
- if (mode === 'observe') {
1782
- for (const observation of bottlenecks) {
1783
- decisions.push(createAdaptiveDecision({
1784
- action: 'observe',
1785
- target: adaptiveDecisionTargetForObservation(observation),
1786
- key: adaptiveDecisionKeyForObservation(observation),
1787
- reason: observation.reasons[0] ?? observation.kind,
1788
- observationIds: [observation.id]
1789
- }));
1790
- }
1791
- }
1792
- else if (mode !== 'off') {
1793
- for (const observation of bottlenecks) {
1794
- applyAdaptiveObservation(effectiveLimits, minLimits, maxLimits, mode, observation, decisions);
1795
- }
1796
- const healthy = observations.filter((observation) => observation.kind === 'healthy-throughput');
1797
- if (bottlenecks.length === 0 && healthy.length > 0) {
1798
- for (const observation of healthy) {
1799
- applyAdaptiveRecovery(effectiveLimits, maxLimits, observation, decisions);
1800
- }
1801
- }
1802
- }
1803
- const summary = {
1804
- observationCount: observations.length,
1805
- bottleneckCount: bottlenecks.length,
1806
- decisionCount: decisions.length,
1807
- reducedCount: decisions.filter((decision) => decision.action === 'decrease').length,
1808
- increasedCount: decisions.filter((decision) => decision.action === 'increase').length,
1809
- ...(effectiveLimits.maxReadyJobs !== undefined ? { effectiveMaxReadyJobs: effectiveLimits.maxReadyJobs } : {}),
1810
- ...(maxLimits.maxReadyJobs !== undefined ? { maxReadyJobs: maxLimits.maxReadyJobs } : {})
1811
- };
1812
- return {
1813
- kind: FRONTIER_SWARM_ADAPTIVE_LOAD_PLAN_KIND,
1814
- version: FRONTIER_SWARM_ADAPTIVE_LOAD_PLAN_VERSION,
1815
- id: input.id ?? 'swarm-adaptive-load-plan:' + stableHash([input.plan?.id, input.run?.id, mode, observations, decisions, generatedAt]),
1816
- ...(input.plan?.id ? { planId: input.plan.id } : {}),
1817
- ...(input.run?.id ? { runId: input.run.id } : {}),
1818
- mode,
1819
- generatedAt,
1820
- maxLimits,
1821
- currentLimits,
1822
- minLimits,
1823
- effectiveLimits,
1824
- observations,
1825
- decisions,
1826
- summary,
1827
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1828
- };
1829
- }
1830
- export function createSwarmScheduleInputFromAdaptiveLoadPlan(plan, adaptive, input = {}) {
1831
- return {
1832
- plan,
1833
- ...(input.run ? { run: input.run } : {}),
1834
- ...(input.now !== undefined ? { now: input.now } : {}),
1835
- ...(adaptive.effectiveLimits.maxReadyJobs !== undefined ? { maxReadyJobs: adaptive.effectiveLimits.maxReadyJobs } : {}),
1836
- maxLaneConcurrency: adaptive.effectiveLimits.maxLaneConcurrency,
1837
- maxConcurrencyKeyConcurrency: adaptive.effectiveLimits.maxConcurrencyKeyConcurrency,
1838
- maxComputeConcurrency: adaptive.effectiveLimits.maxComputeConcurrency,
1839
- resourceQuotas: adaptive.effectiveLimits.resourceQuotas
1840
- };
1841
- }
1842
- export function resolveSwarmCompute(manifestInput, taskInput) {
1843
- const compiled = compileSwarm(manifestInput);
1844
- const task = isSwarmTask(taskInput) ? taskInput : normalizeTask(taskInput);
1845
- return resolveTaskCompute(compiled, task);
1846
- }
1847
- export function createSwarmProof(input, options = {}) {
1848
- const manifestId = 'manifestId' in input ? input.manifestId : input.id;
1849
- const summary = input.summary;
1850
- const generatedAt = options.generatedAt ?? Date.now();
1851
- return {
1852
- kind: FRONTIER_SWARM_PROOF_KIND,
1853
- version: FRONTIER_SWARM_PROOF_VERSION,
1854
- id: 'swarm-proof:' + stableHash([manifestId, summary, generatedAt]),
1855
- manifestId,
1856
- generatedAt,
1857
- hash: stableHash([input, options.validation, options.metadata]),
1858
- summary,
1859
- ...(options.validation ? { validation: options.validation } : {}),
1860
- ...(toJsonObject(options.metadata) ? { metadata: toJsonObject(options.metadata) } : {})
1861
- };
1862
- }
1863
- export function createSwarmSchedule(input) {
1864
- const plan = 'plan' in input ? input.plan : input;
1865
- const run = 'plan' in input ? input.run : undefined;
1866
- const createdAt = 'plan' in input ? input.now ?? Date.now() : Date.now();
1867
- const limits = mergeScheduleLimits(plan.limits, 'plan' in input ? input : {});
1868
- const resultsByJob = new Map((run?.results ?? []).map((result) => [result.jobId, result]));
1869
- const completed = new Set();
1870
- const failed = new Set();
1871
- for (const result of resultsByJob.values()) {
1872
- if (result.status === 'completed' || result.status === 'verified')
1873
- completed.add(result.jobId);
1874
- if (result.status === 'failed' || result.exitCode !== undefined && result.exitCode !== 0)
1875
- failed.add(result.jobId);
1876
- }
1877
- const runningJobs = (run?.jobs ?? plan.jobs)
1878
- .filter((job) => job.status === 'running')
1879
- .map((job) => ({
1880
- jobId: job.id,
1881
- lane: job.lane,
1882
- compute: job.compute.id,
1883
- concurrencyKey: job.concurrencyKey,
1884
- capabilities: [...job.capabilities],
1885
- ...(job.resourceRequirements ? { resourceRequirements: cloneJsonValue(job.resourceRequirements) } : {})
1886
- }));
1887
- const runningByLane = countBy(runningJobs.map((job) => job.lane));
1888
- const runningByKey = countBy(runningJobs.map((job) => job.concurrencyKey));
1889
- const runningByCompute = countBy(runningJobs.map((job) => job.compute));
1890
- const runningByResource = resourceUsageFromScheduled(runningJobs);
1891
- const ready = [];
1892
- const blocked = [];
1893
- const sortedJobs = [...plan.jobs].sort((left, right) => left.priority - right.priority || left.id.localeCompare(right.id));
1894
- for (const job of sortedJobs) {
1895
- if (completed.has(job.id) || failed.has(job.id) || runningJobs.some((running) => running.jobId === job.id))
1896
- continue;
1897
- const dependencyIds = plan.graph.dependenciesByJobId[job.id] ?? [];
1898
- const waitingFor = dependencyIds.filter((dep) => !completed.has(dep));
1899
- const reasons = [];
1900
- if (waitingFor.length)
1901
- reasons.push('waiting-for-dependencies');
1902
- const laneMax = limits.maxLaneConcurrency[job.lane] ?? Number.POSITIVE_INFINITY;
1903
- const keyMax = limits.maxConcurrencyKeyConcurrency[job.concurrencyKey] ?? Number.POSITIVE_INFINITY;
1904
- const computeMax = limits.maxComputeConcurrency[job.compute.id] ?? job.compute.maxConcurrency ?? Number.POSITIVE_INFINITY;
1905
- if ((runningByLane[job.lane] ?? 0) >= laneMax)
1906
- reasons.push('lane-capacity');
1907
- if ((runningByKey[job.concurrencyKey] ?? 0) >= keyMax)
1908
- reasons.push('concurrency-key-capacity');
1909
- if ((runningByCompute[job.compute.id] ?? 0) >= computeMax)
1910
- reasons.push('compute-capacity');
1911
- const scheduled = scheduleJob(job, dependencyIds);
1912
- for (const resource of resourceQuotaViolations(scheduled, runningByResource, limits.resourceQuotas)) {
1913
- reasons.push(`resource-capacity:${resource}`);
1914
- }
1915
- if (reasons.length) {
1916
- blocked.push({ ...scheduled, reasons, waitingFor });
1917
- continue;
1918
- }
1919
- if (limits.maxReadyJobs !== undefined && ready.length >= limits.maxReadyJobs) {
1920
- blocked.push({ ...scheduled, reasons: ['ready-capacity'], waitingFor: [] });
1921
- continue;
1922
- }
1923
- ready.push(scheduled);
1924
- runningByLane[job.lane] = (runningByLane[job.lane] ?? 0) + 1;
1925
- runningByKey[job.concurrencyKey] = (runningByKey[job.concurrencyKey] ?? 0) + 1;
1926
- runningByCompute[job.compute.id] = (runningByCompute[job.compute.id] ?? 0) + 1;
1927
- addResourceUsage(runningByResource, scheduled);
1928
- }
1929
- return {
1930
- kind: FRONTIER_SWARM_SCHEDULE_KIND,
1931
- version: FRONTIER_SWARM_SCHEDULE_VERSION,
1932
- id: 'swarm-schedule:' + stableHash([plan.id, run?.id, ready.map((job) => job.jobId), blocked.map((job) => [job.jobId, job.reasons]), createdAt]),
1933
- planId: plan.id,
1934
- ...(run ? { runId: run.id } : {}),
1935
- createdAt,
1936
- ready,
1937
- blocked,
1938
- running: runningJobs,
1939
- completed: Array.from(completed).sort(),
1940
- failed: Array.from(failed).sort(),
1941
- summary: {
1942
- jobCount: plan.jobs.length,
1943
- readyCount: ready.length,
1944
- blockedCount: blocked.length,
1945
- runningCount: runningJobs.length,
1946
- completedCount: completed.size,
1947
- failedCount: failed.size
1948
- }
1949
- };
1950
- }
1951
- export function createSwarmSchedulerRecommendations(input) {
1952
- const generatedAt = input.generatedAt ?? Date.now();
1953
- const recommendations = [];
1954
- const blockedByReason = new Map();
1955
- for (const job of input.schedule.blocked) {
1956
- for (const reason of job.reasons)
1957
- blockedByReason.set(reason, [...(blockedByReason.get(reason) ?? []), job]);
1958
- }
1959
- for (const [reason, jobs] of blockedByReason) {
1960
- const resource = reason.startsWith('resource-capacity:') ? reason.slice('resource-capacity:'.length) : undefined;
1961
- recommendations.push({
1962
- id: 'swarm-scheduler-recommendation:' + stableHash([input.schedule.id, reason, jobs.map((job) => job.jobId), generatedAt]),
1963
- reason,
1964
- jobIds: jobs.map((job) => job.jobId).sort(),
1965
- ...(resource ? { resource } : {}),
1966
- ...(jobs[0]?.lane ? { lane: jobs[0].lane } : {}),
1967
- action: schedulerActionForReason(reason),
1968
- priority: schedulerPriorityForReason(reason)
1969
- });
1970
- }
1971
- for (const conflict of input.mergeIndex?.conflicts ?? []) {
1972
- recommendations.push({
1973
- id: 'swarm-scheduler-recommendation:' + stableHash([input.schedule.id, conflict.key, conflict.jobIds, generatedAt]),
1974
- reason: `merge-conflict:${conflict.kind}`,
1975
- jobIds: [...conflict.jobIds],
1976
- action: 'serialize-conflicting-surface-or-split-ownership',
1977
- priority: 15
1978
- });
1979
- }
1980
- return {
1981
- kind: FRONTIER_SWARM_SCHEDULER_RECOMMENDATIONS_KIND,
1982
- version: FRONTIER_SWARM_SCHEDULER_RECOMMENDATIONS_VERSION,
1983
- id: input.id ?? 'swarm-scheduler-recommendations:' + stableHash([input.schedule.id, recommendations, generatedAt]),
1984
- scheduleId: input.schedule.id,
1985
- generatedAt,
1986
- recommendations: recommendations.sort((left, right) => left.priority - right.priority || left.id.localeCompare(right.id)),
1987
- summary: { recommendationCount: recommendations.length },
1988
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
1989
- };
1990
- }
1991
- export function createSwarmLeases(input) {
1992
- const now = input.now ?? Date.now();
1993
- const leaseMs = Math.max(1, Math.floor(input.leaseMs ?? 900000));
1994
- const activeJobIds = new Set((input.existingLeases ?? []).filter((lease) => lease.status === 'active' && lease.expiresAt > now).map((lease) => lease.jobId));
1995
- const existingMaxFence = Math.max(0, ...(input.existingLeases ?? []).map((lease) => lease.fencingToken));
1996
- const count = Math.max(0, Math.floor(input.count ?? input.schedule.ready.length));
1997
- return input.schedule.ready
1998
- .filter((job) => !activeJobIds.has(job.jobId))
1999
- .slice(0, count)
2000
- .map((job, index) => ({
2001
- kind: FRONTIER_SWARM_LEASE_KIND,
2002
- version: FRONTIER_SWARM_LEASE_VERSION,
2003
- id: 'swarm-lease:' + stableHash([input.schedule.id, job.jobId, input.workerId, now, existingMaxFence + index + 1]),
2004
- jobId: job.jobId,
2005
- workerId: input.workerId,
2006
- token: stableHash([job.jobId, input.workerId, now, existingMaxFence + index + 1]),
2007
- leasedAt: now,
2008
- expiresAt: now + leaseMs,
2009
- fencingToken: existingMaxFence + index + 1,
2010
- status: 'active'
2011
- }));
2012
- }
2013
- export function renewSwarmLease(input) {
2014
- const now = input.now ?? Date.now();
2015
- const leaseMs = Math.max(1, Math.floor(input.leaseMs ?? Math.max(1, input.lease.expiresAt - input.lease.leasedAt)));
2016
- return {
2017
- ...cloneJsonValue(input.lease),
2018
- leasedAt: now,
2019
- expiresAt: now + leaseMs,
2020
- status: input.status ?? 'active'
2021
- };
2022
- }
2023
- export function createSwarmQueueSnapshot(input) {
2024
- const generatedAt = input.generatedAt ?? Date.now();
2025
- const leases = [...(input.leases ?? [])].map((lease) => cloneJsonValue(lease));
2026
- const jobs = (input.jobs ? input.jobs.map(normalizeQueueJob) : queueJobsFromPlan(input.plan, input.run, leases)).sort((left, right) => (left.priority - right.priority
2027
- || left.jobId.localeCompare(right.jobId)));
2028
- const byStatus = groupIds(jobs, (job) => job.status);
2029
- const byLane = groupIds(jobs, (job) => job.lane ?? 'unassigned');
2030
- return {
2031
- kind: FRONTIER_SWARM_QUEUE_SNAPSHOT_KIND,
2032
- version: FRONTIER_SWARM_QUEUE_SNAPSHOT_VERSION,
2033
- id: input.id ?? 'swarm-queue-snapshot:' + stableHash([input.plan.id, input.run?.id, jobs, leases, generatedAt]),
2034
- planId: input.plan.id,
2035
- runId: input.run?.id ?? input.plan.runId,
2036
- generatedAt,
2037
- jobs,
2038
- byStatus,
2039
- byLane,
2040
- leases,
2041
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {}),
2042
- summary: {
2043
- jobCount: jobs.length,
2044
- leaseCount: leases.length,
2045
- readyCount: byStatus.ready?.length ?? 0,
2046
- leasedCount: byStatus.leased?.length ?? 0,
2047
- completedCount: byStatus.completed?.length ?? 0,
2048
- failedCount: byStatus.failed?.length ?? 0,
2049
- deadLetterCount: byStatus['dead-letter']?.length ?? 0
2050
- }
2051
- };
2052
- }
2053
- export function createSwarmRunCheckpoint(input) {
2054
- const run = 'kind' in input ? input : input.run;
2055
- const sequence = 'kind' in input ? run.events.length + run.results.length : input.sequence ?? run.events.length + run.results.length;
2056
- const savedAt = 'kind' in input ? Date.now() : input.savedAt ?? Date.now();
2057
- return {
2058
- kind: FRONTIER_SWARM_RUN_CHECKPOINT_KIND,
2059
- version: FRONTIER_SWARM_RUN_CHECKPOINT_VERSION,
2060
- id: 'swarm-run-checkpoint:' + stableHash([run.id, sequence, savedAt, run.summary]),
2061
- runId: run.id,
2062
- planId: run.planId,
2063
- sequence,
2064
- savedAt,
2065
- status: run.status,
2066
- eventCount: run.events.length,
2067
- resultCount: run.results.length,
2068
- hash: stableHash(run),
2069
- ...(!('kind' in input) && toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2070
- };
2071
- }
2072
- export function checkSwarmBudget(job, usageInput) {
2073
- const usage = normalizeUsage(usageInput);
2074
- const budget = job.budget;
2075
- const violations = [];
2076
- if (budget?.maxCostUsd !== undefined && usage.costUsd > budget.maxCostUsd)
2077
- violations.push('max-cost-usd');
2078
- if (budget?.maxInputTokens !== undefined && usage.inputTokens > budget.maxInputTokens)
2079
- violations.push('max-input-tokens');
2080
- if (budget?.maxOutputTokens !== undefined && usage.outputTokens > budget.maxOutputTokens)
2081
- violations.push('max-output-tokens');
2082
- if (budget?.maxDurationMs !== undefined && usage.durationMs > budget.maxDurationMs)
2083
- violations.push('max-duration-ms');
2084
- if (budget && usage.attempts > budget.maxRetries + 1)
2085
- violations.push('max-retries');
2086
- return { ok: violations.length === 0, jobId: job.id, usage, ...(budget ? { budget } : {}), violations };
2087
- }
2088
- export function createSwarmArtifactIndex(input) {
2089
- const run = 'kind' in input ? input : input.run;
2090
- const generatedAt = 'kind' in input ? Date.now() : input.generatedAt ?? Date.now();
2091
- const explicit = 'kind' in input ? [] : input.artifacts ?? [];
2092
- const artifacts = [
2093
- ...(run?.results ?? []).flatMap((result) => result.evidencePaths.map((evidencePath) => normalizeArtifact({ jobId: result.jobId, path: evidencePath, kind: 'evidence' }))),
2094
- ...explicit.map(normalizeArtifact)
2095
- ].sort((left, right) => left.jobId.localeCompare(right.jobId) || left.path.localeCompare(right.path));
2096
- const byJobId = groupArtifacts(artifacts, (artifact) => artifact.jobId);
2097
- const byKind = groupArtifacts(artifacts, (artifact) => artifact.kind);
2098
- return {
2099
- kind: FRONTIER_SWARM_ARTIFACT_INDEX_KIND,
2100
- version: FRONTIER_SWARM_ARTIFACT_INDEX_VERSION,
2101
- id: 'swarm-artifact-index:' + stableHash([artifacts, generatedAt]),
2102
- generatedAt,
2103
- artifacts,
2104
- byJobId,
2105
- byKind,
2106
- summary: {
2107
- artifactCount: artifacts.length,
2108
- jobCount: Object.keys(byJobId).length,
2109
- kindCount: Object.keys(byKind).length,
2110
- totalBytes: artifacts.reduce((total, artifact) => total + (artifact.bytes ?? 0), 0)
2111
- }
2112
- };
2113
- }
2114
- export function createSwarmReviewPlan(input) {
2115
- const generatedAt = input.generatedAt ?? Date.now();
2116
- const resultsByJob = new Map((input.run?.results ?? []).map((result) => [result.jobId, result]));
2117
- const budgetsByJob = new Map((input.budgetDecisions ?? []).map((decision) => [decision.jobId, decision]));
2118
- const assignments = [];
2119
- for (const job of input.plan.jobs) {
2120
- const result = resultsByJob.get(job.id);
2121
- const budget = budgetsByJob.get(job.id);
2122
- const reason = reviewReason(job, result, budget, input.sampleSalt ?? input.plan.id);
2123
- if (!reason)
2124
- continue;
2125
- const pool = job.review.reviewerPool.length ? job.review.reviewerPool : [...(input.reviewers ?? [])];
2126
- assignments.push({
2127
- jobId: job.id,
2128
- taskId: job.taskId,
2129
- reviewers: selectReviewers(pool, job.review.requiredReviewers, job.id),
2130
- required: job.review.alwaysReview || reason === 'violations' || reason === 'failed' || reason === 'budget',
2131
- reason
2132
- });
2133
- }
2134
- return {
2135
- kind: FRONTIER_SWARM_REVIEW_PLAN_KIND,
2136
- version: FRONTIER_SWARM_REVIEW_PLAN_VERSION,
2137
- id: 'swarm-review-plan:' + stableHash([input.plan.id, assignments, generatedAt]),
2138
- planId: input.plan.id,
2139
- generatedAt,
2140
- assignments,
2141
- summary: {
2142
- assignmentCount: assignments.length,
2143
- requiredCount: assignments.filter((assignment) => assignment.required).length,
2144
- sampledCount: assignments.filter((assignment) => assignment.reason === 'sampled').length
2145
- }
2146
- };
2147
- }
2148
- export function createSwarmMergePlan(input) {
2149
- const generatedAt = input.generatedAt ?? Date.now();
2150
- const resultsByJob = new Map(input.run.results.map((result) => [result.jobId, result]));
2151
- const reviewRequired = new Set((input.reviewPlan?.assignments ?? []).filter((assignment) => assignment.required).map((assignment) => assignment.jobId));
2152
- const conflicts = conflictMap(input.run.results);
2153
- const ready = [];
2154
- const blocked = [];
2155
- for (const job of input.plan.jobs) {
2156
- const result = resultsByJob.get(job.id);
2157
- const reasons = [];
2158
- if (!result || result.status !== 'completed' && result.status !== 'verified')
2159
- reasons.push('not-completed');
2160
- if (result?.ownershipViolations.length)
2161
- reasons.push('ownership-violations');
2162
- if (reviewRequired.has(job.id))
2163
- reasons.push('review-required');
2164
- const conflictingJobIds = Array.from(conflicts.get(job.id) ?? []).sort();
2165
- if (conflictingJobIds.length)
2166
- reasons.push('conflicting-changes');
2167
- if (reasons.length)
2168
- blocked.push({ jobId: job.id, reasons: uniqueStrings(reasons), conflictingJobIds });
2169
- else
2170
- ready.push(job.id);
2171
- }
2172
- const groups = groupMergeReadyJobs(ready, input.run.results);
2173
- return {
2174
- kind: FRONTIER_SWARM_MERGE_PLAN_KIND,
2175
- version: FRONTIER_SWARM_MERGE_PLAN_VERSION,
2176
- id: 'swarm-merge-plan:' + stableHash([input.plan.id, ready, blocked, generatedAt]),
2177
- planId: input.plan.id,
2178
- generatedAt,
2179
- ready,
2180
- blocked,
2181
- groups,
2182
- summary: { readyCount: ready.length, blockedCount: blocked.length, groupCount: groups.length }
2183
- };
2184
- }
2185
- export function decomposeSwarmFeature(input) {
2186
- const filesByLane = new Map();
2187
- const lanes = input.lanes.length ? [...input.lanes] : ['implementation'];
2188
- for (const lane of lanes)
2189
- filesByLane.set(lane, []);
2190
- for (const file of input.files ?? []) {
2191
- const selected = lanes.find((lane) => file.toLowerCase().includes(lane.toLowerCase())) ?? lanes[filesByLane.size ? stableHash(file).charCodeAt(10) % lanes.length : 0];
2192
- filesByLane.get(selected)?.push(file);
2193
- }
2194
- return lanes.map((lane, index) => ({
2195
- id: `${input.featureId}-${slug(lane)}`,
2196
- lane,
2197
- title: `${titleFromId(lane)} for ${input.featureId}`,
2198
- objective: input.objective,
2199
- priority: 100 + index,
2200
- targetRefs: filesByLane.get(lane) ?? [],
2201
- verification: input.checks ?? [],
2202
- review: input.reviewers?.length ? { requiredReviewers: 1, reviewerPool: input.reviewers } : undefined,
2203
- metadata: toJsonObject(input.metadata)
2204
- }));
2205
- }
2206
- export function encodeSwarmJsonl(records) {
2207
- return records.map((record) => JSON.stringify(record)).join('\n') + (records.length ? '\n' : '');
2208
- }
2209
- export function decodeSwarmJsonl(jsonl) {
2210
- return jsonl.split(/\r?\n/).filter((line) => line.trim().length > 0).map((line) => JSON.parse(line));
2211
- }
2212
- export function matchesGlob(file, glob) {
2213
- const escaped = glob
2214
- .replace(/[.+^${}()|[\]\\]/g, '\\$&')
2215
- .replace(/\*\*/g, '\u0000')
2216
- .replace(/\*/g, '[^/]*')
2217
- .replace(/\u0000/g, '.*');
2218
- return new RegExp('^' + escaped + '$').test(file);
2219
- }
2220
- function createSwarmJobGraph(jobs) {
2221
- const nodes = jobs.map((job) => job.id).sort();
2222
- const nodeSet = new Set(nodes);
2223
- const taskToJob = new Map(jobs.map((job) => [job.taskId, job.id]));
2224
- const edges = [];
2225
- const issues = [];
2226
- for (const job of jobs) {
2227
- for (const rawDep of job.dependsOn) {
2228
- const dep = nodeSet.has(rawDep) ? rawDep : taskToJob.get(rawDep);
2229
- if (!dep) {
2230
- addIssue(issues, 'missing-job-dependency', 'error', `jobs.${job.id}.dependsOn`, `Job dependency is not in this plan: ${rawDep}`);
2231
- continue;
2232
- }
2233
- if (dep === job.id) {
2234
- addIssue(issues, 'self-job-dependency', 'error', `jobs.${job.id}.dependsOn`, `Job cannot depend on itself: ${job.id}`);
2235
- continue;
2236
- }
2237
- edges.push({ from: dep, to: job.id, type: 'depends-on' });
2238
- }
2239
- }
2240
- const dependenciesByJobId = Object.fromEntries(nodes.map((node) => [node, []]));
2241
- const dependentsByJobId = Object.fromEntries(nodes.map((node) => [node, []]));
2242
- for (const edge of edges) {
2243
- dependenciesByJobId[edge.to]?.push(edge.from);
2244
- dependentsByJobId[edge.from]?.push(edge.to);
2245
- }
2246
- for (const key of nodes) {
2247
- dependenciesByJobId[key] = uniqueStrings(dependenciesByJobId[key] ?? []).sort();
2248
- dependentsByJobId[key] = uniqueStrings(dependentsByJobId[key] ?? []).sort();
2249
- if (hasJobDependencyCycle(key, dependenciesByJobId)) {
2250
- addIssue(issues, 'job-dependency-cycle', 'error', `jobs.${key}.dependsOn`, `Job dependency graph contains a cycle at ${key}`);
2251
- }
2252
- }
2253
- return {
2254
- nodes,
2255
- edges: edges.sort((left, right) => left.from.localeCompare(right.from) || left.to.localeCompare(right.to)),
2256
- dependentsByJobId,
2257
- dependenciesByJobId,
2258
- roots: nodes.filter((node) => dependenciesByJobId[node]?.length === 0),
2259
- leaves: nodes.filter((node) => dependentsByJobId[node]?.length === 0),
2260
- issues
2261
- };
2262
- }
2263
- function normalizeScheduleLimits(manifest, options) {
2264
- const maxLaneConcurrency = {};
2265
- for (const lane of manifest.lanes) {
2266
- const browserMax = lane.resourceRequirements?.browser?.maxConcurrency;
2267
- const value = options.maxLaneConcurrency?.[lane.id] ?? lane.maxConcurrency ?? browserMax ?? manifest.policy.defaultConcurrency;
2268
- maxLaneConcurrency[lane.id] = Math.max(1, Math.floor(value));
2269
- }
2270
- return {
2271
- ...(positiveNumber(options.maxReadyJobs) ? { maxReadyJobs: Math.floor(options.maxReadyJobs) } : {}),
2272
- maxLaneConcurrency: { ...maxLaneConcurrency, ...(options.maxLaneConcurrency ?? {}) },
2273
- maxConcurrencyKeyConcurrency: { ...(options.maxConcurrencyKeyConcurrency ?? {}) },
2274
- maxComputeConcurrency: { ...(options.maxComputeConcurrency ?? {}) },
2275
- resourceQuotas: normalizeResourceQuotas(options.resourceQuotas ?? {})
2276
- };
2277
- }
2278
- function mergeScheduleLimits(base, override) {
2279
- return {
2280
- maxReadyJobs: positiveNumber(override.maxReadyJobs) ? Math.floor(override.maxReadyJobs) : base.maxReadyJobs,
2281
- maxLaneConcurrency: { ...base.maxLaneConcurrency, ...(override.maxLaneConcurrency ?? {}) },
2282
- maxConcurrencyKeyConcurrency: { ...base.maxConcurrencyKeyConcurrency, ...(override.maxConcurrencyKeyConcurrency ?? {}) },
2283
- maxComputeConcurrency: { ...base.maxComputeConcurrency, ...(override.maxComputeConcurrency ?? {}) },
2284
- resourceQuotas: { ...base.resourceQuotas, ...normalizeResourceQuotas(override.resourceQuotas ?? {}) }
2285
- };
2286
- }
2287
- function scheduleJob(job, dependsOn = job.dependsOn) {
2288
- return {
2289
- jobId: job.id,
2290
- taskId: job.taskId,
2291
- lane: job.lane,
2292
- compute: job.compute.id,
2293
- concurrencyKey: job.concurrencyKey,
2294
- priority: job.priority,
2295
- dependsOn: [...dependsOn],
2296
- capabilities: [...job.capabilities],
2297
- ...(job.resourceRequirements ? { resourceRequirements: cloneJsonValue(job.resourceRequirements) } : {})
2298
- };
2299
- }
2300
- function normalizeResourceQuotas(input) {
2301
- const quotas = {};
2302
- for (const [resource, value] of Object.entries(input)) {
2303
- if (positiveNumber(value))
2304
- quotas[resource] = value;
2305
- }
2306
- return quotas;
2307
- }
2308
- function resourceUsageFromScheduled(jobs) {
2309
- const usage = {};
2310
- for (const job of jobs)
2311
- addResourceUsage(usage, job);
2312
- return usage;
2313
- }
2314
- function addResourceUsage(usage, job) {
2315
- for (const [resource, amount] of Object.entries(job.resourceRequirements?.resources ?? {})) {
2316
- usage[resource] = (usage[resource] ?? 0) + amount;
2317
- }
2318
- if (job.resourceRequirements?.browser?.required) {
2319
- if (job.resourceRequirements.resources.browser === undefined)
2320
- usage.browser = (usage.browser ?? 0) + 1;
2321
- if (job.resourceRequirements.resources['browser-port'] === undefined)
2322
- usage['browser-port'] = (usage['browser-port'] ?? 0) + 1;
2323
- }
2324
- }
2325
- function resourceQuotaViolations(job, usage, quotas) {
2326
- const next = { ...usage };
2327
- addResourceUsage(next, job);
2328
- return Object.entries(quotas)
2329
- .filter(([resource, quota]) => (next[resource] ?? 0) > quota)
2330
- .map(([resource]) => resource)
2331
- .sort();
2332
- }
2333
- function schedulerActionForReason(reason) {
2334
- if (reason.startsWith('resource-capacity:'))
2335
- return 'lower-concurrency-or-add-resource-pool';
2336
- if (reason === 'lane-capacity')
2337
- return 'increase-lane-capacity-or-split-lane';
2338
- if (reason === 'compute-capacity')
2339
- return 'increase-compute-capacity-or-use-another-compute';
2340
- if (reason === 'concurrency-key-capacity')
2341
- return 'serialize-or-split-concurrency-key';
2342
- if (reason === 'waiting-for-dependencies')
2343
- return 'prioritize-dependency-chain';
2344
- if (reason === 'ready-capacity')
2345
- return 'raise-ready-window-or-drain-ready-jobs';
2346
- return 'review-scheduler-blocker';
2347
- }
2348
- function schedulerPriorityForReason(reason) {
2349
- if (reason.startsWith('resource-capacity:'))
2350
- return 10;
2351
- if (reason === 'waiting-for-dependencies')
2352
- return 20;
2353
- if (reason === 'ready-capacity')
2354
- return 25;
2355
- return 30;
2356
- }
2357
- function createAdaptiveMaxLimits(input) {
2358
- const plan = input.plan;
2359
- const jobCount = Math.max(1, plan?.jobs.length ?? input.schedule?.summary.jobCount ?? input.dashboard?.summary.jobCount ?? 1);
2360
- const raw = mergeAdaptiveScheduleLimitInputs(plan?.limits, input.maxLimits);
2361
- const maxReadyJobs = positiveNumber(raw.maxReadyJobs) ? Math.floor(raw.maxReadyJobs) : jobCount;
2362
- const maxLaneConcurrency = { ...raw.maxLaneConcurrency };
2363
- const maxConcurrencyKeyConcurrency = { ...raw.maxConcurrencyKeyConcurrency };
2364
- const maxComputeConcurrency = { ...raw.maxComputeConcurrency };
2365
- const resourceQuotas = { ...raw.resourceQuotas };
2366
- for (const job of plan?.jobs ?? []) {
2367
- maxLaneConcurrency[job.lane] = adaptivePositiveLimit(maxLaneConcurrency[job.lane], job.compute.maxConcurrency ?? maxReadyJobs);
2368
- maxConcurrencyKeyConcurrency[job.concurrencyKey] = adaptivePositiveLimit(maxConcurrencyKeyConcurrency[job.concurrencyKey], maxReadyJobs);
2369
- maxComputeConcurrency[job.compute.id] = adaptivePositiveLimit(maxComputeConcurrency[job.compute.id], job.compute.maxConcurrency ?? maxReadyJobs);
2370
- for (const [resource, amount] of Object.entries(job.resourceRequirements?.resources ?? {})) {
2371
- resourceQuotas[resource] = adaptivePositiveLimit(resourceQuotas[resource], Math.max(1, Math.ceil(amount), maxReadyJobs));
2372
- }
2373
- if (job.resourceRequirements?.browser?.required) {
2374
- resourceQuotas.browser = adaptivePositiveLimit(resourceQuotas.browser, maxReadyJobs);
2375
- resourceQuotas['browser-port'] = adaptivePositiveLimit(resourceQuotas['browser-port'], maxReadyJobs);
2376
- }
2377
- }
2378
- return {
2379
- maxReadyJobs,
2380
- maxLaneConcurrency: normalizeAdaptiveLimitRecord(maxLaneConcurrency),
2381
- maxConcurrencyKeyConcurrency: normalizeAdaptiveLimitRecord(maxConcurrencyKeyConcurrency),
2382
- maxComputeConcurrency: normalizeAdaptiveLimitRecord(maxComputeConcurrency),
2383
- resourceQuotas: normalizeAdaptiveLimitRecord(resourceQuotas)
2384
- };
2385
- }
2386
- function createAdaptiveCurrentLimits(input, maxLimits) {
2387
- if (!input)
2388
- return cloneJsonValue(maxLimits);
2389
- const raw = mergeAdaptiveScheduleLimitInputs(maxLimits, input);
2390
- return {
2391
- ...(positiveNumber(raw.maxReadyJobs) ? { maxReadyJobs: Math.floor(raw.maxReadyJobs) } : {}),
2392
- maxLaneConcurrency: normalizeAdaptiveLimitRecord(raw.maxLaneConcurrency),
2393
- maxConcurrencyKeyConcurrency: normalizeAdaptiveLimitRecord(raw.maxConcurrencyKeyConcurrency),
2394
- maxComputeConcurrency: normalizeAdaptiveLimitRecord(raw.maxComputeConcurrency),
2395
- resourceQuotas: normalizeAdaptiveLimitRecord(raw.resourceQuotas)
2396
- };
2397
- }
2398
- function createAdaptiveMinLimits(input, maxLimits) {
2399
- const raw = mergeAdaptiveScheduleLimitInputs(undefined, input);
2400
- const laneMinimums = Object.fromEntries(Object.keys(maxLimits.maxLaneConcurrency).map((key) => [key, 1]));
2401
- const keyMinimums = Object.fromEntries(Object.keys(maxLimits.maxConcurrencyKeyConcurrency).map((key) => [key, 1]));
2402
- const computeMinimums = Object.fromEntries(Object.keys(maxLimits.maxComputeConcurrency).map((key) => [key, 1]));
2403
- const resourceMinimums = Object.fromEntries(Object.keys(maxLimits.resourceQuotas).map((key) => [key, 1]));
2404
- return {
2405
- maxReadyJobs: adaptivePositiveLimit(raw.maxReadyJobs, 1),
2406
- maxLaneConcurrency: normalizeAdaptiveLimitRecord({ ...laneMinimums, ...raw.maxLaneConcurrency }),
2407
- maxConcurrencyKeyConcurrency: normalizeAdaptiveLimitRecord({ ...keyMinimums, ...raw.maxConcurrencyKeyConcurrency }),
2408
- maxComputeConcurrency: normalizeAdaptiveLimitRecord({ ...computeMinimums, ...raw.maxComputeConcurrency }),
2409
- resourceQuotas: normalizeAdaptiveLimitRecord({ ...resourceMinimums, ...raw.resourceQuotas })
2410
- };
2411
- }
2412
- function mergeAdaptiveScheduleLimitInputs(left, right) {
2413
- return {
2414
- ...(right?.maxReadyJobs !== undefined ? { maxReadyJobs: right.maxReadyJobs } : left?.maxReadyJobs !== undefined ? { maxReadyJobs: left.maxReadyJobs } : {}),
2415
- maxLaneConcurrency: { ...(left?.maxLaneConcurrency ?? {}), ...(right?.maxLaneConcurrency ?? {}) },
2416
- maxConcurrencyKeyConcurrency: { ...(left?.maxConcurrencyKeyConcurrency ?? {}), ...(right?.maxConcurrencyKeyConcurrency ?? {}) },
2417
- maxComputeConcurrency: { ...(left?.maxComputeConcurrency ?? {}), ...(right?.maxComputeConcurrency ?? {}) },
2418
- resourceQuotas: { ...(left?.resourceQuotas ?? {}), ...(right?.resourceQuotas ?? {}) }
2419
- };
2420
- }
2421
- function clampAdaptiveLimits(value, minLimits, maxLimits) {
2422
- return {
2423
- ...(value.maxReadyJobs !== undefined || maxLimits.maxReadyJobs !== undefined ? {
2424
- maxReadyJobs: clampAdaptiveLimit(value.maxReadyJobs ?? maxLimits.maxReadyJobs ?? 1, minLimits.maxReadyJobs ?? 1, maxLimits.maxReadyJobs ?? value.maxReadyJobs ?? 1)
2425
- } : {}),
2426
- maxLaneConcurrency: clampAdaptiveRecord(value.maxLaneConcurrency, minLimits.maxLaneConcurrency, maxLimits.maxLaneConcurrency),
2427
- maxConcurrencyKeyConcurrency: clampAdaptiveRecord(value.maxConcurrencyKeyConcurrency, minLimits.maxConcurrencyKeyConcurrency, maxLimits.maxConcurrencyKeyConcurrency),
2428
- maxComputeConcurrency: clampAdaptiveRecord(value.maxComputeConcurrency, minLimits.maxComputeConcurrency, maxLimits.maxComputeConcurrency),
2429
- resourceQuotas: clampAdaptiveRecord(value.resourceQuotas, minLimits.resourceQuotas, maxLimits.resourceQuotas)
2430
- };
2431
- }
2432
- function normalizeAdaptiveObservations(input, generatedAt) {
2433
- const out = [];
2434
- input.forEach((entry, index) => {
2435
- const reasons = uniqueStrings([...(entry.reason ? [entry.reason] : []), ...(entry.reasons ?? [])]);
2436
- const at = entry.at ?? generatedAt;
2437
- const observation = {
2438
- id: entry.id ?? 'swarm-adaptive-observation:' + stableHash([entry.kind, entry.jobId, entry.lane, entry.resource, reasons, index, at]),
2439
- kind: entry.kind,
2440
- severity: entry.severity ?? adaptiveDefaultSeverity(entry.kind),
2441
- at,
2442
- value: Number.isFinite(entry.value) ? Number(entry.value) : 1,
2443
- ...(entry.jobId ? { jobId: entry.jobId } : {}),
2444
- ...(entry.taskId ? { taskId: entry.taskId } : {}),
2445
- ...(entry.lane ? { lane: entry.lane } : {}),
2446
- ...(entry.compute ? { compute: entry.compute } : {}),
2447
- ...(entry.concurrencyKey ? { concurrencyKey: entry.concurrencyKey } : {}),
2448
- ...(entry.resource ? { resource: entry.resource } : {}),
2449
- ...(entry.path ? { path: entry.path } : {}),
2450
- ...(entry.region ? { region: entry.region } : {}),
2451
- reasons: reasons.length ? reasons : [entry.kind],
2452
- ...(toJsonObject(entry.metadata) ? { metadata: toJsonObject(entry.metadata) } : {})
2453
- };
2454
- out.push(observation);
2455
- });
2456
- return dedupeAdaptiveObservations(out);
2457
- }
2458
- function deriveAdaptiveScheduleObservations(schedule, at) {
2459
- if (!schedule)
2460
- return [];
2461
- const observations = [];
2462
- for (const blocked of schedule.blocked) {
2463
- for (const reason of blocked.reasons) {
2464
- if (reason === 'waiting-for-dependencies')
2465
- continue;
2466
- const resource = reason.startsWith('resource-capacity:') ? reason.slice('resource-capacity:'.length) : undefined;
2467
- observations.push({
2468
- kind: resource ? 'resource-capacity' : reason,
2469
- severity: reason === 'ready-capacity' ? 'info' : 'warning',
2470
- at,
2471
- jobId: blocked.jobId,
2472
- taskId: blocked.taskId,
2473
- lane: blocked.lane,
2474
- compute: blocked.compute,
2475
- concurrencyKey: blocked.concurrencyKey,
2476
- ...(resource ? { resource } : {}),
2477
- reason
2478
- });
2479
- }
2480
- }
2481
- return observations;
2482
- }
2483
- function deriveAdaptiveRunObservations(run, at) {
2484
- if (!run)
2485
- return [];
2486
- const observations = [];
2487
- for (const result of run.results) {
2488
- if (result.status === 'failed' || result.exitCode !== undefined && result.exitCode !== 0 || result.verification.some((entry) => entry.required !== false && entry.status !== undefined && entry.status !== 0)) {
2489
- observations.push({
2490
- kind: 'evidence-failure',
2491
- severity: 'error',
2492
- at,
2493
- jobId: result.jobId,
2494
- reason: 'worker failed or required evidence command failed'
2495
- });
2496
- }
2497
- if (result.mergeDisposition === 'discovery-only' || result.mergeReadiness === 'discovery-only') {
2498
- observations.push({
2499
- kind: 'discovery-only-output',
2500
- severity: 'info',
2501
- at,
2502
- jobId: result.jobId,
2503
- reason: 'worker produced discovery output instead of a mergeable patch'
2504
- });
2505
- }
2506
- if (semanticSummaryIsEmpty(result.semanticImport)) {
2507
- observations.push({
2508
- kind: 'semantic-empty',
2509
- severity: 'warning',
2510
- at,
2511
- jobId: result.jobId,
2512
- reason: 'semantic import emitted no selected/imported files or symbols'
2513
- });
2514
- }
2515
- else if (semanticSummaryIsWeak(result.semanticImport)) {
2516
- observations.push({
2517
- kind: 'semantic-weak',
2518
- severity: 'info',
2519
- at,
2520
- jobId: result.jobId,
2521
- reason: 'semantic import has limited source maps, regions, or patch hints'
2522
- });
2523
- }
2524
- if (result.durationMs !== undefined && result.durationMs > 900000) {
2525
- observations.push({
2526
- kind: 'slow-job',
2527
- severity: 'warning',
2528
- at,
2529
- jobId: result.jobId,
2530
- value: result.durationMs,
2531
- reason: 'worker duration exceeded adaptive slow-job threshold'
2532
- });
2533
- }
2534
- if ((result.status === 'completed' || result.status === 'verified') && result.exitCode === 0 && result.changedPaths.length > 0 && result.mergeDisposition !== 'discovery-only') {
2535
- observations.push({
2536
- kind: 'healthy-throughput',
2537
- severity: 'info',
2538
- at,
2539
- jobId: result.jobId,
2540
- reason: 'worker completed with changed paths'
2541
- });
2542
- }
2543
- }
2544
- return observations;
2545
- }
2546
- function deriveAdaptiveMergeIndexObservations(index, at) {
2547
- if (!index)
2548
- return [];
2549
- const observations = [];
2550
- for (const entry of index.entries) {
2551
- if (entry.staleAgainstHead || entry.disposition === 'stale-against-head') {
2552
- observations.push({
2553
- kind: 'stale-patch',
2554
- severity: 'warning',
2555
- at,
2556
- jobId: entry.jobId,
2557
- lane: entry.lane,
2558
- path: entry.changedPaths[0],
2559
- region: entry.changedRegions[0],
2560
- reason: 'patch is stale against coordinator head'
2561
- });
2562
- }
2563
- if (entry.conflictingJobIds.length) {
2564
- observations.push({
2565
- kind: 'merge-conflict',
2566
- severity: 'warning',
2567
- at,
2568
- jobId: entry.jobId,
2569
- lane: entry.lane,
2570
- path: entry.changedPaths[0],
2571
- region: entry.changedRegions[0],
2572
- value: entry.conflictingJobIds.length,
2573
- reason: 'merge index found conflicting changed paths or regions'
2574
- });
2575
- }
2576
- if (entry.disposition === 'discovery-only') {
2577
- observations.push({
2578
- kind: 'discovery-only-output',
2579
- severity: 'info',
2580
- at,
2581
- jobId: entry.jobId,
2582
- lane: entry.lane,
2583
- reason: 'merge index classified the bundle as discovery-only'
2584
- });
2585
- }
2586
- if (semanticSummaryIsEmpty(entry.semanticImport)) {
2587
- observations.push({
2588
- kind: 'semantic-empty',
2589
- severity: 'warning',
2590
- at,
2591
- jobId: entry.jobId,
2592
- lane: entry.lane,
2593
- reason: 'semantic sidecar is present but empty'
2594
- });
2595
- }
2596
- else if (semanticSummaryIsWeak(entry.semanticImport)) {
2597
- observations.push({
2598
- kind: 'semantic-weak',
2599
- severity: 'info',
2600
- at,
2601
- jobId: entry.jobId,
2602
- lane: entry.lane,
2603
- reason: 'semantic sidecar lacks merge-useful structure'
2604
- });
2605
- }
2606
- }
2607
- return observations;
2608
- }
2609
- function deriveAdaptiveDashboardObservations(dashboard, at) {
2610
- if (!dashboard)
2611
- return [];
2612
- const observations = [];
2613
- for (const job of dashboard.jobs) {
2614
- if (job.duplicateGroupId) {
2615
- observations.push({
2616
- kind: 'duplicate-output',
2617
- severity: 'info',
2618
- at,
2619
- jobId: job.jobId,
2620
- lane: job.lane,
2621
- path: job.changedPaths[0],
2622
- region: job.changedRegions[0],
2623
- reason: 'coordinator dashboard found duplicate worker output'
2624
- });
2625
- }
2626
- if (job.tests.requiredFailed > 0) {
2627
- observations.push({
2628
- kind: 'evidence-failure',
2629
- severity: 'error',
2630
- at,
2631
- jobId: job.jobId,
2632
- lane: job.lane,
2633
- reason: 'dashboard shows required evidence failures'
2634
- });
2635
- }
2636
- if (job.staleAgainstHead) {
2637
- observations.push({
2638
- kind: 'stale-patch',
2639
- severity: 'warning',
2640
- at,
2641
- jobId: job.jobId,
2642
- lane: job.lane,
2643
- path: job.changedPaths[0],
2644
- reason: 'dashboard marks patch stale against head'
2645
- });
2646
- }
2647
- if (semanticSummaryIsEmpty(job.semanticImport)) {
2648
- observations.push({
2649
- kind: 'semantic-empty',
2650
- severity: 'warning',
2651
- at,
2652
- jobId: job.jobId,
2653
- lane: job.lane,
2654
- reason: 'dashboard semantic import summary is empty'
2655
- });
2656
- }
2657
- }
2658
- return observations;
2659
- }
2660
- function deriveAdaptiveAdmissionObservations(admission, at) {
2661
- if (!admission)
2662
- return [];
2663
- const observations = [];
2664
- for (const deferral of admission.deferred) {
2665
- for (const reason of deferral.reasons) {
2666
- const kind = reason === 'stale-against-head'
2667
- ? 'stale-patch'
2668
- : reason === 'conflicting-changes'
2669
- ? 'merge-conflict'
2670
- : reason === 'not-auto-mergeable'
2671
- ? 'discovery-only-output'
2672
- : reason === 'max-ready'
2673
- ? 'ready-capacity'
2674
- : 'budget-pressure';
2675
- observations.push({
2676
- kind,
2677
- severity: kind === 'ready-capacity' ? 'info' : 'warning',
2678
- at,
2679
- jobId: deferral.jobId,
2680
- reason: `merge admission deferred: ${reason}`
2681
- });
2682
- }
2683
- }
2684
- return observations;
2685
- }
2686
- function applyAdaptiveObservation(limits, minLimits, maxLimits, mode, observation, decisions) {
2687
- if (observation.kind === 'ready-capacity') {
2688
- decisions.push(createAdaptiveDecision({
2689
- action: 'hold',
2690
- target: 'max-ready-jobs',
2691
- previous: limits.maxReadyJobs,
2692
- next: limits.maxReadyJobs,
2693
- max: maxLimits.maxReadyJobs,
2694
- min: minLimits.maxReadyJobs,
2695
- reason: observation.reasons[0] ?? 'ready-capacity',
2696
- observationIds: [observation.id]
2697
- }));
2698
- return;
2699
- }
2700
- const target = adaptiveDecisionTargetForObservation(observation);
2701
- const key = adaptiveDecisionKeyForObservation(observation);
2702
- if (adaptiveObservationIsCapacityBackpressure(observation)) {
2703
- decisions.push(createAdaptiveDecision({
2704
- action: 'hold',
2705
- target,
2706
- ...(key ? { key } : {}),
2707
- reason: observation.reasons[0] ?? observation.kind,
2708
- observationIds: [observation.id]
2709
- }));
2710
- return;
2711
- }
2712
- if (target === 'lane' && key) {
2713
- decreaseAdaptiveRecordLimit(limits.maxLaneConcurrency, minLimits.maxLaneConcurrency, maxLimits.maxLaneConcurrency, key, mode, observation, decisions, target);
2714
- }
2715
- else if (target === 'concurrency-key' && key) {
2716
- decreaseAdaptiveRecordLimit(limits.maxConcurrencyKeyConcurrency, minLimits.maxConcurrencyKeyConcurrency, maxLimits.maxConcurrencyKeyConcurrency, key, mode, observation, decisions, target);
2717
- }
2718
- else if (target === 'compute' && key) {
2719
- decreaseAdaptiveRecordLimit(limits.maxComputeConcurrency, minLimits.maxComputeConcurrency, maxLimits.maxComputeConcurrency, key, mode, observation, decisions, target);
2720
- }
2721
- else if (target === 'resource' && key) {
2722
- decreaseAdaptiveRecordLimit(limits.resourceQuotas, minLimits.resourceQuotas, maxLimits.resourceQuotas, key, mode, observation, decisions, target);
2723
- }
2724
- if (adaptiveObservationShouldReduceReadyWindow(observation)) {
2725
- const previous = limits.maxReadyJobs ?? maxLimits.maxReadyJobs ?? 1;
2726
- const min = minLimits.maxReadyJobs ?? 1;
2727
- const max = maxLimits.maxReadyJobs ?? previous;
2728
- const next = adaptiveReducedValue(previous, min, mode, observation);
2729
- limits.maxReadyJobs = clampAdaptiveLimit(next, min, max);
2730
- decisions.push(createAdaptiveDecision({
2731
- action: limits.maxReadyJobs < previous ? 'decrease' : 'hold',
2732
- target: 'max-ready-jobs',
2733
- previous,
2734
- next: limits.maxReadyJobs,
2735
- max,
2736
- min,
2737
- reason: observation.reasons[0] ?? observation.kind,
2738
- observationIds: [observation.id]
2739
- }));
2740
- }
2741
- }
2742
- function applyAdaptiveRecovery(limits, maxLimits, observation, decisions) {
2743
- if (limits.maxReadyJobs !== undefined && maxLimits.maxReadyJobs !== undefined && limits.maxReadyJobs < maxLimits.maxReadyJobs) {
2744
- const previous = limits.maxReadyJobs;
2745
- limits.maxReadyJobs = Math.min(maxLimits.maxReadyJobs, previous + 1);
2746
- decisions.push(createAdaptiveDecision({
2747
- action: 'increase',
2748
- target: 'max-ready-jobs',
2749
- previous,
2750
- next: limits.maxReadyJobs,
2751
- max: maxLimits.maxReadyJobs,
2752
- reason: observation.reasons[0] ?? observation.kind,
2753
- observationIds: [observation.id]
2754
- }));
2755
- }
2756
- if (observation.lane) {
2757
- increaseAdaptiveRecordLimit(limits.maxLaneConcurrency, maxLimits.maxLaneConcurrency, observation.lane, observation, decisions, 'lane');
2758
- }
2759
- if (observation.compute) {
2760
- increaseAdaptiveRecordLimit(limits.maxComputeConcurrency, maxLimits.maxComputeConcurrency, observation.compute, observation, decisions, 'compute');
2761
- }
2762
- }
2763
- function decreaseAdaptiveRecordLimit(record, minRecord, maxRecord, key, mode, observation, decisions, target) {
2764
- const previous = record[key] ?? maxRecord[key];
2765
- if (!positiveNumber(previous))
2766
- return;
2767
- const min = minRecord[key] ?? 1;
2768
- const max = maxRecord[key] ?? previous;
2769
- const next = clampAdaptiveLimit(adaptiveReducedValue(previous, min, mode, observation), min, max);
2770
- record[key] = next;
2771
- decisions.push(createAdaptiveDecision({
2772
- action: next < previous ? 'decrease' : 'hold',
2773
- target,
2774
- key,
2775
- previous,
2776
- next,
2777
- max,
2778
- min,
2779
- reason: observation.reasons[0] ?? observation.kind,
2780
- observationIds: [observation.id]
2781
- }));
2782
- }
2783
- function increaseAdaptiveRecordLimit(record, maxRecord, key, observation, decisions, target) {
2784
- const previous = record[key];
2785
- const max = maxRecord[key];
2786
- if (!positiveNumber(previous) || !positiveNumber(max) || previous >= max)
2787
- return;
2788
- record[key] = Math.min(max, previous + 1);
2789
- decisions.push(createAdaptiveDecision({
2790
- action: 'increase',
2791
- target,
2792
- key,
2793
- previous,
2794
- next: record[key],
2795
- max,
2796
- reason: observation.reasons[0] ?? observation.kind,
2797
- observationIds: [observation.id]
2798
- }));
2799
- }
2800
- function adaptiveReducedValue(previous, min, mode, observation) {
2801
- if (observation.kind === 'merge-conflict' || observation.kind === 'duplicate-output' || observation.kind === 'concurrency-key-capacity')
2802
- return min;
2803
- const severityFactor = observation.severity === 'critical' ? 0.45 : observation.severity === 'error' ? 0.55 : observation.severity === 'warning' ? 0.7 : 0.85;
2804
- const modeFactor = mode === 'conservative' ? 0.6 : mode === 'aggressive' ? 0.85 : 0.75;
2805
- return Math.max(min, Math.floor(previous * Math.min(severityFactor, modeFactor)));
2806
- }
2807
- function adaptiveObservationShouldReduceReadyWindow(observation) {
2808
- return observation.kind === 'evidence-failure'
2809
- || observation.kind === 'stale-patch'
2810
- || observation.kind === 'browser-contention'
2811
- || observation.kind === 'semantic-empty'
2812
- || observation.kind === 'log-noise'
2813
- || observation.kind === 'discovery-only-output'
2814
- || observation.kind === 'budget-pressure'
2815
- || observation.kind === 'slow-job';
2816
- }
2817
- function adaptiveObservationIsCapacityBackpressure(observation) {
2818
- return observation.kind === 'resource-capacity'
2819
- || observation.kind === 'lane-capacity'
2820
- || observation.kind === 'concurrency-key-capacity'
2821
- || observation.kind === 'compute-capacity';
2822
- }
2823
- function adaptiveDecisionTargetForObservation(observation) {
2824
- if (observation.kind === 'resource-capacity' || observation.kind === 'browser-contention')
2825
- return 'resource';
2826
- if (observation.kind === 'lane-capacity')
2827
- return 'lane';
2828
- if (observation.kind === 'concurrency-key-capacity' || observation.kind === 'merge-conflict' || observation.kind === 'duplicate-output')
2829
- return 'concurrency-key';
2830
- if (observation.kind === 'compute-capacity')
2831
- return 'compute';
2832
- if (observation.lane)
2833
- return 'lane';
2834
- return 'max-ready-jobs';
2835
- }
2836
- function adaptiveDecisionKeyForObservation(observation) {
2837
- const target = adaptiveDecisionTargetForObservation(observation);
2838
- if (target === 'resource')
2839
- return observation.resource ?? (observation.kind === 'browser-contention' ? 'browser' : undefined);
2840
- if (target === 'lane')
2841
- return observation.lane;
2842
- if (target === 'concurrency-key')
2843
- return observation.concurrencyKey ?? observation.region ?? observation.path;
2844
- if (target === 'compute')
2845
- return observation.compute;
2846
- return undefined;
2847
- }
2848
- function adaptiveObservationIsBottleneck(observation) {
2849
- if (observation.kind === 'healthy-throughput' || observation.kind === 'ready-capacity')
2850
- return false;
2851
- return observation.severity !== 'info'
2852
- || observation.kind === 'merge-conflict'
2853
- || observation.kind === 'stale-patch'
2854
- || observation.kind === 'semantic-empty'
2855
- || observation.kind === 'log-noise'
2856
- || observation.kind === 'duplicate-output';
2857
- }
2858
- function adaptiveDefaultSeverity(kind) {
2859
- if (kind === 'evidence-failure' || kind === 'budget-pressure')
2860
- return 'error';
2861
- if (kind === 'merge-conflict' || kind === 'stale-patch' || kind === 'semantic-empty' || kind === 'browser-contention' || kind.endsWith('-capacity'))
2862
- return 'warning';
2863
- return 'info';
2864
- }
2865
- function createAdaptiveDecision(input) {
2866
- return {
2867
- id: 'swarm-adaptive-decision:' + stableHash([input.action, input.target, input.key, input.previous, input.next, input.reason, input.observationIds]),
2868
- ...input
2869
- };
2870
- }
2871
- function semanticSummaryIsEmpty(summary) {
2872
- if (!summary)
2873
- return false;
2874
- return summary.total === 0
2875
- || summary.selected === 0 && summary.eligible === 0 && summary.imported === 0 && summary.semanticIndex.symbols === 0 && summary.semanticSidecars.symbols === 0;
2876
- }
2877
- function semanticSummaryIsWeak(summary) {
2878
- if (!summary || semanticSummaryIsEmpty(summary))
2879
- return false;
2880
- return summary.imported === 0
2881
- || summary.semanticIndex.symbols === 0
2882
- || summary.semanticSidecars.ownershipRegions === 0
2883
- || summary.sourceMapMappingCount === 0
2884
- || summary.proofSpec.failed > 0
2885
- || summary.proofSpec.stale > 0
2886
- || summary.proofSpec.open > 0
2887
- || summary.proofSpec.unknown > 0;
2888
- }
2889
- function dedupeAdaptiveObservations(observations) {
2890
- const byKey = new Map();
2891
- for (const observation of observations) {
2892
- const key = [
2893
- observation.kind,
2894
- observation.jobId ?? '',
2895
- observation.lane ?? '',
2896
- observation.compute ?? '',
2897
- observation.concurrencyKey ?? '',
2898
- observation.resource ?? '',
2899
- observation.path ?? '',
2900
- observation.region ?? '',
2901
- observation.reasons.join('|')
2902
- ].join('\0');
2903
- const existing = byKey.get(key);
2904
- if (!existing || adaptiveSeverityRank(observation.severity) > adaptiveSeverityRank(existing.severity))
2905
- byKey.set(key, observation);
2906
- }
2907
- return Array.from(byKey.values()).sort((left, right) => adaptiveSeverityRank(right.severity) - adaptiveSeverityRank(left.severity) || left.kind.localeCompare(right.kind) || (left.jobId ?? '').localeCompare(right.jobId ?? ''));
2908
- }
2909
- function adaptiveSeverityRank(severity) {
2910
- if (severity === 'critical')
2911
- return 4;
2912
- if (severity === 'error')
2913
- return 3;
2914
- if (severity === 'warning')
2915
- return 2;
2916
- if (severity === 'info')
2917
- return 1;
2918
- return 0;
2919
- }
2920
- function normalizeAdaptiveLimitRecord(input = {}) {
2921
- const out = {};
2922
- for (const [key, value] of Object.entries(input)) {
2923
- if (positiveNumber(value))
2924
- out[key] = Math.max(1, Math.floor(value));
2925
- }
2926
- return out;
2927
- }
2928
- function clampAdaptiveRecord(value, minRecord, maxRecord) {
2929
- const keys = uniqueStrings([...Object.keys(value), ...Object.keys(minRecord), ...Object.keys(maxRecord)]);
2930
- const out = {};
2931
- for (const key of keys) {
2932
- const max = maxRecord[key];
2933
- if (!positiveNumber(max))
2934
- continue;
2935
- const min = minRecord[key] ?? 1;
2936
- out[key] = clampAdaptiveLimit(value[key] ?? max, min, max);
2937
- }
2938
- return out;
2939
- }
2940
- function clampAdaptiveLimit(value, min, max) {
2941
- const upper = Math.max(1, Math.floor(max));
2942
- const lower = Math.min(upper, Math.max(1, Math.floor(min)));
2943
- return Math.min(upper, Math.max(lower, Math.floor(value)));
2944
- }
2945
- function adaptivePositiveLimit(value, fallback) {
2946
- return positiveNumber(value) ? Math.max(1, Math.floor(value)) : Math.max(1, Math.floor(fallback));
2947
- }
2948
- function normalizeBudget(input = {}) {
2949
- return {
2950
- ...(positiveNumber(input.maxCostUsd) ? { maxCostUsd: input.maxCostUsd } : {}),
2951
- ...(positiveNumber(input.maxInputTokens) ? { maxInputTokens: Math.floor(input.maxInputTokens) } : {}),
2952
- ...(positiveNumber(input.maxOutputTokens) ? { maxOutputTokens: Math.floor(input.maxOutputTokens) } : {}),
2953
- ...(positiveNumber(input.maxDurationMs) ? { maxDurationMs: Math.floor(input.maxDurationMs) } : {}),
2954
- maxRetries: Math.max(0, Math.floor(input.maxRetries ?? 0)),
2955
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2956
- };
2957
- }
2958
- function normalizeUsage(input) {
2959
- return {
2960
- costUsd: Math.max(0, input.costUsd ?? 0),
2961
- inputTokens: Math.max(0, Math.floor(input.inputTokens ?? 0)),
2962
- outputTokens: Math.max(0, Math.floor(input.outputTokens ?? 0)),
2963
- durationMs: Math.max(0, Math.floor(input.durationMs ?? 0)),
2964
- attempts: Math.max(1, Math.floor(input.attempts ?? 1)),
2965
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2966
- };
2967
- }
2968
- function normalizeReviewPolicy(input = {}) {
2969
- const sampleRate = typeof input.sampleRate === 'number' && Number.isFinite(input.sampleRate)
2970
- ? Math.min(1, Math.max(0, input.sampleRate))
2971
- : 0;
2972
- return {
2973
- requiredReviewers: Math.max(0, Math.floor(input.requiredReviewers ?? 0)),
2974
- sampleRate,
2975
- alwaysReview: input.alwaysReview ?? false,
2976
- reviewerPool: uniqueStrings(input.reviewerPool ?? []),
2977
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2978
- };
2979
- }
2980
- function normalizeArtifact(input) {
2981
- return {
2982
- jobId: normalizeId(input.jobId, 'artifact job id'),
2983
- path: normalizeId(input.path, 'artifact path'),
2984
- kind: input.kind ?? 'artifact',
2985
- ...(positiveNumber(input.bytes) ? { bytes: Math.floor(input.bytes) } : {}),
2986
- ...(input.hash ? { hash: input.hash } : {}),
2987
- ...(input.producedAt !== undefined ? { producedAt: input.producedAt } : {}),
2988
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
2989
- };
2990
- }
2991
- function reviewReason(job, result, budget, sampleSalt) {
2992
- if (result?.ownershipViolations.length)
2993
- return 'violations';
2994
- if (result?.status === 'failed' || result?.exitCode !== undefined && result.exitCode !== 0)
2995
- return 'failed';
2996
- if (budget && !budget.ok)
2997
- return 'budget';
2998
- if (job.review.alwaysReview)
2999
- return 'always-review';
3000
- if (job.review.sampleRate > 0 && deterministicUnitInterval([sampleSalt, job.id]) < job.review.sampleRate)
3001
- return 'sampled';
3002
- return undefined;
3003
- }
3004
- function selectReviewers(pool, required, salt) {
3005
- if (required <= 0 || pool.length === 0)
3006
- return [];
3007
- const sorted = [...uniqueStrings(pool)].sort((left, right) => stableHash([salt, left]).localeCompare(stableHash([salt, right])));
3008
- return sorted.slice(0, Math.min(required, sorted.length));
3009
- }
3010
- function conflictMap(results) {
3011
- const conflicts = new Map();
3012
- for (let leftIndex = 0; leftIndex < results.length; leftIndex += 1) {
3013
- for (let rightIndex = leftIndex + 1; rightIndex < results.length; rightIndex += 1) {
3014
- const left = results[leftIndex];
3015
- const right = results[rightIndex];
3016
- if (!left || !right || pairConflictKeys(left, right).length === 0)
3017
- continue;
3018
- const leftConflicts = conflicts.get(left.jobId) ?? new Set();
3019
- const rightConflicts = conflicts.get(right.jobId) ?? new Set();
3020
- leftConflicts.add(right.jobId);
3021
- rightConflicts.add(left.jobId);
3022
- conflicts.set(left.jobId, leftConflicts);
3023
- conflicts.set(right.jobId, rightConflicts);
3024
- }
3025
- }
3026
- return conflicts;
3027
- }
3028
- function queueOverlayStatusFromBundle(bundle) {
3029
- if (bundle.staleAgainstHead || bundle.disposition === 'stale-against-head')
3030
- return 'stale-against-head';
3031
- if (bundle.disposition === 'rejected' || bundle.disposition === 'blocked' || bundle.status === 'failed' || bundle.commandsFailed.length > 0) {
3032
- return 'failed-evidence';
3033
- }
3034
- if (bundle.disposition === 'auto-mergeable' && bundle.autoMergeable)
3035
- return 'ready-to-apply';
3036
- if (bundle.disposition === 'needs-port')
3037
- return 'needs-human-port';
3038
- if (bundle.disposition === 'discovery-only')
3039
- return 'discovery-only';
3040
- if (bundle.mergeReadiness === 'blocked')
3041
- return 'blocked';
3042
- if (bundle.mergeReadiness === 'rejected')
3043
- return 'rejected';
3044
- return 'unknown';
3045
- }
3046
- function queueOverlayStatusFromResult(result) {
3047
- if (result.mergeDisposition === 'stale-against-head')
3048
- return 'stale-against-head';
3049
- if (result.status === 'failed' || result.exitCode !== undefined && result.exitCode !== 0 || result.ownershipViolations.length > 0)
3050
- return 'failed-evidence';
3051
- if (result.mergeDisposition === 'auto-mergeable')
3052
- return 'ready-to-apply';
3053
- if (result.mergeDisposition === 'needs-port')
3054
- return 'needs-human-port';
3055
- if (result.mergeDisposition === 'discovery-only')
3056
- return 'discovery-only';
3057
- if (result.status === 'blocked')
3058
- return 'blocked';
3059
- return 'unknown';
3060
- }
3061
- function queueJobStatusFromOverlay(entry) {
3062
- if (entry.status === 'ready-to-apply' || entry.status === 'discovery-only')
3063
- return 'completed';
3064
- if (entry.status === 'needs-human-port')
3065
- return 'blocked';
3066
- if (entry.status === 'failed-evidence' || entry.status === 'rejected' || entry.status === 'stale-against-head')
3067
- return 'failed';
3068
- if (entry.status === 'blocked')
3069
- return 'blocked';
3070
- return 'completed';
3071
- }
3072
- function groupOverlayEntries(entries) {
3073
- const out = {};
3074
- for (const entry of entries)
3075
- out[entry.queueItemId] = [...(out[entry.queueItemId] ?? []), entry];
3076
- for (const key of Object.keys(out)) {
3077
- out[key] = [...(out[key] ?? [])].sort((left, right) => right.generatedAt - left.generatedAt || left.jobId.localeCompare(right.jobId));
3078
- }
3079
- return out;
3080
- }
3081
- function mergeIndexConflictKeys(bundle) {
3082
- return bundle.changedRegions.length
3083
- ? bundle.changedRegions.map((region) => `region:${region}`).sort()
3084
- : bundle.changedPaths.map((file) => `path:${file}`).sort();
3085
- }
3086
- function createMergeIndexConflicts(entries) {
3087
- const conflicts = [];
3088
- for (let leftIndex = 0; leftIndex < entries.length; leftIndex += 1) {
3089
- for (let rightIndex = leftIndex + 1; rightIndex < entries.length; rightIndex += 1) {
3090
- const left = entries[leftIndex];
3091
- const right = entries[rightIndex];
3092
- if (!left || !right)
3093
- continue;
3094
- const keys = pairConflictKeys(left, right);
3095
- for (const key of keys) {
3096
- const kind = key.startsWith('region:') ? 'region' : 'path';
3097
- const value = key.slice(key.indexOf(':') + 1);
3098
- conflicts.push({
3099
- jobIds: [left.jobId, right.jobId].sort(),
3100
- key,
3101
- kind,
3102
- ...(kind === 'region' ? { region: value } : { path: value })
3103
- });
3104
- }
3105
- }
3106
- }
3107
- const deduped = new Map();
3108
- for (const conflict of conflicts)
3109
- deduped.set(`${conflict.key}:${conflict.jobIds.join(',')}`, conflict);
3110
- return Array.from(deduped.values()).sort((left, right) => left.key.localeCompare(right.key) || left.jobIds.join(',').localeCompare(right.jobIds.join(',')));
3111
- }
3112
- function pairConflictKeys(left, right) {
3113
- if (left.changedRegions.length > 0 && right.changedRegions.length > 0) {
3114
- const rightRegions = new Set(right.changedRegions);
3115
- return left.changedRegions.filter((region) => rightRegions.has(region)).map((region) => `region:${region}`).sort();
3116
- }
3117
- const rightPaths = new Set(right.changedPaths);
3118
- return left.changedPaths.filter((file) => rightPaths.has(file)).map((file) => `path:${file}`).sort();
3119
- }
3120
- function groupJobIdsBy(items, key) {
3121
- const out = {};
3122
- for (const item of items)
3123
- out[key(item)] = uniqueStrings([...(out[key(item)] ?? []), item.jobId]);
3124
- return out;
3125
- }
3126
- function groupJobIdsByMany(items, key) {
3127
- const out = {};
3128
- for (const item of items) {
3129
- for (const value of key(item))
3130
- out[value] = uniqueStrings([...(out[value] ?? []), item.jobId]);
3131
- }
3132
- return out;
3133
- }
3134
- function suggestedModuleId(file) {
3135
- const base = file.split('/').pop()?.replace(/\.[^.]+$/, '') ?? file;
3136
- return slug(base).replace(/-/g, '.');
3137
- }
3138
- function reviewerLaneReasons(entry) {
3139
- const reasons = [];
3140
- if (entry.conflictingJobIds.length)
3141
- reasons.push('conflicting-changes');
3142
- if (entry.riskLevel === 'high')
3143
- reasons.push('high-risk');
3144
- if (entry.disposition !== 'auto-mergeable')
3145
- reasons.push(entry.disposition);
3146
- if (!entry.autoMergeable)
3147
- reasons.push('not-auto-mergeable');
3148
- if (entry.staleAgainstHead)
3149
- reasons.push('stale-against-head');
3150
- return uniqueStrings(reasons);
3151
- }
3152
- function hashBucket(value, buckets) {
3153
- const hex = stableHash(value).split(':')[1] ?? '0';
3154
- return parseInt(hex, 16) % Math.max(1, buckets);
3155
- }
3156
- function normalizeOracleArtifact(input) {
3157
- return {
3158
- id: normalizeId(input.id, 'oracle artifact id'),
3159
- path: normalizeId(input.path, 'oracle artifact path'),
3160
- kind: input.kind ?? 'oracle',
3161
- ...(input.command ? { command: typeof input.command === 'string' ? normalizeCommands([input.command])[0] : normalizeCommands([input.command])[0] } : {}),
3162
- ...(input.hash ? { hash: input.hash } : {}),
3163
- ...(input.sourceRef ? { sourceRef: input.sourceRef } : {}),
3164
- tags: uniqueStrings(input.tags ?? []),
3165
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3166
- };
3167
- }
3168
- function normalizeCommand(input) {
3169
- return normalizeCommands([input])[0];
3170
- }
3171
- function normalizeNamedRef(input, fallbackKind) {
3172
- if (typeof input === 'string' || typeof input === 'number') {
3173
- const value = String(input);
3174
- return { id: value, kind: fallbackKind, path: value, tags: [] };
3175
- }
3176
- const path = input.path ?? input.uri;
3177
- const id = input.id ?? path ?? stableHash(input);
3178
- return {
3179
- id,
3180
- kind: input.kind ?? fallbackKind,
3181
- ...(input.path ? { path: input.path } : {}),
3182
- ...(input.uri ? { uri: input.uri } : {}),
3183
- ...(input.role ? { role: input.role } : {}),
3184
- ...(input.hash ? { hash: input.hash } : {}),
3185
- ...(positiveNumber(input.bytes) ? { bytes: Math.floor(input.bytes) } : {}),
3186
- tags: uniqueStrings(input.tags ?? []),
3187
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3188
- };
3189
- }
3190
- function normalizeNamedRefs(input, fallbackKind) {
3191
- return input.map((entry) => normalizeNamedRef(entry, fallbackKind)).sort((left, right) => left.id.localeCompare(right.id));
3192
- }
3193
- function normalizeSeedRefs(input) {
3194
- return input.map((entry) => normalizeNamedRef(entry, 'seed')).sort((left, right) => left.id.localeCompare(right.id));
3195
- }
3196
- function normalizeParityComparator(input) {
3197
- const title = input.title ?? titleFromId(input.id ?? input.path ?? 'comparator');
3198
- return {
3199
- id: input.id ?? 'swarm-parity-comparator:' + stableHash([title, input.status, input.expected, input.actual, input.path, input.operationIndex]),
3200
- title,
3201
- status: input.status ?? (input.expected !== undefined && input.actual !== undefined && stableStringify(input.expected) === stableStringify(input.actual) ? 'passed' : 'unknown'),
3202
- ...(input.expected !== undefined ? { expected: toJsonValue(input.expected) } : {}),
3203
- ...(input.actual !== undefined ? { actual: toJsonValue(input.actual) } : {}),
3204
- ...(input.path ? { path: input.path } : {}),
3205
- ...(input.operationIndex !== undefined ? { operationIndex: Math.max(0, Math.floor(input.operationIndex)) } : {}),
3206
- evidenceRefs: normalizeNamedRefs(input.evidenceRefs ?? [], 'evidence'),
3207
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3208
- };
3209
- }
3210
- function inferParityStatus(comparators) {
3211
- if (comparators.some((comparator) => comparator.status === 'failed'))
3212
- return 'failed';
3213
- if (comparators.some((comparator) => comparator.status === 'blocked'))
3214
- return 'blocked';
3215
- if (comparators.length > 0 && comparators.every((comparator) => comparator.status === 'passed'))
3216
- return 'passed';
3217
- return 'pending';
3218
- }
3219
- function normalizeWatchpoint(input) {
3220
- const title = input.title ?? titleFromId(input.id ?? input.path ?? input.selector ?? input.target ?? 'watchpoint');
3221
- return {
3222
- id: input.id ?? 'swarm-watchpoint:' + stableHash([input.target, input.path, input.selector, input.operator, input.value, input.action]),
3223
- title,
3224
- ...(input.target ? { target: input.target } : {}),
3225
- ...(input.path ? { path: input.path } : {}),
3226
- ...(input.selector ? { selector: input.selector } : {}),
3227
- operator: input.operator ?? 'changed',
3228
- ...(input.value !== undefined ? { value: toJsonValue(input.value) } : {}),
3229
- action: input.action ?? 'capture',
3230
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3231
- };
3232
- }
3233
- function normalizeBottleneckSource(input) {
3234
- if (input.kind === FRONTIER_SWARM_MERGE_BUNDLE_KIND) {
3235
- const bundle = input;
3236
- return {
3237
- jobId: bundle.jobId,
3238
- lane: bundle.lane,
3239
- status: bundle.status,
3240
- reasons: bundle.reasons,
3241
- evidencePaths: bundle.evidencePaths,
3242
- changedPaths: bundle.changedPaths,
3243
- text: bundle.title,
3244
- metadata: bundle.metadata
3245
- };
3246
- }
3247
- if ('text' in input || 'reasons' in input || 'lane' in input)
3248
- return input;
3249
- const result = input;
3250
- if (result.jobId) {
3251
- return {
3252
- jobId: result.jobId,
3253
- status: result.status,
3254
- reasons: result.error !== undefined ? [stringifyError(result.error)] : [],
3255
- verification: result.verification,
3256
- evidencePaths: result.evidencePaths,
3257
- changedPaths: result.changedPaths,
3258
- text: result.lastMessage,
3259
- metadata: result.metadata
3260
- };
3261
- }
3262
- return input;
3263
- }
3264
- function routeForBottleneck(kind, lane) {
3265
- if (kind === 'missing-oracle')
3266
- return { lane: lane ?? 'verification', workKind: 'oracle', priority: 20 };
3267
- if (kind === 'flaky-harness')
3268
- return { lane: lane ?? 'evidence', workKind: 'harness', priority: 25 };
3269
- if (kind === 'merge-review')
3270
- return { lane: lane ?? 'review', workKind: 'review', priority: 10 };
3271
- if (kind === 'instrumentation-overhead')
3272
- return { lane: lane ?? 'diagnostics', workKind: 'instrumentation', priority: 30 };
3273
- if (kind === 'performance')
3274
- return { lane: lane ?? 'performance', workKind: 'benchmark', priority: 35 };
3275
- if (kind === 'correctness')
3276
- return { lane: lane ?? 'implementation', workKind: 'debug', priority: 15 };
3277
- return { ...(lane ? { lane } : {}), workKind: 'triage', priority: 50 };
3278
- }
3279
- function evidenceKindFromPath(path) {
3280
- const lower = path.toLowerCase();
3281
- if (lower.endsWith('.patch') || lower.endsWith('.diff'))
3282
- return 'patch';
3283
- if (lower.endsWith('.jsonl'))
3284
- return 'jsonl';
3285
- if (lower.endsWith('.json'))
3286
- return 'json';
3287
- if (lower.includes('trace'))
3288
- return 'trace';
3289
- if (lower.includes('screenshot'))
3290
- return 'screenshot';
3291
- if (lower.includes('last-message'))
3292
- return 'handoff';
3293
- return 'evidence';
3294
- }
3295
- function normalizeEvidenceIndexEntry(input) {
3296
- const generatedAt = input.generatedAt ?? Date.now();
3297
- return {
3298
- id: input.id ?? 'swarm-evidence-entry:' + stableHash([input.jobId, input.queueItemId, input.path, input.topic, input.kind, generatedAt]),
3299
- ...(input.jobId ? { jobId: input.jobId } : {}),
3300
- ...(input.queueItemId ? { queueItemId: input.queueItemId } : {}),
3301
- ...(input.lane ? { lane: input.lane } : {}),
3302
- ...(input.topic ? { topic: input.topic } : {}),
3303
- ...(input.path ? { path: input.path } : {}),
3304
- kind: input.kind ?? (input.path ? evidenceKindFromPath(input.path) : 'evidence'),
3305
- status: input.status ?? 'unknown',
3306
- confidence: clamp01(input.confidence ?? 0.5),
3307
- tags: uniqueStrings(input.tags ?? []),
3308
- facets: normalizeFacets(input.facets ?? {}),
3309
- generatedAt,
3310
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3311
- };
3312
- }
3313
- function matchesFacetQuery(facets, query) {
3314
- if (!query)
3315
- return true;
3316
- return Object.entries(query).every(([key, value]) => facets[key] === value);
3317
- }
3318
- function normalizeFacets(input) {
3319
- return Object.fromEntries(Object.entries(input).filter(([, value]) => ['string', 'number', 'boolean'].includes(typeof value)));
3320
- }
3321
- function normalizeBlackboardEntry(input) {
3322
- const generatedAt = input.generatedAt ?? Date.now();
3323
- const topic = input.topic ?? input.kind ?? 'general';
3324
- return {
3325
- id: input.id ?? 'swarm-blackboard-entry:' + stableHash([input.kind, topic, input.text, input.sourceIds, generatedAt]),
3326
- kind: input.kind ?? 'fact',
3327
- topic,
3328
- status: input.status ?? 'fresh',
3329
- text: input.text ?? '',
3330
- ...(input.lane ? { lane: input.lane } : {}),
3331
- ...(input.jobId ? { jobId: input.jobId } : {}),
3332
- ...(input.owner ? { owner: input.owner } : {}),
3333
- confidence: input.confidence ?? 'medium',
3334
- sourceIds: uniqueStrings(input.sourceIds ?? []),
3335
- paths: uniqueStrings(input.paths ?? []),
3336
- tags: uniqueStrings(input.tags ?? []),
3337
- supersedes: uniqueStrings(input.supersedes ?? []),
3338
- generatedAt,
3339
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3340
- };
3341
- }
3342
- function normalizeReferenceWindow(input) {
3343
- return {
3344
- ...(input.start !== undefined ? { start: Math.max(0, Math.floor(input.start)) } : {}),
3345
- ...(input.end !== undefined ? { end: Math.max(0, Math.floor(input.end)) } : {}),
3346
- ...(input.focus ? { focus: input.focus } : {}),
3347
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3348
- };
3349
- }
3350
- function normalizeRoutingHint(input) {
3351
- return {
3352
- ...(input.artifactKind ? { artifactKind: input.artifactKind } : {}),
3353
- ...(input.pathPattern ? { pathPattern: input.pathPattern } : {}),
3354
- ...(input.lane ? { lane: input.lane } : {}),
3355
- bucket: input.bucket ?? 'needs-human-port',
3356
- reason: input.reason ?? 'matched-routing-hint',
3357
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3358
- };
3359
- }
3360
- function defaultArtifactBucket(artifact) {
3361
- if (artifact.kind === 'patch')
3362
- return 'ready-to-apply';
3363
- if (artifact.kind === 'handoff' || artifact.kind === 'trace' || artifact.kind === 'jsonl')
3364
- return 'discovery-only';
3365
- return 'needs-human-port';
3366
- }
3367
- function normalizeFixture(input) {
3368
- return {
3369
- id: normalizeId(input.id, 'fixture id'),
3370
- title: input.title ?? titleFromId(input.id),
3371
- ...(input.description ? { description: input.description } : {}),
3372
- ...(input.state !== undefined ? { state: toJsonValue(input.state) } : {}),
3373
- artifacts: normalizeNamedRefs(input.artifacts ?? [], 'fixture-artifact'),
3374
- setupCommands: normalizeCommands(input.setupCommands ?? []),
3375
- tags: uniqueStrings(input.tags ?? []),
3376
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3377
- };
3378
- }
3379
- function deriveAutoReviewFindings(bundle, generatedAt) {
3380
- const findings = [];
3381
- if (bundle.ownershipViolations.length) {
3382
- findings.push(normalizeAutoReviewFinding({
3383
- jobId: bundle.jobId,
3384
- kind: 'ownership-violation',
3385
- severity: 'error',
3386
- message: 'Bundle changed paths outside its ownership lease.',
3387
- paths: bundle.ownershipViolations,
3388
- evidencePaths: bundle.evidencePaths
3389
- }, generatedAt));
3390
- }
3391
- if (bundle.evidencePaths.length === 0 && bundle.changedPaths.length > 0) {
3392
- findings.push(normalizeAutoReviewFinding({
3393
- jobId: bundle.jobId,
3394
- kind: 'missing-evidence',
3395
- severity: 'warning',
3396
- message: 'Patch bundle has changed paths but no evidence paths.',
3397
- paths: bundle.changedPaths
3398
- }, generatedAt));
3399
- }
3400
- if (bundle.changedPaths.length > 12) {
3401
- findings.push(normalizeAutoReviewFinding({
3402
- jobId: bundle.jobId,
3403
- kind: 'overlarge-patch',
3404
- severity: 'warning',
3405
- message: 'Patch bundle touches many files and should be split or reviewed manually.',
3406
- paths: bundle.changedPaths,
3407
- evidencePaths: bundle.evidencePaths
3408
- }, generatedAt));
3409
- }
3410
- return findings;
3411
- }
3412
- function normalizeAutoReviewFinding(input, generatedAt) {
3413
- return {
3414
- id: input.id ?? 'swarm-auto-review-finding:' + stableHash([input.jobId, input.kind, input.message, input.paths, generatedAt]),
3415
- ...(input.jobId ? { jobId: input.jobId } : {}),
3416
- kind: input.kind ?? 'weak-evidence',
3417
- severity: input.severity ?? 'warning',
3418
- message: input.message,
3419
- paths: uniqueStrings(input.paths ?? []),
3420
- evidencePaths: uniqueStrings(input.evidencePaths ?? []),
3421
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3422
- };
3423
- }
3424
- function isSwarmObservabilityPoint(value) {
3425
- return !!value && typeof value === 'object' && value.kind === FRONTIER_SWARM_OBSERVABILITY_POINT_KIND;
3426
- }
3427
- function isSwarmInstrumentationBudget(value) {
3428
- return !!value && typeof value === 'object' && value.kind === FRONTIER_SWARM_INSTRUMENTATION_BUDGET_KIND;
3429
- }
3430
- function isSwarmUsageGovernor(value) {
3431
- return !!value && typeof value === 'object' && value.kind === FRONTIER_SWARM_USAGE_GOVERNOR_KIND;
3432
- }
3433
- function isSwarmRun(value) {
3434
- return !!value && typeof value === 'object' && value.kind === FRONTIER_SWARM_RUN_KIND;
3435
- }
3436
- function groupObjects(items, key) {
3437
- const out = {};
3438
- for (const item of items) {
3439
- const group = key(item);
3440
- out[group] = [...(out[group] ?? []), item];
3441
- }
3442
- return out;
3443
- }
3444
- function clamp01(value) {
3445
- if (!Number.isFinite(value))
3446
- return 0;
3447
- return Math.max(0, Math.min(1, value));
3448
- }
3449
- function groupArtifactIdsBy(artifacts, key) {
3450
- const out = {};
3451
- for (const artifact of artifacts) {
3452
- for (const value of key(artifact))
3453
- out[value] = uniqueStrings([...(out[value] ?? []), artifact.id]);
3454
- }
3455
- return out;
3456
- }
3457
- function patchStackKey(entry) {
3458
- const lane = entry.lane ?? 'unassigned';
3459
- if (entry.changedRegions.length)
3460
- return `${lane}:${entry.changedRegions[0]}`;
3461
- const firstPath = entry.changedPaths[0] ?? 'evidence-only';
3462
- return `${lane}:${firstPath.split('/').slice(0, 2).join('/') || firstPath}`;
3463
- }
3464
- function normalizeCoordinatorProcesses(input) {
3465
- return input.map((entry) => ({
3466
- ...(entry.pid !== undefined ? { pid: Math.floor(entry.pid) } : {}),
3467
- role: entry.role ?? 'worker',
3468
- ...(entry.jobId ? { jobId: entry.jobId } : {}),
3469
- ...(entry.runId ? { runId: entry.runId } : {}),
3470
- status: entry.status ?? 'unknown',
3471
- ...(entry.startedAt !== undefined ? { startedAt: entry.startedAt } : {}),
3472
- ...(entry.lastSeenAt !== undefined ? { lastSeenAt: entry.lastSeenAt } : {}),
3473
- command: [...(entry.command ?? [])],
3474
- ...(toJsonObject(entry.metadata) ? { metadata: toJsonObject(entry.metadata) } : {})
3475
- })).sort((left, right) => (left.jobId ?? '').localeCompare(right.jobId ?? '') || (left.pid ?? 0) - (right.pid ?? 0));
3476
- }
3477
- function createCoordinatorDuplicateGroups(entries) {
3478
- const groups = new Map();
3479
- for (const entry of entries) {
3480
- for (const key of coordinatorDuplicateKeys(entry))
3481
- groups.set(key, [...(groups.get(key) ?? []), entry]);
3482
- }
3483
- return Array.from(groups.entries())
3484
- .filter(([, groupEntries]) => groupEntries.length > 1)
3485
- .map(([key, groupEntries]) => {
3486
- const jobIds = groupEntries.map((entry) => entry.jobId).sort();
3487
- return {
3488
- id: 'swarm-duplicate-group:' + stableHash([key, jobIds]),
3489
- key,
3490
- jobIds,
3491
- reason: key.startsWith('queue:') ? 'same-queue-item' : key.startsWith('region:') ? 'same-semantic-region' : 'same-changed-paths'
3492
- };
3493
- })
3494
- .sort((left, right) => left.key.localeCompare(right.key));
3495
- }
3496
- function coordinatorDuplicateKeys(entry) {
3497
- const keys = [];
3498
- if (entry.queueItemIds.length)
3499
- keys.push(`queue:${entry.queueItemIds.slice().sort().join('|')}`);
3500
- if (entry.changedRegions.length)
3501
- keys.push(`region:${entry.changedRegions.slice().sort().join('|')}`);
3502
- if (entry.changedPaths.length)
3503
- keys.push(`path:${entry.changedPaths.slice().sort().join('|')}`);
3504
- return keys;
3505
- }
3506
- function scoreCoordinatorMergeJob(entry, bundle, evidenceEntryCount, duplicateGroup, admissionStatus, admissionReasons) {
3507
- if (!entry)
3508
- return { score: 10, reasons: ['no-merge-index-entry'] };
3509
- let score = entry.disposition === 'auto-mergeable' && entry.autoMergeable ? 85 : entry.disposition === 'needs-port' ? 60 : entry.disposition === 'discovery-only' ? 35 : 15;
3510
- const reasons = [];
3511
- if (entry.staleAgainstHead) {
3512
- score -= 45;
3513
- reasons.push('stale-against-head');
3514
- }
3515
- if (entry.conflictingJobIds.length) {
3516
- score -= Math.min(35, 15 + entry.conflictingJobIds.length * 5);
3517
- reasons.push('conflicting-changes');
3518
- }
3519
- if (entry.ownershipViolations.length) {
3520
- score -= 40;
3521
- reasons.push('ownership-violations');
3522
- }
3523
- if (bundle?.commandsFailed.length) {
3524
- score -= Math.min(35, 15 + bundle.commandsFailed.length * 10);
3525
- reasons.push('failed-required-commands');
3526
- }
3527
- if (entry.evidencePaths.length === 0 && evidenceEntryCount === 0) {
3528
- score -= 15;
3529
- reasons.push('missing-evidence');
3530
- }
3531
- if (entry.riskLevel === 'high') {
3532
- score -= 15;
3533
- reasons.push('high-risk');
3534
- }
3535
- else if (entry.riskLevel === 'unknown') {
3536
- score -= 8;
3537
- reasons.push('unknown-risk');
3538
- }
3539
- else if (entry.riskLevel === 'medium') {
3540
- score -= 5;
3541
- }
3542
- if (entry.disposition === 'needs-port')
3543
- reasons.push('needs-human-port');
3544
- if (entry.disposition === 'discovery-only')
3545
- reasons.push('discovery-only');
3546
- if (duplicateGroup) {
3547
- score -= 12;
3548
- reasons.push('duplicate-candidate');
3549
- }
3550
- if (admissionStatus === 'deferred') {
3551
- score -= 10;
3552
- reasons.push(...admissionReasons);
3553
- }
3554
- if (entry.semanticImport && entry.changedPaths.length > 0) {
3555
- const symbols = entry.semanticImport.semanticIndex.symbols;
3556
- const regions = entry.semanticImport.semanticSidecars.ownershipRegions;
3557
- const errors = entry.semanticImport.errors;
3558
- if (symbols > 0 && regions > 0) {
3559
- score += 8;
3560
- reasons.push('semantic-sidecar-usable');
3561
- }
3562
- if (entry.semanticImport.semanticSidecars.empty > 0 || symbols === 0) {
3563
- score -= 8;
3564
- reasons.push('weak-semantic-sidecar');
3565
- }
3566
- if (errors > 0) {
3567
- score -= Math.min(25, errors * 10);
3568
- reasons.push('semantic-import-errors');
3569
- }
3570
- }
3571
- else if (entry.changedPaths.length > 0) {
3572
- score -= 5;
3573
- reasons.push('missing-semantic-sidecar');
3574
- }
3575
- if (bundle?.commandsPassed.length)
3576
- score += Math.min(8, bundle.commandsPassed.length * 2);
3577
- return { score: clampScore(score), reasons: uniqueStrings(reasons) };
3578
- }
3579
- function coordinatorJobLiveness(result, entry, processes) {
3580
- if (result || entry)
3581
- return 'finished';
3582
- if (processes.some((process) => process.status === 'running'))
3583
- return 'running';
3584
- if (processes.some((process) => process.status === 'missing'))
3585
- return 'missing';
3586
- return 'unknown';
3587
- }
3588
- function primaryEvidencePath(paths) {
3589
- return paths.find((entry) => entry.endsWith('/evidence.json') || entry === 'evidence.json')
3590
- ?? paths.find((entry) => entry.endsWith('/merge.json') || entry === 'merge.json')
3591
- ?? paths.find((entry) => entry.endsWith('/last-message.md') || entry.endsWith('/last.md'))
3592
- ?? paths[0];
3593
- }
3594
- function createCoordinatorSourceCitations(entry, evidenceIndex) {
3595
- const citations = [];
3596
- for (const file of entry?.changedPaths ?? [])
3597
- citations.push({ path: file, kind: 'changed-source' });
3598
- for (const region of entry?.changedRegions ?? [])
3599
- citations.push({ path: region, kind: 'semantic-region', region });
3600
- for (const evidence of evidenceIndex?.byJobId[entry?.jobId ?? ''] ?? []) {
3601
- if (!evidence.path)
3602
- continue;
3603
- citations.push({
3604
- path: evidence.path,
3605
- kind: evidence.kind,
3606
- ...(evidence.topic ? { symbol: evidence.topic } : {}),
3607
- confidence: evidence.confidence,
3608
- ...(Object.keys(evidence.facets).length ? { metadata: { facets: evidence.facets } } : {})
3609
- });
3610
- }
3611
- const seen = new Set();
3612
- return citations.filter((citation) => {
3613
- const key = `${citation.kind}:${citation.path}:${citation.symbol ?? ''}:${citation.region ?? ''}`;
3614
- if (seen.has(key))
3615
- return false;
3616
- seen.add(key);
3617
- return true;
3618
- }).sort((left, right) => left.kind.localeCompare(right.kind) || left.path.localeCompare(right.path));
3619
- }
3620
- function averageScore(scores) {
3621
- if (scores.length === 0)
3622
- return 0;
3623
- return Math.round(scores.reduce((sum, score) => sum + score, 0) / scores.length);
3624
- }
3625
- function clampScore(value) {
3626
- if (!Number.isFinite(value))
3627
- return 0;
3628
- return Math.max(0, Math.min(100, Math.round(value)));
3629
- }
3630
- function riskRank(risk) {
3631
- if (risk === 'low')
3632
- return 0;
3633
- if (risk === 'medium')
3634
- return 1;
3635
- if (risk === 'unknown')
3636
- return 2;
3637
- if (risk === 'high')
3638
- return 3;
3639
- return 4;
3640
- }
3641
- function groupMergeReadyJobs(ready, results) {
3642
- const byJob = new Map(results.map((result) => [result.jobId, result]));
3643
- return ready.map((jobId) => {
3644
- const changedPaths = [...(byJob.get(jobId)?.changedPaths ?? [])].sort();
3645
- return {
3646
- id: 'merge-group:' + stableHash([jobId, changedPaths]),
3647
- jobIds: [jobId],
3648
- changedPaths
3649
- };
3650
- });
3651
- }
3652
- function groupArtifacts(artifacts, key) {
3653
- const out = {};
3654
- for (const artifact of artifacts) {
3655
- const group = key(artifact);
3656
- out[group] = [...(out[group] ?? []), artifact];
3657
- }
3658
- return out;
3659
- }
3660
- function groupIds(items, key) {
3661
- const out = {};
3662
- for (const item of items) {
3663
- const group = key(item);
3664
- const id = item.jobId ?? item.id;
3665
- if (!id)
3666
- continue;
3667
- out[group] = [...(out[group] ?? []), id];
3668
- }
3669
- for (const ids of Object.values(out))
3670
- ids.sort();
3671
- return out;
3672
- }
3673
- function countBy(values) {
3674
- const out = {};
3675
- for (const value of values)
3676
- out[value] = (out[value] ?? 0) + 1;
3677
- return out;
3678
- }
3679
- function hasJobDependencyCycle(start, dependenciesByJobId) {
3680
- const visiting = new Set();
3681
- const visited = new Set();
3682
- const visit = (node) => {
3683
- if (visiting.has(node))
3684
- return true;
3685
- if (visited.has(node))
3686
- return false;
3687
- visiting.add(node);
3688
- for (const dep of dependenciesByJobId[node] ?? []) {
3689
- if (visit(dep))
3690
- return true;
3691
- }
3692
- visiting.delete(node);
3693
- visited.add(node);
3694
- return false;
3695
- };
3696
- return visit(start);
3697
- }
3698
- function deterministicUnitInterval(value) {
3699
- const hex = stableHash(value).split(':')[1] ?? '0';
3700
- return parseInt(hex, 16) / 0xffffffff;
3701
- }
3702
- function normalizeComputeList(input) {
3703
- const values = input && input.length > 0 ? input : [{
3704
- id: FRONTIER_SWARM_DEFAULT_CODEX_COMPUTE_ID,
3705
- kind: 'codex',
3706
- model: FRONTIER_SWARM_DEFAULT_MODEL,
3707
- reasoningEffort: FRONTIER_SWARM_DEFAULT_REASONING_EFFORT
3708
- }];
3709
- return values.map((compute) => ({
3710
- id: normalizeId(compute.id, 'compute id'),
3711
- kind: compute.kind ?? 'external',
3712
- ...(compute.title ? { title: compute.title } : {}),
3713
- ...(compute.model ? { model: compute.model } : {}),
3714
- ...(compute.reasoningEffort ? { reasoningEffort: compute.reasoningEffort } : {}),
3715
- ...(compute.serviceTier ? { serviceTier: compute.serviceTier } : {}),
3716
- ...(compute.profile ? { profile: compute.profile } : {}),
3717
- ...(compute.sandbox ? { sandbox: compute.sandbox } : {}),
3718
- ...(compute.approval ? { approval: compute.approval } : {}),
3719
- ...(positiveNumber(compute.maxConcurrency) ? { maxConcurrency: Math.floor(compute.maxConcurrency) } : {}),
3720
- ...(positiveNumber(compute.timeoutMs) ? { timeoutMs: Math.floor(compute.timeoutMs) } : {}),
3721
- ...(toJsonObject(compute.metadata) ? { metadata: toJsonObject(compute.metadata) } : {})
3722
- }));
3723
- }
3724
- function normalizeLayer(input) {
3725
- return {
3726
- id: normalizeId(input.id, 'layer id'),
3727
- title: input.title ?? titleFromId(input.id),
3728
- ...(input.description ? { description: input.description } : {}),
3729
- ...(input.parentId ? { parentId: normalizeId(input.parentId, 'parent layer id') } : {}),
3730
- ...(input.compute ? { compute: input.compute } : {}),
3731
- ...(input.defaultCompute ? { defaultCompute: input.defaultCompute } : {}),
3732
- childCompute: { ...(input.childCompute ?? {}) },
3733
- tags: uniqueStrings(input.tags ?? []),
3734
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3735
- };
3736
- }
3737
- function normalizeLane(input) {
3738
- const allowedWrites = uniqueStrings([...(input.allowedWrites ?? []), ...(input.allowedGlobs ?? [])]);
3739
- return {
3740
- id: normalizeId(input.id, 'lane id'),
3741
- title: input.title ?? titleFromId(input.id),
3742
- ...(input.description ? { description: input.description } : {}),
3743
- ...(input.layer ? { layer: input.layer } : {}),
3744
- ...(input.compute ? { compute: input.compute } : {}),
3745
- allowedWrites,
3746
- sharedReadOnly: uniqueStrings(input.sharedReadOnly ?? []),
3747
- neverEdit: uniqueStrings(input.neverEdit ?? []),
3748
- ownershipRegions: normalizeOwnershipRegions(input.ownershipRegions ?? []),
3749
- capabilities: uniqueStrings(input.capabilities ?? []),
3750
- ...(input.resourceRequirements ? { resourceRequirements: normalizeResourceRequirements(input.resourceRequirements) } : {}),
3751
- ...(input.worktreePath ? { worktreePath: input.worktreePath } : {}),
3752
- ...(input.evidencePrefix || input.evidenceOutDirPrefix ? { evidencePrefix: input.evidencePrefix ?? input.evidenceOutDirPrefix } : {}),
3753
- concurrencyKey: input.concurrencyKey ?? input.id,
3754
- ...(positiveNumber(input.maxConcurrency) ? { maxConcurrency: Math.floor(input.maxConcurrency) } : {}),
3755
- handoffCommands: normalizeCommands(input.handoffCommands ?? []),
3756
- tags: uniqueStrings(input.tags ?? []),
3757
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3758
- };
3759
- }
3760
- function normalizeOwnershipRegions(input = []) {
3761
- return input.map((region) => {
3762
- const globs = uniqueStrings([...(region.globs ?? []), ...(region.paths ?? [])]);
3763
- return {
3764
- id: normalizeId(region.id, 'ownership region id'),
3765
- title: region.title ?? titleFromId(region.id),
3766
- ...(region.description ? { description: region.description } : {}),
3767
- globs,
3768
- selectors: uniqueStrings(region.selectors ?? []),
3769
- ...(region.owner ? { owner: region.owner } : {}),
3770
- ...(toJsonObject(region.metadata) ? { metadata: toJsonObject(region.metadata) } : {})
3771
- };
3772
- });
3773
- }
3774
- function mergeOwnershipRegions(laneRegions, taskRegions) {
3775
- const byId = new Map();
3776
- for (const region of laneRegions)
3777
- byId.set(region.id, cloneJsonValue(region));
3778
- for (const region of taskRegions)
3779
- byId.set(region.id, cloneJsonValue(region));
3780
- return Array.from(byId.values()).sort((left, right) => left.id.localeCompare(right.id));
3781
- }
3782
- function normalizeResourceRequirements(input = {}) {
3783
- const resources = {};
3784
- for (const [key, value] of Object.entries(input.resources ?? {})) {
3785
- if (Number.isFinite(value) && value > 0)
3786
- resources[key] = value;
3787
- }
3788
- return {
3789
- capabilities: uniqueStrings(input.capabilities ?? []),
3790
- resources,
3791
- ...(input.browser ? { browser: normalizeBrowserResource(input.browser) } : {}),
3792
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3793
- };
3794
- }
3795
- function normalizeBrowserResource(input) {
3796
- return {
3797
- required: input.required ?? true,
3798
- portPool: uniqueStrings((input.portPool ?? []).map((port) => String(port))),
3799
- ...(input.profileDir ? { profileDir: input.profileDir } : {}),
3800
- ...(input.profileDirPrefix ? { profileDirPrefix: input.profileDirPrefix } : {}),
3801
- ...(positiveNumber(input.maxConcurrency) ? { maxConcurrency: Math.floor(input.maxConcurrency) } : {}),
3802
- ...(input.headless !== undefined ? { headless: input.headless } : {}),
3803
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3804
- };
3805
- }
3806
- function mergeResourceRequirements(lane, task, extraCapabilities = []) {
3807
- if (!lane && !task && extraCapabilities.length === 0)
3808
- return undefined;
3809
- const capabilities = uniqueStrings([...(lane?.capabilities ?? []), ...(task?.capabilities ?? []), ...extraCapabilities]);
3810
- const resources = { ...(lane?.resources ?? {}) };
3811
- for (const [key, value] of Object.entries(task?.resources ?? {}))
3812
- resources[key] = Math.max(resources[key] ?? 0, value);
3813
- const browser = task?.browser ?? lane?.browser;
3814
- const metadata = toJsonObject({ ...(lane?.metadata ?? {}), ...(task?.metadata ?? {}) });
3815
- return {
3816
- capabilities,
3817
- resources,
3818
- ...(browser ? { browser } : {}),
3819
- ...(metadata && Object.keys(metadata).length ? { metadata } : {})
3820
- };
3821
- }
3822
- function normalizePolicy(input, defaultCompute) {
3823
- return {
3824
- mode: input?.mode ?? 'hard-file-ownership',
3825
- defaultConcurrency: Math.max(1, Math.floor(input?.defaultConcurrency ?? 1)),
3826
- defaultCompute: input?.defaultCompute ?? defaultCompute,
3827
- ...(input?.defaultLayer ? { defaultLayer: input.defaultLayer } : {}),
3828
- completedStatuses: uniqueStrings(input?.completedStatuses ?? DEFAULT_COMPLETED_STATUSES),
3829
- sharedReadOnly: uniqueStrings(input?.sharedReadOnly ?? []),
3830
- neverEditWithoutParent: uniqueStrings(input?.neverEditWithoutParent ?? []),
3831
- requireCleanWorktree: input?.requireCleanWorktree ?? true,
3832
- ...(toJsonObject(input?.metadata) ? { metadata: toJsonObject(input?.metadata) } : {})
3833
- };
3834
- }
3835
- function normalizeTask(input) {
3836
- const targetRefs = uniqueStrings([...(input.targetRefs ?? []), ...(input.ownedFiles ?? [])]);
3837
- return {
3838
- kind: FRONTIER_SWARM_TASK_KIND,
3839
- version: FRONTIER_SWARM_TASK_VERSION,
3840
- id: normalizeId(input.id, 'task id'),
3841
- title: input.title ?? titleFromId(input.id),
3842
- objective: input.objective ?? input.description ?? input.title ?? input.id,
3843
- ...(input.description ? { description: input.description } : {}),
3844
- workKind: input.kind ?? 'agent-task',
3845
- status: input.status ?? 'open',
3846
- ...(input.lane ? { lane: input.lane } : {}),
3847
- ...(input.layer ? { layer: input.layer } : {}),
3848
- ...(input.compute ? { compute: input.compute } : {}),
3849
- ...(input.parentTaskId ? { parentTaskId: input.parentTaskId } : {}),
3850
- dependsOn: uniqueStrings(input.dependsOn ?? []),
3851
- ...(input.concurrencyKey ? { concurrencyKey: input.concurrencyKey } : {}),
3852
- ...(input.budget ? { budget: normalizeBudget(input.budget) } : {}),
3853
- ...(input.review ? { review: normalizeReviewPolicy(input.review) } : {}),
3854
- priority: Number.isFinite(input.priority) ? Number(input.priority) : 100,
3855
- sourceRefs: uniqueStrings(input.sourceRefs ?? []),
3856
- targetRefs,
3857
- allowedWrites: uniqueStrings([...(input.allowedWrites ?? []), ...targetRefs]),
3858
- ownershipRegions: normalizeOwnershipRegions(input.ownershipRegions ?? []),
3859
- ownedRegions: uniqueStrings(input.ownedRegions ?? []),
3860
- changedRegions: uniqueStrings(input.changedRegions ?? []),
3861
- capabilities: uniqueStrings(input.capabilities ?? []),
3862
- ...(input.resourceRequirements ? { resourceRequirements: normalizeResourceRequirements(input.resourceRequirements) } : {}),
3863
- acceptance: normalizeAcceptance(input),
3864
- verification: normalizeCommands(input.verification ?? []),
3865
- ...(input.evidenceCommand ? { evidenceCommand: input.evidenceCommand } : {}),
3866
- ...(input.shardCommand ? { shardCommand: input.shardCommand } : {}),
3867
- tags: uniqueStrings(input.tags ?? []),
3868
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
3869
- };
3870
- }
3871
- function normalizeTaskList(input) {
3872
- if (Array.isArray(input))
3873
- return input.map((task) => isSwarmTask(task) ? cloneJsonValue(task) : normalizeTask(task));
3874
- return defineSwarmTasks(input);
3875
- }
3876
- function normalizeAcceptance(input) {
3877
- const checks = (input.acceptanceChecks ?? []).map((check) => typeof check === 'string' ? check : check.description ?? check.id ?? '').filter(Boolean);
3878
- return uniqueStrings([...(input.acceptance ?? []), ...checks]);
3879
- }
3880
- function normalizeCommands(input) {
3881
- return input.map((entry) => {
3882
- if (typeof entry === 'string') {
3883
- return { name: entry, command: entry, args: [], required: true };
3884
- }
3885
- return {
3886
- name: entry.name ?? [entry.command, ...(entry.args ?? [])].join(' '),
3887
- command: entry.command,
3888
- args: [...(entry.args ?? [])],
3889
- required: entry.required ?? true,
3890
- ...(entry.cwd ? { cwd: entry.cwd } : {}),
3891
- ...(toJsonObject(entry.metadata) ? { metadata: toJsonObject(entry.metadata) } : {})
3892
- };
3893
- });
3894
- }
3895
- function selectSwarmTasks(manifest, tasks, options) {
3896
- const lanes = new Set(options.lanes ?? []);
3897
- const layers = new Set(options.layers ?? []);
3898
- const statuses = new Set(options.statuses ?? []);
3899
- const selectors = (options.selectors ?? []).map((selector) => selector.toLowerCase());
3900
- const completed = new Set(manifest.policy.completedStatuses);
3901
- const limit = options.limit === undefined ? tasks.length : Math.max(0, Math.floor(options.limit));
3902
- return tasks
3903
- .filter((task) => !task.lane || manifest.lanes.some((lane) => lane.id === task.lane))
3904
- .filter((task) => lanes.size === 0 || (task.lane !== undefined && lanes.has(task.lane)))
3905
- .filter((task) => layers.size === 0 || taskLayer(manifest, task) !== undefined && layers.has(taskLayer(manifest, task)))
3906
- .filter((task) => statuses.size === 0 || statuses.has(task.status))
3907
- .filter((task) => options.includeCompleted || !completed.has(task.status))
3908
- .filter((task) => selectors.length === 0 || selectors.some((selector) => searchableTask(task).includes(selector)))
3909
- .sort((left, right) => left.priority - right.priority || left.id.localeCompare(right.id))
3910
- .slice(0, limit);
3911
- }
3912
- function createSelectionEntry(manifest, task, priority) {
3913
- const lane = task.lane ? manifest.lanes.find((entry) => entry.id === task.lane) : undefined;
3914
- return {
3915
- task,
3916
- ...(lane ? { lane } : {}),
3917
- ownershipWarnings: selectionOwnershipWarnings(task, lane),
3918
- selectionPriority: selectionPriority(task, priority)
3919
- };
3920
- }
3921
- function selectionOwnershipWarnings(task, lane) {
3922
- if (!lane || lane.allowedWrites.length === 0)
3923
- return [];
3924
- return task.targetRefs
3925
- .filter((file) => !lane.allowedWrites.some((glob) => matchesGlob(file, glob)))
3926
- .map((file) => `${file} is outside allowed write globs for ${lane.id}`);
3927
- }
3928
- function selectionPriority(task, input) {
3929
- const statusRanks = input?.statuses ?? {};
3930
- const workKindRanks = input?.workKinds ?? {};
3931
- const statusRank = statusRanks[task.status] ?? input?.defaultStatusRank ?? 100;
3932
- const workKindRank = workKindRanks[task.workKind] ?? input?.defaultWorkKindRank ?? 100;
3933
- const statusWeight = input?.statusWeight ?? 1000;
3934
- const workKindWeight = input?.workKindWeight ?? 1;
3935
- return statusRank * statusWeight + workKindRank * workKindWeight;
3936
- }
3937
- function roundRobinSelectionByLane(entries) {
3938
- const groups = new Map();
3939
- for (const entry of entries)
3940
- groups.set(entry.task.lane ?? 'unassigned', [...(groups.get(entry.task.lane ?? 'unassigned') ?? []), entry]);
3941
- const selected = [];
3942
- while (Array.from(groups.values()).some((group) => group.length > 0)) {
3943
- for (const group of groups.values()) {
3944
- const next = group.shift();
3945
- if (next)
3946
- selected.push(next);
3947
- }
3948
- }
3949
- return selected;
3950
- }
3951
- function summarizeTaskSelection(entries) {
3952
- return entries.reduce((summary, entry) => {
3953
- const lane = entry.task.lane ?? 'unassigned';
3954
- summary.total += 1;
3955
- summary.byLane[lane] = (summary.byLane[lane] ?? 0) + 1;
3956
- summary.byWorkKind[entry.task.workKind] = (summary.byWorkKind[entry.task.workKind] ?? 0) + 1;
3957
- summary.ownershipWarningCount += entry.ownershipWarnings.length;
3958
- return summary;
3959
- }, { total: 0, byLane: {}, byWorkKind: {}, ownershipWarningCount: 0 });
3960
- }
3961
- function createJob(compiled, task, options) {
3962
- const lane = task.lane ? compiled.lanesById.get(task.lane) : undefined;
3963
- const layer = task.layer ?? lane?.layer ?? compiled.manifest.policy.defaultLayer;
3964
- const compute = options.compute
3965
- ? readCompute(compiled, options.compute)
3966
- : resolveTaskCompute(compiled, task);
3967
- const evidencePrefix = lane?.evidencePrefix ? lane.evidencePrefix.replace(/\/?$/, '/') + slug(task.id) + '/' : undefined;
3968
- const allowedWrites = uniqueStrings([
3969
- ...(lane?.allowedWrites ?? []),
3970
- ...task.allowedWrites,
3971
- ...(evidencePrefix ? [evidencePrefix + '**'] : [])
3972
- ]);
3973
- const ownershipWarnings = task.targetRefs
3974
- .filter((file) => allowedWrites.length > 0 && !allowedWrites.some((glob) => matchesGlob(file, glob)))
3975
- .map((file) => `${file} is outside allowed write globs for ${lane?.id ?? 'unassigned'}`);
3976
- const capabilities = uniqueStrings([...(lane?.capabilities ?? []), ...task.capabilities]);
3977
- const resourceRequirements = mergeResourceRequirements(lane?.resourceRequirements, task.resourceRequirements, capabilities);
3978
- const ownershipRegions = mergeOwnershipRegions(lane?.ownershipRegions ?? [], task.ownershipRegions);
3979
- const ownedRegions = uniqueStrings([...task.ownedRegions, ...ownershipRegions.map((region) => region.id)]);
3980
- return {
3981
- id: `${lane?.id ?? 'unassigned'}-${slug(task.id)}`,
3982
- taskId: task.id,
3983
- title: task.title,
3984
- lane: lane?.id ?? 'unassigned',
3985
- ...(layer ? { layer } : {}),
3986
- compute,
3987
- status: 'planned',
3988
- priority: task.priority,
3989
- task,
3990
- allowedWrites,
3991
- sharedReadOnly: uniqueStrings([...(compiled.manifest.policy.sharedReadOnly ?? []), ...(lane?.sharedReadOnly ?? [])]),
3992
- neverEdit: uniqueStrings([...(compiled.manifest.policy.neverEditWithoutParent ?? []), ...(lane?.neverEdit ?? [])]),
3993
- ownershipRegions,
3994
- ownedRegions,
3995
- changedRegions: uniqueStrings(task.changedRegions),
3996
- capabilities,
3997
- ...(resourceRequirements ? { resourceRequirements } : {}),
3998
- ...(lane?.worktreePath ? { worktreePath: lane.worktreePath } : {}),
3999
- ...(evidencePrefix ? { evidencePrefix } : {}),
4000
- concurrencyKey: task.concurrencyKey ?? lane?.concurrencyKey ?? task.lane ?? compute.id,
4001
- ownershipWarnings,
4002
- verification: task.verification.length ? task.verification : (lane?.handoffCommands ?? []),
4003
- acceptance: [...task.acceptance],
4004
- dependsOn: resolveJobDependencies(task),
4005
- ...(task.budget ? { budget: task.budget } : {}),
4006
- review: task.review ?? normalizeReviewPolicy(),
4007
- tags: uniqueStrings([...task.tags, ...(lane?.tags ?? []), ...(layer ? [layer] : []), compute.id]),
4008
- ...(task.metadata ? { metadata: task.metadata } : {})
4009
- };
4010
- }
4011
- function resolveJobDependencies(task) {
4012
- return uniqueStrings([
4013
- ...(task.parentTaskId ? [task.parentTaskId] : []),
4014
- ...task.dependsOn
4015
- ]);
4016
- }
4017
- function resolveTaskCompute(compiled, task) {
4018
- if (task.compute)
4019
- return readCompute(compiled, task.compute);
4020
- const lane = task.lane ? compiled.lanesById.get(task.lane) : undefined;
4021
- if (lane?.compute)
4022
- return readCompute(compiled, lane.compute);
4023
- const layerId = task.layer ?? lane?.layer ?? compiled.manifest.policy.defaultLayer;
4024
- const layered = layerId ? resolveLayerCompute(compiled, layerId) : undefined;
4025
- return layered ?? readCompute(compiled, compiled.manifest.policy.defaultCompute);
4026
- }
4027
- function resolveLayerCompute(compiled, layerId) {
4028
- const layer = compiled.layersById.get(layerId);
4029
- if (!layer)
4030
- return undefined;
4031
- let childId = layer.id;
4032
- let parentId = layer.parentId;
4033
- while (parentId) {
4034
- const parent = compiled.layersById.get(parentId);
4035
- if (!parent)
4036
- break;
4037
- const selected = parent.childCompute[childId] ?? parent.childCompute['*'];
4038
- if (selected)
4039
- return readCompute(compiled, selected);
4040
- childId = parent.id;
4041
- parentId = parent.parentId;
4042
- }
4043
- const own = layer.compute ?? layer.defaultCompute;
4044
- return own ? readCompute(compiled, own) : undefined;
4045
- }
4046
- function readCompute(compiled, id) {
4047
- return compiled.computeById.get(id) ?? compiled.computeById.get(compiled.manifest.policy.defaultCompute) ?? compiled.manifest.compute[0];
4048
- }
4049
- function validateTasksForManifest(compiled, tasks, graph) {
4050
- const issues = [...compiled.validation.issues, ...(graph?.issues ?? [])];
4051
- const taskIds = new Set(tasks.map((task) => task.id));
4052
- for (const task of tasks) {
4053
- if (task.lane && !compiled.lanesById.has(task.lane)) {
4054
- addIssue(issues, 'missing-task-lane', 'error', `tasks.${task.id}.lane`, `Task lane is not declared: ${task.lane}`);
4055
- }
4056
- if (task.layer && !compiled.layersById.has(task.layer)) {
4057
- addIssue(issues, 'missing-task-layer', 'error', `tasks.${task.id}.layer`, `Task layer is not declared: ${task.layer}`);
4058
- }
4059
- if (task.compute && !compiled.computeById.has(task.compute)) {
4060
- addIssue(issues, 'missing-task-compute', 'error', `tasks.${task.id}.compute`, `Task compute is not declared: ${task.compute}`);
4061
- }
4062
- for (const dependency of task.dependsOn) {
4063
- if (!taskIds.has(dependency)) {
4064
- addIssue(issues, 'missing-task-dependency', 'warning', `tasks.${task.id}.dependsOn`, `Task dependency is not declared in the task set: ${dependency}`);
4065
- }
4066
- }
4067
- if (task.parentTaskId && !taskIds.has(task.parentTaskId)) {
4068
- addIssue(issues, 'missing-parent-task', 'warning', `tasks.${task.id}.parentTaskId`, `Task parent is not declared in the task set: ${task.parentTaskId}`);
4069
- }
4070
- }
4071
- return { valid: issues.every((issue) => issue.severity !== 'error'), issues };
4072
- }
4073
- function normalizeEvent(input) {
4074
- const at = input.at ?? Date.now();
4075
- return {
4076
- kind: FRONTIER_SWARM_EVENT_KIND,
4077
- version: FRONTIER_SWARM_EVENT_VERSION,
4078
- id: input.id ?? 'swarm-event:' + stableHash([input.type, input.runId, input.jobId, at, input.data]),
4079
- type: input.type,
4080
- ...(input.runId ? { runId: input.runId } : {}),
4081
- ...(input.jobId ? { jobId: input.jobId } : {}),
4082
- ...(input.taskId ? { taskId: input.taskId } : {}),
4083
- ...(input.lane ? { lane: input.lane } : {}),
4084
- ...(input.layer ? { layer: input.layer } : {}),
4085
- ...(input.compute ? { compute: input.compute } : {}),
4086
- at,
4087
- ...(input.message ? { message: input.message } : {}),
4088
- ...(input.data !== undefined ? { data: toJsonValue(input.data) } : {}),
4089
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
4090
- };
4091
- }
4092
- function normalizeQueueJob(input) {
4093
- return {
4094
- jobId: input.jobId,
4095
- ...(input.taskId ? { taskId: input.taskId } : {}),
4096
- ...(input.runId ? { runId: input.runId } : {}),
4097
- status: input.status ?? 'ready',
4098
- ...(input.lane ? { lane: input.lane } : {}),
4099
- ...(input.compute ? { compute: input.compute } : {}),
4100
- ...(input.concurrencyKey ? { concurrencyKey: input.concurrencyKey } : {}),
4101
- priority: input.priority ?? 100,
4102
- attempts: Math.max(0, Math.floor(input.attempts ?? 0)),
4103
- maxAttempts: Math.max(1, Math.floor(input.maxAttempts ?? 1)),
4104
- ...(input.availableAt !== undefined ? { availableAt: input.availableAt } : {}),
4105
- ...(input.lease ? { lease: cloneJsonValue(input.lease) } : {}),
4106
- ...(input.lastError ? { lastError: input.lastError } : {}),
4107
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
4108
- };
4109
- }
4110
- function queueJobsFromPlan(plan, run, leases) {
4111
- const resultsByJob = new Map((run?.results ?? []).map((result) => [result.jobId, result]));
4112
- const activeLeases = new Map(leases.filter((lease) => lease.status === 'active').map((lease) => [lease.jobId, lease]));
4113
- return plan.jobs.map((job) => {
4114
- const result = resultsByJob.get(job.id);
4115
- const lease = activeLeases.get(job.id);
4116
- const failed = result?.status === 'failed' || result?.exitCode !== undefined && result.exitCode !== 0;
4117
- const completed = result?.status === 'completed' || result?.status === 'verified';
4118
- const status = completed
4119
- ? 'completed'
4120
- : failed
4121
- ? 'failed'
4122
- : lease
4123
- ? 'leased'
4124
- : job.status === 'running'
4125
- ? 'running'
4126
- : 'ready';
4127
- return normalizeQueueJob({
4128
- jobId: job.id,
4129
- taskId: job.taskId,
4130
- runId: run?.id ?? plan.runId,
4131
- status,
4132
- lane: job.lane,
4133
- compute: job.compute.id,
4134
- concurrencyKey: job.concurrencyKey,
4135
- priority: job.priority,
4136
- attempts: result?.metadata && typeof result.metadata.attempts === 'number' ? result.metadata.attempts : undefined,
4137
- maxAttempts: job.budget?.maxRetries !== undefined ? job.budget.maxRetries + 1 : 1,
4138
- lease,
4139
- lastError: result?.error
4140
- });
4141
- });
4142
- }
4143
- function normalizeSemanticImportSummary(input) {
4144
- const object = toJsonObject(input);
4145
- if (!object)
4146
- return undefined;
4147
- const metadata = toJsonObject(object.metadata);
4148
- return {
4149
- total: nonNegativeCount(object.total),
4150
- selected: nonNegativeCount(object.selected),
4151
- eligible: nonNegativeCount(object.eligible),
4152
- omitted: nonNegativeCount(object.omitted),
4153
- imported: nonNegativeCount(object.imported),
4154
- skipped: nonNegativeCount(object.skipped),
4155
- errors: nonNegativeCount(object.errors),
4156
- sourceMapCount: nonNegativeCount(object.sourceMapCount),
4157
- sourceMapMappingCount: nonNegativeCount(object.sourceMapMappingCount),
4158
- lossCount: nonNegativeCount(object.lossCount),
4159
- lossesBySeverity: normalizeCounterRecord(object.lossesBySeverity),
4160
- semanticIndex: normalizeSemanticIndexSummary(object.semanticIndex),
4161
- semanticSidecars: normalizeSemanticSidecarSummary(object.semanticSidecars),
4162
- proofSpec: normalizeProofSpecSummary(object.proofSpec),
4163
- paradigmSemantics: normalizeParadigmSemanticsSummary(object.paradigmSemantics),
4164
- sourceProjections: normalizeSourceProjectionSummary(object.sourceProjections),
4165
- nativeCompiles: normalizeNativeCompileSummary(object.nativeCompiles),
4166
- readiness: normalizeCounterRecord(object.readiness),
4167
- ...(metadata ? { metadata } : {})
4168
- };
4169
- }
4170
- const paradigmSemanticsSummaryGroups = [
4171
- 'bindingScopes',
4172
- 'bindings',
4173
- 'patterns',
4174
- 'typeConstraints',
4175
- 'evaluationModels',
4176
- 'memoryLocations',
4177
- 'effectRegions',
4178
- 'controlRegions',
4179
- 'logicPrograms',
4180
- 'actorSystems',
4181
- 'stackEffects',
4182
- 'arrayShapes',
4183
- 'numericKernels',
4184
- 'dataflowNetworks',
4185
- 'clockModels',
4186
- 'objectModels',
4187
- 'macroExpansions',
4188
- 'reflectionBoundaries',
4189
- 'loweringRecords'
4190
- ];
4191
- function normalizeParadigmSemanticsSummary(input) {
4192
- const object = toJsonObject(input);
4193
- const counts = Object.fromEntries(paradigmSemanticsSummaryGroups.map((group) => [
4194
- group,
4195
- nonNegativeCount(object?.[group])
4196
- ]));
4197
- const total = nonNegativeCount(object?.total) || Object.values(counts).reduce((sum, count) => sum + count, 0);
4198
- const byGroup = normalizeCounterRecord(object?.byGroup);
4199
- const byKind = normalizeCounterRecord(object?.byKind);
4200
- const hasRuntimeSemantics = object?.hasRuntimeSemantics === true
4201
- || counts.evaluationModels > 0
4202
- || counts.memoryLocations > 0
4203
- || counts.effectRegions > 0
4204
- || counts.controlRegions > 0
4205
- || counts.actorSystems > 0
4206
- || counts.clockModels > 0;
4207
- const hasLogicSemantics = object?.hasLogicSemantics === true || counts.logicPrograms > 0;
4208
- const hasStackSemantics = object?.hasStackSemantics === true || counts.stackEffects > 0;
4209
- const hasArraySemantics = object?.hasArraySemantics === true || counts.arrayShapes > 0 || counts.numericKernels > 0;
4210
- const hasMacroOrReflection = object?.hasMacroOrReflection === true || counts.macroExpansions > 0 || counts.reflectionBoundaries > 0;
4211
- const hasLowering = object?.hasLowering === true || counts.loweringRecords > 0;
4212
- return {
4213
- total,
4214
- ids: uniqueStrings(stringArray(object?.ids)),
4215
- groups: uniqueStrings(stringArray(object?.groups)),
4216
- kinds: uniqueStrings(stringArray(object?.kinds)),
4217
- evidence: nonNegativeCount(object?.evidence),
4218
- ...counts,
4219
- byGroup,
4220
- byKind,
4221
- hasRuntimeSemantics,
4222
- hasLogicSemantics,
4223
- hasStackSemantics,
4224
- hasArraySemantics,
4225
- hasMacroOrReflection,
4226
- hasLowering,
4227
- empty: object?.empty === true || total === 0
4228
- };
4229
- }
4230
- function normalizeProofSpecSummary(input) {
4231
- const object = toJsonObject(input);
4232
- const total = nonNegativeCount(object?.total);
4233
- return {
4234
- total,
4235
- ids: uniqueStrings(stringArray(object?.ids)),
4236
- contracts: nonNegativeCount(object?.contracts),
4237
- refinements: nonNegativeCount(object?.refinements),
4238
- invariants: nonNegativeCount(object?.invariants),
4239
- termination: nonNegativeCount(object?.termination),
4240
- temporal: nonNegativeCount(object?.temporal),
4241
- obligations: nonNegativeCount(object?.obligations),
4242
- artifacts: nonNegativeCount(object?.artifacts),
4243
- assumptions: nonNegativeCount(object?.assumptions),
4244
- evidence: nonNegativeCount(object?.evidence),
4245
- discharged: nonNegativeCount(object?.discharged),
4246
- failed: nonNegativeCount(object?.failed),
4247
- open: nonNegativeCount(object?.open),
4248
- unknown: nonNegativeCount(object?.unknown),
4249
- stale: nonNegativeCount(object?.stale),
4250
- assumed: nonNegativeCount(object?.assumed),
4251
- contractKinds: uniqueStrings(stringArray(object?.contractKinds)),
4252
- artifactKinds: uniqueStrings(stringArray(object?.artifactKinds)),
4253
- byStatus: normalizeCounterRecord(object?.byStatus),
4254
- byContractKind: normalizeCounterRecord(object?.byContractKind),
4255
- byArtifactKind: normalizeCounterRecord(object?.byArtifactKind),
4256
- empty: object?.empty === true || total === 0
4257
- };
4258
- }
4259
- function normalizeSemanticIndexSummary(input) {
4260
- const object = toJsonObject(input);
4261
- return {
4262
- documents: nonNegativeCount(object?.documents),
4263
- symbols: nonNegativeCount(object?.symbols),
4264
- occurrences: nonNegativeCount(object?.occurrences),
4265
- relations: nonNegativeCount(object?.relations),
4266
- facts: nonNegativeCount(object?.facts)
4267
- };
4268
- }
4269
- function normalizeSemanticSidecarSummary(input) {
4270
- const object = toJsonObject(input);
4271
- return {
4272
- total: nonNegativeCount(object?.total),
4273
- symbols: nonNegativeCount(object?.symbols),
4274
- ownershipRegions: nonNegativeCount(object?.ownershipRegions),
4275
- patchHints: nonNegativeCount(object?.patchHints),
4276
- empty: nonNegativeCount(object?.empty)
4277
- };
4278
- }
4279
- function normalizeSourceProjectionSummary(input) {
4280
- const object = toJsonObject(input);
4281
- return {
4282
- total: nonNegativeCount(object?.total),
4283
- preserved: nonNegativeCount(object?.preserved),
4284
- stubs: nonNegativeCount(object?.stubs),
4285
- ready: nonNegativeCount(object?.ready),
4286
- needsReview: nonNegativeCount(object?.needsReview),
4287
- blocked: nonNegativeCount(object?.blocked)
4288
- };
4289
- }
4290
- function normalizeNativeCompileSummary(input) {
4291
- const object = toJsonObject(input);
4292
- return {
4293
- total: nonNegativeCount(object?.total),
4294
- emitted: nonNegativeCount(object?.emitted),
4295
- preserved: nonNegativeCount(object?.preserved),
4296
- targetStubs: nonNegativeCount(object?.targetStubs),
4297
- ready: nonNegativeCount(object?.ready),
4298
- needsReview: nonNegativeCount(object?.needsReview),
4299
- blocked: nonNegativeCount(object?.blocked)
4300
- };
4301
- }
4302
- function normalizeCounterRecord(input) {
4303
- const object = toJsonObject(input);
4304
- if (!object)
4305
- return {};
4306
- const entries = Object.entries(object)
4307
- .map(([key, value]) => [key, nonNegativeCount(value)])
4308
- .filter(([, value]) => value > 0)
4309
- .sort(([left], [right]) => left.localeCompare(right));
4310
- return Object.fromEntries(entries);
4311
- }
4312
- function nonNegativeCount(value) {
4313
- const number = typeof value === 'number' ? value : Number(value);
4314
- return Number.isFinite(number) && number > 0 ? Math.floor(number) : 0;
4315
- }
4316
- function normalizeResult(input) {
4317
- const startedAt = input.startedAt;
4318
- const finishedAt = input.finishedAt;
4319
- const status = input.status ?? (input.exitCode === 0 || input.exitCode === undefined ? 'completed' : 'failed');
4320
- const inputMetadata = toJsonObject(input.metadata);
4321
- const semanticImport = normalizeSemanticImportSummary(input.semanticImport ?? inputMetadata?.semanticImport);
4322
- return {
4323
- jobId: input.jobId,
4324
- status,
4325
- mergeReadiness: classifySwarmMergeReadiness({ ...input, status }),
4326
- ...(startedAt !== undefined ? { startedAt } : {}),
4327
- ...(finishedAt !== undefined ? { finishedAt } : {}),
4328
- ...(startedAt !== undefined && finishedAt !== undefined ? { durationMs: Math.max(0, finishedAt - startedAt) } : {}),
4329
- ...(input.exitCode !== undefined ? { exitCode: input.exitCode } : {}),
4330
- ...(input.signal ? { signal: input.signal } : {}),
4331
- changedPaths: uniqueStrings(input.changedPaths ?? []),
4332
- changedRegions: uniqueStrings(input.changedRegions ?? []),
4333
- ownershipViolations: uniqueStrings(input.ownershipViolations ?? []),
4334
- evidencePaths: uniqueStrings(input.evidencePaths ?? []),
4335
- ...(input.patchPath ? { patchPath: input.patchPath } : {}),
4336
- queueItemIds: uniqueStrings(input.queueItemIds ?? []),
4337
- riskLevel: input.riskLevel ?? 'unknown',
4338
- mergeDisposition: input.mergeDisposition ?? classifySwarmMergeDisposition({ ...input, status }),
4339
- verification: (input.verification ?? []).map(normalizeVerificationResult),
4340
- ...(semanticImport ? { semanticImport } : {}),
4341
- ...(input.lastMessage ? { lastMessage: input.lastMessage } : {}),
4342
- ...(input.error !== undefined ? { error: stringifyError(input.error) } : {}),
4343
- ...(inputMetadata ? { metadata: inputMetadata } : {})
4344
- };
4345
- }
4346
- function isSwarmJobResult(value) {
4347
- return Array.isArray(value.changedPaths)
4348
- && Array.isArray(value.changedRegions)
4349
- && Array.isArray(value.verification)
4350
- && Array.isArray(value.queueItemIds)
4351
- && typeof value.riskLevel === 'string'
4352
- && typeof value.mergeDisposition === 'string';
4353
- }
4354
- function mergeBundleReasons(result, disposition, staleAgainstHead) {
4355
- const reasons = [];
4356
- if (staleAgainstHead)
4357
- reasons.push('stale-against-head');
4358
- if (result.status === 'blocked')
4359
- reasons.push('blocked');
4360
- if (result.status === 'failed')
4361
- reasons.push('failed');
4362
- if (result.ownershipViolations.length)
4363
- reasons.push('ownership-violations');
4364
- if (result.verification.some((entry) => entry.required !== false && entry.status !== 0))
4365
- reasons.push('failed-verification');
4366
- if (disposition === 'needs-port')
4367
- reasons.push('needs-human-port');
4368
- if (disposition === 'rejected')
4369
- reasons.push('rejected');
4370
- return uniqueStrings(reasons);
4371
- }
4372
- function inferMergeRisk(result, disposition) {
4373
- if (disposition === 'discovery-only')
4374
- return 'low';
4375
- if (disposition === 'rejected' || disposition === 'blocked' || disposition === 'stale-against-head')
4376
- return 'high';
4377
- if (result.changedPaths.length <= 2 && result.ownershipViolations.length === 0)
4378
- return 'low';
4379
- if (result.changedPaths.length <= 8)
4380
- return 'medium';
4381
- return 'high';
4382
- }
4383
- function normalizeVerificationResult(input) {
4384
- return {
4385
- name: input.name ?? ((input.command ?? []).join(' ') || 'verification'),
4386
- command: [...(input.command ?? [])],
4387
- ...(input.status !== undefined ? { status: input.status } : {}),
4388
- ...(input.durationMs !== undefined ? { durationMs: input.durationMs } : {}),
4389
- stdoutTail: [...(input.stdoutTail ?? [])],
4390
- stderrTail: [...(input.stderrTail ?? [])],
4391
- required: input.required ?? true,
4392
- ...(toJsonObject(input.metadata) ? { metadata: toJsonObject(input.metadata) } : {})
4393
- };
4394
- }
4395
- function summarizeJobs(jobs) {
4396
- return {
4397
- computeCount: new Set(jobs.map((job) => job.compute.id)).size,
4398
- layerCount: new Set(jobs.map((job) => job.layer).filter((layer) => !!layer)).size,
4399
- laneCount: new Set(jobs.map((job) => job.lane)).size,
4400
- taskCount: jobs.length,
4401
- jobCount: jobs.length,
4402
- ownershipViolationCount: jobs.reduce((total, job) => total + job.ownershipWarnings.length, 0)
4403
- };
4404
- }
4405
- function summarizeRun(jobs, results) {
4406
- const ownershipViolationCount = results.reduce((total, result) => total + result.ownershipViolations.length, 0);
4407
- return {
4408
- ...summarizeJobs(jobs),
4409
- completedCount: results.filter((result) => result.status === 'completed' || result.status === 'verified').length,
4410
- blockedCount: results.filter((result) => result.status === 'blocked').length,
4411
- failedCount: results.filter((result) => result.status === 'failed' || result.exitCode !== undefined && result.exitCode !== 0).length,
4412
- ownershipViolationCount
4413
- };
4414
- }
4415
- function taskLayer(manifest, task) {
4416
- if (task.layer)
4417
- return task.layer;
4418
- const lane = task.lane ? manifest.lanes.find((entry) => entry.id === task.lane) : undefined;
4419
- return lane?.layer ?? manifest.policy.defaultLayer;
4420
- }
4421
- function searchableTask(task) {
4422
- return [
4423
- task.id,
4424
- task.title,
4425
- task.objective,
4426
- task.description,
4427
- task.workKind,
4428
- task.status,
4429
- task.lane,
4430
- task.layer,
4431
- task.compute,
4432
- ...task.sourceRefs,
4433
- ...task.targetRefs,
4434
- ...task.tags
4435
- ].filter(Boolean).join(' ').toLowerCase();
4436
- }
4437
- function addIssue(issues, code, severity, path, message) {
4438
- issues.push({ code, severity, path, message });
4439
- }
4440
- function hasLayerCycle(layerId, layers) {
4441
- const byId = new Map(layers.map((layer) => [layer.id, layer]));
4442
- const seen = new Set();
4443
- let cursor = layerId;
4444
- while (cursor) {
4445
- if (seen.has(cursor))
4446
- return true;
4447
- seen.add(cursor);
4448
- cursor = byId.get(cursor)?.parentId;
4449
- }
4450
- return false;
4451
- }
4452
- function isSwarmManifest(value) {
4453
- return !!value && typeof value === 'object' && value.kind === FRONTIER_SWARM_MANIFEST_KIND;
4454
- }
4455
- function isSwarmTask(value) {
4456
- return !!value && typeof value === 'object' && value.kind === FRONTIER_SWARM_TASK_KIND;
4457
- }
4458
- function isSwarmEvent(value) {
4459
- return !!value && typeof value === 'object' && value.kind === FRONTIER_SWARM_EVENT_KIND;
4460
- }
4461
- function readLaneId(value) {
4462
- return typeof value === 'string' ? value : value.id;
4463
- }
4464
- function joinPathParts(...parts) {
4465
- const first = parts[0] ? String(parts[0]) : '';
4466
- const prefix = first.startsWith('/') ? '/' : '';
4467
- return prefix + parts
4468
- .map((part, index) => String(part).replace(index === 0 ? /\/+$/g : /^\/+|\/+$/g, ''))
4469
- .filter(Boolean)
4470
- .join('/');
4471
- }
4472
- function normalizeId(value, label) {
4473
- const id = String(value || '').trim();
4474
- if (!id)
4475
- throw new Error(`Missing ${label}`);
4476
- return id;
4477
- }
4478
- function titleFromId(id) {
4479
- const parts = String(id).split(/[.:/_-]+/).filter(Boolean);
4480
- return parts.length ? parts.map((part) => part[0]?.toUpperCase() + part.slice(1)).join(' ') : String(id);
4481
- }
4482
- function slug(value) {
4483
- return String(value).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'item';
4484
- }
4485
- function uniqueStrings(values) {
4486
- return Array.from(new Set(values.map((value) => String(value ?? '').trim()).filter(Boolean)));
4487
- }
4488
- function stringArray(value) {
4489
- return Array.isArray(value) ? value.map((entry) => String(entry ?? '').trim()).filter(Boolean) : [];
4490
- }
4491
- function positiveNumber(value) {
4492
- return typeof value === 'number' && Number.isFinite(value) && value > 0;
4493
- }
4494
- function toJsonObject(value) {
4495
- if (!value || typeof value !== 'object' || Array.isArray(value))
4496
- return undefined;
4497
- return cloneJsonValue(value);
4498
- }
4499
- function toJsonValue(value) {
4500
- if (value === undefined)
4501
- return null;
4502
- return cloneJsonValue(value);
4503
- }
4504
- function cloneJsonValue(value) {
4505
- return JSON.parse(JSON.stringify(value));
4506
- }
4507
- function stringifyError(error) {
4508
- return error instanceof Error ? error.message : String(error);
4509
- }
4510
- function stableHash(value) {
4511
- const text = stableStringify(value);
4512
- let hash = 2166136261;
4513
- for (let index = 0; index < text.length; index += 1) {
4514
- hash ^= text.charCodeAt(index);
4515
- hash = Math.imul(hash, 16777619);
4516
- }
4517
- return 'fnv1a32:' + (hash >>> 0).toString(16).padStart(8, '0');
4518
- }
4519
- function stableStringify(value) {
4520
- if (value === null || typeof value !== 'object')
4521
- return JSON.stringify(value);
4522
- if (Array.isArray(value))
4523
- return '[' + value.map(stableStringify).join(',') + ']';
4524
- const object = value;
4525
- return '{' + Object.keys(object).sort().map((key) => JSON.stringify(key) + ':' + stableStringify(object[key])).join(',') + '}';
4526
- }
1
+ export * from './constants.js';
2
+ export { compileSwarm, createSwarmManifest, defineSwarmManifest, defineSwarmTasks, validateSwarmManifest } from './manifest.js';
3
+ export * from './plan.js';
4
+ export * from './scheduler.js';
5
+ export * from './leases.js';
6
+ export { completeSwarmJob, createSwarmEventStream, createSwarmMailbox, createSwarmRun, recordSwarmEvent, routeSwarmEventToMailboxes } from './run-records.js';
7
+ export { matchesGlob } from './internal.js';
8
+ export { classifySwarmMergeDisposition, classifySwarmMergeReadiness } from './merge-bundles.js';
9
+ export { createSwarmMergeAdmission } from './merge-admission.js';
10
+ export { createSwarmMergeIndex } from './merge-index.js';
11
+ export { createSwarmReviewerLanePlan } from './merge-review.js';
12
+ export { deriveSwarmQueueStatus } from './queue-overlays.js';
13
+ export { createSwarmBlackboard, querySwarmBlackboard } from './blackboard.js';
14
+ export { createSwarmFixtureCatalog } from './fixtures.js';
15
+ export { createSwarmProgressModel } from './progress.js';
16
+ export { createSwarmAutoReviewReport } from './auto-review.js';
17
+ export { createSwarmUsageGovernor, checkSwarmUsageGovernor } from './usage-governor.js';
18
+ export { createSwarmAdaptiveLoadPlan, createSwarmScheduleInputFromAdaptiveLoadPlan } from './adaptive-load.js';
19
+ export { createSwarmCoordinatorDashboard, querySwarmCoordinatorDashboard } from './coordinator-dashboard.js';
20
+ export * from './ownership-runtime.js';
21
+ export * from './run-store-shards-runtime.js';
22
+ export * from './merge-wrappers.js';
23
+ export * from './context-pack-runtime.js';
24
+ export * from './oracle-runtime.js';
25
+ export * from './trace-runtime.js';
26
+ export * from './debug-runtime.js';
27
+ export * from './instrumentation-runtime.js';
28
+ export * from './evidence-index.js';
29
+ export * from './reference-oracle-runtime.js';
30
+ export * from './artifact-routing-runtime.js';
31
+ export * from './rebase-report.js';
32
+ export * from './patch-stack-runtime.js';
33
+ export * from './proof-runtime.js';
34
+ export * from './merge-plans-runtime.js';
4527
35
  //# sourceMappingURL=index.js.map