@shapeshift-labs/frontier-swarm-codex 0.5.119 → 0.5.121

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 (197) hide show
  1. package/README.md +8 -0
  2. package/dist/apply.d.ts.map +1 -1
  3. package/dist/apply.js +152 -17
  4. package/dist/apply.js.map +1 -1
  5. package/dist/artifact-store.d.ts.map +1 -1
  6. package/dist/artifact-store.js +20 -6
  7. package/dist/artifact-store.js.map +1 -1
  8. package/dist/cli-args.d.ts +1 -1
  9. package/dist/cli-args.d.ts.map +1 -1
  10. package/dist/cli-args.js +15 -2
  11. package/dist/cli-args.js.map +1 -1
  12. package/dist/cli-help.d.ts.map +1 -1
  13. package/dist/cli-help.js +6 -0
  14. package/dist/cli-help.js.map +1 -1
  15. package/dist/cli-run-events-args.d.ts +5 -0
  16. package/dist/cli-run-events-args.d.ts.map +1 -0
  17. package/dist/cli-run-events-args.js +19 -0
  18. package/dist/cli-run-events-args.js.map +1 -0
  19. package/dist/cli.js +20 -1
  20. package/dist/cli.js.map +1 -1
  21. package/dist/codex-events.d.ts +3 -0
  22. package/dist/codex-events.d.ts.map +1 -1
  23. package/dist/codex-events.js +38 -0
  24. package/dist/codex-events.js.map +1 -1
  25. package/dist/codex-executor.d.ts.map +1 -1
  26. package/dist/codex-executor.js +67 -4
  27. package/dist/codex-executor.js.map +1 -1
  28. package/dist/codex-run-metadata.d.ts +1 -0
  29. package/dist/codex-run-metadata.d.ts.map +1 -1
  30. package/dist/codex-run-metadata.js +1 -0
  31. package/dist/codex-run-metadata.js.map +1 -1
  32. package/dist/codex-run-swarm.d.ts.map +1 -1
  33. package/dist/codex-run-swarm.js +69 -5
  34. package/dist/codex-run-swarm.js.map +1 -1
  35. package/dist/codex-run-timeline.d.ts +10 -0
  36. package/dist/codex-run-timeline.d.ts.map +1 -0
  37. package/dist/codex-run-timeline.js +31 -0
  38. package/dist/codex-run-timeline.js.map +1 -0
  39. package/dist/codex-run.d.ts.map +1 -1
  40. package/dist/codex-run.js +74 -6
  41. package/dist/codex-run.js.map +1 -1
  42. package/dist/collect-bundle-reasons.d.ts.map +1 -1
  43. package/dist/collect-bundle-reasons.js +7 -0
  44. package/dist/collect-bundle-reasons.js.map +1 -1
  45. package/dist/collect-bundles.d.ts +2 -0
  46. package/dist/collect-bundles.d.ts.map +1 -1
  47. package/dist/collect-bundles.js +52 -3
  48. package/dist/collect-bundles.js.map +1 -1
  49. package/dist/collect-dashboard-quality.d.ts.map +1 -1
  50. package/dist/collect-dashboard-quality.js +12 -3
  51. package/dist/collect-dashboard-quality.js.map +1 -1
  52. package/dist/collect-dashboard.d.ts.map +1 -1
  53. package/dist/collect-dashboard.js +54 -1
  54. package/dist/collect-dashboard.js.map +1 -1
  55. package/dist/collect-evidence.d.ts.map +1 -1
  56. package/dist/collect-evidence.js +49 -7
  57. package/dist/collect-evidence.js.map +1 -1
  58. package/dist/collect-finalize.d.ts.map +1 -1
  59. package/dist/collect-finalize.js +5 -1
  60. package/dist/collect-finalize.js.map +1 -1
  61. package/dist/collect-landed.d.ts.map +1 -1
  62. package/dist/collect-landed.js +4 -1
  63. package/dist/collect-landed.js.map +1 -1
  64. package/dist/collect-pids.d.ts.map +1 -1
  65. package/dist/collect-pids.js +9 -2
  66. package/dist/collect-pids.js.map +1 -1
  67. package/dist/collect-research-outcomes.d.ts +6 -0
  68. package/dist/collect-research-outcomes.d.ts.map +1 -0
  69. package/dist/collect-research-outcomes.js +79 -0
  70. package/dist/collect-research-outcomes.js.map +1 -0
  71. package/dist/collect-run-graph.d.ts +7 -0
  72. package/dist/collect-run-graph.d.ts.map +1 -0
  73. package/dist/collect-run-graph.js +1246 -0
  74. package/dist/collect-run-graph.js.map +1 -0
  75. package/dist/collect-semantic-admission.d.ts +18 -0
  76. package/dist/collect-semantic-admission.d.ts.map +1 -0
  77. package/dist/collect-semantic-admission.js +500 -0
  78. package/dist/collect-semantic-admission.js.map +1 -0
  79. package/dist/collect-setup.d.ts.map +1 -1
  80. package/dist/collect-setup.js +2 -0
  81. package/dist/collect-setup.js.map +1 -1
  82. package/dist/collect-terminal-state.d.ts.map +1 -1
  83. package/dist/collect-terminal-state.js +38 -1
  84. package/dist/collect-terminal-state.js.map +1 -1
  85. package/dist/collect-workspace-only.d.ts +1 -0
  86. package/dist/collect-workspace-only.d.ts.map +1 -1
  87. package/dist/collect-workspace-only.js +123 -21
  88. package/dist/collect-workspace-only.js.map +1 -1
  89. package/dist/collect.d.ts.map +1 -1
  90. package/dist/collect.js +57 -10
  91. package/dist/collect.js.map +1 -1
  92. package/dist/common.d.ts.map +1 -1
  93. package/dist/common.js +1 -0
  94. package/dist/common.js.map +1 -1
  95. package/dist/dashboard-ui-health.d.ts.map +1 -1
  96. package/dist/dashboard-ui-health.js +2 -0
  97. package/dist/dashboard-ui-health.js.map +1 -1
  98. package/dist/dashboard-ui-jobs.js +3 -0
  99. package/dist/dashboard-ui-jobs.js.map +1 -1
  100. package/dist/dashboard-ui-semantic.d.ts +6 -2
  101. package/dist/dashboard-ui-semantic.d.ts.map +1 -1
  102. package/dist/dashboard-ui-semantic.js +245 -53
  103. package/dist/dashboard-ui-semantic.js.map +1 -1
  104. package/dist/dashboard-ui.js +3 -2
  105. package/dist/dashboard-ui.js.map +1 -1
  106. package/dist/dashboard.d.ts.map +1 -1
  107. package/dist/dashboard.js +48 -0
  108. package/dist/dashboard.js.map +1 -1
  109. package/dist/index.d.ts +4 -0
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +2 -0
  112. package/dist/index.js.map +1 -1
  113. package/dist/query-matchers.d.ts.map +1 -1
  114. package/dist/query-matchers.js +4 -1
  115. package/dist/query-matchers.js.map +1 -1
  116. package/dist/query-semantic-edit.d.ts +41 -0
  117. package/dist/query-semantic-edit.d.ts.map +1 -1
  118. package/dist/query-semantic-edit.js +468 -0
  119. package/dist/query-semantic-edit.js.map +1 -1
  120. package/dist/query-summaries.d.ts +2 -0
  121. package/dist/query-summaries.d.ts.map +1 -1
  122. package/dist/query-summaries.js +4 -2
  123. package/dist/query-summaries.js.map +1 -1
  124. package/dist/query-types.d.ts +3 -0
  125. package/dist/query-types.d.ts.map +1 -1
  126. package/dist/query.d.ts +5 -0
  127. package/dist/query.d.ts.map +1 -1
  128. package/dist/query.js +7 -1
  129. package/dist/query.js.map +1 -1
  130. package/dist/run-events.d.ts +54 -0
  131. package/dist/run-events.d.ts.map +1 -0
  132. package/dist/run-events.js +196 -0
  133. package/dist/run-events.js.map +1 -0
  134. package/dist/run-graph-live.d.ts +46 -0
  135. package/dist/run-graph-live.d.ts.map +1 -0
  136. package/dist/run-graph-live.js +799 -0
  137. package/dist/run-graph-live.js.map +1 -0
  138. package/dist/run-graph-utils.d.ts +8 -0
  139. package/dist/run-graph-utils.d.ts.map +1 -0
  140. package/dist/run-graph-utils.js +44 -0
  141. package/dist/run-graph-utils.js.map +1 -0
  142. package/dist/score.js +1 -1
  143. package/dist/score.js.map +1 -1
  144. package/dist/semantic-import-quality-helpers.d.ts +55 -0
  145. package/dist/semantic-import-quality-helpers.d.ts.map +1 -0
  146. package/dist/semantic-import-quality-helpers.js +146 -0
  147. package/dist/semantic-import-quality-helpers.js.map +1 -0
  148. package/dist/semantic-import-quality.d.ts.map +1 -1
  149. package/dist/semantic-import-quality.js +519 -115
  150. package/dist/semantic-import-quality.js.map +1 -1
  151. package/dist/semantic-import-select.d.ts +2 -0
  152. package/dist/semantic-import-select.d.ts.map +1 -1
  153. package/dist/semantic-import-select.js +2 -0
  154. package/dist/semantic-import-select.js.map +1 -1
  155. package/dist/semantic-import-sidecar.d.ts +2 -0
  156. package/dist/semantic-import-sidecar.d.ts.map +1 -1
  157. package/dist/semantic-import-sidecar.js +196 -0
  158. package/dist/semantic-import-sidecar.js.map +1 -1
  159. package/dist/semantic-import.d.ts +6 -2
  160. package/dist/semantic-import.d.ts.map +1 -1
  161. package/dist/semantic-import.js +213 -8
  162. package/dist/semantic-import.js.map +1 -1
  163. package/dist/semantic-merge-admission.d.ts +59 -0
  164. package/dist/semantic-merge-admission.d.ts.map +1 -0
  165. package/dist/semantic-merge-admission.js +399 -0
  166. package/dist/semantic-merge-admission.js.map +1 -0
  167. package/dist/types-apply-lease.d.ts +26 -0
  168. package/dist/types-apply-lease.d.ts.map +1 -0
  169. package/dist/types-apply-lease.js +2 -0
  170. package/dist/types-apply-lease.js.map +1 -0
  171. package/dist/types-collection-quality.d.ts +6 -0
  172. package/dist/types-collection-quality.d.ts.map +1 -1
  173. package/dist/types-collection.d.ts +6 -1
  174. package/dist/types-collection.d.ts.map +1 -1
  175. package/dist/types-dashboard-metrics.d.ts +68 -1
  176. package/dist/types-dashboard-metrics.d.ts.map +1 -1
  177. package/dist/types-dashboard.d.ts +14 -0
  178. package/dist/types-dashboard.d.ts.map +1 -1
  179. package/dist/types-evidence.d.ts +15 -0
  180. package/dist/types-evidence.d.ts.map +1 -1
  181. package/dist/types-run-graph.d.ts +74 -0
  182. package/dist/types-run-graph.d.ts.map +1 -0
  183. package/dist/types-run-graph.js +2 -0
  184. package/dist/types-run-graph.js.map +1 -0
  185. package/dist/types-run.d.ts +22 -0
  186. package/dist/types-run.d.ts.map +1 -1
  187. package/dist/types-semantic-quality.d.ts +118 -0
  188. package/dist/types-semantic-quality.d.ts.map +1 -0
  189. package/dist/types-semantic-quality.js +2 -0
  190. package/dist/types-semantic-quality.js.map +1 -0
  191. package/dist/types-semantic.d.ts +98 -47
  192. package/dist/types-semantic.d.ts.map +1 -1
  193. package/dist/types-workspace.d.ts +3 -0
  194. package/dist/types-workspace.d.ts.map +1 -1
  195. package/dist/types.d.ts +2 -0
  196. package/dist/types.d.ts.map +1 -1
  197. package/package.json +3 -2
@@ -0,0 +1,1246 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { classifyCodexSemanticCollectAdmission } from './collect-semantic-admission.js';
4
+ import { countCodexRunGraphValues, createCodexRunGraphIndexes, hashCodexRunGraphString, mergeCodexRunGraphNode, stableCodexRunGraphPart, stringFromUnknown } from './run-graph-utils.js';
5
+ export function createCodexCollectRunGraph(input) {
6
+ const result = input.result;
7
+ const generatedAt = input.generatedAt ?? result.generatedAt ?? Date.now();
8
+ const nodes = new Map();
9
+ const edges = new Map();
10
+ const runNodeId = `run:${path.basename(result.runDir) || 'run'}`;
11
+ const addNode = (node) => {
12
+ const existing = nodes.get(node.id);
13
+ nodes.set(node.id, existing ? mergeCodexRunGraphNode(existing, node) : node);
14
+ return node.id;
15
+ };
16
+ const addEdge = (kind, from, to, label, data) => {
17
+ if (!from || !to || from === to)
18
+ return;
19
+ const id = `${kind}:${from}->${to}`;
20
+ if (!edges.has(id))
21
+ edges.set(id, { id, kind, from, to, ...(label ? { label } : {}), ...(data ? { data } : {}) });
22
+ };
23
+ addNode({
24
+ id: runNodeId,
25
+ kind: 'run',
26
+ label: path.basename(result.runDir) || result.runDir,
27
+ path: result.runDir,
28
+ generatedAt,
29
+ data: { outDir: result.outDir, ok: result.ok, summary: result.summary }
30
+ });
31
+ const graphRefs = createRunGraphReferenceIndex(result);
32
+ for (const bucket of Object.keys(result.buckets)) {
33
+ const bucketNodeId = `bucket:${bucket}`;
34
+ addNode({ id: bucketNodeId, kind: 'bucket', label: bucket, bucket, generatedAt });
35
+ addEdge('contains', runNodeId, bucketNodeId);
36
+ for (const entry of result.buckets[bucket]) {
37
+ addCollectedBundleNodes(bucket, entry, result.semanticImport?.expected === true, runNodeId, bucketNodeId, addNode, addEdge);
38
+ }
39
+ }
40
+ addQueueOutcomeNodes(result, runNodeId, addNode, addEdge);
41
+ addTerminalStateNodes(result, runNodeId, addNode, addEdge);
42
+ addTournamentNodes(result, runNodeId, graphRefs, addNode, addEdge);
43
+ addAdaptiveFeedbackNodes(result, runNodeId, graphRefs, addNode, addEdge);
44
+ const nodeList = Array.from(nodes.values()).sort((left, right) => left.id.localeCompare(right.id));
45
+ const edgeList = Array.from(edges.values()).sort((left, right) => left.id.localeCompare(right.id));
46
+ const indexes = createCodexRunGraphIndexes(nodeList);
47
+ const nodeKinds = countCodexRunGraphValues(nodeList.map((node) => node.kind));
48
+ const edgeKinds = countCodexRunGraphValues(edgeList.map((edge) => edge.kind));
49
+ return {
50
+ kind: 'frontier.swarm-codex.run-graph',
51
+ version: 1,
52
+ id: `frontier-swarm-codex.run-graph:${path.basename(result.runDir) || 'run'}`,
53
+ generatedAt,
54
+ runDir: result.runDir,
55
+ outDir: result.outDir,
56
+ nodes: nodeList,
57
+ edges: edgeList,
58
+ indexes,
59
+ summary: {
60
+ nodeCount: nodeList.length,
61
+ edgeCount: edgeList.length,
62
+ nodeKinds,
63
+ edgeKinds,
64
+ taskCount: nodeKinds.task ?? 0,
65
+ jobCount: nodeKinds.job ?? 0,
66
+ candidateCount: nodeKinds.candidate ?? 0,
67
+ evidenceCount: nodeKinds.evidence ?? 0,
68
+ decisionCount: (nodeKinds.decision ?? 0) + (nodeKinds['queue-outcome'] ?? 0) + (nodeKinds['terminal-outcome'] ?? 0),
69
+ gateCount: nodeKinds.gate ?? 0,
70
+ semanticAdmissionCount: nodeKinds['semantic-admission'] ?? 0,
71
+ semanticCandidateCount: nodeKinds['semantic-candidate'] ?? 0,
72
+ semanticOwnershipRegionCount: nodeKinds['semantic-ownership-region'] ?? 0
73
+ }
74
+ };
75
+ }
76
+ function addCollectedBundleNodes(bucket, entry, semanticImportExpected, runNodeId, bucketNodeId, addNode, addEdge) {
77
+ const bundle = entry.bundle;
78
+ const semanticAdmission = classifyCodexSemanticCollectAdmission(bundle, {
79
+ hasActionablePatch: Boolean(entry.patchPath ?? bundle.patchPath),
80
+ semanticImportExpected
81
+ });
82
+ const jobNodeId = `job:${bundle.jobId}`;
83
+ const candidateNodeId = `candidate:${bundle.jobId}`;
84
+ const mergeNodeId = `merge:${bundle.id || bundle.jobId}`;
85
+ const taskNodeId = bundle.taskId ? `task:${bundle.taskId}` : undefined;
86
+ const bundleRefs = graphRefsFromUnknown(bundle);
87
+ const semanticSidecarProjection = createSemanticSidecarGraphProjection(entry);
88
+ if (taskNodeId) {
89
+ addNode({
90
+ id: taskNodeId,
91
+ kind: 'task',
92
+ label: bundle.taskId,
93
+ taskId: bundle.taskId,
94
+ lane: bundle.lane,
95
+ generatedAt: bundle.generatedAt,
96
+ data: { lane: bundle.lane, queueItemIds: bundle.queueItemIds, title: bundle.title }
97
+ });
98
+ addEdge('contains', runNodeId, taskNodeId);
99
+ addEdge('produces', taskNodeId, jobNodeId);
100
+ }
101
+ addNode({
102
+ id: jobNodeId,
103
+ kind: 'job',
104
+ label: bundle.jobId,
105
+ jobId: bundle.jobId,
106
+ taskId: bundle.taskId,
107
+ lane: bundle.lane,
108
+ model: bundleRefs.model,
109
+ computeId: bundleRefs.computeId,
110
+ modelTier: bundleRefs.modelTier,
111
+ bucket,
112
+ status: bundle.status,
113
+ generatedAt: bundle.generatedAt,
114
+ data: { lane: bundle.lane, changedPaths: bundle.changedPaths, ownershipViolations: bundle.ownershipViolations }
115
+ });
116
+ addEdge('contains', runNodeId, jobNodeId);
117
+ addNode({
118
+ id: candidateNodeId,
119
+ kind: 'candidate',
120
+ label: bundle.title ?? bundle.jobId,
121
+ jobId: bundle.jobId,
122
+ taskId: bundle.taskId,
123
+ lane: bundle.lane,
124
+ model: bundleRefs.model,
125
+ computeId: bundleRefs.computeId,
126
+ modelTier: bundleRefs.modelTier,
127
+ bucket,
128
+ status: bundle.status,
129
+ outcome: bundle.disposition,
130
+ generatedAt: bundle.generatedAt,
131
+ refs: graphNodeRefs(bundleRefs, { merge: mergeNodeId, bucket: bucketNodeId }),
132
+ data: {
133
+ mergeReadiness: bundle.mergeReadiness,
134
+ riskLevel: bundle.riskLevel,
135
+ autoMergeable: bundle.autoMergeable,
136
+ staleAgainstHead: bundle.staleAgainstHead,
137
+ semanticAdmission,
138
+ semanticSidecarAdmission: semanticSidecarProjection.summary,
139
+ reasons: bundle.reasons,
140
+ changedPaths: bundle.changedPaths,
141
+ patchPath: entry.patchPath ?? bundle.patchPath,
142
+ outputDir: entry.outputDir
143
+ }
144
+ });
145
+ addEdge('produces', jobNodeId, candidateNodeId);
146
+ addEdge('classifiedAs', bucketNodeId, candidateNodeId);
147
+ addSemanticSidecarAdmissionNodes(semanticSidecarProjection, bundle, candidateNodeId, addNode, addEdge);
148
+ addNode({
149
+ id: mergeNodeId,
150
+ kind: 'merge',
151
+ label: bundle.id || bundle.jobId,
152
+ jobId: bundle.jobId,
153
+ taskId: bundle.taskId,
154
+ lane: bundle.lane,
155
+ model: bundleRefs.model,
156
+ computeId: bundleRefs.computeId,
157
+ modelTier: bundleRefs.modelTier,
158
+ bucket,
159
+ status: bundle.status,
160
+ path: entry.mergePath,
161
+ generatedAt: bundle.generatedAt,
162
+ data: { patchHash: bundle.patchHash, branchName: bundle.branchName, commit: bundle.commit }
163
+ });
164
+ addEdge('produces', candidateNodeId, mergeNodeId);
165
+ for (const evidencePath of bundle.evidencePaths ?? []) {
166
+ const evidenceNodeId = `evidence:${stableCodexRunGraphPart(evidencePath)}`;
167
+ addNode({
168
+ id: evidenceNodeId,
169
+ kind: 'evidence',
170
+ label: path.basename(evidencePath),
171
+ jobId: bundle.jobId,
172
+ taskId: bundle.taskId,
173
+ lane: bundle.lane,
174
+ bucket,
175
+ path: evidencePath,
176
+ generatedAt: bundle.generatedAt
177
+ });
178
+ addEdge('produces', candidateNodeId, evidenceNodeId);
179
+ }
180
+ for (const command of bundle.commandsPassed ?? [])
181
+ addGateNode(command, 'passed', candidateNodeId, bundle.jobId, bundle.taskId, bucket, addNode, addEdge);
182
+ for (const command of bundle.commandsFailed ?? [])
183
+ addGateNode(command, 'failed', candidateNodeId, bundle.jobId, bundle.taskId, bucket, addNode, addEdge);
184
+ addBundleRoutingDecisionNodes(bundle, jobNodeId, candidateNodeId, addNode, addEdge);
185
+ }
186
+ function addSemanticSidecarAdmissionNodes(projection, bundle, candidateNodeId, addNode, addEdge) {
187
+ if (projection.candidates.length === 0)
188
+ return;
189
+ const refs = graphRefsFromUnknown(bundle);
190
+ const regionNodeIds = new Map();
191
+ for (const region of projection.ownershipRegions) {
192
+ const regionNodeId = `semantic-region:${bundle.jobId}:${stableCodexRunGraphPart(region.key)}`;
193
+ regionNodeIds.set(region.id ?? region.key, regionNodeId);
194
+ addNode({
195
+ id: regionNodeId,
196
+ kind: 'semantic-ownership-region',
197
+ label: region.label,
198
+ jobId: bundle.jobId,
199
+ taskId: bundle.taskId,
200
+ lane: bundle.lane,
201
+ status: region.status,
202
+ path: region.path,
203
+ generatedAt: bundle.generatedAt,
204
+ refs: graphNodeRefs(refs),
205
+ data: region.data
206
+ });
207
+ addEdge('produces', candidateNodeId, regionNodeId, 'semantic-ownership-region');
208
+ }
209
+ for (const candidate of projection.candidates) {
210
+ const semanticCandidateNodeId = `semantic-candidate:${bundle.jobId}:${stableCodexRunGraphPart(candidate.key)}`;
211
+ const admissionNodeId = `semantic-admission:${bundle.jobId}:${stableCodexRunGraphPart(candidate.key)}`;
212
+ addNode({
213
+ id: semanticCandidateNodeId,
214
+ kind: 'semantic-candidate',
215
+ label: candidate.label,
216
+ jobId: bundle.jobId,
217
+ taskId: bundle.taskId,
218
+ lane: bundle.lane,
219
+ status: candidate.status,
220
+ outcome: candidate.action,
221
+ path: candidate.path,
222
+ generatedAt: bundle.generatedAt,
223
+ refs: graphNodeRefs(refs, { candidate: candidateNodeId, admission: admissionNodeId }),
224
+ data: candidate.data
225
+ });
226
+ addEdge('produces', candidateNodeId, semanticCandidateNodeId, candidate.status);
227
+ addNode({
228
+ id: admissionNodeId,
229
+ kind: 'semantic-admission',
230
+ label: candidate.status,
231
+ jobId: bundle.jobId,
232
+ taskId: bundle.taskId,
233
+ lane: bundle.lane,
234
+ status: candidate.status,
235
+ outcome: candidate.action,
236
+ generatedAt: bundle.generatedAt,
237
+ refs: graphNodeRefs(refs, { candidate: semanticCandidateNodeId, bundleCandidate: candidateNodeId }),
238
+ data: compactRecord({
239
+ status: candidate.status,
240
+ action: candidate.action,
241
+ readiness: candidate.readiness,
242
+ risk: candidate.risk,
243
+ reasonCodes: candidate.reasonCodes,
244
+ reasons: candidate.reasons,
245
+ conflictKeys: candidate.conflictKeys,
246
+ ownershipRegionIds: candidate.ownershipRegionIds
247
+ })
248
+ });
249
+ addEdge('decides', admissionNodeId, semanticCandidateNodeId, candidate.status);
250
+ addEdge('decides', admissionNodeId, candidateNodeId, candidate.status);
251
+ for (const regionId of candidate.ownershipRegionIds) {
252
+ const regionNodeId = regionNodeIds.get(regionId);
253
+ if (regionNodeId)
254
+ addEdge('touches', semanticCandidateNodeId, regionNodeId, candidate.status);
255
+ }
256
+ }
257
+ }
258
+ function createSemanticSidecarGraphProjection(entry) {
259
+ const candidates = new Map();
260
+ const ownershipRegions = new Map();
261
+ const addRegion = (region) => {
262
+ const existing = ownershipRegions.get(region.key);
263
+ ownershipRegions.set(region.key, existing ? {
264
+ ...existing,
265
+ ...region,
266
+ data: compactRecord({ ...(existing.data ?? {}), ...(region.data ?? {}) })
267
+ } : region);
268
+ };
269
+ const addCandidate = (candidate) => {
270
+ const existing = candidates.get(candidate.key);
271
+ candidates.set(candidate.key, existing ? {
272
+ ...existing,
273
+ ...candidate,
274
+ reasonCodes: uniqueRunGraphStrings([...existing.reasonCodes, ...candidate.reasonCodes]),
275
+ reasons: uniqueRunGraphStrings([...existing.reasons, ...candidate.reasons]),
276
+ conflictKeys: uniqueRunGraphStrings([...existing.conflictKeys, ...candidate.conflictKeys]),
277
+ ownershipRegionIds: uniqueRunGraphStrings([...existing.ownershipRegionIds, ...candidate.ownershipRegionIds]),
278
+ data: compactRecord({ ...(existing.data ?? {}), ...(candidate.data ?? {}) })
279
+ } : candidate);
280
+ };
281
+ for (const source of semanticSidecarProjectionSources(entry)) {
282
+ addSemanticSidecarSourceProjection(source, addCandidate, addRegion);
283
+ }
284
+ const candidateList = Array.from(candidates.values()).sort((left, right) => left.key.localeCompare(right.key));
285
+ const regionList = Array.from(ownershipRegions.values()).sort((left, right) => left.key.localeCompare(right.key));
286
+ if (candidateList.length === 0)
287
+ return { candidates: candidateList, ownershipRegions: regionList };
288
+ const reasonCodes = uniqueRunGraphStrings(candidateList.flatMap((candidate) => candidate.reasonCodes));
289
+ const ownershipRegionIds = uniqueRunGraphStrings(candidateList.flatMap((candidate) => candidate.ownershipRegionIds));
290
+ return {
291
+ candidates: candidateList,
292
+ ownershipRegions: regionList,
293
+ summary: {
294
+ candidateCount: candidateList.length,
295
+ safeCount: candidateList.filter((candidate) => candidate.status === 'safe').length,
296
+ reviewCount: candidateList.filter((candidate) => candidate.status === 'review').length,
297
+ conflictCount: candidateList.filter((candidate) => candidate.status === 'conflict').length,
298
+ reasonCodes,
299
+ ownershipRegionIds
300
+ }
301
+ };
302
+ }
303
+ function semanticSidecarProjectionSources(entry) {
304
+ const bundle = entry.bundle;
305
+ const metadata = recordValue(bundle.metadata);
306
+ const sources = [];
307
+ pushSemanticSidecarSources(sources, bundle.semanticImport);
308
+ pushSemanticSidecarSources(sources, metadata?.semanticImport);
309
+ pushSemanticSidecarSources(sources, metadata?.semanticImports);
310
+ pushSemanticSidecarSources(sources, metadata?.semanticImportSidecar);
311
+ pushSemanticSidecarSources(sources, metadata?.semanticSidecar);
312
+ pushSemanticSidecarSources(sources, metadata?.semanticSidecars);
313
+ for (const sidecar of readSemanticSidecarEvidenceFiles(entry))
314
+ pushSemanticSidecarSources(sources, sidecar);
315
+ return uniqueRecords(sources);
316
+ }
317
+ function pushSemanticSidecarSources(out, value) {
318
+ if (Array.isArray(value)) {
319
+ for (const entry of value)
320
+ pushSemanticSidecarSources(out, entry);
321
+ return;
322
+ }
323
+ const record = recordValue(value);
324
+ if (record)
325
+ out.push(record);
326
+ }
327
+ function readSemanticSidecarEvidenceFiles(entry) {
328
+ const files = semanticSidecarEvidenceFileCandidates(entry);
329
+ const records = [];
330
+ const seen = new Set();
331
+ for (const file of files) {
332
+ if (seen.has(file))
333
+ continue;
334
+ seen.add(file);
335
+ try {
336
+ if (!fs.existsSync(file))
337
+ continue;
338
+ const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
339
+ const record = recordValue(parsed);
340
+ if (record)
341
+ records.push(record);
342
+ }
343
+ catch {
344
+ // Sidecar evidence is advisory for graph projection; collection should not fail on a malformed optional sidecar.
345
+ }
346
+ }
347
+ return records;
348
+ }
349
+ function semanticSidecarEvidenceFileCandidates(entry) {
350
+ const dirs = uniqueRunGraphStrings([entry.outputDir, path.dirname(entry.mergePath)]);
351
+ const files = [];
352
+ for (const evidencePath of entry.bundle.evidencePaths ?? []) {
353
+ if (path.basename(evidencePath) !== 'semantic-imports.json')
354
+ continue;
355
+ if (path.isAbsolute(evidencePath))
356
+ files.push(evidencePath);
357
+ else {
358
+ for (const dir of dirs)
359
+ files.push(path.resolve(dir, evidencePath));
360
+ for (const dir of dirs)
361
+ files.push(path.resolve(dir, path.basename(evidencePath)));
362
+ }
363
+ }
364
+ return uniqueRunGraphStrings(files);
365
+ }
366
+ function addSemanticSidecarSourceProjection(source, addCandidate, addRegion) {
367
+ const sourceRegions = semanticOwnershipRegionProjections(source);
368
+ for (const region of sourceRegions)
369
+ addRegion(region);
370
+ const records = recordsFromUnknown(source.records);
371
+ if (records.length > 0) {
372
+ records.forEach((record, index) => {
373
+ const recordRegions = uniqueSemanticRegionProjections([
374
+ ...sourceRegions,
375
+ ...semanticOwnershipRegionProjections(record)
376
+ ]);
377
+ for (const region of recordRegions)
378
+ addRegion(region);
379
+ const candidate = semanticCandidateProjectionFromRecord(record, recordRegions, index);
380
+ if (candidate)
381
+ addCandidate(candidate);
382
+ });
383
+ return;
384
+ }
385
+ const mergeCandidates = recordsFromUnknown(source.mergeCandidates);
386
+ mergeCandidates.forEach((candidate, index) => {
387
+ const projection = semanticCandidateProjectionFromCandidate(source, candidate, sourceRegions, index);
388
+ if (projection)
389
+ addCandidate(projection);
390
+ });
391
+ if (mergeCandidates.length === 0) {
392
+ const candidate = semanticCandidateProjectionFromRecord(source, sourceRegions, 0);
393
+ if (candidate)
394
+ addCandidate(candidate);
395
+ }
396
+ }
397
+ function semanticCandidateProjectionFromRecord(record, regions, index) {
398
+ const mergeCandidate = recordValue(record.mergeCandidate) ?? firstRecordAtPaths(record, [['semanticSidecar', 'mergeCandidate']]);
399
+ const semanticSliceAdmission = recordValue(record.semanticSliceAdmission);
400
+ const semanticSidecar = recordValue(record.semanticSidecar);
401
+ const semanticSlice = recordValue(record.semanticSlice);
402
+ const hasCandidate = Boolean(mergeCandidate || semanticSliceAdmission || semanticSidecar || semanticSlice);
403
+ if (!hasCandidate)
404
+ return undefined;
405
+ return semanticCandidateProjectionFromParts({
406
+ record,
407
+ candidate: mergeCandidate ?? {},
408
+ semanticSliceAdmission,
409
+ semanticSidecar,
410
+ semanticSlice,
411
+ regions,
412
+ index
413
+ });
414
+ }
415
+ function semanticCandidateProjectionFromCandidate(source, candidate, regions, index) {
416
+ return semanticCandidateProjectionFromParts({
417
+ record: source,
418
+ candidate,
419
+ semanticSidecar: source,
420
+ regions,
421
+ index
422
+ });
423
+ }
424
+ function semanticCandidateProjectionFromParts(input) {
425
+ const candidate = input.candidate;
426
+ const admission = input.semanticSliceAdmission;
427
+ const sidecar = input.semanticSidecar;
428
+ const semanticSlice = input.semanticSlice;
429
+ const sourcePath = firstString([input.record.path, candidate.path, sidecar?.sourcePath, recordValue(sidecar?.summary)?.sourcePath]);
430
+ const readiness = firstString([
431
+ candidate.readiness,
432
+ admission?.readiness,
433
+ semanticSlice?.readiness,
434
+ sidecar?.readiness,
435
+ recordValue(sidecar?.summary)?.readiness,
436
+ recordValue(input.record.nativeDiff)?.readiness
437
+ ]);
438
+ const action = firstString([
439
+ admission?.action,
440
+ candidate.action,
441
+ recordValue(sidecar?.admission)?.action,
442
+ sidecar?.semanticImportRecordAction,
443
+ recordValue(sidecar?.summary)?.semanticImportRecordAction
444
+ ]);
445
+ const risk = firstString([candidate.risk, admission?.risk, recordValue(admission?.mergeScore)?.risk]);
446
+ const reasonCodes = uniqueRunGraphStrings([
447
+ ...stringValues(candidate.reasonCodes),
448
+ ...stringValues(candidate.reasons),
449
+ ...stringValues(admission?.reasonCodes),
450
+ ...stringValues(admission?.reasons),
451
+ ...stringValues(recordValue(sidecar?.admission)?.reasonCodes),
452
+ ...stringValues(recordValue(sidecar?.quality)?.reasonCodes),
453
+ ...stringValues(sidecar?.semanticImportExpectedMissingReasonCodes),
454
+ ...stringValues(recordValue(sidecar?.summary)?.semanticImportExpectedMissingReasonCodes),
455
+ ...stringValues(recordValue(sidecar?.summary)?.semanticImportRecordReasonCode),
456
+ ...stringValues(input.record.reasonCodes),
457
+ ...stringValues(input.record.reasons)
458
+ ]);
459
+ const reasons = uniqueRunGraphStrings([
460
+ ...stringValues(candidate.reasons),
461
+ ...stringValues(admission?.reasons),
462
+ ...stringValues(recordValue(sidecar?.admission)?.reasons),
463
+ ...stringValues(input.record.reasons)
464
+ ]);
465
+ const conflictKeys = uniqueRunGraphStrings([
466
+ ...stringValues(candidate.conflictKeys),
467
+ ...stringValues(admission?.conflictKeys),
468
+ ...stringValues(semanticSlice?.conflictKeys),
469
+ ...stringValues(recordValue(candidate.conflictSummary)?.conflictKeys)
470
+ ]);
471
+ const ownershipRegionIds = uniqueRunGraphStrings([
472
+ ...input.regions.map((region) => region.id ?? region.key),
473
+ ...stringValues(candidate.ownershipRegionIds),
474
+ ...stringValues(admission?.ownershipRegionIds)
475
+ ]);
476
+ const status = semanticSidecarAdmissionStatus({
477
+ candidate,
478
+ admission,
479
+ sidecar,
480
+ semanticSlice,
481
+ readiness,
482
+ risk,
483
+ action,
484
+ reasonCodes,
485
+ reasons,
486
+ conflictKeys
487
+ });
488
+ const key = firstString([
489
+ candidate.id,
490
+ admission?.id,
491
+ input.record.importId,
492
+ sidecar?.id,
493
+ semanticSlice?.id,
494
+ sourcePath ? `${sourcePath}:${input.index}` : undefined
495
+ ]) ?? `semantic-candidate:${input.index}`;
496
+ return {
497
+ key,
498
+ label: sourcePath ? `${path.basename(sourcePath)}:${status}` : `${key}:${status}`,
499
+ status,
500
+ action,
501
+ path: sourcePath,
502
+ readiness,
503
+ risk,
504
+ reasonCodes,
505
+ reasons,
506
+ conflictKeys,
507
+ ownershipRegionIds,
508
+ data: compactRecord({
509
+ path: sourcePath,
510
+ readiness,
511
+ risk,
512
+ action,
513
+ reasonCodes,
514
+ reasons,
515
+ conflictKeys,
516
+ ownershipRegionIds,
517
+ mergeCandidate: Object.keys(candidate).length ? candidate : undefined,
518
+ semanticSliceAdmission: admission,
519
+ semanticSlice,
520
+ semanticSidecar: sidecar ? compactRecord({
521
+ kind: sidecar.kind,
522
+ id: sidecar.id,
523
+ readiness: sidecar.readiness,
524
+ symbols: sidecar.symbols,
525
+ ownershipRegions: sidecar.ownershipRegions,
526
+ patchHints: sidecar.patchHints,
527
+ semanticImportExpectedSatisfied: sidecar.semanticImportExpectedSatisfied,
528
+ semanticImportExpectedMissingReasonCodes: sidecar.semanticImportExpectedMissingReasonCodes,
529
+ sampleOwnershipRegions: sidecar.sampleOwnershipRegions
530
+ }) : undefined
531
+ })
532
+ };
533
+ }
534
+ function semanticSidecarAdmissionStatus(input) {
535
+ const statusSignals = uniqueRunGraphStrings([
536
+ input.readiness,
537
+ input.action,
538
+ input.risk,
539
+ stringFromUnknown(input.candidate.status),
540
+ stringFromUnknown(input.admission?.status),
541
+ stringFromUnknown(input.sidecar?.status),
542
+ stringFromUnknown(input.semanticSlice?.status),
543
+ ...input.reasonCodes,
544
+ ...input.reasons
545
+ ].filter((entry) => typeof entry === 'string')).map(normalizedSemanticSidecarSignal);
546
+ if (input.conflictKeys.length > 0 ||
547
+ statusSignals.some((signal) => signal.includes('conflict') || signal === 'blocked' || signal === 'block' || signal === 'reject' || signal === 'rejected')) {
548
+ return 'conflict';
549
+ }
550
+ if (booleanFromUnknown(input.admission?.reviewRequired) === true ||
551
+ statusSignals.some((signal) => signal === 'needs-review' ||
552
+ signal === 'ready-with-losses' ||
553
+ signal === 'manual-review' ||
554
+ signal === 'human-review' ||
555
+ signal === 'review' ||
556
+ signal === 'prioritize' ||
557
+ signal === 'medium' ||
558
+ signal === 'high')) {
559
+ return 'review';
560
+ }
561
+ if (booleanFromUnknown(input.candidate.mergeable) === true ||
562
+ booleanFromUnknown(input.admission?.autoMergeClaim) === true ||
563
+ statusSignals.some((signal) => signal === 'ready' || signal === 'admit' || signal === 'apply' || signal === 'auto-merge')) {
564
+ return 'safe';
565
+ }
566
+ return 'review';
567
+ }
568
+ function normalizedSemanticSidecarSignal(value) {
569
+ return value.trim().replace(/_/g, '-').toLowerCase();
570
+ }
571
+ function semanticOwnershipRegionProjections(record) {
572
+ const sidecar = recordValue(record.semanticSidecar);
573
+ return uniqueSemanticRegionProjections([
574
+ ...recordsFromUnknown(record.ownershipRegions).map(semanticOwnershipRegionProjection),
575
+ ...recordsFromUnknown(record.sampleOwnershipRegions).map(semanticOwnershipRegionProjection),
576
+ ...recordsFromUnknown(sidecar?.ownershipRegions).map(semanticOwnershipRegionProjection),
577
+ ...recordsFromUnknown(sidecar?.sampleOwnershipRegions).map(semanticOwnershipRegionProjection),
578
+ ...recordsFromUnknown(recordValue(record.semanticSlice)?.ownershipRegions).map(semanticOwnershipRegionProjection)
579
+ ].filter((entry) => Boolean(entry)));
580
+ }
581
+ function semanticOwnershipRegionProjection(region) {
582
+ const id = firstString([region.id, region.key, region.conflictKey]);
583
+ const sourcePath = firstString([region.sourcePath, region.path]);
584
+ const symbolName = stringFromUnknown(region.symbolName);
585
+ const key = id ?? firstString([sourcePath && symbolName ? `${sourcePath}:${symbolName}` : undefined, sourcePath]);
586
+ if (!key)
587
+ return undefined;
588
+ return {
589
+ key,
590
+ id,
591
+ label: symbolName ?? id ?? sourcePath ?? key,
592
+ path: sourcePath,
593
+ status: firstString([region.readiness, region.mergePolicy, region.precision]),
594
+ data: compactRecord({
595
+ id: region.id,
596
+ key: region.key,
597
+ conflictKey: region.conflictKey,
598
+ sourcePath,
599
+ symbolName,
600
+ symbolKind: region.symbolKind,
601
+ regionKind: region.regionKind,
602
+ granularity: region.granularity,
603
+ precision: region.precision,
604
+ mergePolicy: region.mergePolicy,
605
+ sourceSpan: region.sourceSpan
606
+ })
607
+ };
608
+ }
609
+ function uniqueSemanticRegionProjections(regions) {
610
+ const out = new Map();
611
+ for (const region of regions) {
612
+ if (!region)
613
+ continue;
614
+ const existing = out.get(region.key);
615
+ out.set(region.key, existing ? {
616
+ ...existing,
617
+ ...region,
618
+ data: compactRecord({ ...(existing.data ?? {}), ...(region.data ?? {}) })
619
+ } : region);
620
+ }
621
+ return Array.from(out.values());
622
+ }
623
+ function addGateNode(command, status, candidateNodeId, jobId, taskId, bucket, addNode, addEdge) {
624
+ const label = command.name ?? command.commandLine ?? command.command?.join(' ') ?? 'command';
625
+ const id = `gate:${jobId}:${stableCodexRunGraphPart(label)}`;
626
+ addNode({ id, kind: 'gate', label, jobId, taskId, bucket, status, data: { command } });
627
+ addEdge('verifies', id, candidateNodeId, status);
628
+ }
629
+ function addQueueOutcomeNodes(result, runNodeId, addNode, addEdge) {
630
+ for (const decision of result.queueOutcomeModel?.latestDecisions ?? []) {
631
+ const id = `decision:queue:${decision.id}`;
632
+ addNode({
633
+ id,
634
+ kind: 'decision',
635
+ label: decision.subjectId || decision.outcome,
636
+ jobId: decision.jobId,
637
+ taskId: decision.taskId,
638
+ lane: stringFromUnknown(decision.lane),
639
+ status: decision.category,
640
+ outcome: decision.outcome,
641
+ generatedAt: decision.generatedAt,
642
+ refs: graphNodeRefs(graphRefsFromUnknown(decision)),
643
+ data: { reasons: decision.reasons, queueItemIds: decision.queueItemIds }
644
+ });
645
+ addEdge('contains', runNodeId, id);
646
+ if (decision.jobId)
647
+ addEdge('decides', id, `candidate:${decision.jobId}`);
648
+ }
649
+ }
650
+ function addTerminalStateNodes(result, runNodeId, addNode, addEdge) {
651
+ for (const item of result.terminalState?.items ?? []) {
652
+ const id = `decision:terminal:${item.id}`;
653
+ addNode({
654
+ id,
655
+ kind: 'decision',
656
+ label: item.subjectId,
657
+ jobId: item.jobId,
658
+ taskId: stringFromUnknown(item.taskId),
659
+ lane: stringFromUnknown(item.lane),
660
+ status: item.bucket,
661
+ outcome: item.status,
662
+ generatedAt: item.generatedAt,
663
+ refs: graphNodeRefs(graphRefsFromUnknown(item)),
664
+ data: { queueItemIds: item.queueItemIds, subjectAliases: item.subjectAliases }
665
+ });
666
+ addEdge('contains', runNodeId, id);
667
+ if (item.jobId)
668
+ addEdge('decides', id, `candidate:${item.jobId}`);
669
+ }
670
+ }
671
+ function addTournamentNodes(result, runNodeId, graphRefs, addNode, addEdge) {
672
+ const tournament = result.strategyTournament;
673
+ if (!tournament)
674
+ return;
675
+ const tournamentRecord = tournament;
676
+ const tournamentSummary = recordValue(tournamentRecord.summary) ?? {};
677
+ const tournamentWinnerId = stringFromUnknown(tournamentRecord.winnerId);
678
+ const tournamentNodeId = `rsi:tournament:${tournament.id}`;
679
+ addNode({
680
+ id: tournamentNodeId,
681
+ kind: 'rsi',
682
+ label: tournament.title ?? tournament.id,
683
+ generatedAt: tournament.generatedAt,
684
+ data: { summary: tournament.summary, winnerId: tournamentWinnerId }
685
+ });
686
+ addEdge('contains', runNodeId, tournamentNodeId);
687
+ const projectedCandidates = [];
688
+ const candidates = Array.isArray(tournamentRecord.candidates) ? tournamentRecord.candidates : [];
689
+ for (const candidate of candidates) {
690
+ const record = recordValue(candidate) ?? {};
691
+ const refs = graphRefsFromUnknown(record);
692
+ const candidateId = tournamentCandidateGraphId(record, refs);
693
+ const outcome = tournamentCandidateOutcome(record);
694
+ const selected = isSelectedTournamentCandidate(record, tournamentWinnerId ?? stringFromUnknown(tournamentSummary.topStrategyId), refs);
695
+ const rejected = isRejectedTournamentCandidate(record);
696
+ const status = stringFromUnknown(record.status) ?? (selected ? 'selected' : outcome);
697
+ const id = `candidate:strategy:${candidateId}`;
698
+ const label = stringFromUnknown(record.title) ?? stringFromUnknown(record.strategyId) ?? refs.jobId ?? candidateId;
699
+ addNode({
700
+ id,
701
+ kind: 'candidate',
702
+ label,
703
+ jobId: refs.jobId,
704
+ taskId: refs.taskId,
705
+ lane: refs.lane,
706
+ model: refs.model,
707
+ computeId: refs.computeId,
708
+ modelTier: refs.modelTier,
709
+ status,
710
+ outcome,
711
+ generatedAt: tournament.generatedAt,
712
+ refs: graphNodeRefs(refs, { tournament: tournamentNodeId }),
713
+ data: compactRecord({
714
+ score: record.score,
715
+ reasons: record.reasons,
716
+ strategyId: record.strategyId,
717
+ disposition: record.disposition,
718
+ selected,
719
+ rejected,
720
+ taskKind: refs.taskKind,
721
+ workKind: refs.workKind
722
+ })
723
+ });
724
+ addEdge('contains', tournamentNodeId, id);
725
+ addEdge('decides', tournamentNodeId, id, status ?? outcome);
726
+ if (refs.jobId) {
727
+ addEdge('produces', `job:${refs.jobId}`, id, refs.lane);
728
+ addEdge(selected ? 'mergesInto' : 'supersedes', id, `candidate:${refs.jobId}`, status ?? outcome);
729
+ }
730
+ if (refs.taskId)
731
+ addEdge('produces', `task:${refs.taskId}`, id, refs.lane);
732
+ linkGraphNodeToAffectedRefs('decides', id, refs, graphRefs, addEdge, status ?? outcome);
733
+ projectedCandidates.push({ id, refs, selected, rejected, label });
734
+ }
735
+ const selectedCandidates = projectedCandidates.filter((candidate) => candidate.selected);
736
+ if (selectedCandidates.length > 0) {
737
+ for (const rejected of projectedCandidates.filter((candidate) => candidate.rejected || !candidate.selected)) {
738
+ for (const selected of selectedCandidates) {
739
+ if (selected.id !== rejected.id)
740
+ addEdge('supersedes', selected.id, rejected.id, 'selected-over-rejected');
741
+ }
742
+ }
743
+ }
744
+ }
745
+ function addAdaptiveFeedbackNodes(result, runNodeId, graphRefs, addNode, addEdge) {
746
+ const feedback = result.tournamentAdaptiveFeedback;
747
+ if (!feedback)
748
+ return;
749
+ const id = `rsi:adaptive:${feedback.id}`;
750
+ addNode({
751
+ id,
752
+ kind: 'rsi',
753
+ label: feedback.id,
754
+ generatedAt: feedback.generatedAt,
755
+ refs: graphNodeRefs({}, {
756
+ ...(feedback.tournamentId ? { tournament: `rsi:tournament:${feedback.tournamentId}` } : {}),
757
+ ...(feedback.historyId ? { history: `rsi:history:${feedback.historyId}` } : {})
758
+ }),
759
+ data: { summary: feedback.summary, tournamentId: feedback.tournamentId, historyId: feedback.historyId }
760
+ });
761
+ addEdge('contains', runNodeId, id);
762
+ if (feedback.tournamentId)
763
+ addEdge('mergesInto', id, `rsi:tournament:${feedback.tournamentId}`);
764
+ feedback.observations.forEach((observation, index) => {
765
+ const refs = graphRefsFromUnknown(observation);
766
+ const observationRecord = recordValue(observation) ?? {};
767
+ const observationId = `feedback:adaptive-observation:${stableCodexRunGraphPart(`${feedback.id}:${stringFromUnknown(observationRecord.id) ?? index}:${JSON.stringify(observation)}`)}`;
768
+ addNode({
769
+ id: observationId,
770
+ kind: 'feedback',
771
+ label: stringFromUnknown(observation.reason) ?? stringFromUnknown(observation.kind) ?? `observation-${index + 1}`,
772
+ jobId: refs.jobId,
773
+ taskId: refs.taskId,
774
+ lane: refs.lane,
775
+ model: refs.model,
776
+ computeId: refs.computeId,
777
+ modelTier: refs.modelTier,
778
+ status: stringFromUnknown(observation.severity),
779
+ outcome: stringFromUnknown(observation.kind),
780
+ generatedAt: observation.at ?? feedback.generatedAt,
781
+ refs: graphNodeRefs(refs, { adaptive: id }),
782
+ data: compactRecord({ observation, taskKind: refs.taskKind, workKind: refs.workKind })
783
+ });
784
+ addEdge('produces', id, observationId, stringFromUnknown(observation.kind));
785
+ linkGraphNodeToAffectedRefs('verifies', observationId, refs, graphRefs, addEdge, stringFromUnknown(observation.kind));
786
+ });
787
+ feedback.recommendations.forEach((recommendation, index) => {
788
+ const recommendationRecord = recordValue(recommendation) ?? {};
789
+ const refs = graphRefsFromAdaptiveRecommendation(recommendationRecord);
790
+ const recommendationKey = stringFromUnknown(recommendationRecord.id) ?? `${recommendation.action ?? 'recommendation'}:${recommendation.target ?? ''}:${recommendation.key ?? ''}:${index}`;
791
+ const recommendationId = `decision:adaptive-recommendation:${stableCodexRunGraphPart(`${feedback.id}:${recommendationKey}`)}`;
792
+ addNode({
793
+ id: recommendationId,
794
+ kind: 'decision',
795
+ label: recommendation.reason ?? stringFromUnknown(recommendationRecord.id) ?? `recommendation-${index + 1}`,
796
+ jobId: refs.jobId,
797
+ taskId: refs.taskId,
798
+ lane: refs.lane,
799
+ model: refs.model,
800
+ computeId: refs.computeId,
801
+ modelTier: refs.modelTier,
802
+ status: recommendation.action,
803
+ outcome: recommendation.target,
804
+ generatedAt: feedback.generatedAt,
805
+ refs: graphNodeRefs(refs, { adaptive: id }),
806
+ data: compactRecord({
807
+ recommendation,
808
+ key: recommendation.key,
809
+ score: recommendation.score,
810
+ taskKind: refs.taskKind,
811
+ workKind: refs.workKind
812
+ })
813
+ });
814
+ addEdge('produces', id, recommendationId, recommendation.action);
815
+ linkGraphNodeToAffectedRefs('decides', recommendationId, refs, graphRefs, addEdge, recommendation.action);
816
+ });
817
+ }
818
+ function addBundleRoutingDecisionNodes(bundle, jobNodeId, candidateNodeId, addNode, addEdge) {
819
+ const metadata = recordValue(bundle.metadata);
820
+ if (!metadata)
821
+ return;
822
+ const bundleRefs = graphRefsFromUnknown(bundle);
823
+ const modelRoute = firstRecordAtPaths(metadata, [
824
+ ['modelRoute'],
825
+ ['routing', 'modelRoute'],
826
+ ['modelRouting', 'route'],
827
+ ['route']
828
+ ]);
829
+ const routeNodeId = modelRoute
830
+ ? addModelRouteDecisionNode(modelRoute, bundleRefs, jobNodeId, candidateNodeId, bundle.generatedAt, addNode, addEdge)
831
+ : undefined;
832
+ const panel = firstRecord([
833
+ modelRoute ? recordValue(modelRoute.panel) : undefined,
834
+ firstRecordAtPaths(metadata, [['panel'], ['panelEvaluation'], ['modelRouting', 'panel'], ['routing', 'panel']])
835
+ ]);
836
+ const panelNodeId = panel
837
+ ? addPanelDecisionNode(panel, bundleRefs, routeNodeId ?? jobNodeId, candidateNodeId, bundle.generatedAt, addNode, addEdge)
838
+ : undefined;
839
+ const fuserComputeId = stringValueAtPaths(modelRoute ?? {}, [['fuserComputeId'], ['fuser', 'computeId']])
840
+ ?? stringValueAtPaths(panel ?? {}, [['fuserComputeId'], ['fuser', 'computeId']])
841
+ ?? stringValueAtPaths(metadata, [['fusionDecision', 'fuserComputeId'], ['fusion', 'fuserComputeId']]);
842
+ if (fuserComputeId) {
843
+ addFusionDecisionNode({ fuserComputeId, route: stringFromUnknown(modelRoute?.route), panelId: stringFromUnknown(panel?.id) }, bundleRefs, panelNodeId ?? routeNodeId ?? jobNodeId, routeNodeId ?? panelNodeId, candidateNodeId, bundle.generatedAt, addNode, addEdge);
844
+ }
845
+ for (const feedback of modelRoutingFeedbackRecords(metadata)) {
846
+ const refs = { ...bundleRefs, ...graphRefsFromUnknown(feedback) };
847
+ const feedbackId = `feedback:model-routing:${stableCodexRunGraphPart(stringFromUnknown(feedback.id) ?? `${bundle.jobId}:${JSON.stringify(feedback)}`)}`;
848
+ addNode({
849
+ id: feedbackId,
850
+ kind: 'feedback',
851
+ label: stringFromUnknown(feedback.resultStatus) ?? stringFromUnknown(feedback.id) ?? 'model-routing-feedback',
852
+ jobId: refs.jobId,
853
+ taskId: refs.taskId,
854
+ lane: refs.lane,
855
+ model: refs.model,
856
+ computeId: refs.computeId,
857
+ modelTier: refs.modelTier,
858
+ status: stringFromUnknown(feedback.resultStatus),
859
+ outcome: stringFromUnknown(feedback.mergeDisposition),
860
+ generatedAt: numberFromUnknown(feedback.generatedAt) ?? bundle.generatedAt,
861
+ refs: graphNodeRefs(refs, { ...(routeNodeId ? { route: routeNodeId } : {}) }),
862
+ data: compactRecord({ feedback, taskKind: refs.taskKind, workKind: refs.workKind })
863
+ });
864
+ addEdge('produces', jobNodeId, feedbackId);
865
+ addEdge('verifies', feedbackId, candidateNodeId, stringFromUnknown(feedback.resultStatus));
866
+ if (routeNodeId)
867
+ addEdge('mergesInto', feedbackId, routeNodeId);
868
+ }
869
+ }
870
+ function addModelRouteDecisionNode(route, fallbackRefs, jobNodeId, candidateNodeId, generatedAt, addNode, addEdge) {
871
+ const refs = {
872
+ ...fallbackRefs,
873
+ computeId: stringFromUnknown(route.selectedComputeId)
874
+ ?? stringFromUnknown(route.recommendedComputeId)
875
+ ?? stringArray(route.recommendedComputeIds)[0]
876
+ ?? fallbackRefs.computeId
877
+ };
878
+ const routeId = `decision:model-route:${fallbackRefs.jobId ?? 'job'}:${stableCodexRunGraphPart(stringFromUnknown(route.id) ?? JSON.stringify(route))}`;
879
+ addNode({
880
+ id: routeId,
881
+ kind: 'decision',
882
+ label: stringFromUnknown(route.route) ?? stringFromUnknown(route.id) ?? 'model-route',
883
+ jobId: refs.jobId,
884
+ taskId: refs.taskId,
885
+ lane: refs.lane,
886
+ model: refs.model,
887
+ computeId: refs.computeId,
888
+ modelTier: refs.modelTier,
889
+ status: stringFromUnknown(route.mode),
890
+ outcome: stringFromUnknown(route.route),
891
+ generatedAt,
892
+ refs: graphNodeRefs(refs),
893
+ data: compactRecord({
894
+ route: route.route,
895
+ mode: route.mode,
896
+ fallbackComputeId: route.fallbackComputeId,
897
+ selectedComputeId: route.selectedComputeId,
898
+ recommendedComputeIds: route.recommendedComputeIds,
899
+ summary: route.summary,
900
+ reasons: route.reasons
901
+ })
902
+ });
903
+ addEdge('produces', jobNodeId, routeId, stringFromUnknown(route.route));
904
+ addEdge('decides', routeId, candidateNodeId, stringFromUnknown(route.route));
905
+ return routeId;
906
+ }
907
+ function addPanelDecisionNode(panel, fallbackRefs, parentNodeId, candidateNodeId, generatedAt, addNode, addEdge) {
908
+ const panelId = `decision:panel:${fallbackRefs.jobId ?? 'job'}:${stableCodexRunGraphPart(stringFromUnknown(panel.id) ?? JSON.stringify(panel))}`;
909
+ const refs = {
910
+ ...fallbackRefs,
911
+ computeId: stringFromUnknown(panel.fuserComputeId) ?? fallbackRefs.computeId
912
+ };
913
+ addNode({
914
+ id: panelId,
915
+ kind: 'decision',
916
+ label: stringFromUnknown(panel.strategy) ?? stringFromUnknown(panel.id) ?? 'panel',
917
+ jobId: refs.jobId,
918
+ taskId: refs.taskId,
919
+ lane: refs.lane,
920
+ model: refs.model,
921
+ computeId: refs.computeId,
922
+ modelTier: refs.modelTier,
923
+ status: booleanFromUnknown(panel.recommended) === false ? 'observed' : 'recommended',
924
+ outcome: stringFromUnknown(panel.strategy),
925
+ generatedAt,
926
+ refs: graphNodeRefs(refs),
927
+ data: compactRecord({
928
+ strategy: panel.strategy,
929
+ memberComputeIds: panel.memberComputeIds,
930
+ fuserComputeId: panel.fuserComputeId,
931
+ summary: panel.summary,
932
+ reasons: panel.reasons
933
+ })
934
+ });
935
+ addEdge('produces', parentNodeId, panelId, stringFromUnknown(panel.strategy));
936
+ addEdge('decides', panelId, candidateNodeId, stringFromUnknown(panel.strategy));
937
+ addEdge('mergesInto', panelId, parentNodeId);
938
+ return panelId;
939
+ }
940
+ function addFusionDecisionNode(fusion, fallbackRefs, parentNodeId, routeNodeId, candidateNodeId, generatedAt, addNode, addEdge) {
941
+ const fusionNodeId = `decision:fusion:${fallbackRefs.jobId ?? 'job'}:${stableCodexRunGraphPart(fusion.fuserComputeId)}`;
942
+ const refs = { ...fallbackRefs, computeId: fusion.fuserComputeId };
943
+ addNode({
944
+ id: fusionNodeId,
945
+ kind: 'decision',
946
+ label: fusion.fuserComputeId,
947
+ jobId: refs.jobId,
948
+ taskId: refs.taskId,
949
+ lane: refs.lane,
950
+ model: refs.model,
951
+ computeId: refs.computeId,
952
+ modelTier: refs.modelTier,
953
+ status: 'fusion',
954
+ outcome: fusion.route,
955
+ generatedAt,
956
+ refs: graphNodeRefs(refs, { ...(fusion.panelId ? { panel: fusion.panelId } : {}) }),
957
+ data: compactRecord({ fuserComputeId: fusion.fuserComputeId, route: fusion.route })
958
+ });
959
+ addEdge('produces', parentNodeId, fusionNodeId, 'fusion');
960
+ addEdge('decides', fusionNodeId, candidateNodeId, 'fusion');
961
+ if (routeNodeId)
962
+ addEdge('mergesInto', fusionNodeId, routeNodeId);
963
+ return fusionNodeId;
964
+ }
965
+ function createRunGraphReferenceIndex(result) {
966
+ const jobIds = new Set();
967
+ const taskIds = new Set();
968
+ const jobIdsByLane = new Map();
969
+ const taskIdsByLane = new Map();
970
+ for (const entries of Object.values(result.buckets)) {
971
+ for (const entry of entries) {
972
+ const { bundle } = entry;
973
+ jobIds.add(bundle.jobId);
974
+ if (bundle.taskId)
975
+ taskIds.add(bundle.taskId);
976
+ if (bundle.lane) {
977
+ pushUniqueMapValue(jobIdsByLane, bundle.lane, bundle.jobId);
978
+ if (bundle.taskId)
979
+ pushUniqueMapValue(taskIdsByLane, bundle.lane, bundle.taskId);
980
+ }
981
+ }
982
+ }
983
+ return { jobIds, taskIds, jobIdsByLane, taskIdsByLane };
984
+ }
985
+ function pushUniqueMapValue(map, key, value) {
986
+ const next = map.get(key) ?? [];
987
+ if (!next.includes(value))
988
+ next.push(value);
989
+ map.set(key, next);
990
+ }
991
+ function linkGraphNodeToAffectedRefs(kind, from, refs, graphRefs, addEdge, label) {
992
+ const targets = new Set();
993
+ if (refs.jobId && graphRefs.jobIds.has(refs.jobId)) {
994
+ targets.add(`job:${refs.jobId}`);
995
+ targets.add(`candidate:${refs.jobId}`);
996
+ }
997
+ if (refs.taskId && graphRefs.taskIds.has(refs.taskId))
998
+ targets.add(`task:${refs.taskId}`);
999
+ if (targets.size === 0 && refs.lane) {
1000
+ for (const jobId of graphRefs.jobIdsByLane.get(refs.lane) ?? []) {
1001
+ targets.add(`job:${jobId}`);
1002
+ targets.add(`candidate:${jobId}`);
1003
+ }
1004
+ for (const taskId of graphRefs.taskIdsByLane.get(refs.lane) ?? [])
1005
+ targets.add(`task:${taskId}`);
1006
+ }
1007
+ for (const target of targets)
1008
+ addEdge(kind, from, target, label);
1009
+ }
1010
+ function graphRefsFromAdaptiveRecommendation(recommendation) {
1011
+ const metadata = recordValue(recommendation.metadata);
1012
+ const refs = graphRefsFromUnknown({ ...recommendation, ...(metadata ? { metadata } : {}) });
1013
+ const target = recommendation.target?.trim().toLowerCase();
1014
+ return {
1015
+ ...refs,
1016
+ lane: refs.lane ?? (target && ['lane', 'max-ready-jobs', 'concurrency'].includes(target) ? recommendation.key : undefined),
1017
+ model: refs.model ?? recommendation.model,
1018
+ computeId: refs.computeId ?? recommendation.computeId
1019
+ };
1020
+ }
1021
+ function graphRefsFromUnknown(value) {
1022
+ const record = recordValue(value) ?? {};
1023
+ const metadata = recordValue(record.metadata) ?? {};
1024
+ const bundle = recordValue(record.bundle) ?? {};
1025
+ const routing = firstRecord([recordValue(record.routing), recordValue(metadata.routing)]);
1026
+ const routingKey = firstRecord([recordValue(record.routingKey), recordValue(metadata.routingKey)]);
1027
+ const task = firstRecord([recordValue(record.task), recordValue(metadata.task), recordValue(bundle.task)]);
1028
+ const compute = firstRecord([recordValue(record.compute), recordValue(metadata.compute), recordValue(recordValue(record.job)?.compute), recordValue(recordValue(metadata.job)?.compute)]);
1029
+ const tournamentStrategy = firstRecord([recordValue(record.tournamentStrategy), recordValue(metadata.tournamentStrategy), recordValue(recordValue(bundle.metadata)?.tournamentStrategy)]);
1030
+ const tournamentRoutingKey = recordValue(tournamentStrategy?.routingKey);
1031
+ return {
1032
+ jobId: firstString([
1033
+ record.jobId,
1034
+ bundle.jobId,
1035
+ metadata.jobId,
1036
+ recordValue(metadata.job)?.id,
1037
+ recordValue(metadata.source)?.jobId
1038
+ ]),
1039
+ taskId: firstString([
1040
+ record.taskId,
1041
+ bundle.taskId,
1042
+ metadata.taskId,
1043
+ task?.id,
1044
+ recordValue(metadata.source)?.taskId
1045
+ ]),
1046
+ lane: firstString([
1047
+ record.lane,
1048
+ bundle.lane,
1049
+ metadata.lane,
1050
+ routingKey?.lane,
1051
+ routing?.lane,
1052
+ task?.lane,
1053
+ tournamentStrategy?.lane,
1054
+ tournamentRoutingKey?.lane
1055
+ ]),
1056
+ model: firstString([
1057
+ record.model,
1058
+ metadata.model,
1059
+ compute?.model,
1060
+ tournamentStrategy?.model
1061
+ ]),
1062
+ computeId: firstString([
1063
+ record.computeId,
1064
+ metadata.computeId,
1065
+ compute?.id,
1066
+ recordValue(metadata.resourceAllocation)?.env && recordValue(recordValue(metadata.resourceAllocation)?.env)?.FRONTIER_SWARM_COMPUTE_ID
1067
+ ]),
1068
+ modelTier: firstString([
1069
+ record.modelTier,
1070
+ metadata.modelTier,
1071
+ metadata.tier,
1072
+ routingKey?.modelTier,
1073
+ routing?.modelTier,
1074
+ compute?.modelTier,
1075
+ compute?.tier,
1076
+ tournamentStrategy?.modelTier,
1077
+ tournamentRoutingKey?.modelTier
1078
+ ]),
1079
+ taskKind: firstString([
1080
+ record.taskKind,
1081
+ metadata.taskKind,
1082
+ routingKey?.taskKind,
1083
+ routing?.taskKind,
1084
+ task?.kind,
1085
+ tournamentStrategy?.taskKind,
1086
+ tournamentRoutingKey?.taskKind
1087
+ ]),
1088
+ workKind: firstString([
1089
+ record.workKind,
1090
+ metadata.workKind,
1091
+ routingKey?.workKind,
1092
+ routing?.workKind,
1093
+ task?.workKind,
1094
+ tournamentStrategy?.workKind,
1095
+ tournamentRoutingKey?.workKind
1096
+ ])
1097
+ };
1098
+ }
1099
+ function graphNodeRefs(refs, extra = {}) {
1100
+ const out = {};
1101
+ if (refs.jobId) {
1102
+ out.job = `job:${refs.jobId}`;
1103
+ out.candidate = `candidate:${refs.jobId}`;
1104
+ }
1105
+ if (refs.taskId)
1106
+ out.task = `task:${refs.taskId}`;
1107
+ if (refs.lane)
1108
+ out.lane = refs.lane;
1109
+ if (refs.model)
1110
+ out.model = refs.model;
1111
+ if (refs.computeId)
1112
+ out.compute = refs.computeId;
1113
+ if (refs.modelTier)
1114
+ out.modelTier = refs.modelTier;
1115
+ for (const [key, value] of Object.entries(extra)) {
1116
+ if (value)
1117
+ out[key] = value;
1118
+ }
1119
+ return Object.keys(out).length > 0 ? out : undefined;
1120
+ }
1121
+ function tournamentCandidateGraphId(record, refs) {
1122
+ return stringFromUnknown(record.id)
1123
+ ?? stringFromUnknown(record.candidateId)
1124
+ ?? stringFromUnknown(record.strategyId)
1125
+ ?? refs.jobId
1126
+ ?? String(hashCodexRunGraphString(JSON.stringify(record)));
1127
+ }
1128
+ function tournamentCandidateOutcome(record) {
1129
+ return firstString([record.outcome, record.disposition, record.status]);
1130
+ }
1131
+ function isSelectedTournamentCandidate(record, winnerId, refs) {
1132
+ const status = tournamentCandidateOutcome(record)?.toLowerCase();
1133
+ if (booleanFromUnknown(record.selected) === true || status === 'selected' || status === 'winner')
1134
+ return true;
1135
+ if (!winnerId)
1136
+ return false;
1137
+ return [
1138
+ stringFromUnknown(record.id),
1139
+ stringFromUnknown(record.strategyId),
1140
+ stringFromUnknown(record.candidateId),
1141
+ refs.jobId,
1142
+ refs.lane
1143
+ ].includes(winnerId);
1144
+ }
1145
+ function isRejectedTournamentCandidate(record) {
1146
+ const outcome = tournamentCandidateOutcome(record)?.toLowerCase();
1147
+ return ['rejected', 'blocked', 'failed', 'declined', 'superseded'].includes(outcome ?? '');
1148
+ }
1149
+ function modelRoutingFeedbackRecords(metadata) {
1150
+ return uniqueRecords([
1151
+ ...recordsFromUnknown(metadata.modelRoutingFeedback),
1152
+ ...recordsFromUnknown(metadata.routingFeedback),
1153
+ ...recordsFromUnknown(recordValue(metadata.routingPolicy)?.feedback),
1154
+ ...recordsFromUnknown(recordValue(metadata.modelRoutingPolicy)?.feedback)
1155
+ ]);
1156
+ }
1157
+ function uniqueRecords(records) {
1158
+ const seen = new Set();
1159
+ const out = [];
1160
+ for (const record of records) {
1161
+ const key = stringFromUnknown(record.id) ?? JSON.stringify(record);
1162
+ if (seen.has(key))
1163
+ continue;
1164
+ seen.add(key);
1165
+ out.push(record);
1166
+ }
1167
+ return out;
1168
+ }
1169
+ function recordsFromUnknown(value) {
1170
+ if (Array.isArray(value))
1171
+ return value.map(recordValue).filter((entry) => Boolean(entry));
1172
+ const record = recordValue(value);
1173
+ return record ? [record] : [];
1174
+ }
1175
+ function firstRecordAtPaths(record, paths) {
1176
+ for (const pathParts of paths) {
1177
+ let cursor = record;
1178
+ for (const part of pathParts)
1179
+ cursor = recordValue(cursor)?.[part];
1180
+ const found = recordValue(cursor);
1181
+ if (found)
1182
+ return found;
1183
+ }
1184
+ return undefined;
1185
+ }
1186
+ function stringValueAtPaths(record, paths) {
1187
+ for (const pathParts of paths) {
1188
+ let cursor = record;
1189
+ for (const part of pathParts)
1190
+ cursor = recordValue(cursor)?.[part];
1191
+ const found = stringFromUnknown(cursor);
1192
+ if (found)
1193
+ return found;
1194
+ }
1195
+ return undefined;
1196
+ }
1197
+ function firstRecord(records) {
1198
+ return records.find((entry) => Boolean(entry));
1199
+ }
1200
+ function firstString(values) {
1201
+ for (const value of values) {
1202
+ const found = stringFromUnknown(value);
1203
+ if (found)
1204
+ return found;
1205
+ }
1206
+ return undefined;
1207
+ }
1208
+ function stringArray(value) {
1209
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === 'string' && entry.length > 0) : [];
1210
+ }
1211
+ function stringValues(value) {
1212
+ const single = stringFromUnknown(value);
1213
+ if (single)
1214
+ return [single];
1215
+ return stringArray(value);
1216
+ }
1217
+ function uniqueRunGraphStrings(values) {
1218
+ const out = [];
1219
+ const seen = new Set();
1220
+ for (const value of values) {
1221
+ const normalized = stringFromUnknown(value);
1222
+ if (!normalized || seen.has(normalized))
1223
+ continue;
1224
+ seen.add(normalized);
1225
+ out.push(normalized);
1226
+ }
1227
+ return out;
1228
+ }
1229
+ function recordValue(value) {
1230
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
1231
+ }
1232
+ function numberFromUnknown(value) {
1233
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
1234
+ }
1235
+ function booleanFromUnknown(value) {
1236
+ return typeof value === 'boolean' ? value : undefined;
1237
+ }
1238
+ function compactRecord(record) {
1239
+ const out = {};
1240
+ for (const [key, value] of Object.entries(record)) {
1241
+ if (value !== undefined)
1242
+ out[key] = value;
1243
+ }
1244
+ return out;
1245
+ }
1246
+ //# sourceMappingURL=collect-run-graph.js.map