1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// SPDX-License-Identifier: MIT OR Apache-2.0
//! `RuntimeSelector` — Picker 2 substrate.
//!
//! Phase 5.1 of the picker-work arc. The optimizer ranker (Picker
//! 1, Phases 1.1–1.5 + 3) commits to a per-decision-point top-N
//! [`AlternativeSet`]; the runtime selector (this module) picks
//! among them at dispatch time.
//!
//! # Why two pickers
//!
//! Per architecture v1.0 §04 and the 2026-05-30 picker-alternatives
//! audit, the architectural endpoint is two distinct pick surfaces:
//!
//! - **Picker 1 — the optimizer ranker.** Plan-time. Enumerates
//! candidates via SystemTopology, hard-filters by correctness
//! (precision floor, capability), ranks by composite cost (Layer-1
//! static + Layer-2 Judge data), preserves top-N. The output is
//! the [`crate::plan::ExecutionPlan`] consumed by the executor.
//!
//! - **Picker 2 — the runtime selector (this trait).** Dispatch-time.
//! Receives the plan's top-N for a node, picks one based on
//! layer-3 signals (device load, recent failures, fresh
//! telemetry) that didn't exist at plan time.
//!
//! Picker 1 commits to a *ranking*; Picker 2 commits to a *pick*.
//! The seam between them lets Picker 1 cache plans across realizes
//! while Picker 2's choices reflect current state at the moment of
//! dispatch.
//!
//! # What ships in 5.1
//!
//! Just the trait surface + [`WinnerSelector`] (the static default —
//! returns `set.winner()` = the top-N's first entry = the static-
//! cost winner). Behaviorally identical to pre-5.1 dispatch.
//!
//! # What 5.2+ will add
//!
//! Concrete selectors that exploit runtime signals. Likely
//! candidates (sketched, not committed):
//!
//! - `JudgeAwareSelector` — re-query the Judge at dispatch time
//! for any candidate whose Layer-2 measurement landed after the
//! plan's snapshot.
//! - `SizeAwareDownscaleSelector` — for tiny ops, prefer CPU over
//! GPU to avoid kernel-launch overhead even when GPU is the
//! static-cost winner.
//! - `FailureMemorySelector` — per-(op, backend) recent-failure
//! tracking; demote a backend in the top-N after a transient
//! error.
//! - `DeviceLoadSelector` — queue-depth + stream-utilization
//! probing; spread work across co-located backends.
//!
//! None of these are wired today — each needs telemetry
//! infrastructure that doesn't exist yet. The seam lets them ship
//! independently when the signals do.
use crate;
/// Dispatch-time selector over a plan's per-node [`AlternativeSet`].
/// Phase 5.1 of the picker-work arc.
///
/// # Contract
///
/// - `select` is called exactly once per kernel-bearing node, at
/// dispatch time, by the executor's `resolve_compiled`.
/// - Implementors return `None` only when the set is empty — every
/// non-empty set MUST have a pick. The default implementation
/// ([`WinnerSelector`]) returns `set.winner()` (the first entry).
/// - The selected `Candidate`'s `kernel`, `caps`, and `backend`
/// become the executor's dispatch parameters for that node.
/// - Selectors are queried per-node and may make different picks
/// for different nodes in the same realize — the trait carries
/// no per-realize state.
///
/// # Sendness
///
/// Implementations must be `Send + Sync` so the executor can share
/// a single selector across the compiler thread and the executor
/// thread.
///
/// # Why not pass NodeId / OpKind / dtypes?
///
/// The [`Candidate`] already carries `backend`, `precision`,
/// `static_cost`, and `op_params`; selectors that need op identity
/// can derive it from these fields. If a future selector needs the
/// graph-level NodeId or OpKind, we add a `SelectorContext`
/// param — but Phase 5.1 keeps the surface minimal so the trait
/// stays usable by selectors that only care about set contents.
/// The trivial selector: always returns `set.winner()`. Behaviorally
/// identical to pre-Phase-5 dispatch — every call that doesn't
/// explicitly supply a selector falls back to this.
///
/// Implements [`RuntimeSelector`] via the trait's default `select`.
/// Use directly via `WinnerSelector` or wrap in
/// `Arc::new(WinnerSelector) as Arc<dyn RuntimeSelector>` for the
/// executor APIs that take an `Arc<dyn RuntimeSelector>`.
;