fix(companion): restore the v1.27 -> v1.28 scope migrations

eed6d31d dropped two one-time migrations as "old cruft", but both are needed
by exactly the upgrade path this release ships: v1.27 has sentinel 0xC0DE002A
and the scope list is new in this cycle.

- loadScopeList() again turns an existing single default_scope_name/key into
  list entry 1 (default) and MyMesh::begin() seeds the channels that already
  exist with it. Without it an upgrader's DMs and channels silently go out
  unscoped, contradicting the release note.
- loadPrefsInt() again zeroes repeat_extra_scope_mask/ch_scope_idx when the
  file predates 0xC0DE002B, so the old sentinel tail can't read back as real
  scope picks.

The older-than-v1.27 backfills stay removed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-19 13:19:46 +02:00
co-authored by Claude Sonnet 5
parent 745aefcee1
commit dff56a3c53
3 changed files with 82 additions and 28 deletions
+17 -1
View File
@@ -1910,7 +1910,9 @@ void MyMesh::begin(bool has_display) {
// load persisted prefs
_store->loadPrefs(_prefs, sensors.node_lat, sensors.node_lon);
_store->loadScopeList(_scope_list);
// True only on the first boot after upgrading a device that had the old
// single Scope field set -- acted on once the channels are loaded, below.
bool scope_migrated_legacy = _store->loadScopeList(_scope_list, _prefs);
rebuildRepeatScopes();
// sanitise bad pref values. NaN/inf must be reset BEFORE constrain(): constrain
@@ -1962,6 +1964,20 @@ void MyMesh::begin(bool has_display) {
addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
}
// First boot after upgrading from the single device-wide Scope field: every
// channel now carries its own pick, and an unset pick means "*" == unscoped,
// not "inherit the default". Left alone, an upgrader's channel traffic would
// quietly go out unscoped while their DMs kept the old scope. Seed only the
// slots that actually hold a channel today -- a blanket fill would also hand
// the scope to whatever channel gets created in an empty slot later on.
if (scope_migrated_legacy && _scope_list.default_idx >= 1) {
for (uint8_t i = 0; i < NodePrefs::MAX_SCOPED_CHANNELS; i++) {
ChannelDetails ch;
if (getChannel(i, ch) && ch.name[0]) _prefs.ch_scope_idx[i] = _scope_list.default_idx;
}
savePrefs();
}
applyRepeaterRadio(); // companion params, or the repeater profile if relaying with one set
applyApc(); // sets TX power to the ceiling and arms APC if enabled
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);