Stop injecting phantom core profiles into a customized profile set
All checks were successful
Mirror to GitHub / mirror (push) Successful in 2s
release / build (push) Successful in 38s
Build and publish package / package (push) Successful in 45s

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.
This commit is contained in:
Breadway 2026-07-10 13:37:41 +08:00
parent 941742c5b6
commit b786060517
3 changed files with 29 additions and 3 deletions

2
Cargo.lock generated
View file

@ -54,7 +54,7 @@ dependencies = [
[[package]] [[package]]
name = "breadcrumbs" name = "breadcrumbs"
version = "2.1.4" version = "2.1.5"
dependencies = [ dependencies = [
"clap", "clap",
"serde", "serde",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "breadcrumbs" name = "breadcrumbs"
version = "2.1.4" version = "2.1.5"
edition = "2021" edition = "2021"
description = "Profile-aware Wi-Fi state machine with Tailscale handling and self-healing watch daemon" description = "Profile-aware Wi-Fi state machine with Tailscale handling and self-healing watch daemon"
license = "MIT" license = "MIT"

View file

@ -218,8 +218,15 @@ fn core_profiles() -> BTreeMap<String, Profile> {
} }
fn ensure_core_profiles(cfg: &mut Config) { 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() { 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")); 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] #[test]
fn network_lookup_found_and_not_found() { fn network_lookup_found_and_not_found() {
let mut cfg = build_initial_config(); let mut cfg = build_initial_config();