From b786060517777e70f5084edbe0ffda2f3606a958 Mon Sep 17 00:00:00 2001 From: Breadway Date: Fri, 10 Jul 2026 13:37:41 +0800 Subject: [PATCH] Stop injecting phantom core profiles into a customized profile set ensure_core_profiles() was unconditionally backfilling home/work/away into the in-memory config on every load, even when the user had already defined their own profiles (e.g. Home/Away/School). This showed up as duplicate/phantom lowercase entries in `breadcrumbs profile list`, and downstream in breadbar and bos-settings. Now the self-heal only fires when the profile set is completely empty (a genuinely fresh or corrupted config), never padding an existing customized set. --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/config.rs | 28 +++++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2f1d99..222284d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,7 +54,7 @@ dependencies = [ [[package]] name = "breadcrumbs" -version = "2.1.4" +version = "2.1.5" dependencies = [ "clap", "serde", diff --git a/Cargo.toml b/Cargo.toml index e2333f2..b62a77a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadcrumbs" -version = "2.1.4" +version = "2.1.5" edition = "2021" description = "Profile-aware Wi-Fi state machine with Tailscale handling and self-healing watch daemon" license = "MIT" diff --git a/src/config.rs b/src/config.rs index da094aa..2eb8cd6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -218,8 +218,15 @@ fn core_profiles() -> BTreeMap { } fn ensure_core_profiles(cfg: &mut Config) { + // Only self-heal a genuinely empty/corrupted profile set. A user who has + // defined their own profiles (any names, any case) should never have + // unused core-profile stubs ("home"/"work"/"away") silently padded in + // alongside them. + if !cfg.profiles.is_empty() { + return; + } for (name, prof) in core_profiles() { - cfg.profiles.entry(name).or_insert(prof); + cfg.profiles.insert(name, prof); } } @@ -293,6 +300,25 @@ mod tests { assert_eq!(home.exit_node.as_deref(), Some("mynode")); } + #[test] + fn ensure_core_profiles_does_not_pad_a_customized_profile_set() { + let mut cfg = Config { + settings: Settings::default(), + networks: vec![], + profiles: BTreeMap::new(), + }; + cfg.profiles.insert("Home".to_string(), Profile::default()); + cfg.profiles.insert("Away".to_string(), Profile::default()); + cfg.profiles.insert("School".to_string(), Profile::default()); + ensure_core_profiles(&mut cfg); + // The user's own profile names are untouched, and no unused + // core-profile stubs (home/work/away) get injected alongside them. + assert_eq!(cfg.profiles.len(), 3); + assert!(cfg.profile("home").is_none()); + assert!(cfg.profile("work").is_none()); + assert!(cfg.profile("away").is_none()); + } + #[test] fn network_lookup_found_and_not_found() { let mut cfg = build_initial_config();