storage(pi): isolate typed provider writes and canonical restore

Replace the generic Provider save/upsert surface with strict typed
create/update/rename/endpoint operations. Keep aggregate hydration read-only,
preserve nullable endpoint timestamps end to end, and make service create own
the initial endpoint set atomically.

Restore SQL and binary backups only through UntrustedScratch, migrate and copy
fixed data columns into a fresh CanonicalStage, validate the canonical result,
then publish through the Backup API. The imported schema is never eligible to
become the live schema.

Old save_provider callsite classification
==========================================

Inventory authority: abandoned 5a385fc8 tree. The old definition at
src-tauri/src/database/dao/providers.rs:180 is deleted and is not a callsite.

Production callsites:

- src-tauri/src/commands/provider.rs:253 [create] Claude Desktop import creates
  one absent aggregate; it now strict-inserts the row and initial endpoints in
  one transaction.
- src-tauri/src/database/dao/providers.rs:638 [create] official seed first
  proves absence, then strict-creates; a racing insert is a conflict.
- src-tauri/src/database/dao/providers.rs:704 [create] on-demand seed first
  proves absence, then strict-creates; it cannot overwrite an existing row.
- src-tauri/src/services/omo.rs:291 [create] OMO import constructs a new
  aggregate and strict-creates it; OMO is not eligible for rename.
- src-tauri/src/services/provider/endpoints.rs:85 [update] endpoint last-used
  is not a Provider-row save; it now calls the exact touch endpoint operation.
- src-tauri/src/services/provider/live.rs:1567 [create/update] default live
  import is reconciliation: read first, then strict create or strict update.
- src-tauri/src/services/provider/live.rs:1743 [update] an existing OpenCode
  live provider follows the strict row-update branch.
- src-tauri/src/services/provider/live.rs:1770 [create] a new OpenCode live
  provider follows the strict aggregate-create branch.
- src-tauri/src/services/provider/live.rs:1825 [update] an existing OpenClaw
  live provider follows the strict row-update branch.
- src-tauri/src/services/provider/live.rs:1858 [create] a new OpenClaw live
  provider follows the strict aggregate-create branch.
- src-tauri/src/services/provider/live.rs:1900 [update] an existing Hermes live
  provider follows the strict row-update branch.
- src-tauri/src/services/provider/live.rs:1926 [create] a new Hermes live
  provider follows the strict aggregate-create branch.
- src-tauri/src/services/provider/mod.rs:2568 [create] ProviderService::add owns
  strict aggregate creation and all initial endpoints.
- src-tauri/src/services/provider/mod.rs:2680 [rename] an additive DB-only key
  change now uses the dedicated transactional rename after eligibility checks.
- src-tauri/src/services/provider/mod.rs:2711 [update] OMO edit updates exactly
  the existing main row after its live-file coordination.
- src-tauri/src/services/provider/mod.rs:2740 [update] additive-provider edit
  updates exactly the existing main row after resolving live ownership.
- src-tauri/src/services/provider/mod.rs:2750 [update] switch-mode edit updates
  exactly the existing main row and never inserts.
- src-tauri/src/services/provider/mod.rs:2948 [update] remove-from-live changes
  only the existing provider's live-managed marker.
- src-tauri/src/services/provider/mod.rs:3120 [update] switch backfill updates
  only the existing current provider row.
- src-tauri/src/services/provider/mod.rs:3174 [update] successful additive
  switch changes only the existing live-managed marker.
- src-tauri/src/services/provider/mod.rs:3315 [update] common-config migration
  updates only each already-read existing row.
- src-tauri/src/services/provider/mod.rs:3895 [update] Gemini credential scrub
  updates only each already-read existing row.
- src-tauri/src/services/provider/mod.rs:4082 [update] sort ordering is routed
  to the dedicated sort-index state operation, not row replacement.
- src-tauri/src/services/provider/mod.rs:4636 [create/update] universal-to-
  Claude reconciliation reads the target and selects strict create or update.
- src-tauri/src/services/provider/mod.rs:4651 [create/update] universal-to-
  Codex reconciliation reads the target and selects strict create or update.
- src-tauri/src/services/provider/mod.rs:4665 [create/update] universal-to-
  Gemini reconciliation reads the target and selects strict create or update.

Required indirect ownership paths:

- src-tauri/src/deeplink/provider.rs [create] the old indirect flow called
  ProviderService::add and then appended endpoints one by one. It now supplies
  every non-primary endpoint to one strict aggregate create, so hydration is
  complete atomically and a duplicate is zero-side-effect.
- [restore] no old generic-save callsite is reclassified as restore. Exact
  aggregate replacement exists only as the sealed
  restore_provider_aggregate_on_tx compensation primitive.

Test-only callsites:

Every item below is classified [test]. Each is fixture setup, not a production
write authority, and is migrated to a real ProviderService entry where the
behavior is under test or to the cfg(test)-only typed fixture reconciler where
the test merely needs pre-existing rows.

- src-tauri/src/codex_history_migration.rs:1442 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:1452 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2174 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2176 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2199 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2219 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2247 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2267 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2288 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2320 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2393 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2449 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2498 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2555 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2604 [test] migration fixture setup.
- src-tauri/src/codex_history_migration.rs:2625 [test] migration fixture setup.
- src-tauri/src/database/dao/providers.rs:754 [test] DAO fixture setup.
- src-tauri/src/proxy/provider_router.rs:351 [test] router fixture setup.
- src-tauri/src/proxy/provider_router.rs:352 [test] router fixture setup.
- src-tauri/src/proxy/provider_router.rs:377 [test] router fixture setup.
- src-tauri/src/proxy/provider_router.rs:378 [test] router fixture setup.
- src-tauri/src/proxy/provider_router.rs:410 [test] router fixture setup.
- src-tauri/src/proxy/provider_router.rs:411 [test] router fixture setup.
- src-tauri/src/proxy/provider_router.rs:447 [test] router fixture setup.
- src-tauri/src/proxy/provider_router.rs:448 [test] router fixture setup.
- src-tauri/src/proxy/provider_router.rs:488 [test] router fixture setup.
- src-tauri/src/services/provider/mod.rs:485 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:586 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:813 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:825 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:1472 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:1607 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:1737 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:1945 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:1952 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:1978 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:2006 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:2056 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:2130 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:2167 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:2207 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:2235 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:2270 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:2320 [test] service fixture setup.
- src-tauri/src/services/provider/mod.rs:2362 [test] service fixture setup.
- src-tauri/src/services/proxy.rs:3762 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:3948 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:4034 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:4095 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:4114 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:4263 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:4341 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:4421 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:4533 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:4651 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:4787 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:5264 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:5320 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:5385 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:5387 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:5460 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:5462 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:5611 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:5613 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:5615 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:5698 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:5700 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:5998 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:6000 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:6173 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:6175 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:6417 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:6419 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:6553 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:6555 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:6635 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:6637 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:6919 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:7173 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:7175 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:7240 [test] proxy fixture setup.
- src-tauri/src/services/proxy.rs:7242 [test] proxy fixture setup.
- src-tauri/tests/profile_roundtrip.rs:112 [test] profile fixture create.
- src-tauri/tests/profile_roundtrip.rs:116 [test] profile fixture create.
- src-tauri/tests/profile_roundtrip.rs:126 [test] profile fixture create.
- src-tauri/tests/profile_roundtrip.rs:133 [test] profile fixture create.
- src-tauri/tests/profile_roundtrip.rs:292 [test] profile fixture create.
- src-tauri/tests/profile_roundtrip.rs:501 [test] profile fixture create.
- src-tauri/tests/profile_roundtrip.rs:505 [test] profile fixture create.
- src-tauri/tests/profile_roundtrip.rs:670 [test] profile fixture create.
- src-tauri/tests/profile_roundtrip.rs:677 [test] profile fixture create.
- src-tauri/tests/profile_roundtrip.rs:762 [test] Linux Desktop fixture create.
- src-tauri/tests/profile_roundtrip.rs:769 [test] Linux Desktop fixture create.
- src-tauri/tests/provider_commands.rs:69 [test] command fixture create.
- src-tauri/tests/provider_service.rs:2927 [test] service fixture create.
This commit is contained in:
SaladDay
2026-07-31 17:58:07 +00:00
parent 3dc188313e
commit 4f78451405
34 changed files with 6210 additions and 831 deletions
+5 -1
View File
@@ -1,4 +1,5 @@
use crate::config::{atomic_write, write_json_file};
use crate::database::NewProviderAggregate;
use crate::error::AppError;
use crate::opencode_config::get_opencode_dir;
use crate::provider::Provider;
@@ -288,7 +289,10 @@ impl OmoService {
in_failover_queue: false,
};
state.db.save_provider("opencode", &provider)?;
state.db.create_provider(NewProviderAggregate::from_input(
"opencode",
crate::services::provider::provider_to_mutation_input(provider.clone()),
)?)?;
state
.db
.set_omo_provider_current("opencode", &provider.id, v.category)?;
+9 -15
View File
@@ -5,6 +5,7 @@
use std::time::{SystemTime, UNIX_EPOCH};
use crate::app_config::AppType;
use crate::database::{NewEndpoint, ProviderKey};
use crate::error::AppError;
use crate::settings::CustomEndpoint;
use crate::store::AppState;
@@ -47,9 +48,10 @@ pub fn add_custom_endpoint(
));
}
let key = ProviderKey::new(app_type.as_str(), provider_id)?;
state
.db
.add_custom_endpoint(app_type.as_str(), provider_id, &normalized)?;
.add_provider_endpoint(&key, NewEndpoint::now(normalized)?)?;
Ok(())
}
@@ -61,9 +63,8 @@ pub fn remove_custom_endpoint(
url: String,
) -> Result<(), AppError> {
let normalized = url.trim().trim_end_matches('/').to_string();
state
.db
.remove_custom_endpoint(app_type.as_str(), provider_id, &normalized)?;
let key = ProviderKey::new(app_type.as_str(), provider_id)?;
state.db.remove_provider_endpoint(&key, &normalized)?;
Ok(())
}
@@ -76,17 +77,10 @@ pub fn update_endpoint_last_used(
) -> Result<(), AppError> {
let normalized = url.trim().trim_end_matches('/').to_string();
// Get provider, update last_used, save back
let mut providers = state.db.get_all_providers(app_type.as_str())?;
if let Some(provider) = providers.get_mut(provider_id) {
if let Some(meta) = provider.meta.as_mut() {
if let Some(endpoint) = meta.custom_endpoints.get_mut(&normalized) {
endpoint.last_used = Some(now_millis());
state.db.save_provider(app_type.as_str(), provider)?;
}
}
}
Ok(())
let key = ProviderKey::new(app_type.as_str(), provider_id)?;
state
.db
.touch_provider_endpoint(&key, &normalized, now_millis())
}
/// Get current timestamp in milliseconds
+38 -11
View File
@@ -19,7 +19,9 @@ use crate::store::AppState;
use super::gemini_auth::{
detect_gemini_auth_type, ensure_google_oauth_security_flag, GeminiAuthType,
};
use super::normalize_claude_models_in_value;
use super::{
normalize_claude_models_in_value, provider_to_mutation_input, reconcile_provider_record,
};
/// ChatGPT Codex catalogs gpt-5.6 at a 372K context window with a ~353K
/// effective budget (openai/codex#31860), far below the 1.05M API spec.
@@ -1564,7 +1566,11 @@ pub fn import_default_config(state: &AppState, app_type: AppType) -> Result<bool
.to_string(),
);
state.db.save_provider(app_type.as_str(), &provider)?;
reconcile_provider_record(
&state.db,
app_type.as_str(),
provider_to_mutation_input(provider.clone()),
)?;
state
.db
.set_current_provider(app_type.as_str(), &provider.id)?;
@@ -1732,15 +1738,20 @@ pub fn import_opencode_providers_from_live(state: &AppState) -> Result<usize, Ap
};
if existing_ids.contains(&id) {
match state.db.get_provider_by_id(&id, "opencode") {
match state.db.get_provider_aggregate("opencode", &id) {
Ok(Some(existing)) => {
let existing = existing.provider;
let display_name = config.name.clone().unwrap_or_else(|| existing.name.clone());
if existing.settings_config != settings_config || existing.name != display_name
{
let mut provider = existing;
provider.name = display_name;
provider.settings_config = settings_config;
if let Err(e) = state.db.save_provider("opencode", &provider) {
if let Err(e) = reconcile_provider_record(
&state.db,
"opencode",
provider_to_mutation_input(provider),
) {
log::warn!(
"Failed to update OpenCode provider '{id}' from live config: {e}"
);
@@ -1767,7 +1778,9 @@ pub fn import_opencode_providers_from_live(state: &AppState) -> Result<usize, Ap
});
// Save to database
if let Err(e) = state.db.save_provider("opencode", &provider) {
if let Err(e) =
reconcile_provider_record(&state.db, "opencode", provider_to_mutation_input(provider))
{
log::warn!("Failed to import OpenCode provider '{id}': {e}");
continue;
}
@@ -1817,12 +1830,17 @@ pub fn import_openclaw_providers_from_live(state: &AppState) -> Result<usize, Ap
};
if existing_ids.contains(&id) {
match state.db.get_provider_by_id(&id, "openclaw") {
match state.db.get_provider_aggregate("openclaw", &id) {
Ok(Some(existing)) => {
let existing = existing.provider;
if existing.settings_config != settings_config {
let mut provider = existing;
provider.settings_config = settings_config;
if let Err(e) = state.db.save_provider("openclaw", &provider) {
if let Err(e) = reconcile_provider_record(
&state.db,
"openclaw",
provider_to_mutation_input(provider),
) {
log::warn!(
"Failed to update OpenClaw provider '{id}' from live config: {e}"
);
@@ -1855,7 +1873,9 @@ pub fn import_openclaw_providers_from_live(state: &AppState) -> Result<usize, Ap
});
// Save to database
if let Err(e) = state.db.save_provider("openclaw", &provider) {
if let Err(e) =
reconcile_provider_record(&state.db, "openclaw", provider_to_mutation_input(provider))
{
log::warn!("Failed to import OpenClaw provider '{id}': {e}");
continue;
}
@@ -1892,12 +1912,17 @@ pub fn import_hermes_providers_from_live(state: &AppState) -> Result<usize, AppE
}
if existing_ids.contains(&name) {
match state.db.get_provider_by_id(&name, "hermes") {
match state.db.get_provider_aggregate("hermes", &name) {
Ok(Some(existing)) => {
let existing = existing.provider;
if existing.settings_config != config {
let mut provider = existing;
provider.settings_config = config;
if let Err(e) = state.db.save_provider("hermes", &provider) {
if let Err(e) = reconcile_provider_record(
&state.db,
"hermes",
provider_to_mutation_input(provider),
) {
log::warn!(
"Failed to update Hermes provider '{name}' from live config: {e}"
);
@@ -1923,7 +1948,9 @@ pub fn import_hermes_providers_from_live(state: &AppState) -> Result<usize, AppE
});
// Save to database
if let Err(e) = state.db.save_provider("hermes", &provider) {
if let Err(e) =
reconcile_provider_record(&state.db, "hermes", provider_to_mutation_input(provider))
{
log::warn!("Failed to import Hermes provider '{name}': {e}");
continue;
}
File diff suppressed because it is too large Load Diff
+38 -37
View File
@@ -3759,7 +3759,7 @@ mod tests {
}),
None,
);
db.save_provider("claude", &provider)
db.reconcile_provider_fixture("claude", &provider)
.expect("save provider");
db.set_current_provider("claude", "p1")
.expect("set db current provider");
@@ -3945,7 +3945,7 @@ wire_api = "responses"
None,
);
provider.category = Some("cn_official".to_string());
db.save_provider("codex", &provider)
db.reconcile_provider_fixture("codex", &provider)
.expect("save DeepSeek provider");
db.set_current_provider("codex", "deepseek")
.expect("set current provider");
@@ -4031,7 +4031,7 @@ wire_api = "responses"
None,
);
provider.category = Some("official".to_string());
db.save_provider("codex", &provider)
db.reconcile_provider_fixture("codex", &provider)
.expect("save misclassified DeepSeek provider");
db.set_current_provider("codex", "deepseek")
.expect("set current provider");
@@ -4092,7 +4092,7 @@ wire_api = "responses"
None,
);
official.category = Some("official".to_string());
db.save_provider("codex", &official)
db.reconcile_provider_fixture("codex", &official)
.expect("save official provider");
let mut third_party = Provider::with_id(
@@ -4111,7 +4111,7 @@ wire_api = "responses"
None,
);
third_party.category = Some("custom".to_string());
db.save_provider("codex", &third_party)
db.reconcile_provider_fixture("codex", &third_party)
.expect("save third-party provider");
db.set_current_provider("codex", "codex-official")
.expect("set current provider");
@@ -4260,7 +4260,8 @@ wire_api = "responses"
None,
);
official.category = Some("official".to_string());
db.save_provider("codex", &official).expect("save official");
db.reconcile_provider_fixture("codex", &official)
.expect("save official");
db.set_current_provider("codex", crate::database::CODEX_OFFICIAL_PROVIDER_ID)
.expect("set current");
crate::settings::set_current_provider(
@@ -4338,7 +4339,7 @@ wire_api = "responses"
None,
);
provider.category = Some("official".to_string());
db.save_provider("codex", &provider)
db.reconcile_provider_fixture("codex", &provider)
.expect("save misclassified DeepSeek provider");
db.set_current_provider("codex", "deepseek")
.expect("set current provider");
@@ -4418,7 +4419,7 @@ wire_api = "responses"
None,
);
provider.category = Some("official".to_string());
db.save_provider("codex", &provider)
db.reconcile_provider_fixture("codex", &provider)
.expect("save misclassified DeepSeek provider");
db.set_current_provider("codex", "deepseek")
.expect("set current provider");
@@ -4530,7 +4531,7 @@ wire_api = "responses"
None,
);
provider.category = Some("official".to_string());
db.save_provider("codex", &provider)
db.reconcile_provider_fixture("codex", &provider)
.expect("save misclassified DeepSeek provider");
db.set_current_provider("codex", "deepseek")
.expect("set current provider");
@@ -4648,7 +4649,7 @@ wire_api = "responses"
None,
);
provider.category = Some("official".to_string());
db.save_provider("codex", &provider)
db.reconcile_provider_fixture("codex", &provider)
.expect("save misclassified DeepSeek provider");
db.set_current_provider("codex", "deepseek")
.expect("set current provider");
@@ -4784,7 +4785,7 @@ wire_api = "responses"
None,
);
provider.category = Some("cn_official".to_string());
db.save_provider("codex", &provider)
db.reconcile_provider_fixture("codex", &provider)
.expect("save DeepSeek provider");
db.set_current_provider("codex", "deepseek")
.expect("set current provider");
@@ -5261,7 +5262,7 @@ model = "gpt-5.1-codex"
}),
None,
);
db.save_provider("claude", &provider)
db.reconcile_provider_fixture("claude", &provider)
.expect("save provider");
db.set_current_provider("claude", "p1")
.expect("set current provider");
@@ -5317,7 +5318,7 @@ model = "gpt-5.1-codex"
}),
None,
);
db.save_provider("claude", &provider)
db.reconcile_provider_fixture("claude", &provider)
.expect("save provider");
db.set_current_provider("claude", "p1")
.expect("set current provider");
@@ -5382,9 +5383,9 @@ model = "gpt-5.1-codex"
}),
None,
);
db.save_provider("claude", &provider_a)
db.reconcile_provider_fixture("claude", &provider_a)
.expect("save provider a");
db.save_provider("claude", &provider_b)
db.reconcile_provider_fixture("claude", &provider_b)
.expect("save provider b");
db.set_current_provider("claude", "a")
.expect("set current provider");
@@ -5457,9 +5458,9 @@ model = "gpt-5.1-codex"
None,
);
db.save_provider("claude", &provider_a)
db.reconcile_provider_fixture("claude", &provider_a)
.expect("save provider a");
db.save_provider("claude", &provider_b)
db.reconcile_provider_fixture("claude", &provider_b)
.expect("save provider b");
db.set_current_provider("claude", "a")
.expect("set current provider");
@@ -5608,11 +5609,11 @@ model = "gpt-5.1-codex"
None,
);
db.save_provider("claude", &provider_a)
db.reconcile_provider_fixture("claude", &provider_a)
.expect("save provider a");
db.save_provider("claude", &provider_b)
db.reconcile_provider_fixture("claude", &provider_b)
.expect("save provider b");
db.save_provider("claude", &provider_c)
db.reconcile_provider_fixture("claude", &provider_c)
.expect("save provider c");
db.set_current_provider("claude", "a")
.expect("set current provider");
@@ -5695,9 +5696,9 @@ model = "gpt-5.1-codex"
None,
);
db.save_provider("claude", &provider_a)
db.reconcile_provider_fixture("claude", &provider_a)
.expect("save provider a");
db.save_provider("claude", &provider_b)
db.reconcile_provider_fixture("claude", &provider_b)
.expect("save provider b");
db.set_current_provider("claude", "a")
.expect("set current provider");
@@ -5995,9 +5996,9 @@ requires_openai_auth = true
None,
);
db.save_provider("codex", &provider_a)
db.reconcile_provider_fixture("codex", &provider_a)
.expect("save provider a");
db.save_provider("codex", &provider_b)
db.reconcile_provider_fixture("codex", &provider_b)
.expect("save provider b");
db.set_current_provider("codex", "a")
.expect("set current provider");
@@ -6170,9 +6171,9 @@ requires_openai_auth = true
..Default::default()
});
db.save_provider("codex", &provider_a)
db.reconcile_provider_fixture("codex", &provider_a)
.expect("save provider a");
db.save_provider("codex", &provider_b)
db.reconcile_provider_fixture("codex", &provider_b)
.expect("save provider b");
db.set_current_provider("codex", "a")
.expect("set current provider");
@@ -6414,9 +6415,9 @@ requires_openai_auth = true
..Default::default()
});
db.save_provider("codex", &provider_a)
db.reconcile_provider_fixture("codex", &provider_a)
.expect("save provider a");
db.save_provider("codex", &provider_b)
db.reconcile_provider_fixture("codex", &provider_b)
.expect("save provider b");
db.set_current_provider("codex", "a")
.expect("set current provider a");
@@ -6550,9 +6551,9 @@ requires_openai_auth = true
None,
);
db.save_provider("codex", &provider_a)
db.reconcile_provider_fixture("codex", &provider_a)
.expect("save provider a");
db.save_provider("codex", &provider_b)
db.reconcile_provider_fixture("codex", &provider_b)
.expect("save provider b");
db.set_current_provider("codex", "a")
.expect("set current provider a");
@@ -6632,9 +6633,9 @@ requires_openai_auth = true
}),
None,
);
db.save_provider("codex", &provider_a)
db.reconcile_provider_fixture("codex", &provider_a)
.expect("save provider a");
db.save_provider("codex", &provider_b)
db.reconcile_provider_fixture("codex", &provider_b)
.expect("save provider b");
db.set_current_provider("codex", "a")
.expect("set current provider a");
@@ -6916,7 +6917,7 @@ requires_openai_auth = true
}),
None,
);
db.save_provider("claude", &provider)
db.reconcile_provider_fixture("claude", &provider)
.expect("save provider");
db.set_current_provider("claude", "p1")
.expect("set current provider");
@@ -7170,9 +7171,9 @@ experimental_bearer_token = "PROXY_MANAGED"
grok_provider_config("https://b.example.com/v1", "b-key"),
None,
);
db.save_provider("grokbuild", &provider_a)
db.reconcile_provider_fixture("grokbuild", &provider_a)
.expect("save provider a");
db.save_provider("grokbuild", &provider_b)
db.reconcile_provider_fixture("grokbuild", &provider_b)
.expect("save provider b");
db.set_current_provider("grokbuild", "grok-a")
.expect("set db current");
@@ -7237,9 +7238,9 @@ experimental_bearer_token = "PROXY_MANAGED"
json!({ "config": "not valid toml = [" }),
None,
);
db.save_provider("grokbuild", &provider_a)
db.reconcile_provider_fixture("grokbuild", &provider_a)
.expect("save provider a");
db.save_provider("grokbuild", &provider_b)
db.reconcile_provider_fixture("grokbuild", &provider_b)
.expect("save provider b");
db.set_current_provider("grokbuild", "grok-a")
.expect("set db current");