use crate::cognition::CognitionOverride;
use crate::config::Config;
use crate::initiative::{effective_initiative, Initiative};
use crate::role_profile::Cognition;
use crate::tenacity::{effective_tenacity, Tenacity};
use serde::{Deserialize, Serialize};
use std::sync::Mutex;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct BackendState {
pub configured: Option<String>,
pub operator: Option<String>,
pub persona: Option<String>,
pub effective: Option<String>,
pub model: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RuntimeSettingsSnapshot {
pub cognition: Option<Cognition>,
pub tenacity: Tenacity,
pub initiative: Initiative,
pub crew: bool,
pub persona: Option<String>,
pub backend: BackendState,
}
impl RuntimeSettingsSnapshot {
#[must_use]
pub fn resolve(cfg: &Config, persona: Option<&str>, persona_backend: Option<&str>) -> Self {
use crate::config::{SelectedBackend, SelectionOutcome};
let (effective, model) = match cfg.select_backend() {
SelectionOutcome::Selected(SelectedBackend::Configured(b)) => (
Some(b.name.clone()).filter(|n| !n.is_empty()),
b.effective_model().map(str::to_string),
),
SelectionOutcome::Selected(SelectedBackend::Provider(p)) => (
Some(p.name.clone()).filter(|n| !n.is_empty()),
p.model.clone(),
),
SelectionOutcome::UnknownNamed(_)
| SelectionOutcome::UnroutableNamed(_)
| SelectionOutcome::Unset => (None, None),
};
Self {
cognition: crate::cognition::effective_cognition(),
tenacity: effective_tenacity(),
initiative: effective_initiative(),
crew: std::env::var_os("NEWT_TEAM").is_some(),
persona: persona.map(str::to_string),
backend: BackendState {
configured: cfg.default_backend.clone().filter(|s| !s.is_empty()),
operator: std::env::var("NEWT_PROVIDER")
.ok()
.filter(|s| !s.is_empty()),
persona: persona_backend.map(str::to_string),
effective,
model,
},
}
}
#[must_use]
pub fn summary(&self) -> String {
format!(
"psyche · persona {} · cognition {} · tenacity {} · initiative {} · crew {}",
self.persona.as_deref().unwrap_or("none"),
self.cognition.map_or("off", Cognition::label),
self.tenacity.label(),
self.initiative.label(),
if self.crew { "on" } else { "off" },
)
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct OperatorPreferencePin {
#[serde(skip_serializing_if = "Option::is_none")]
pub backend: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cognition: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tenacity: Option<Tenacity>,
#[serde(skip_serializing_if = "Option::is_none")]
pub initiative: Option<Initiative>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PreferenceApplyPlan {
pub cognition: Option<CognitionOverride>,
pub tenacity: Option<Tenacity>,
pub initiative: Option<Initiative>,
pub backend_axis: BackendAxisAction,
pub notices: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RouteModel {
Set(String),
Clear,
Keep,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BackendAxisAction {
Leave,
Route { provider: String, model: RouteModel },
ModelOnly(String),
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct PreferenceAxes {
pub backend: bool,
pub model: bool,
pub cognition: bool,
pub tenacity: bool,
pub initiative: bool,
}
impl PreferenceAxes {
pub fn merge(&mut self, other: Self) {
self.backend |= other.backend;
self.model |= other.model;
self.cognition |= other.cognition;
self.tenacity |= other.tenacity;
self.initiative |= other.initiative;
}
#[must_use]
pub fn is_empty(&self) -> bool {
*self == Self::default()
}
#[must_use]
pub fn labels(&self) -> Vec<&'static str> {
[
(self.backend, "backend"),
(self.model, "model"),
(self.cognition, "cognition"),
(self.tenacity, "tenacity"),
(self.initiative, "initiative"),
]
.into_iter()
.filter_map(|(on, label)| on.then_some(label))
.collect()
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct PreferenceActions {
pub backend: Option<Option<String>>,
pub model: Option<Option<String>>,
pub cognition: Option<CognitionOverride>,
pub tenacity: Option<Option<Tenacity>>,
pub initiative: Option<Option<Initiative>>,
}
impl PreferenceActions {
#[must_use]
pub fn is_empty(&self) -> bool {
*self == Self::default()
}
pub fn merge(&mut self, other: Self) {
if other.backend.is_some() {
self.backend = other.backend;
}
if other.model.is_some() {
self.model = other.model;
}
if other.cognition.is_some() {
self.cognition = other.cognition;
}
if other.tenacity.is_some() {
self.tenacity = other.tenacity;
}
if other.initiative.is_some() {
self.initiative = other.initiative;
}
}
}
static PREFERENCE_ACTIONS: Mutex<PreferenceActions> = Mutex::new(PreferenceActions {
backend: None,
model: None,
cognition: None,
tenacity: None,
initiative: None,
});
static CLI_PREFERENCE_AXES: Mutex<PreferenceAxes> = Mutex::new(PreferenceAxes {
backend: false,
model: false,
cognition: false,
tenacity: false,
initiative: false,
});
pub fn mark_backend_pick(name: &str) {
if let Ok(mut slot) = PREFERENCE_ACTIONS.lock() {
slot.backend = Some(Some(name.to_string()));
slot.model = Some(None);
}
}
pub fn mark_model_pick(name: &str) {
if let Ok(mut slot) = PREFERENCE_ACTIONS.lock() {
slot.model = Some(Some(name.to_string()));
}
}
pub fn mark_cognition_choice(o: CognitionOverride) {
if let Ok(mut slot) = PREFERENCE_ACTIONS.lock() {
slot.cognition = Some(o);
}
}
pub fn mark_tenacity_choice(t: Option<Tenacity>) {
if let Ok(mut slot) = PREFERENCE_ACTIONS.lock() {
slot.tenacity = Some(t);
}
}
pub fn mark_initiative_choice(i: Option<Initiative>) {
if let Ok(mut slot) = PREFERENCE_ACTIONS.lock() {
slot.initiative = Some(i);
}
}
#[must_use]
pub fn drain_preference_actions() -> PreferenceActions {
PREFERENCE_ACTIONS
.lock()
.map(|mut slot| std::mem::take(&mut *slot))
.unwrap_or_default()
}
pub fn record_cli_preference_axes(axes: PreferenceAxes) {
if let Ok(mut slot) = CLI_PREFERENCE_AXES.lock() {
slot.merge(axes);
}
}
#[must_use]
pub fn cli_preference_axes() -> PreferenceAxes {
CLI_PREFERENCE_AXES.lock().map(|s| *s).unwrap_or_default()
}
#[doc(hidden)]
pub struct PreferenceRuntimeSnapshot {
actions: PreferenceActions,
cli_axes: PreferenceAxes,
}
#[doc(hidden)]
#[must_use]
pub fn snapshot_runtime_state() -> PreferenceRuntimeSnapshot {
PreferenceRuntimeSnapshot {
actions: PREFERENCE_ACTIONS
.lock()
.map(|s| s.clone())
.unwrap_or_default(),
cli_axes: cli_preference_axes(),
}
}
#[doc(hidden)]
pub fn restore_runtime_state(snapshot: PreferenceRuntimeSnapshot) {
if let Ok(mut slot) = PREFERENCE_ACTIONS.lock() {
*slot = snapshot.actions;
}
if let Ok(mut slot) = CLI_PREFERENCE_AXES.lock() {
*slot = snapshot.cli_axes;
}
}
impl OperatorPreferencePin {
#[must_use]
pub fn merged(&self, actions: &PreferenceActions) -> Self {
let mut next = self.clone();
if let Some(backend) = &actions.backend {
next.backend = backend.clone();
}
if let Some(model) = &actions.model {
next.model = model.clone();
}
if let Some(cognition) = actions.cognition {
next.cognition = Self::cognition_field(cognition);
}
if let Some(tenacity) = actions.tenacity {
next.tenacity = tenacity;
}
if let Some(initiative) = actions.initiative {
next.initiative = initiative;
}
next
}
#[must_use]
pub fn cognition_field(o: CognitionOverride) -> Option<String> {
match o {
CognitionOverride::Unset => None,
CognitionOverride::Off => Some("off".to_string()),
CognitionOverride::Set(level) => Some(level.label().to_string()),
}
}
fn parse_cognition(s: &str) -> Result<CognitionOverride, String> {
if s.trim().eq_ignore_ascii_case("off") {
return Ok(CognitionOverride::Off);
}
s.parse::<Cognition>().map(CognitionOverride::Set)
}
#[must_use]
pub fn is_empty(&self) -> bool {
*self == Self::default()
}
#[must_use]
pub fn apply_plan(&self, configured: &[&str], owned: PreferenceAxes) -> PreferenceApplyPlan {
let mut notices = Vec::new();
let cognition = self
.cognition
.as_deref()
.filter(|_| !owned.cognition)
.and_then(|s| {
Self::parse_cognition(s)
.map_err(|e| notices.push(format!("pinned cognition ignored: {e}")))
.ok()
});
let backend = self.backend.as_ref().filter(|_| !owned.backend);
let model = self.model.as_ref().filter(|_| !owned.model);
let backend_axis = match (backend, model) {
(None, None) => BackendAxisAction::Leave,
(Some(name), model) => {
if configured.contains(&name.as_str()) {
BackendAxisAction::Route {
provider: name.clone(),
model: match (model, owned.model) {
(Some(m), _) => RouteModel::Set(m.clone()),
(None, true) => RouteModel::Keep,
(None, false) => RouteModel::Clear,
},
}
} else {
notices.push(format!(
"pinned backend '{name}' is no longer configured — \
using this run's baseline backend"
));
BackendAxisAction::Leave
}
}
(None, Some(model)) => BackendAxisAction::ModelOnly(model.clone()),
};
PreferenceApplyPlan {
cognition,
tenacity: self.tenacity.filter(|_| !owned.tenacity),
initiative: self.initiative.filter(|_| !owned.initiative),
backend_axis,
notices,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::{BackendConfig, Config};
use crate::test_guard::GlobalSettingsGuard;
fn cfg_with(default: Option<&str>, backends: &[(&str, &str)]) -> Config {
Config {
default_backend: default.map(str::to_string),
backends: backends
.iter()
.map(|(n, e)| BackendConfig {
name: (*n).to_string(),
endpoint: (*e).to_string(),
..Default::default()
})
.collect(),
..Default::default()
}
}
#[test]
fn resolve_never_displays_a_fallback_for_a_refused_explicit_selection() {
let _g = GlobalSettingsGuard::acquire();
unsafe { std::env::set_var("NEWT_PROVIDER", "ghost") };
let cfg = cfg_with(None, &[("real", "http://r:1")]);
let snap = RuntimeSettingsSnapshot::resolve(&cfg, None, None);
assert_eq!(snap.backend.operator.as_deref(), Some("ghost"));
assert_eq!(
snap.backend.effective, None,
"an unknown explicit selection resolves to nothing, not to `real`"
);
unsafe { std::env::set_var("NEWT_PROVIDER", "hollow") };
let cfg = cfg_with(None, &[("real", "http://r:1"), ("hollow", "")]);
let snap = RuntimeSettingsSnapshot::resolve(&cfg, None, None);
assert_eq!(snap.backend.effective, None);
unsafe { std::env::set_var("NEWT_PROVIDER", "acme") };
let mut cfg = cfg_with(None, &[("real", "http://r:1")]);
cfg.providers = vec![crate::config::ProviderConfig {
name: "acme".into(),
command: "newt-provider-openai".into(),
model: Some("gpt-test".into()),
env_pass: vec![],
tiers: vec![],
}];
let snap = RuntimeSettingsSnapshot::resolve(&cfg, None, None);
assert_eq!(snap.backend.effective.as_deref(), Some("acme"));
assert_eq!(snap.backend.model.as_deref(), Some("gpt-test"));
}
#[test]
fn resolve_reads_effective_dials_and_the_selected_backend() {
let _g = GlobalSettingsGuard::acquire();
crate::tenacity::set_cli_tenacity(Tenacity::Relentless);
crate::initiative::clear_cli_initiative();
crate::initiative::set_persona_initiative(Some(Initiative::Decisive));
crate::cognition::set_cli_cognition(crate::cognition::CognitionOverride::Set(
Cognition::Meticulous,
));
unsafe {
std::env::remove_var("NEWT_PROVIDER");
std::env::remove_var("NEWT_TEAM");
}
let mut cfg = cfg_with(
Some("sol"),
&[("other", "http://o:1"), ("sol", "http://s:1")],
);
cfg.backends
.iter_mut()
.find(|b| b.name == "sol")
.expect("sol backend present")
.model = Some("sol-large".to_string());
let snap = RuntimeSettingsSnapshot::resolve(&cfg, Some("bob"), Some("sol"));
assert_eq!(snap.cognition, Some(Cognition::Meticulous));
assert_eq!(snap.tenacity, Tenacity::Relentless);
assert_eq!(snap.initiative, Initiative::Decisive);
assert!(!snap.crew);
assert_eq!(snap.persona.as_deref(), Some("bob"));
assert_eq!(snap.backend.effective.as_deref(), Some("sol"));
assert_eq!(snap.backend.configured.as_deref(), Some("sol"));
assert_eq!(snap.backend.model.as_deref(), Some("sol-large"));
assert_eq!(snap.backend.persona.as_deref(), Some("sol"));
assert!(snap.summary().contains("persona bob"));
assert!(snap.summary().contains("tenacity relentless"));
assert!(snap.summary().contains("initiative decisive"));
}
#[test]
fn operator_pin_and_crew_gate_are_reflected() {
let _g = GlobalSettingsGuard::acquire();
unsafe {
std::env::set_var("NEWT_PROVIDER", "other");
std::env::set_var("NEWT_TEAM", "1");
}
let cfg = cfg_with(
Some("sol"),
&[("other", "http://o:1"), ("sol", "http://s:1")],
);
let snap = RuntimeSettingsSnapshot::resolve(&cfg, None, None);
assert!(snap.crew, "NEWT_TEAM gates crew on");
assert_eq!(snap.backend.operator.as_deref(), Some("other"));
assert_eq!(snap.backend.effective.as_deref(), Some("other"));
assert!(snap.summary().contains("crew on"));
}
#[test]
fn a_session_with_no_operator_action_writes_nothing() {
let _g = GlobalSettingsGuard::acquire();
let _ = drain_preference_actions();
crate::cognition::set_cli_cognition(CognitionOverride::Off);
crate::tenacity::set_cli_tenacity(Tenacity::Relentless);
crate::cognition::set_persona_cognition(Some(Cognition::Meticulous));
crate::initiative::set_persona_initiative(Some(Initiative::Decisive));
let actions = drain_preference_actions();
assert!(actions.is_empty(), "no action marked: {actions:?}");
let stored = OperatorPreferencePin::default();
assert_eq!(stored.merged(&actions), stored);
assert_eq!(
serde_json::to_string(&stored.merged(&actions)).unwrap(),
"{}"
);
}
#[test]
fn each_action_marks_exactly_its_own_axes() {
let _g = GlobalSettingsGuard::acquire();
let _ = drain_preference_actions();
mark_backend_pick("sol");
let a = drain_preference_actions();
assert_eq!(a.backend, Some(Some("sol".to_string())));
assert_eq!(a.model, Some(None), "/backends clears the model override");
assert_eq!(a.cognition, None);
assert_eq!(a.tenacity, None);
mark_model_pick("m1");
let a = drain_preference_actions();
assert_eq!(a.backend, None, "a model pick must not touch the backend");
assert_eq!(a.model, Some(Some("m1".to_string())));
mark_cognition_choice(CognitionOverride::Off);
mark_tenacity_choice(Some(Tenacity::Relentless));
mark_initiative_choice(Some(Initiative::Eager));
let a = drain_preference_actions();
assert_eq!(a.cognition, Some(CognitionOverride::Off));
assert_eq!(a.tenacity, Some(Some(Tenacity::Relentless)));
assert_eq!(a.initiative, Some(Some(Initiative::Eager)));
assert_eq!((a.backend, a.model), (None, None));
mark_cognition_choice(CognitionOverride::Unset);
mark_tenacity_choice(None);
mark_initiative_choice(None);
let a = drain_preference_actions();
assert_eq!(
a.cognition,
Some(CognitionOverride::Unset),
"`auto` is an action too — it UNPINS the axis"
);
assert_eq!(a.tenacity, Some(None));
assert_eq!(a.initiative, Some(None));
assert!(drain_preference_actions().is_empty());
}
#[test]
fn merge_updates_only_the_acted_axes() {
let stored = OperatorPreferencePin {
backend: Some("retired-dgx".into()),
model: Some("nemotron-340b".into()),
cognition: Some("off".into()),
tenacity: Some(Tenacity::Relentless),
initiative: Some(Initiative::Patient),
};
let dial_only = PreferenceActions {
cognition: Some(CognitionOverride::Set(Cognition::Zen)),
..Default::default()
};
let merged = stored.merged(&dial_only);
assert_eq!(merged.backend.as_deref(), Some("retired-dgx"), "kept");
assert_eq!(merged.model.as_deref(), Some("nemotron-340b"), "kept");
assert_eq!(merged.tenacity, Some(Tenacity::Relentless), "kept");
assert_eq!(merged.initiative, Some(Initiative::Patient), "kept");
assert_eq!(merged.cognition.as_deref(), Some("zen"));
let cleared = OperatorPreferencePin::default().merged(&PreferenceActions {
tenacity: Some(None),
initiative: Some(None),
cognition: Some(CognitionOverride::Unset),
model: Some(None),
backend: None,
});
assert!(
cleared.is_empty(),
"clearing every axis unpins: {cleared:?}"
);
let repointed = stored.merged(&PreferenceActions {
backend: Some(Some("sol".into())),
model: Some(None),
..Default::default()
});
assert_eq!(repointed.backend.as_deref(), Some("sol"));
assert_eq!(repointed.model, None);
let json = serde_json::to_string(&merged).unwrap();
assert_eq!(
serde_json::from_str::<OperatorPreferencePin>(&json).unwrap(),
merged
);
}
#[test]
fn pending_actions_fold_latest_wins_per_axis() {
let mut pending = PreferenceActions {
backend: Some(Some("sol".into())),
model: Some(None),
..Default::default()
};
pending.merge(PreferenceActions {
model: Some(Some("m2".into())),
initiative: Some(Some(Initiative::Patient)),
..Default::default()
});
assert_eq!(pending.backend, Some(Some("sol".to_string())), "kept");
assert_eq!(pending.model, Some(Some("m2".to_string())), "latest wins");
assert_eq!(pending.initiative, Some(Some(Initiative::Patient)));
assert_eq!(
OperatorPreferencePin::default().merged(&pending),
OperatorPreferencePin {
backend: Some("sol".into()),
model: Some("m2".into()),
initiative: Some(Initiative::Patient),
tenacity: None,
cognition: None,
}
);
}
#[test]
fn the_empty_column_backfill_parses_to_the_empty_pin() {
let pin: OperatorPreferencePin = serde_json::from_str("{}").unwrap();
assert!(pin.is_empty());
let plan = pin.apply_plan(&["sol"], PreferenceAxes::default());
assert_eq!(plan.backend_axis, BackendAxisAction::Leave);
assert!(plan.notices.is_empty());
}
#[test]
fn cognition_field_round_trips_every_override_state() {
assert_eq!(
OperatorPreferencePin::cognition_field(CognitionOverride::Unset),
None
);
assert_eq!(
OperatorPreferencePin::cognition_field(CognitionOverride::Off).as_deref(),
Some("off")
);
assert_eq!(
OperatorPreferencePin::cognition_field(CognitionOverride::Set(Cognition::Zen))
.as_deref(),
Some("zen")
);
let off = OperatorPreferencePin {
cognition: Some("off".into()),
..Default::default()
};
assert_eq!(
off.apply_plan(&[], PreferenceAxes::default()).cognition,
Some(CognitionOverride::Off),
"'off' must restore the explicit Off override, not Unset"
);
let set = OperatorPreferencePin {
cognition: Some("meticulous".into()),
..Default::default()
};
assert_eq!(
set.apply_plan(&[], PreferenceAxes::default()).cognition,
Some(CognitionOverride::Set(Cognition::Meticulous))
);
}
#[test]
fn unparseable_pinned_cognition_fails_open_with_a_notice() {
let pin = OperatorPreferencePin {
cognition: Some("transcending".into()),
initiative: Some(Initiative::Decisive),
..Default::default()
};
let plan = pin.apply_plan(&[], PreferenceAxes::default());
assert_eq!(plan.cognition, None, "bad string must leave the dial alone");
assert_eq!(
plan.initiative,
Some(Initiative::Decisive),
"other dials still apply"
);
assert_eq!(plan.notices.len(), 1);
assert!(
plan.notices[0].contains("transcending"),
"{:?}",
plan.notices
);
}
#[test]
fn pinned_backend_routes_when_still_configured() {
let pin = OperatorPreferencePin {
backend: Some("sol".into()),
model: Some("gpt-5.6-sol".into()),
..Default::default()
};
let plan = pin.apply_plan(&["other", "sol"], PreferenceAxes::default());
assert_eq!(
plan.backend_axis,
BackendAxisAction::Route {
provider: "sol".into(),
model: RouteModel::Set("gpt-5.6-sol".into()),
}
);
assert!(plan.notices.is_empty());
let bare = OperatorPreferencePin {
backend: Some("sol".into()),
..Default::default()
};
assert_eq!(
bare.apply_plan(&["sol"], PreferenceAxes::default())
.backend_axis,
BackendAxisAction::Route {
provider: "sol".into(),
model: RouteModel::Clear,
}
);
}
#[test]
fn an_owned_model_axis_survives_a_pinned_backend_route() {
let pin = OperatorPreferencePin {
backend: Some("sol".into()),
model: Some("pinned-model".into()),
..Default::default()
};
let owned = PreferenceAxes {
model: true,
..Default::default()
};
assert_eq!(
pin.apply_plan(&["sol"], owned).backend_axis,
BackendAxisAction::Route {
provider: "sol".into(),
model: RouteModel::Keep,
},
"an owned model axis is KEPT, never cleared, by a pinned route"
);
let bare = OperatorPreferencePin {
backend: Some("sol".into()),
..Default::default()
};
assert_eq!(
bare.apply_plan(&["sol"], owned).backend_axis,
BackendAxisAction::Route {
provider: "sol".into(),
model: RouteModel::Keep,
},
"owning the axis keeps the operator's model even when the pin names none"
);
}
#[test]
fn missing_pinned_backend_fails_open_and_drops_its_model() {
let pin = OperatorPreferencePin {
backend: Some("retired-dgx".into()),
model: Some("nemotron-340b".into()),
tenacity: Some(Tenacity::Relentless),
..Default::default()
};
let plan = pin.apply_plan(&["sol", "other"], PreferenceAxes::default());
assert_eq!(plan.backend_axis, BackendAxisAction::Leave);
assert_eq!(plan.notices.len(), 1);
assert!(
plan.notices[0].contains("retired-dgx"),
"{:?}",
plan.notices
);
assert!(plan.notices[0].contains("no longer configured"));
assert_eq!(plan.tenacity, Some(Tenacity::Relentless));
}
#[test]
fn model_only_pin_applies_over_the_current_provider() {
let pin = OperatorPreferencePin {
model: Some("qwen3-coder_30b".into()),
..Default::default()
};
assert_eq!(
pin.apply_plan(&[], PreferenceAxes::default()).backend_axis,
BackendAxisAction::ModelOnly("qwen3-coder_30b".into())
);
}
#[test]
fn this_invocations_explicit_flags_beat_the_pin_per_axis() {
let pin = OperatorPreferencePin {
backend: Some("sol".into()),
model: Some("m1".into()),
cognition: Some("off".into()),
tenacity: Some(Tenacity::Relentless),
initiative: Some(Initiative::Eager),
};
let plan = pin.apply_plan(
&["sol"],
PreferenceAxes {
cognition: true,
backend: true,
initiative: true,
..Default::default()
},
);
assert_eq!(plan.cognition, None, "--cognition wins for the invocation");
assert_eq!(
plan.backend_axis,
BackendAxisAction::ModelOnly("m1".into()),
"the flag owns the provider; the unowned model axis still applies"
);
assert_eq!(plan.tenacity, Some(Tenacity::Relentless), "unowned applies");
assert_eq!(
plan.initiative, None,
"--initiative wins for the invocation"
);
assert!(plan.notices.is_empty());
let stale = OperatorPreferencePin {
backend: Some("retired-dgx".into()),
..Default::default()
};
let plan = stale.apply_plan(
&["sol"],
PreferenceAxes {
backend: true,
..Default::default()
},
);
assert_eq!(plan.backend_axis, BackendAxisAction::Leave);
assert!(plan.notices.is_empty(), "{:?}", plan.notices);
}
#[test]
fn recorded_cli_axes_union_per_axis() {
let _g = GlobalSettingsGuard::acquire();
record_cli_preference_axes(PreferenceAxes {
cognition: true,
..Default::default()
});
record_cli_preference_axes(PreferenceAxes {
backend: true,
..Default::default()
});
let axes = cli_preference_axes();
assert!(axes.cognition && axes.backend);
assert!(
!axes.model && !axes.tenacity && !axes.initiative,
"recording is per-axis"
);
assert_eq!(axes.labels(), vec!["backend", "cognition"]);
assert!(!axes.is_empty());
assert!(PreferenceAxes::default().is_empty());
}
#[test]
fn pin_serializes_exactly_the_five_preference_axes() {
let full = OperatorPreferencePin {
backend: Some("sol".into()),
model: Some("m1".into()),
cognition: Some("off".into()),
tenacity: Some(Tenacity::Relentless),
initiative: Some(Initiative::Eager),
};
let value: serde_json::Value = serde_json::to_value(&full).unwrap();
let mut keys: Vec<&str> = value
.as_object()
.expect("a pin serializes as an object")
.keys()
.map(String::as_str)
.collect();
keys.sort_unstable();
assert_eq!(
keys,
vec!["backend", "cognition", "initiative", "model", "tenacity"],
"a pin may carry ONLY these five preference axes; it is operator preference, never authority — audit any new field against the type doc first"
);
}
#[test]
fn an_authority_shaped_pin_field_is_refused_not_absorbed() {
for hostile in [
r#"{"backend":"sol","ocap":["fs:/"]}"#,
r#"{"grants":{"net":true}}"#,
r#"{"sandbox":"off","cognition":"off"}"#,
r#"{"enforcement_floor":0}"#,
] {
let decoded = serde_json::from_str::<OperatorPreferencePin>(hostile);
assert!(
decoded.is_err(),
"an authority-shaped key must be refused, got {decoded:?} from {hostile}"
);
}
for malformed in [
r#"{"backend":42}"#,
r#"{"tenacity":"nonsense"}"#,
r#"{"initiative":"nonsense"}"#,
r#"{"tenacity":"insistent"}"#,
"not json",
] {
assert!(
serde_json::from_str::<OperatorPreferencePin>(malformed).is_err(),
"a malformed pin must be refused: {malformed}"
);
}
}
#[test]
fn a_pin_plan_never_yields_anything_but_the_preference_axes() {
let pin = OperatorPreferencePin {
backend: Some("sol".into()),
model: Some("m1".into()),
cognition: Some("off".into()),
tenacity: Some(Tenacity::Relentless),
initiative: Some(Initiative::Eager),
};
let PreferenceApplyPlan {
cognition,
tenacity,
initiative,
backend_axis,
notices,
} = pin.apply_plan(&["sol"], PreferenceAxes::default());
assert_eq!(cognition, Some(CognitionOverride::Off));
assert_eq!(tenacity, Some(Tenacity::Relentless));
assert_eq!(initiative, Some(Initiative::Eager));
assert!(matches!(backend_axis, BackendAxisAction::Route { .. }));
assert!(notices.is_empty());
}
}