Skip to content

fix(connection): a failed USB connect closed the dialog silently and told the user nothing - #209

Merged
tylerkron merged 2 commits into
mainfrom
parity/serial-connect-error
Sep 3, 2026
Merged

tylerkron merged 2 commits into
mainfrom
parity/serial-connect-error

Conversation

@tylerkron

@tylerkron tylerkron commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Not merging — for review.

closes #207

Before

Pick a device out of the USB tab's discovered list, press Connect, and if the connect fails: the dialog closes, the device never appears in the device list, and nothing tells you why. ConnectSerialAsync connected each selected device and then called RaiseCloseRequested() unconditionally, so failure and success looked identical. The only signal was the absence of the thing you asked for.

Not a corner case. A device left streaming over WiFi answers the switch-to-USB with a SCPI error; a port held by another application fails the open. Both land here.

After

The dialog stays open and says which device failed:

Could not connect to 'COM7'. The device may be in use by another application or not responding.

Discovery resumes — it was drained before the connect — so the list is not left frozen and you can try again without reopening the dialog. The list is deliberately not cleared: the device that just failed is the one you are most likely to retry, and blanking it back to "Scanning…" would make you wait for rediscovery first.

Why this was a parity gap and not just a bug

daqifi-desktop has done this for years — SerialConnectError, a per-device status check, and a red TextBlock in the USB tab. The port has neither the property nor the element; SerialConnectError appeared nowhere in Daqifi.Avalonia.

Two things made it easy to miss. The Manual USB tab in the same file already does it correctly fifteen lines below, with a comment saying exactly why — "the dialog would close silently and the user would have no idea the connection failed" — so the file looks like it handles this. And the view model's own CloseRequested doc comment already claimed the behaviour ("Not raised on empty input or failed connect"); the serial path simply did not honour its own contract.

The per-device check is load-bearing

The status is read after each device, not once after the loop. ConnectionStatus is a single shared field on the ConnectionManager singleton and the USB list is SelectionMode="Multiple", so with several devices selected a later device's success would overwrite an earlier one's failure and the dialog would close reporting nothing. Upstream's comment records the same reasoning.

What I deliberately did not port

  • No extra logging. Upstream logs nothing here and neither does this — ConnectionManager.Connect already logs every path that sets Error. A second log line would be duplicate noise.
  • The WiFi tab is untouched. ConnectAsync has the same unconditional close, in both apps. That is an upstream bug, not a parity gap, so fixing it here would be scope creep and would put the two apps out of step. Worth filing separately.

Verification

result
dotnet build desktop green
dotnet restore + build -c Release -r iossimulator-arm64 --no-restore iOS green
dotnet test 341 passed, up from 336
distinct first-party warnings 179 → 179, no new warnings (identical sets; the only diff is two pre-existing warnings in the edited file shifting line number)
packages.lock.json untouched

Android cannot be built on this Mac (missing workload), so I did not build it — but CI did, and every check passed on this branch, Android and iOS heads included.

The new tests are non-vacuous. Removing the early return on the failure path turns both dialog-closing assertions red with their own messages; I ran that deliberately before restoring it. They drive the real ConnectSerialAsync without opening a COM port, by setting DeviceBeingUpdated so Connect refuses USB connects and reports Error before any I/O — the same gate holds the failure path's discovery restart down, so no finder is ever created.

The dialog has been rendered — see this comment for the screenshots. The original disclosure here said it had not been, because tools/parity-audit/AvaloniaCapture constructs no dialogs at all. A shepherd pass closed that by temporarily extending the harness to build the real ConnectionDialog with a real view model, seed one discovered device, select the USB tab and capture it with and without SerialConnectError set — same headless Skia + HarfBuzz path and settle loop as every other capture. The harness edit was reverted and nothing was pushed; tools/ is untouched on this branch.

The message renders: red, wrapped over two lines, above the separator, with the failed device still listed and selectable for a retry. Read straight out of the visual tree rather than inferred from pixels — a bound-but-blank TextBlock looks the same in a screenshot as one that never rendered — the element reports IsVisible=True, Bounds=0,338,528,27, Foreground=#fff43f5e (the same StatusRed rose ManualPortError uses), and the full expected text. So the reflection-resolved binding, the NotNullToVis converter and the StatusRed resource are all confirmed, none of which a green build could establish. With the error null the new row collapses to zero height, leaving the separator and Connect button exactly where they were.

Still not verified: nobody has driven this dialog interactively in a windowed app, and it has not been exercised against a device that genuinely fails to connect. The harness's inability to photograph any dialog is filed as #213.

No bench validation — reproducing this needs a device that fails to connect, and the board was not required for anything else here.

A pre-existing flake found on the way, filed not fixed

Stabilising this surfaced a rare ObjectDisposedException: SQLitePCL.sqlite3 in an unrelated test — once in 32 full-suite runs on this branch, zero in 20 on the merge-base. Cause: five test classes call the process-global SqliteConnection.ClearAllPools() while xUnit runs test classes in parallel. All five call sites are on main; this branch only perturbs scheduling. Filed as #210 rather than fixed here, to keep this PR to its target and stay out of concurrent work in the test project.

I am not claiming it is proven unrelated — 20 clean base runs do not rule out a ~1-in-32 event. What is established is the mechanism, which is entirely on main and independent of anything here.

Review

Qodo raised two findings, both accurate descriptions of pre-existing weaknesses rather than anything this PR introduces, and both now tracked in #212:

  1. (High) The dialog infers a per-device connect result from the shared mutable ConnectionStatus. True — and it is the third instance of that pattern, joining ConnectManualSerialAsync (line 646) and ConnectManualWifiAsync (line 731), both on main. The alternative this PR replaces was no failure check at all. The proposed remedy — a typed per-call result from ConnectionManager.Connect, all callers updated — is the right direction and is what Qodo's own High-Level Assessment recommends deferring to a separate connection API redesign.
  2. (Medium) The discovery restart does not clear AvailableSerialDevices, unlike the WiFi path. The asymmetry is real and pre-existing at three call sites. Clearing on the failure path would be actively worse, though: the device that just failed is the one the user wants to retry, and blanking the list to "Scanning…" makes them wait for rediscovery. Upstream does not clear either. What was wrong was my comment promising a refresh it does not perform — corrected in 52823c9.

Round 2 on head 52823c9: 0 bugs, 0 rule violations, both threads answered and resolved.

Diffstat

+298 / -2 across 5 files — of which production code is +43 / -2 across 2 files; the remaining +255 is tests.

 34 +   Daqifi.Avalonia.Tests/ConnectionManagerSingletonCollection.cs      (new)
  1 +   Daqifi.Avalonia.Tests/ViewModels/ConnectionDialogFirmwarePauseTests.cs
220 +   Daqifi.Avalonia.Tests/ViewModels/ConnectionDialogSerialConnectErrorTests.cs (new)
  9 +/2- Daqifi.Avalonia/Daqifi.Desktop/View/ConnectionDialog.axaml
 34 +   Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ConnectionDialogViewModel.cs

About that test-collection line

Adding a second class that writes ConnectionManager.Instance created a real race with ConnectionDialogFirmwarePauseTests: xUnit runs distinct classes in parallel, one clears the firmware gate the other relies on to keep discovery off the bus, and it reached a real UDP finder once during development. Both classes now share a non-parallel collection. ConnectionManagerTeardownTests constructs its own ConnectionManager, shares no state, and is deliberately not a member — the collection's doc says so, so nobody adds it later by pattern-matching.

…told the user nothing

Picking a discovered device out of the USB tab and pressing Connect ran
ConnectSerialAsync, which connected each selected device and then called
RaiseCloseRequested() unconditionally. A failed connect was therefore
indistinguishable from a successful one: the dialog dismissed itself, the device
never appeared in the device list, and nothing on screen said why. The user's
only signal was the absence of the thing they asked for.

Not a rare path. A device left streaming over WiFi answers the switch-to-USB
with a SCPI error; a port held by another application fails the open. Both land
here.

The Manual USB tab in the same file already does the right thing fifteen lines
below, and its comment says why — "the dialog would close silently and the user
would have no idea the connection failed". The discovered-device path, which is
the common one, never got it. The view model's own CloseRequested doc comment
already claimed the behaviour ("Not raised on empty input or failed connect");
the serial path did not honour it.

Port the capability from daqifi-desktop: a SerialConnectError property, a status
check after each device, and a red TextBlock bound to it in the USB tab. The
check is per-device rather than once after the loop because ConnectionStatus is
a single shared field on the ConnectionManager singleton and the list is
multi-select, so a later device's success would otherwise overwrite an earlier
device's failure and the dialog would close reporting nothing. On failure,
discovery restarts so the list keeps refreshing.

Left behind deliberately: upstream logs nothing extra here and neither does
this, because ConnectionManager.Connect already logs every path that sets Error.

The tests drive the real ConnectSerialAsync without opening a COM port, by
setting DeviceBeingUpdated so Connect refuses USB connects and reports Error
before any I/O — the same gate also holds the failure path's discovery restart
down, so no finder is ever created. Verified non-vacuous: dropping the early
return turns both dialog-closing assertions red.

That made a second class a writer of ConnectionManager.Instance, which xUnit
would run in parallel with ConnectionDialogFirmwarePauseTests — one clearing the
firmware gate the other relies on to keep discovery off the bus, which reached a
real UDP finder once. Both classes now share a non-parallel collection.
ConnectionManagerTeardownTests builds its own ConnectionManager and is
deliberately not a member.

closes #207
@tylerkron

Copy link
Copy Markdown
Contributor Author

/agentic_review

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Surface failed USB connections without closing the dialog

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Keep the USB connection dialog open and identify devices when connection fails.
• Restart serial discovery after failures so users can retry without reopening the dialog.
• Add regression coverage and serialize tests sharing the process-wide connection manager.
Diagram

graph TD
  A["Connect request"] --> B["Stop discovery"] --> C["Connect device"] --> D{"Status valid?"}
  D -- Yes --> E{"More devices?"}
  E -- Yes --> C
  E -- No --> F["Close dialog"]
  D -- No --> G["Show error"] --> H["Restart discovery"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Return a typed connection result
  • ➕ Avoids reading mutable singleton status after each operation
  • ➕ Can carry structured failure reasons directly to the UI
  • ➕ Makes concurrent or batched connection behavior easier to reason about
  • ➖ Requires a broader ConnectionManager API change
  • ➖ Expands scope beyond the intended desktop parity fix
  • ➖ Would require updating other connection callers and tests
2. Attempt and aggregate every failure
  • ➕ Reports all unsuccessful devices from a multi-selection in one attempt
  • ➕ Allows independent later devices to connect despite an earlier failure
  • ➖ Needs richer per-device result tracking because ConnectionStatus is shared
  • ➖ Could perform unnecessary connection attempts after a known failure
  • ➖ Produces more complex recovery and error presentation behavior

Recommendation: Keep the PR's fail-fast, per-device status check for this fix. It matches the existing ConnectionManager contract and upstream behavior while preventing later successes from masking failures; a typed per-call result would be the preferable direction only as a separate connection API redesign.

Files changed (5) +292 / -2

Bug fix (2) +37 / -2
ConnectionDialog.axamlDisplay USB connection errors in the dialog +9/-2

Display USB connection errors in the dialog

• Adds a conditionally visible red error message bound to SerialConnectError in the USB tab. The grid gains a dedicated row so feedback appears above the Connect button.

Daqifi.Avalonia/Daqifi.Desktop/View/ConnectionDialog.axaml

ConnectionDialogViewModel.csHandle failed discovered-device USB connections +28/-0

Handle failed discovered-device USB connections

• Introduces SerialConnectError and validates ConnectionManager status after every selected device. The flow stops on the first failure, names that device, restarts discovery, and leaves the dialog open; it closes only after all devices connect successfully.

Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ConnectionDialogViewModel.cs

Tests (3) +255 / -0
ConnectionManagerSingletonCollection.csSerialize tests that mutate the connection manager singleton +34/-0

Serialize tests that mutate the connection manager singleton

• Adds an xUnit collection for test classes that modify process-wide ConnectionManager state. Disabling parallel execution between these classes prevents firmware guards and discovery behavior from interleaving or reaching real hardware.

Daqifi.Avalonia.Tests/ConnectionManagerSingletonCollection.cs

ConnectionDialogFirmwarePauseTests.csJoin firmware pause tests to the singleton collection +1/-0

Join firmware pause tests to the singleton collection

• Marks the existing firmware pause test class as a member of the shared ConnectionManager singleton collection, preventing races with the new serial connection tests.

Daqifi.Avalonia.Tests/ViewModels/ConnectionDialogFirmwarePauseTests.cs

ConnectionDialogSerialConnectErrorTests.csCover USB connection failure feedback and recovery +220/-0

Cover USB connection failure feedback and recovery

• Adds regression tests verifying that failed USB connections keep the dialog open, identify the first failed device, replace stale messages, and avoid errors for empty selections. It also verifies that discovery recovery respects the firmware-update pause guard without opening real ports.

Daqifi.Avalonia.Tests/ViewModels/ConnectionDialogSerialConnectErrorTests.cs

@qodo-code-review

qodo-code-review Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Shared status misattributes connects ✗ Dismissed 🐞 Bug ≡ Correctness
Description
ConnectSerialAsync treats the singleton ConnectionStatus as the result of the device it just
awaited, but another concurrent manual, WiFi, or discovered-device connect can overwrite that field
first. This can still close the dialog after a failed USB connect or display an error after a
successful one.
Code

Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ConnectionDialogViewModel.cs[R590-591]

+            var status = ConnectionManager.Instance.ConnectionStatus;
+            if (status is not (DAQiFiConnectionStatus.Connected or DAQiFiConnectionStatus.AlreadyConnected))
Relevance

●●● Strong

Accepted race-safety findings show the team prioritizes preventing stale or misattributed device
state during concurrent operations.

PR-#55

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
ConnectionManager.Connect sets the process-wide status to Connecting, AlreadyConnected,
Disconnected, Error, or Connected on each call. The dialog's serial, manual-serial, and
manual-WiFi commands independently invoke that singleton, so awaited operations can overlap and the
value read at lines 590-591 is not tied to the completed device.

Daqifi.Avalonia/Daqifi.Desktop/ConnectionManager.cs[392-523]
Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ConnectionDialogViewModel.cs[555-567]
Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ConnectionDialogViewModel.cs[605-656]
Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ConnectionDialogViewModel.cs[677-739]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ConnectSerialAsync` reads a shared singleton status after awaiting `ConnectionManager.Connect`, so concurrent connection commands can overwrite the result before it is inspected. Make each connect operation return an immutable per-call result, or otherwise serialize connection attempts, and use that result for the USB failure decision.

## Issue Context
`ConnectionManager.Connect` writes `ConnectionStatus` throughout every operation, while the dialog exposes several independent asynchronous commands that can call it concurrently. Preserve the existing `AlreadyConnected` success behavior and ensure all callers are updated consistently.

## Fix Focus Areas
- Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ConnectionDialogViewModel.cs[586-602]
- Daqifi.Avalonia/Daqifi.Desktop/ConnectionManager.cs[392-523]
- Daqifi.Avalonia/Daqifi/Desktop/ViewModels/ConnectionDialogViewModel.cs[605-739]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Restart retains stale devices ✗ Dismissed 🐞 Bug ≡ Correctness
Description
The failure branch recreates serial discovery without clearing AvailableSerialDevices, while
discovery only adds or updates entries and never removes devices absent from the new session.
Devices that disappeared during the failed attempt therefore remain selectable even though the
advertised restart is supposed to refresh the list.
Code

Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ConnectionDialogViewModel.cs[R596-597]

+                // Restart discovery so the dialog keeps finding devices after a failed connect attempt.
+                StartSerialDiscovery();
Relevance

●●● Strong

PR #49 explicitly accepted clearing discovery lists when recreating finders to prevent stale or
duplicate devices.

PR-#49

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
StartSerialDiscovery disposes and replaces the finder but does not reset its bound collection, and
AddSerialDeviceFromDiscovery only adds or updates devices. The WiFi implementation explicitly
clears its corresponding list on finder recreation to prevent the same finder/list lifetime
mismatch.

Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ConnectionDialogViewModel.cs[331-409]
Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ConnectionDialogViewModel.cs[860-890]
PR-#49

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Restarting serial discovery after a failed connection creates a new finder but leaves entries from the prior discovery session in the bound collection. Clear and reset the serial list when recreating the finder, marshalled to the UI thread, so only devices rediscovered in the new session remain selectable.

## Issue Context
The analogous WiFi restart already clears its collection because the collection outlives the finder. Preserve serial deduplication and ensure `HasNoSerialDevices` reflects the cleared list until devices are rediscovered.

## Fix Focus Areas
- Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ConnectionDialogViewModel.cs[380-409]
- Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ConnectionDialogViewModel.cs[596-598]
- Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ConnectionDialogViewModel.cs[860-890]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: ⏭️ Skipped: The latest push only expands a source-code comment and introduces no behavioral or functional change.

Grey Divider

Tip of the day
💡 Did you know, you can add REVIEW.md to your repo root and Qodo follows it on every PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Qodo read the previous comment ("keeps finding devices") as promising a refreshed
list, and it does not deliver one: StartSerialDiscovery recreates the finder but
never clears AvailableSerialDevices, unlike StartWiFiDiscovery.

Not clearing is the right behaviour on this path — the device that just failed is
the one the user is most likely to retry, and blanking the list back to
"Scanning..." would make them wait for rediscovery first; upstream does not clear
here either. So the comment was what was wrong, not the call. The underlying
serial/WiFi asymmetry is pre-existing at three other call sites and is filed as
#212 for a deliberate decision.
@tylerkron

Copy link
Copy Markdown
Contributor Author

Both findings are correct descriptions of real weaknesses. Neither is introduced by this PR, and the remediation each asks for is a change to shared connection/discovery behaviour rather than to this PR's parity target — so both are filed as #212 and one comment here has been corrected. Detail:

1. Shared status misattributes connects — High — valid, pre-existing, filed

Accurate. ConnectionStatus is a single process-wide field that Connect writes throughout its run, and nothing ties the value read after the await to the device just awaited. AsyncRelayCommand blocks concurrent execution of itself, but the four connect commands are independent instances and the dialog disables nothing while one is in flight, so a user can press Connect on the USB tab and then on Manual WiFi before the first finishes.

What the finding does not say is that this is the established pattern of the file, at two call sites that predate this PR:

  • ConnectionDialogViewModel.cs:646 — ConnectManualSerialAsync, on main
  • ConnectionDialogViewModel.cs:731 — ConnectManualWifiAsync, on main
  • ConnectionDialogViewModel.cs:590 — this PR, the third instance

So the choice here is not "correct result vs. shared field"; it is "no failure check at all vs. the same check the other two paths already use". Before this PR the discovered-device path read nothing and closed unconditionally, which is strictly worse than a check that can be misattributed under a rare interleaving.

The suggested fix — an immutable per-call result from ConnectionManager.Connect, with every caller updated — is right, and is exactly what this review's own High-Level Assessment recommends against doing here:

Keep the PR's fail-fast, per-device status check for this fix. It matches the existing ConnectionManager contract and upstream behavior while preventing later successes from masking failures; a typed per-call result would be the preferable direction only as a separate connection API redesign.

Taking that recommendation. Filed as #212 §1 with all three call sites.

2. Restart retains stale devices — Medium — valid asymmetry, but clearing here would be wrong

The asymmetry is real: StartWiFiDiscovery clears AvailableWiFiDevices on finder recreate (issue #621, PR #49) and StartSerialDiscovery has no equivalent, so the serial list accumulates. Pre-existing at three call sites on main — StartConnectionFinders, the ConnectManualSerialAsync failure path, and the firmware-flash resume path.

But clearing on this path specifically would make the feature worse, not better. The whole point of the PR is that the user can retry after a failure without reopening the dialog, and the device most likely to be retried is the one that just failed. Blanking the list back to "Scanning for USB devices…" would force them to wait for rediscovery before they can try again. Upstream daqifi-desktop does not clear here either.

What was wrong is my comment, which said "keeps finding devices" and so read as promising a refresh it does not perform. Fixed in 52823c9 — it now states plainly that the list is not cleared, why that is deliberate here, and that the serial/WiFi asymmetry exists and is tracked. Filed as #212 §2, which notes the dialog-open and firmware-resume paths are the ones where a clear looks straightforwardly right, and that the split needs a decision rather than a reflex.

No behavioural change in that commit; 341 tests still pass and desktop + iOS still build.

@tylerkron

Copy link
Copy Markdown
Contributor Author

/agentic_review

@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 52823c9

@tylerkron

Copy link
Copy Markdown
Contributor Author

The dialog has now been rendered and looked at

This PR shipped with an explicit disclosure that the connect dialog had never been photographed,
because tools/parity-audit/AvaloniaCapture constructs no dialogs at all — it renders the main
window's panes, drawers and flyouts, and the mobile shell, and nothing else. That disclosure is now
closed by observation rather than repeated.

Method. I temporarily extended the capture harness to build the real ConnectionDialog window
with a real ConnectionDialogViewModel, put one discovered SerialStreamingDevice in
AvailableSerialDevices, select the USB tab, and shoot it twice — once clean, once with
SerialConnectError set. Same headless Skia + HarfBuzz path, same settle loop, same
DAQIFI_DATA_DIR isolation as every other capture; both screens settled in one round. The
harness edit has been reverted
— tools/ is untouched on this branch and no commit was pushed.

The error renders

Attached below, in order: the failed-connect state, then the same tab with no error for contrast.

Red, wrapped over two lines, sitting above the separator, with the failed device still in the list
and still selectable for a retry — which is the behaviour the PR argues for.

Above the pixels, the harness also read the element straight out of the visual tree, because a
bound-but-blank TextBlock and one that never rendered look identical in a screenshot:

SerialConnectError TextBlock: IsVisible=True  Bounds=0,338,528,27
                              Foreground=#fff43f5e
                              Text="Could not connect to 'DAQiFi Nq1'. The device may be in use
                                    by another application or not responding."

So all three things a green build could not tell us are confirmed:

  • {Binding SerialConnectError} resolves against the view model. These views carry no x:DataType,
    so they bind by reflection — a typo here would have produced a blank line and no build error.
  • The NotNullToVis converter drives IsVisible correctly.
  • StatusRed resolves: #fff43f5e is the same rose the Manual USB tab's ManualPortError uses
    fifteen lines above in the same file.

The second image is the contrast, and it earns its place

With SerialConnectError null the new RowDefinition collapses to zero height — the separator and
the Connect button sit at exactly the same y as before this change. The added row costs nothing in
the normal case; it is not a reserved blank gap.

Also re-verified on this head (52823c9)

result
desktop build green
iOS restore + -c Release -r iossimulator-arm64 --no-restore green
dotnet test 341/341, 0 failed, 0 skipped
capture harness all 20 screens captured, none skipped
packages.lock.json untouched
main moved? no — merge-base is origin/main (cd44a11), so there is nothing to test-merge
CI all 6 checks green, Android and iOS heads included

The harness's inability to photograph any dialog is a standing hole in the visual gate rather than
a problem with this PR, so it is filed separately as #213 — including what had to be done by
hand here, and why the indeterminate "Scanning…" progress bar makes naive dialog capture fail. Not
fixed here: #204 is mid-flight in that directory.

Connect dialog USB tab after a failed connect: the device DAQiFi Nq1 on COM7 still listed, and a red two-line message reading Could not connect to DAQiFi Nq1. The device may be in use by another application or not responding.

The same USB tab with no error set: identical layout, no red message, separator and Connect button at the same position, showing the new row collapses to zero height

@tylerkron
tylerkron added this pull request to the merge queue Sep 3, 2026
@tylerkron

Copy link
Copy Markdown
Contributor Author

Qodo-clean, CI green — ready for review. The connect dialog was rendered and looked at (screenshots above), so the "did not render the dialog" disclosure is closed by observation rather than repeated; the harness gap that forced it is filed as #213.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

parity(connection): a failed USB connect closes the dialog silently — the user is never told the device did not connect

1 participant