From 92ce4ba2aef70967a5e9f0ab927b00668fd07873 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Thu, 27 Aug 2026 18:04:16 +0200 Subject: [PATCH] Refuse to join the pair, rather than undoing it a second later MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Connecting both pods is the configuration in which the `−` pod stops reporting its own paddle (§2.3.1). `Cmd::Seen` has always known that and declined; an explicit `Cmd::Connect` walked straight past it, and housekeeping then closed the redundant link a second or so later — which looks like the app handling the case and is not the same thing. On the tablet, 2026-08-27 15:55:39, the pair was joined for 1.4 s and the `−` pod did not report another press for the rest of the session, across two fresh links and an app reinstall. So the rule now lives on both paths. `auto` is still armed by the refused request, so the fallback stands: the moment the `−` pod goes away, the `+` pod is taken on its next advertisement. And the UI stops offering the trap. The `+` pod's "Connect anyway" sat next to a working controller, which put the one action that breaks shifting a single tap from a rider hunting for a way to fix shifting. It now reads "Held in reserve", which is what that pod is. Co-Authored-By: Claude Opus 5 (1M context) --- src-tauri/src/controller.rs | 26 ++++++++++++++++++++++++++ src-tauri/tauri.conf.json | 4 ++-- ui/src/components/ClickPanel.svelte | 17 ++++++++++++++--- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/controller.rs b/src-tauri/src/controller.rs index b496f1d..d8e9408 100644 --- a/src-tauri/src/controller.rs +++ b/src-tauri/src/controller.rs @@ -646,10 +646,36 @@ async fn run( }; match cmd { Cmd::Connect { pod, address } => { + // The rule `Cmd::Seen` has always enforced, applied + // here too: with the `−` pod up there is nothing for a + // `+` link to add and one specific thing for it to + // break — connected as a pair, the `−` pod stops + // reporting its own paddle (§2.3.1). + // + // Only the scan-driven path was guarded, so an explicit + // request walked straight past it. Housekeeping then + // closed the redundant link a second or so later, which + // reads as the app handling it and is not the same + // thing at all: on the tablet, 2026-08-27 15:55:39, the + // pair was joined for 1.4 s and the `−` pod did not + // report a press again for the rest of the session. + // + // `auto` is still armed below, so the fallback stands: + // the moment the `−` pod goes away, the `+` pod is + // taken on its next advertisement. + let minus_up = !minus.idle(); let slot = slot_mut(&mut minus, &mut plus, pod); // Asking for it by hand re-arms auto-connect, whatever // came before. slot.auto = true; + if pod == PodId::Plus && minus_up { + tracing::info!( + "controller: refusing a + pod link while the − pod is up — \ + it relays the pair, and joining both stops it reporting \ + its own paddle" + ); + continue; + } // A second click while a search is running means // "connect", which is what is already happening. if slot.connecting() { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 1d4dc3c..b94d860 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "BikeControl", - "version": "0.2.4", + "version": "0.2.5", "identifier": "paris.tourolle.bikecontrol", "build": { "frontendDist": "../ui/dist", @@ -43,7 +43,7 @@ ], "category": "Utility", "android": { - "versionCode": 1204 + "versionCode": 1205 }, "shortDescription": "Indoor cycling trainer control", "longDescription": "Control a smart trainer over BLE, ride gradient profiles and synthetic waveforms, and record the result." diff --git a/ui/src/components/ClickPanel.svelte b/ui/src/components/ClickPanel.svelte index c939335..d97e2ed 100644 --- a/ui/src/components/ClickPanel.svelte +++ b/ui/src/components/ClickPanel.svelte @@ -158,10 +158,15 @@ {:else if pod.state === 'searching'} + {:else if dormant} + + Held in reserve {:else} - + {/if} @@ -353,6 +358,12 @@ font-size: 0.82rem; } + .held { + align-self: center; + font-size: 0.8rem; + color: var(--ink-faint); + } + .note { flex: 1 1 100%; margin: 0;