Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:02:26.364Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

router.rs

1391 lines · 54.4 KB · rust
1//! The route decision. `OMEGA-DELTA-0029`, omega#78.
2//!
3//! Omega Agent is a router. It owns routing, disclosure, and receipts, and it
4//! owns no execution (omega#74, admitted by the owner 2026-07-25). This module
5//! is the decision half of that: a pure function from typed inputs to a typed
6//! decision, with no clock, no randomness, and no map iteration in the path.
7//!
8//! The dispatch half — the `AgentConnection` implementation that hands the turn
9//! to the executor this function names — lives in
10//! `crates/agent_ui/src/omega_router.rs`, because it needs GPUI and the three
11//! executor crates. Keeping the decision here means the routing law can be
12//! checked in a second, and means no decision is ever made inside a widget.
13//!
14//! # Determinism, stated as three rules
15//!
16//! 1. [`route`] reads nothing but its argument. No clock, no environment, no
17//!    global.
18//! 2. Every collection it walks is an ordered slice, and every choice among
19//!    equals is resolved by a total order on the lane reference — never by the
20//!    order the engine happened to answer in, which is not a stable input.
21//! 3. Every decision it can make is recordable, and the record is total: the
22//!    canonical form escapes the four characters that carry structure, so no
23//!    lane reference is refused for a cosmetic reason. The one lane the router
24//!    does refuse is a nameless one, because there is nothing to dispatch to
25//!    and nothing to write down.
26//!
27//! `crates/omega_deltas` asserts (1) against the source text as well, so a
28//! later edit that reaches for `SystemTime` or a `HashMap` fails a test rather
29//! than quietly making the router non-reproducible.
30//!
31//! # Why an unpinned thread is never routed to an engine lane
32//!
33//! Owner gate 8: *no model-initiated path can start Full Auto authority; only
34//! an explicit human action can, wherever that action lives.* An engine lane is
35//! Full Auto authority. So a router that preferred an engine lane for an
36//! unpinned thread would be exactly the model-initiated start the gate forbids,
37//! reached through a new door nobody had flagged — which is how
38//! `full_auto_enable` survived until today.
39//!
40//! v1 therefore routes an unpinned thread to the native loop, always. Engine
41//! lanes are reachable only through a pin, and a pin is set by a visible
42//! control a person operates. Model-advisory routing is out of scope for v1 by
43//! the packet's own terms, and this is the shape that keeps it out.
44
45use crate::ExecutorClass;
46
47// -------------------------------------------------------------------------
48// Inputs
49// -------------------------------------------------------------------------
50
51/// A user's explicit choice of executor for a thread.
52///
53/// A pin is the *only* way a thread reaches anything but the native loop. It is
54/// set by a human gesture on a visible control and never by a turn, a slash
55/// command, or a restored draft.
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct ExecutorPin {
58    /// The class the user pinned.
59    pub class: ExecutorClass,
60    /// The engine lane the user pinned, when they named one.
61    ///
62    /// Meaningful only for [`ExecutorClass::EngineLane`]. A pin with no lane
63    /// means "any lane the engine says is ready", resolved by the total order
64    /// in [`select_lane`].
65    pub lane_ref: Option<String>,
66}
67
68impl ExecutorPin {
69    /// Pin a class without naming a lane.
70    #[must_use]
71    pub const fn new(class: ExecutorClass) -> Self {
72        Self {
73            class,
74            lane_ref: None,
75        }
76    }
77
78    /// Pin one named engine lane.
79    #[must_use]
80    pub fn on_lane(lane_ref: impl Into<String>) -> Self {
81        Self {
82            class: ExecutorClass::EngineLane,
83            lane_ref: Some(lane_ref.into()),
84        }
85    }
86
87    /// The stable token this pin is recorded under.
88    #[must_use]
89    pub fn token(&self) -> String {
90        match &self.lane_ref {
91            Some(lane_ref) => format!("{}@{}", self.class.token(), encode_field(lane_ref)),
92            None => self.class.token().to_owned(),
93        }
94    }
95
96    /// Read a pin back from [`token`](Self::token).
97    #[must_use]
98    pub fn parse_token(token: &str) -> Option<Self> {
99        let (class_token, lane_ref) = match token.split_once('@') {
100            Some((class_token, lane_ref)) => (class_token, Some(decode_field(lane_ref)?)),
101            None => (token, None),
102        };
103        let class = *ExecutorClass::all()
104            .iter()
105            .find(|class| class.token() == class_token)?;
106        Some(Self { class, lane_ref })
107    }
108}
109
110/// What one engine lane says about itself in a `get_capacity` answer.
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub enum LaneState {
113    /// Ready and idle.
114    Available,
115    /// Ready, but already serving a run.
116    Busy,
117    /// Anything else the engine says, including a state this build does not
118    /// recognise. Unrecognised is *not* available: a router that read an
119    /// unknown state as ready would route into a lane on the strength of not
120    /// understanding it.
121    Unavailable,
122}
123
124impl LaneState {
125    /// The stable token for this state.
126    #[must_use]
127    pub const fn token(self) -> &'static str {
128        match self {
129            Self::Available => "available",
130            Self::Busy => "busy",
131            Self::Unavailable => "unavailable",
132        }
133    }
134
135    /// Read the engine's `state` string.
136    ///
137    /// Anything unrecognised is [`Unavailable`](Self::Unavailable), which is
138    /// the fail-closed direction.
139    #[must_use]
140    pub fn parse(state: &str) -> Self {
141        match state {
142            "available" => Self::Available,
143            "busy" => Self::Busy,
144            _ => Self::Unavailable,
145        }
146    }
147
148    /// Whether a turn may be routed onto a lane in this state.
149    #[must_use]
150    pub const fn can_serve(self) -> bool {
151        matches!(self, Self::Available)
152    }
153}
154
155/// One lane of the engine's declared capacity.
156#[derive(Debug, Clone, PartialEq, Eq)]
157pub struct EngineLane {
158    /// The lane reference, exactly as the engine reports it.
159    pub lane_ref: String,
160    /// What the engine says about it.
161    pub state: LaneState,
162}
163
164impl EngineLane {
165    /// A lane record.
166    #[must_use]
167    pub fn new(lane_ref: impl Into<String>, state: LaneState) -> Self {
168        Self {
169            lane_ref: lane_ref.into(),
170            state,
171        }
172    }
173}
174
175/// Why the engine could not be asked.
176#[derive(Debug, Clone, Copy, PartialEq, Eq)]
177pub enum EngineUnreachable {
178    /// No supervised `omega-effectd` process is running.
179    NotRunning,
180    /// The framed request did not answer in time.
181    Timeout,
182    /// The engine answered something this build could not read.
183    ProtocolError,
184}
185
186impl EngineUnreachable {
187    /// The stable token for this cause.
188    #[must_use]
189    pub const fn token(self) -> &'static str {
190        match self {
191            Self::NotRunning => "not_running",
192            Self::Timeout => "timeout",
193            Self::ProtocolError => "protocol_error",
194        }
195    }
196
197    /// Every admitted cause, in declaration order.
198    #[must_use]
199    pub const fn all() -> &'static [Self] {
200        &[Self::NotRunning, Self::Timeout, Self::ProtocolError]
201    }
202}
203
204/// What `omega-effectd` last said about itself.
205///
206/// A snapshot the router *reads*. The engine remains the sole run authority:
207/// nothing here is written back, and nothing here is treated as run state.
208#[derive(Debug, Clone, PartialEq, Eq)]
209pub enum EngineReadiness {
210    /// The engine answered `get_capacity`.
211    Answered {
212        /// Runs the engine currently has active.
213        active_run_count: u32,
214        /// The engine's own ceiling on active runs.
215        active_run_limit: u32,
216        /// The lanes it declared, in the order it declared them. The order is
217        /// deliberately not trusted; see [`select_lane`].
218        lanes: Vec<EngineLane>,
219    },
220    /// The engine could not be asked, or could not be understood.
221    Unreachable(EngineUnreachable),
222}
223
224impl EngineReadiness {
225    /// Whether the engine could be asked at all.
226    #[must_use]
227    pub const fn answered(&self) -> bool {
228        matches!(self, Self::Answered { .. })
229    }
230}
231
232/// Everything [`route`] is allowed to read.
233///
234/// If a decision cannot be explained from these fields, it is not a decision
235/// this router made — which is omega#78's falsifier, stated as a type.
236#[derive(Debug, Clone, PartialEq, Eq)]
237pub struct RouteInputs {
238    /// What the user pinned for this thread, if anything.
239    pub pin: Option<ExecutorPin>,
240    /// What the engine last said about itself.
241    pub engine: EngineReadiness,
242    /// The external ACP agent connected for this thread, if one is.
243    ///
244    /// `None` means no external agent is connected — not that none is
245    /// configured. A pin to an external agent that is not connected fails
246    /// closed rather than waiting.
247    pub external_acp: Option<String>,
248    /// The executor registered to serve engine lanes, if one is.
249    ///
250    /// Separate from [`engine`](Self::engine) on purpose, because they are two
251    /// different facts. The engine answering `get_capacity` says a lane exists;
252    /// this says Omega has something to hand the turn to. In this build they
253    /// come apart: engine lanes are started by a person on the Full Auto
254    /// surface and driven by the host bridge, not through
255    /// `AgentConnection::prompt`, so the router can *decide* an engine-lane
256    /// route it cannot itself dispatch. Modelling that as an input keeps the
257    /// gap a stated fallback with a reason instead of a panic or a silent
258    /// substitution.
259    pub engine_lane: Option<String>,
260}
261
262impl RouteInputs {
263    /// The inputs of a machine with no engine and no external agent.
264    #[must_use]
265    pub const fn native_only() -> Self {
266        Self {
267            pin: None,
268            engine: EngineReadiness::Unreachable(EngineUnreachable::NotRunning),
269            external_acp: None,
270            engine_lane: None,
271        }
272    }
273
274    /// The same inputs with a pin set.
275    #[must_use]
276    pub fn pinned(mut self, pin: ExecutorPin) -> Self {
277        self.pin = Some(pin);
278        self
279    }
280
281    /// The same inputs with an engine answer.
282    #[must_use]
283    pub fn with_engine(mut self, engine: EngineReadiness) -> Self {
284        self.engine = engine;
285        self
286    }
287
288    /// The same inputs with an external ACP agent connected.
289    #[must_use]
290    pub fn with_external_acp(mut self, agent_id: impl Into<String>) -> Self {
291        self.external_acp = Some(agent_id.into());
292        self
293    }
294
295    /// The same inputs with an executor registered for engine lanes.
296    #[must_use]
297    pub fn with_engine_lane(mut self, agent_id: impl Into<String>) -> Self {
298        self.engine_lane = Some(agent_id.into());
299        self
300    }
301}
302
303// -------------------------------------------------------------------------
304// The decision
305// -------------------------------------------------------------------------
306
307/// Why an executor was chosen.
308///
309/// A closed set of typed reasons, not a message. A route the user cannot have
310/// explained to them is the same defect class as a handoff with no system note.
311#[derive(Debug, Clone, Copy, PartialEq, Eq)]
312pub enum RouteReason {
313    /// Nothing was pinned, so the thread ran on the native loop. v1 never
314    /// routes an unpinned thread anywhere else; see the module docs.
315    UnpinnedDefault,
316    /// The user pinned this executor and it could serve.
317    PinHonored,
318    /// An engine lane was pinned and the engine could not be asked.
319    EngineUnreachable,
320    /// An engine lane was pinned and the engine was at its active-run limit.
321    EngineAtCapacity,
322    /// An engine lane was pinned without naming one, and the engine declared no
323    /// lane that could serve.
324    EngineHasNoReadyLane,
325    /// A named engine lane was pinned and the engine did not declare it ready.
326    PinnedLaneUnavailable,
327    /// An external ACP agent was pinned and none is connected.
328    ExternalAcpUnavailable,
329    /// The engine declared a lane the router could have used, and Omega has no
330    /// executor registered to hand the turn to. See
331    /// [`RouteInputs::engine_lane`].
332    EngineLaneNotConnected,
333    /// The lane that would have been chosen carries a reference the decision
334    /// record cannot hold. A decision that cannot be written down is not one
335    /// the router makes.
336    UnrecordableLane,
337}
338
339impl RouteReason {
340    /// The stable token this reason is recorded under.
341    #[must_use]
342    pub const fn token(self) -> &'static str {
343        match self {
344            Self::UnpinnedDefault => "unpinned_default",
345            Self::PinHonored => "pin_honored",
346            Self::EngineUnreachable => "engine_unreachable",
347            Self::EngineAtCapacity => "engine_at_capacity",
348            Self::EngineHasNoReadyLane => "engine_has_no_ready_lane",
349            Self::PinnedLaneUnavailable => "pinned_lane_unavailable",
350            Self::ExternalAcpUnavailable => "external_acp_unavailable",
351            Self::EngineLaneNotConnected => "engine_lane_not_connected",
352            Self::UnrecordableLane => "unrecordable_lane",
353        }
354    }
355
356    /// Every admitted reason, in declaration order.
357    #[must_use]
358    pub const fn all() -> &'static [Self] {
359        &[
360            Self::UnpinnedDefault,
361            Self::PinHonored,
362            Self::EngineUnreachable,
363            Self::EngineAtCapacity,
364            Self::EngineHasNoReadyLane,
365            Self::PinnedLaneUnavailable,
366            Self::ExternalAcpUnavailable,
367            Self::EngineLaneNotConnected,
368            Self::UnrecordableLane,
369        ]
370    }
371
372    /// Read a reason back from [`token`](Self::token).
373    #[must_use]
374    pub fn parse_token(token: &str) -> Option<Self> {
375        Self::all()
376            .iter()
377            .find(|reason| reason.token() == token)
378            .copied()
379    }
380
381    /// Whether this reason means a pin could not be honoured.
382    ///
383    /// Every fail-closed reason lands on the native loop. That is asserted in
384    /// [`RouteDecision::is_coherent`] rather than merely intended.
385    #[must_use]
386    pub const fn is_fallback(self) -> bool {
387        match self {
388            Self::UnpinnedDefault | Self::PinHonored => false,
389            Self::EngineUnreachable
390            | Self::EngineAtCapacity
391            | Self::EngineHasNoReadyLane
392            | Self::PinnedLaneUnavailable
393            | Self::ExternalAcpUnavailable
394            | Self::EngineLaneNotConnected
395            | Self::UnrecordableLane => true,
396        }
397    }
398
399    /// What the user is told, in their terms.
400    ///
401    /// Derived on every call. Nothing stores it — the reason is the record, and
402    /// this is one rendering of it. A fallback says it fell back, because a
403    /// fallback the user cannot see is the defect this packet exists to avoid.
404    #[must_use]
405    pub const fn phrase(self) -> &'static str {
406        match self {
407            Self::UnpinnedDefault => "unpinned",
408            Self::PinHonored => "pinned",
409            Self::EngineUnreachable => "engine unreachable, fell back to the native loop",
410            Self::EngineAtCapacity => "engine at capacity, fell back to the native loop",
411            Self::EngineHasNoReadyLane => "engine has no ready lane, fell back to the native loop",
412            Self::PinnedLaneUnavailable => {
413                "pinned engine lane unavailable, fell back to the native loop"
414            }
415            Self::ExternalAcpUnavailable => {
416                "pinned external agent not connected, fell back to the native loop"
417            }
418            Self::EngineLaneNotConnected => {
419                "no executor is connected for engine lanes, fell back to the native loop"
420            }
421            Self::UnrecordableLane => {
422                "engine lane could not be recorded, fell back to the native loop"
423            }
424        }
425    }
426}
427
428/// One routing decision, in full.
429///
430/// Durable and inspectable through [`canonical_record`](Self::canonical_record),
431/// which round-trips. It carries no timestamp on purpose: a clock in the record
432/// would make two identical decisions look different, and would put a
433/// non-deterministic value in the decision path this packet exists to keep
434/// reproducible.
435#[derive(Debug, Clone, PartialEq, Eq)]
436pub struct RouteDecision {
437    /// The class the turn was dispatched to.
438    pub chosen: ExecutorClass,
439    /// Why.
440    pub reason: RouteReason,
441    /// What was pinned when the decision was made, if anything. Kept even when
442    /// the pin could not be honoured — an unhonoured pin the record forgot is
443    /// indistinguishable from no pin at all.
444    pub pin: Option<ExecutorPin>,
445    /// The engine lane the turn was dispatched to.
446    ///
447    /// `Some` exactly when [`chosen`](Self::chosen) is
448    /// [`ExecutorClass::EngineLane`].
449    pub lane_ref: Option<String>,
450}
451
452/// Characters the canonical record escapes, and the escape character itself.
453///
454/// The record is a flat `key=value;…` line in which a pin may name a lane after
455/// an `@`, so these three would otherwise give it a second reading. `%` is
456/// escaped too, which is what makes the encoding a bijection rather than merely
457/// a substitution — without it, a literal `%3B` in a lane reference and an
458/// escaped `;` would decode to the same thing.
459pub const RESERVED_RECORD_CHARACTERS: &[char] = &['%', ';', '=', '@'];
460
461/// Write one field of the canonical record.
462///
463/// Percent-escapes the four reserved characters and nothing else, so a lane
464/// reference such as `acp:cursor-agent` is written verbatim and stays legible
465/// to a person reading the journal.
466#[must_use]
467pub fn encode_field(value: &str) -> String {
468    let mut encoded = String::with_capacity(value.len());
469    for character in value.chars() {
470        match character {
471            '%' => encoded.push_str("%25"),
472            ';' => encoded.push_str("%3B"),
473            '=' => encoded.push_str("%3D"),
474            '@' => encoded.push_str("%40"),
475            other => encoded.push(other),
476        }
477    }
478    encoded
479}
480
481/// Read one field of the canonical record.
482///
483/// Returns `None` for a truncated or unknown escape rather than guessing, so a
484/// half-written journal entry is rejected instead of decoded into something
485/// nobody wrote.
486#[must_use]
487pub fn decode_field(value: &str) -> Option<String> {
488    let mut decoded = String::with_capacity(value.len());
489    let mut characters = value.chars();
490    while let Some(character) = characters.next() {
491        if character != '%' {
492            decoded.push(character);
493            continue;
494        }
495        let escape: String = characters.by_ref().take(2).collect();
496        match escape.as_str() {
497            "25" => decoded.push('%'),
498            "3B" => decoded.push(';'),
499            "3D" => decoded.push('='),
500            "40" => decoded.push('@'),
501            _ => return None,
502        }
503    }
504    Some(decoded)
505}
506
507/// Whether a lane reference can be routed to.
508///
509/// Every non-empty reference can: the record escapes anything that would make
510/// it ambiguous. An *empty* one cannot, and that is not a cosmetic problem —
511/// there is nothing to dispatch to and nothing to write down. The router refuses
512/// it rather than routing into a lane it cannot name.
513#[must_use]
514pub fn lane_ref_is_recordable(lane_ref: &str) -> bool {
515    !lane_ref.is_empty()
516}
517
518impl RouteDecision {
519    /// A decision that landed on the native loop.
520    fn native(reason: RouteReason, pin: Option<ExecutorPin>) -> Self {
521        Self {
522            chosen: ExecutorClass::NativeLoop,
523            reason,
524            pin,
525            lane_ref: None,
526        }
527    }
528
529    /// Whether this decision is internally consistent.
530    ///
531    /// Every clause here is a way the router could have lied about itself: a
532    /// fallback that did not land on the native loop, a lane reference on a
533    /// non-engine route, an honoured pin that names a different class from the
534    /// one that ran.
535    #[must_use]
536    pub fn is_coherent(&self) -> bool {
537        let lane_matches_class = match self.chosen {
538            ExecutorClass::EngineLane => self
539                .lane_ref
540                .as_deref()
541                .is_some_and(lane_ref_is_recordable),
542            ExecutorClass::NativeLoop | ExecutorClass::ExternalAcp => self.lane_ref.is_none(),
543        };
544        let fallback_lands_native = !self.reason.is_fallback()
545            || (self.chosen == ExecutorClass::NativeLoop && self.pin.is_some());
546        let unpinned_is_unpinned =
547            self.reason != RouteReason::UnpinnedDefault || self.pin.is_none();
548        let honoured_pin_names_the_class = self.reason != RouteReason::PinHonored
549            || self
550                .pin
551                .as_ref()
552                .is_some_and(|pin| pin.class == self.chosen);
553
554        lane_matches_class
555            && fallback_lands_native
556            && unpinned_is_unpinned
557            && honoured_pin_names_the_class
558    }
559
560    /// The typed reason this decision contributes to a thread's disclosure.
561    #[must_use]
562    pub const fn disclosed_route(&self) -> RouteReason {
563        self.reason
564    }
565
566    /// The durable form. Deterministic, total, and round-trippable.
567    ///
568    /// Written to the route journal verbatim. An empty value is the absent
569    /// marker, which is unambiguous because no pin token and no routable lane
570    /// reference is ever empty — a sentinel such as `-` would collide with a
571    /// lane genuinely named `-`, and escaping `-` would make `claude-local`
572    /// unreadable.
573    #[must_use]
574    pub fn canonical_record(&self) -> String {
575        let pin = match &self.pin {
576            Some(pin) => pin.token(),
577            None => String::new(),
578        };
579        let lane = match &self.lane_ref {
580            Some(lane_ref) => encode_field(lane_ref),
581            None => String::new(),
582        };
583        format!(
584            "chosen={};reason={};pin={pin};lane={lane}",
585            self.chosen.token(),
586            self.reason.token(),
587        )
588    }
589
590    /// Read a decision back from [`canonical_record`](Self::canonical_record).
591    ///
592    /// Returns `None` for anything it cannot read *or* for a record that is
593    /// readable but incoherent, so a hand-edited or half-written journal entry
594    /// is rejected rather than believed. Every one of the four keys must be
595    /// present exactly once: a partial record is a truncated write, and reading
596    /// one as a decision would invent the missing parts.
597    #[must_use]
598    pub fn parse_canonical_record(record: &str) -> Option<Self> {
599        let mut chosen = None;
600        let mut reason = None;
601        let mut pin = None;
602        let mut lane = None;
603        let mut keys = 0usize;
604        for field in record.split(';') {
605            let (key, value) = field.split_once('=')?;
606            keys += 1;
607            match key {
608                "chosen" if chosen.is_none() => {
609                    chosen = Some(
610                        *ExecutorClass::all()
611                            .iter()
612                            .find(|class| class.token() == value)?,
613                    );
614                }
615                "reason" if reason.is_none() => reason = Some(RouteReason::parse_token(value)?),
616                "pin" if pin.is_none() => {
617                    pin = Some(if value.is_empty() {
618                        None
619                    } else {
620                        Some(ExecutorPin::parse_token(value)?)
621                    });
622                }
623                "lane" if lane.is_none() => {
624                    lane = Some(if value.is_empty() {
625                        None
626                    } else {
627                        Some(decode_field(value)?)
628                    });
629                }
630                _ => return None,
631            }
632        }
633        if keys != 4 {
634            return None;
635        }
636        let decision = Self {
637            chosen: chosen?,
638            reason: reason?,
639            pin: pin?,
640            lane_ref: lane?,
641        };
642        decision.is_coherent().then_some(decision)
643    }
644
645    /// One sentence explaining the decision from its parts.
646    ///
647    /// Derived, never stored. Used in logs and in the inspector.
648    #[must_use]
649    pub fn explain(&self) -> String {
650        let mut line = format!("{} ({})", self.chosen.token(), self.reason.phrase());
651        if let Some(lane_ref) = &self.lane_ref {
652            line.push_str(" on ");
653            line.push_str(lane_ref);
654        }
655        if let Some(pin) = &self.pin {
656            line.push_str("; pin ");
657            line.push_str(&pin.token());
658            if self.reason.is_fallback() {
659                line.push_str(" could not be honoured");
660            }
661        }
662        line
663    }
664}
665
666// -------------------------------------------------------------------------
667// The decision itself
668// -------------------------------------------------------------------------
669
670/// Choose the lane a decision would use.
671///
672/// Two rules, both total:
673///
674/// * A pin that names a lane matches that lane and no other, and only when the
675///   engine declared it able to serve.
676/// * A pin that names no lane takes the **lexicographically smallest** lane
677///   able to serve. Not the first the engine listed: the engine's array order
678///   is not a stable input, so trusting it would make the same capacity answer
679///   route two ways on two runs. A total order on the reference is the cheapest
680///   thing that cannot do that.
681#[must_use]
682pub fn select_lane(lanes: &[EngineLane], pinned: Option<&str>) -> Option<String> {
683    match pinned {
684        Some(pinned) => lanes
685            .iter()
686            .find(|lane| lane.lane_ref == pinned && lane.state.can_serve())
687            .map(|lane| lane.lane_ref.clone()),
688        None => lanes
689            .iter()
690            .filter(|lane| lane.state.can_serve())
691            .map(|lane| lane.lane_ref.as_str())
692            .min()
693            .map(str::to_owned),
694    }
695}
696
697/// Decide where a turn runs.
698///
699/// The whole routing law. Pure: same inputs, same decision, every time.
700#[must_use]
701pub fn route(inputs: &RouteInputs) -> RouteDecision {
702    let Some(pin) = inputs.pin.clone() else {
703        // Nothing pinned. The native loop, always — see the module docs on
704        // owner gate 8.
705        return RouteDecision::native(RouteReason::UnpinnedDefault, None);
706    };
707
708    match pin.class {
709        // Pinning the first-party loop is always honourable: it is the
710        // fail-closed target, so it is available whenever Omega is running.
711        ExecutorClass::NativeLoop => RouteDecision {
712            chosen: ExecutorClass::NativeLoop,
713            reason: RouteReason::PinHonored,
714            pin: Some(pin),
715            lane_ref: None,
716        },
717
718        ExecutorClass::ExternalAcp => {
719            if inputs.external_acp.is_some() {
720                RouteDecision {
721                    chosen: ExecutorClass::ExternalAcp,
722                    reason: RouteReason::PinHonored,
723                    pin: Some(pin),
724                    lane_ref: None,
725                }
726            } else {
727                RouteDecision::native(RouteReason::ExternalAcpUnavailable, Some(pin))
728            }
729        }
730
731        ExecutorClass::EngineLane => match &inputs.engine {
732            // Engine down. Fail closed to the native loop, and say which way it
733            // was down: `EngineUnreachable` is one reason, but the cause it
734            // carries is what an operator needs.
735            EngineReadiness::Unreachable(_) => {
736                RouteDecision::native(RouteReason::EngineUnreachable, Some(pin))
737            }
738            EngineReadiness::Answered {
739                active_run_count,
740                active_run_limit,
741                lanes,
742            } => {
743                if active_run_count >= active_run_limit {
744                    return RouteDecision::native(RouteReason::EngineAtCapacity, Some(pin));
745                }
746                match select_lane(lanes, pin.lane_ref.as_deref()) {
747                    Some(lane_ref) if !lane_ref_is_recordable(&lane_ref) => {
748                        RouteDecision::native(RouteReason::UnrecordableLane, Some(pin))
749                    }
750                    // The lane exists and the engine says it is ready. Whether
751                    // Omega can hand a turn to it is a separate fact, checked
752                    // last so an operator hears about the engine first.
753                    Some(_) if inputs.engine_lane.is_none() => {
754                        RouteDecision::native(RouteReason::EngineLaneNotConnected, Some(pin))
755                    }
756                    Some(lane_ref) => RouteDecision {
757                        chosen: ExecutorClass::EngineLane,
758                        reason: RouteReason::PinHonored,
759                        pin: Some(pin),
760                        lane_ref: Some(lane_ref),
761                    },
762                    None if pin.lane_ref.is_some() => {
763                        RouteDecision::native(RouteReason::PinnedLaneUnavailable, Some(pin))
764                    }
765                    None => RouteDecision::native(RouteReason::EngineHasNoReadyLane, Some(pin)),
766                }
767            }
768        },
769    }
770}
771
772#[cfg(test)]
773mod tests {
774    use super::*;
775
776    fn ready_engine() -> EngineReadiness {
777        EngineReadiness::Answered {
778            active_run_count: 1,
779            active_run_limit: 8,
780            lanes: vec![
781                EngineLane::new("codex-local", LaneState::Busy),
782                EngineLane::new("claude-local", LaneState::Available),
783                EngineLane::new("acp:cursor-agent", LaneState::Available),
784            ],
785        }
786    }
787
788    // ---------------------------------------------------------------------
789    // Exit property 1: pins honoured
790    // ---------------------------------------------------------------------
791
792    /// An explicitly pinned, available executor is used. Every time, for every
793    /// class, regardless of what else is ready.
794    ///
795    /// Falsified by making `route` prefer an available engine lane over a
796    /// native pin: this test fails on the native case.
797    #[test]
798    fn a_pin_to_an_available_executor_is_always_used() {
799        let base = RouteInputs::native_only()
800            .with_engine(ready_engine())
801            .with_external_acp("codex-acp")
802            .with_engine_lane("codex-local");
803
804        let native = route(&base.clone().pinned(ExecutorPin::new(ExecutorClass::NativeLoop)));
805        assert_eq!(native.chosen, ExecutorClass::NativeLoop);
806        assert_eq!(native.reason, RouteReason::PinHonored);
807
808        let external = route(
809            &base
810                .clone()
811                .pinned(ExecutorPin::new(ExecutorClass::ExternalAcp)),
812        );
813        assert_eq!(external.chosen, ExecutorClass::ExternalAcp);
814        assert_eq!(external.reason, RouteReason::PinHonored);
815
816        let lane = route(&base.pinned(ExecutorPin::on_lane("claude-local")));
817        assert_eq!(lane.chosen, ExecutorClass::EngineLane);
818        assert_eq!(lane.reason, RouteReason::PinHonored);
819        assert_eq!(lane.lane_ref.as_deref(), Some("claude-local"));
820
821        for decision in [native, external, lane] {
822            assert!(decision.is_coherent(), "{decision:?}");
823        }
824    }
825
826    /// A pin outranks everything the engine is offering. The engine being ready
827    /// and idle is not a reason to move a turn the user placed.
828    #[test]
829    fn a_ready_engine_never_takes_a_thread_away_from_its_pin() {
830        let inputs = RouteInputs::native_only()
831            .with_engine(ready_engine())
832            .with_external_acp("codex-acp")
833            .pinned(ExecutorPin::new(ExecutorClass::NativeLoop));
834        assert_eq!(route(&inputs).chosen, ExecutorClass::NativeLoop);
835        assert!(route(&inputs).lane_ref.is_none());
836    }
837
838    /// Owner gate 8, as a routing law: nothing but a pin reaches an engine
839    /// lane, so no model-initiated path can start Full Auto authority through
840    /// the router.
841    #[test]
842    fn an_unpinned_thread_never_reaches_an_engine_lane() {
843        let inputs = RouteInputs::native_only()
844            .with_engine(ready_engine())
845            .with_external_acp("codex-acp")
846            // Everything an engine-lane route needs is present, so the native
847            // answer below is a decision rather than an absence.
848            .with_engine_lane("codex-local");
849        assert_eq!(
850            route(&inputs.clone().pinned(ExecutorPin::on_lane("claude-local"))).chosen,
851            ExecutorClass::EngineLane,
852            "an engine lane is reachable from these inputs, so the unpinned \
853             case below is not passing by default"
854        );
855        let decision = route(&inputs);
856        assert_eq!(decision.chosen, ExecutorClass::NativeLoop);
857        assert_eq!(decision.reason, RouteReason::UnpinnedDefault);
858        assert!(decision.lane_ref.is_none());
859        assert!(decision.pin.is_none());
860        assert!(decision.is_coherent());
861    }
862
863    // ---------------------------------------------------------------------
864    // Exit property 2: engine-down fails closed to native, visibly
865    // ---------------------------------------------------------------------
866
867    /// Every way the engine can be unavailable lands on the native loop, keeps
868    /// the pin it could not honour, and says so with a fallback reason.
869    ///
870    /// Falsified by returning `EngineLane` from the `Unreachable` arm: this
871    /// test fails on `chosen`, and `a_fallback_is_never_silent` fails too.
872    #[test]
873    fn an_unavailable_engine_falls_closed_to_the_native_loop() {
874        let pin = ExecutorPin::on_lane("claude-local");
875
876        let mut cases: Vec<(EngineReadiness, RouteReason)> = EngineUnreachable::all()
877            .iter()
878            .map(|cause| {
879                (
880                    EngineReadiness::Unreachable(*cause),
881                    RouteReason::EngineUnreachable,
882                )
883            })
884            .collect();
885        cases.push((
886            EngineReadiness::Answered {
887                active_run_count: 8,
888                active_run_limit: 8,
889                lanes: vec![EngineLane::new("claude-local", LaneState::Available)],
890            },
891            RouteReason::EngineAtCapacity,
892        ));
893        cases.push((
894            EngineReadiness::Answered {
895                active_run_count: 0,
896                active_run_limit: 8,
897                lanes: vec![EngineLane::new("claude-local", LaneState::Busy)],
898            },
899            RouteReason::PinnedLaneUnavailable,
900        ));
901        cases.push((
902            EngineReadiness::Answered {
903                active_run_count: 0,
904                active_run_limit: 8,
905                lanes: vec![],
906            },
907            RouteReason::PinnedLaneUnavailable,
908        ));
909
910        for (engine, expected) in cases {
911            let decision = route(&RouteInputs::native_only().with_engine(engine.clone()).pinned(pin.clone()));
912            assert_eq!(
913                decision.chosen,
914                ExecutorClass::NativeLoop,
915                "engine {engine:?} did not fail closed"
916            );
917            assert_eq!(decision.reason, expected, "engine {engine:?}");
918            assert!(decision.reason.is_fallback());
919            assert_eq!(
920                decision.pin.as_ref(),
921                Some(&pin),
922                "a fallback that forgets the pin it could not honour is \
923                 indistinguishable from an unpinned thread"
924            );
925            assert!(decision.is_coherent(), "{decision:?}");
926        }
927    }
928
929    /// An engine lane pinned without naming one, with nothing ready, is a
930    /// different fallback from a named lane that is busy — and the record says
931    /// which.
932    #[test]
933    fn an_engine_with_no_ready_lane_is_its_own_reason() {
934        let decision = route(
935            &RouteInputs::native_only()
936                .with_engine(EngineReadiness::Answered {
937                    active_run_count: 0,
938                    active_run_limit: 8,
939                    lanes: vec![EngineLane::new("codex-local", LaneState::Busy)],
940                })
941                .pinned(ExecutorPin::new(ExecutorClass::EngineLane)),
942        );
943        assert_eq!(decision.reason, RouteReason::EngineHasNoReadyLane);
944        assert_eq!(decision.chosen, ExecutorClass::NativeLoop);
945    }
946
947    /// A pinned external agent that is not connected fails closed the same way
948    /// the engine does, rather than waiting or erroring.
949    #[test]
950    fn a_disconnected_external_agent_falls_closed_to_the_native_loop() {
951        let decision = route(
952            &RouteInputs::native_only().pinned(ExecutorPin::new(ExecutorClass::ExternalAcp)),
953        );
954        assert_eq!(decision.chosen, ExecutorClass::NativeLoop);
955        assert_eq!(decision.reason, RouteReason::ExternalAcpUnavailable);
956        assert!(decision.is_coherent());
957    }
958
959    /// A ready engine with nothing to dispatch onto is its own fallback, and
960    /// the operator hears about the engine first.
961    ///
962    /// This is the gap between "the engine has a lane" and "Omega can hand this
963    /// turn to it". In this build they come apart, and the honest answer is a
964    /// named reason rather than a panic, a hang, or a substitution nobody sees.
965    #[test]
966    fn a_ready_lane_with_no_executor_behind_it_falls_closed() {
967        let decision = route(
968            &RouteInputs::native_only()
969                .with_engine(ready_engine())
970                .pinned(ExecutorPin::on_lane("claude-local")),
971        );
972        assert_eq!(decision.chosen, ExecutorClass::NativeLoop);
973        assert_eq!(decision.reason, RouteReason::EngineLaneNotConnected);
974        assert!(decision.is_coherent());
975
976        // The engine being down outranks it: an operator needs the runtime
977        // fact before the wiring fact.
978        let engine_down = route(
979            &RouteInputs::native_only()
980                .with_engine(EngineReadiness::Unreachable(EngineUnreachable::Timeout))
981                .pinned(ExecutorPin::on_lane("claude-local")),
982        );
983        assert_eq!(engine_down.reason, RouteReason::EngineUnreachable);
984    }
985
986    /// A fallback is always disclosable, because every fallback reason renders
987    /// a phrase that says it fell back.
988    ///
989    /// A fallback the user cannot see is the same defect class as a handoff
990    /// with no system note, which shipped in rc11.
991    #[test]
992    fn a_fallback_is_never_silent() {
993        for reason in RouteReason::all() {
994            if !reason.is_fallback() {
995                continue;
996            }
997            assert!(
998                reason.phrase().contains("fell back to the native loop"),
999                "{} renders as {:?}, which does not tell the reader a pin was \
1000                 not honoured",
1001                reason.token(),
1002                reason.phrase()
1003            );
1004        }
1005    }
1006
1007    /// An unrecognised lane state is not a ready lane.
1008    #[test]
1009    fn an_unrecognised_lane_state_is_not_available() {
1010        assert_eq!(LaneState::parse("available"), LaneState::Available);
1011        assert_eq!(LaneState::parse("busy"), LaneState::Busy);
1012        assert_eq!(LaneState::parse("draining"), LaneState::Unavailable);
1013        assert!(!LaneState::parse("draining").can_serve());
1014
1015        let decision = route(
1016            &RouteInputs::native_only()
1017                .with_engine(EngineReadiness::Answered {
1018                    active_run_count: 0,
1019                    active_run_limit: 8,
1020                    lanes: vec![EngineLane::new("codex-local", LaneState::parse("draining"))],
1021                })
1022                .pinned(ExecutorPin::on_lane("codex-local")),
1023        );
1024        assert_eq!(decision.chosen, ExecutorClass::NativeLoop);
1025    }
1026
1027    // ---------------------------------------------------------------------
1028    // Determinism
1029    // ---------------------------------------------------------------------
1030
1031    /// The same inputs give the same route, whatever order the engine listed
1032    /// its lanes in.
1033    ///
1034    /// The engine's array order is not a stable input: the same logical
1035    /// capacity can come back ordered differently. A router that took "the
1036    /// first available lane" would route the same thread two ways on two runs
1037    /// and nothing would be wrong with either.
1038    ///
1039    /// Falsified by replacing `.min()` with `.next()` in `select_lane`: this
1040    /// test fails on the reversed permutation.
1041    #[test]
1042    fn routing_is_invariant_under_lane_order() {
1043        let lanes = vec![
1044            EngineLane::new("harness:opencode", LaneState::Available),
1045            EngineLane::new("claude-local", LaneState::Available),
1046            EngineLane::new("acp:cursor-agent", LaneState::Available),
1047            EngineLane::new("codex-local", LaneState::Busy),
1048        ];
1049        let pin = ExecutorPin::new(ExecutorClass::EngineLane);
1050        let connected = RouteInputs::native_only().with_engine_lane("codex-local");
1051
1052        let mut permutations = vec![lanes.clone()];
1053        let mut reversed = lanes.clone();
1054        reversed.reverse();
1055        permutations.push(reversed);
1056        let mut rotated = lanes.clone();
1057        rotated.rotate_left(2);
1058        permutations.push(rotated);
1059        let mut swapped = lanes;
1060        swapped.swap(0, 1);
1061        permutations.push(swapped);
1062
1063        let expected = RouteDecision {
1064            chosen: ExecutorClass::EngineLane,
1065            reason: RouteReason::PinHonored,
1066            pin: Some(pin.clone()),
1067            // Lexicographically smallest available lane. `codex-local` sorts
1068            // after it but is busy; `harness:opencode` sorts after it too.
1069            lane_ref: Some("acp:cursor-agent".to_owned()),
1070        };
1071
1072        for lanes in permutations {
1073            let decision = route(
1074                &connected
1075                    .clone()
1076                    .with_engine(EngineReadiness::Answered {
1077                        active_run_count: 0,
1078                        active_run_limit: 8,
1079                        lanes: lanes.clone(),
1080                    })
1081                    .pinned(pin.clone()),
1082            );
1083            assert_eq!(decision, expected, "lane order {lanes:?} changed the route");
1084        }
1085    }
1086
1087    /// Routing the same inputs repeatedly gives byte-identical records.
1088    ///
1089    /// This is the property a clock in the record would break, which is why
1090    /// there is not one.
1091    #[test]
1092    fn the_same_inputs_give_the_same_record_every_time() {
1093        let inputs = RouteInputs::native_only()
1094            .with_engine(ready_engine())
1095            .with_engine_lane("codex-local")
1096            .pinned(ExecutorPin::on_lane("claude-local"));
1097        let first = route(&inputs).canonical_record();
1098        for _ in 0..64 {
1099            assert_eq!(route(&inputs).canonical_record(), first);
1100        }
1101        assert_eq!(
1102            first,
1103            "chosen=engine_lane;reason=pin_honored;pin=engine_lane@claude-local;lane=claude-local"
1104        );
1105    }
1106
1107    // ---------------------------------------------------------------------
1108    // Exit property 3: the decision is recorded
1109    // ---------------------------------------------------------------------
1110
1111    /// Every decision the router can reach survives being written down and read
1112    /// back.
1113    ///
1114    /// Falsified by dropping the `pin` field from `canonical_record`: this test
1115    /// fails on every fallback, because the pin comes back `None`.
1116    #[test]
1117    fn every_reachable_decision_round_trips_through_its_record() {
1118        let engines = [
1119            EngineReadiness::Unreachable(EngineUnreachable::NotRunning),
1120            EngineReadiness::Unreachable(EngineUnreachable::Timeout),
1121            EngineReadiness::Unreachable(EngineUnreachable::ProtocolError),
1122            EngineReadiness::Answered {
1123                active_run_count: 8,
1124                active_run_limit: 8,
1125                lanes: vec![],
1126            },
1127            ready_engine(),
1128            EngineReadiness::Answered {
1129                active_run_count: 0,
1130                active_run_limit: 8,
1131                lanes: vec![EngineLane::new("codex-local", LaneState::Busy)],
1132            },
1133            EngineReadiness::Answered {
1134                active_run_count: 0,
1135                active_run_limit: 8,
1136                lanes: vec![EngineLane::new("", LaneState::Available)],
1137            },
1138        ];
1139        let pins = [
1140            None,
1141            Some(ExecutorPin::new(ExecutorClass::NativeLoop)),
1142            Some(ExecutorPin::new(ExecutorClass::ExternalAcp)),
1143            Some(ExecutorPin::new(ExecutorClass::EngineLane)),
1144            Some(ExecutorPin::on_lane("claude-local")),
1145            Some(ExecutorPin::on_lane("a;b=c@d")),
1146        ];
1147
1148        let mut seen_reasons = Vec::new();
1149        for engine in &engines {
1150            for pin in &pins {
1151                for (external, engine_lane) in [
1152                    (None, None),
1153                    (Some("codex-acp".to_owned()), None),
1154                    (None, Some("codex-local".to_owned())),
1155                    (Some("codex-acp".to_owned()), Some("codex-local".to_owned())),
1156                ] {
1157                    let inputs = RouteInputs {
1158                        pin: pin.clone(),
1159                        engine: engine.clone(),
1160                        external_acp: external,
1161                        engine_lane,
1162                    };
1163                    let decision = route(&inputs);
1164                    assert!(decision.is_coherent(), "{decision:?} from {inputs:?}");
1165
1166                    let record = decision.canonical_record();
1167                    assert_eq!(
1168                        RouteDecision::parse_canonical_record(&record).as_ref(),
1169                        Some(&decision),
1170                        "record {record:?} did not read back"
1171                    );
1172                    if !seen_reasons.contains(&decision.reason) {
1173                        seen_reasons.push(decision.reason);
1174                    }
1175                }
1176            }
1177        }
1178
1179        // A round-trip suite that never reaches a reason proves nothing about
1180        // it. Every admitted reason has to appear.
1181        for reason in RouteReason::all() {
1182            assert!(
1183                seen_reasons.contains(reason),
1184                "no input in this suite produced {}; the round-trip check is \
1185                 vacuous for it",
1186                reason.token()
1187            );
1188        }
1189    }
1190
1191    /// A lane whose reference contains a character the record uses structurally
1192    /// is still routable, because the record escapes it and reads it back
1193    /// unchanged.
1194    ///
1195    /// The first draft of this packet refused such lanes instead. The
1196    /// round-trip suite falsified that immediately: an *unhonoured pin* can
1197    /// carry the same reference, and a refusal at the routing step does not
1198    /// stop it reaching the record, so a decision the router really made could
1199    /// not be written down. Escaping is total; refusal was not.
1200    #[test]
1201    fn a_lane_with_a_structural_character_still_round_trips() {
1202        for lane_ref in ["bad;lane", "bad=lane", "bad@lane", "100%lane", "-"] {
1203            assert!(lane_ref_is_recordable(lane_ref), "{lane_ref:?}");
1204
1205            let decision = route(
1206                &RouteInputs::native_only()
1207                    .with_engine_lane("codex-local")
1208                    .with_engine(EngineReadiness::Answered {
1209                        active_run_count: 0,
1210                        active_run_limit: 8,
1211                        lanes: vec![EngineLane::new(lane_ref, LaneState::Available)],
1212                    })
1213                    .pinned(ExecutorPin::on_lane(lane_ref)),
1214            );
1215            assert_eq!(decision.chosen, ExecutorClass::EngineLane, "{lane_ref:?}");
1216            assert_eq!(decision.lane_ref.as_deref(), Some(lane_ref));
1217
1218            let record = decision.canonical_record();
1219            assert_eq!(record.split(';').count(), 4, "{record:?} lost its shape");
1220            assert_eq!(
1221                RouteDecision::parse_canonical_record(&record).as_ref(),
1222                Some(&decision),
1223                "{record:?}"
1224            );
1225        }
1226    }
1227
1228    /// A lane with no name cannot be dispatched to and cannot be written down,
1229    /// so the router refuses it instead of routing into a lane it cannot name.
1230    #[test]
1231    fn a_nameless_lane_is_not_routed_to() {
1232        assert!(!lane_ref_is_recordable(""));
1233        let decision = route(
1234            &RouteInputs::native_only()
1235                .with_engine(EngineReadiness::Answered {
1236                    active_run_count: 0,
1237                    active_run_limit: 8,
1238                    lanes: vec![EngineLane::new("", LaneState::Available)],
1239                })
1240                .pinned(ExecutorPin::new(ExecutorClass::EngineLane)),
1241        );
1242        assert_eq!(decision.chosen, ExecutorClass::NativeLoop);
1243        assert_eq!(decision.reason, RouteReason::UnrecordableLane);
1244        assert!(decision.is_coherent());
1245    }
1246
1247    /// The field encoding is a bijection, which is the property that stops an
1248    /// escaped `;` and a literal `%3B` decoding to the same lane.
1249    #[test]
1250    fn the_field_encoding_round_trips_and_rejects_bad_escapes() {
1251        for value in [
1252            "claude-local",
1253            "acp:cursor-agent",
1254            "a;b=c@d",
1255            "%3B",
1256            "%",
1257            "%%",
1258            "",
1259        ] {
1260            assert_eq!(
1261                decode_field(&encode_field(value)).as_deref(),
1262                Some(value),
1263                "{value:?}"
1264            );
1265        }
1266        assert!(encode_field("%3B") != encode_field(";"));
1267        for broken in ["%", "%3", "%zz", "%3b"] {
1268            assert!(decode_field(broken).is_none(), "{broken:?}");
1269        }
1270    }
1271
1272    /// A record that reads cleanly but describes an impossible decision is
1273    /// rejected, not believed.
1274    #[test]
1275    fn an_incoherent_record_does_not_read_back() {
1276        // A fallback that claims an engine lane.
1277        assert!(
1278            RouteDecision::parse_canonical_record(
1279                "chosen=engine_lane;reason=engine_unreachable;pin=engine_lane;lane=claude-local"
1280            )
1281            .is_none()
1282        );
1283        // An honoured pin that names a different class from the one that ran.
1284        assert!(
1285            RouteDecision::parse_canonical_record(
1286                "chosen=native_loop;reason=pin_honored;pin=external_acp;lane="
1287            )
1288            .is_none()
1289        );
1290        // An unpinned decision that carries a pin.
1291        assert!(
1292            RouteDecision::parse_canonical_record(
1293                "chosen=native_loop;reason=unpinned_default;pin=native_loop;lane="
1294            )
1295            .is_none()
1296        );
1297        // A partial record, as a truncated write leaves behind.
1298        assert!(
1299            RouteDecision::parse_canonical_record("chosen=native_loop;reason=unpinned_default")
1300                .is_none()
1301        );
1302        // A duplicated key, where the second value would silently win.
1303        assert!(
1304            RouteDecision::parse_canonical_record(
1305                "chosen=native_loop;chosen=engine_lane;reason=unpinned_default;pin=;lane="
1306            )
1307            .is_none()
1308        );
1309        // A native route carrying a lane.
1310        assert!(
1311            RouteDecision::parse_canonical_record(
1312                "chosen=native_loop;reason=pin_honored;pin=native_loop;lane=claude-local"
1313            )
1314            .is_none()
1315        );
1316        // Junk.
1317        assert!(RouteDecision::parse_canonical_record("chosen=wat;reason=pin_honored").is_none());
1318        assert!(RouteDecision::parse_canonical_record("").is_none());
1319    }
1320
1321    /// The record holds no rendered explanation, only parts.
1322    ///
1323    /// Same law as `ExecutorDisclosure`: a record of parts can be re-rendered,
1324    /// re-signed, or re-read by a later reader. A stored sentence cannot.
1325    #[test]
1326    fn the_decision_record_holds_no_rendered_explanation() {
1327        let decision = route(&RouteInputs::native_only());
1328        let dumped = format!("{decision:?}");
1329        for caption in ["explain", "label", "line", "text", "summary", "message"] {
1330            assert!(
1331                !dumped.contains(caption),
1332                "RouteDecision grew a `{caption}` field: {dumped}"
1333            );
1334        }
1335        assert!(!decision.canonical_record().contains(' '));
1336    }
1337
1338    /// The explanation is derived from the parts, and names both the pin and
1339    /// the fact it was not honoured.
1340    #[test]
1341    fn a_fallback_explains_itself_from_its_parts() {
1342        let decision = route(
1343            &RouteInputs::native_only()
1344                .with_engine(EngineReadiness::Unreachable(EngineUnreachable::NotRunning))
1345                .pinned(ExecutorPin::on_lane("claude-local")),
1346        );
1347        let explanation = decision.explain();
1348        assert!(explanation.contains("native_loop"), "{explanation}");
1349        assert!(explanation.contains("engine unreachable"), "{explanation}");
1350        assert!(
1351            explanation.contains("engine_lane@claude-local"),
1352            "{explanation}"
1353        );
1354        assert!(
1355            explanation.contains("could not be honoured"),
1356            "{explanation}"
1357        );
1358    }
1359
1360    /// Pin tokens round-trip, including the lane a pin names.
1361    #[test]
1362    fn pin_tokens_round_trip() {
1363        for pin in [
1364            ExecutorPin::new(ExecutorClass::NativeLoop),
1365            ExecutorPin::new(ExecutorClass::ExternalAcp),
1366            ExecutorPin::new(ExecutorClass::EngineLane),
1367            ExecutorPin::on_lane("acp:cursor-agent"),
1368        ] {
1369            assert_eq!(ExecutorPin::parse_token(&pin.token()).as_ref(), Some(&pin));
1370        }
1371        assert!(ExecutorPin::parse_token("zed_agent").is_none());
1372    }
1373
1374    /// The reason set is closed, and every reason has a distinct token.
1375    #[test]
1376    fn the_reason_set_is_closed_and_distinct() {
1377        let mut tokens: Vec<&str> = RouteReason::all().iter().map(|r| r.token()).collect();
1378        let count = tokens.len();
1379        tokens.sort_unstable();
1380        tokens.dedup();
1381        assert_eq!(tokens.len(), count, "two reasons share a token");
1382        assert_eq!(
1383            count,
1384            9,
1385            "the reason set changed. Every reason is a thing the router can \
1386             tell a user; adding one is a deliberate edit, and removing one \
1387             means a route it used to explain is now unexplained."
1388        );
1389    }
1390}
1391
Served at tenant.openagents/omega Member data and write actions are omitted.