Skip to content

fix: cancel pending Setup Connection attempts - #152

Merged
bluegrassiot merged 17 commits into
release/v1.0.10from
fix/150-cancel-connection
Oct 17, 2026
Merged

bluegrassiot merged 17 commits into
release/v1.0.10from
fix/150-cancel-connection

Conversation

@bluegrassiot

@bluegrassiot bluegrassiot commented Oct 16, 2026 •

Copy link
Copy Markdown
Owner

Cancelling a pending Setup Connection attempt now cancels the in-flight connect without closing the dialog or losing entered values. Cancel remains visible beside Connecting… while an attempt is pending.

Each explicit Start runs in its own generation (own raw client, lifetime token and handlers), so a cancelled or superseded attempt cannot fire stale callbacks, establish a reconnect, or resurrect itself after cancellation. Closing/disposing the dialog and stopping while an automatic reconnect is pending are both safe.

Validation: full solution build (0 warnings / 0 errors); Core 1765 passed / 1 skipped; UI 1731 passed / 1 skipped; integration 59 passed; formatting, security, comments, blocking-awaits, file-length and the CI script tests pass; coverage 89%; browser checks (desktop 1280x900, mobile 390x844, short 390x480) pass for the dialog flows.

Compatibility: the raw adopted-client constructor (IMqttClient) is now single-generation - repeat starts need the IMqttClientFactory constructor. Production DI uses the factory path and is unaffected.

AGENTS.md comment guidance is tightened to keep added comments minimal.

Closes #150

Each explicit Start gets its own generation (raw client, lifetime token, handlers) so a canceled or superseded attempt cannot reach the next one. Adds StartAttempt/ConnectAttemptCompletedAsync for caller-visible attempt correlation, and makes Stop/DisposeAsync await every retiring generation's cleanup.
The Connect button becomes Cancel while an attempt is in flight; cancelling retires the attempt through the managed client and preserves the dialog and its values. Close/dispose and stop-while-reconnecting are safe. Uses StartAttempt attempt correlation to ignore stale outcomes.
Copilot AI balanced review requested due to automatic review settings October 16, 2026 03:11
@bluegrassiot
bluegrassiot removed the request for review from Copilot October 16, 2026 03:13
@greptile-apps

greptile-apps Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[High impact] The changes appear safe to merge; no new actionable issues remain.

Summary

Cancels pending connection attempts without closing the dialog or clearing entered values.

  • Each MQTT start now owns a cancellable connection attempt.
  • The connection dialog can stop a pending attempt and stay open.
  • Comment guidance now keeps added comments to a minimum.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Start[User starts connection] --> Attempt[Create separate client and lifetime]
    Attempt --> Pending[Connect pending]
    Pending --> Success[Accept connection]
    Pending --> Cancel[User stops or closes dialog]
    Cancel --> Retire[Retire this attempt]
    Retire --> Cleanup[Wait for connection work and clean up]
    Cleanup --> Retry[Keep entered values for retry]
    Retire --> Ignore[Ignore stale callbacks]
Loading

Reviews (13) · Last reviewed commit: "test: release disconnect ownership gates..." · Reviewed by Greptile

Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Events.cs Outdated
Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Events.cs Outdated
Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Teardown.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Connection-result handling, startup tracking, and notification lifecycle issues remain unresolved.

4 open findings
What changed in this PR

Adds cancellation to Setup Connection while preserving entered values, supported by isolated MQTT connection generations.

Changes:

  • Replaces Connect with Cancel during pending attempts.
  • Adds attempt outcomes and generation-scoped connection cleanup.
  • Expands cancellation/race coverage and tightens comment guidance.
File Description
tests/​MqttProbe.UI.Tests/​Components/​Browser/​Connection/​ConnectionDialogTests.cs Covers cancellation, retry, disposal, and completion races.
tests/​MqttProbe.IntegrationTests/​Integration/​ConnectCancellationIntegrationTests.cs Adds broker-backed cancellation and replacement tests.
tests/​MqttProbe.Core.Tests/​Services/​Mqtt/​MqttManagedClientTests.cs Updates tests for generation-based startup.
tests/​MqttProbe.Core.Tests/​Services/​Mqtt/​MqttManagedClientLifecycleTests.cs Covers teardown and notification races.
tests/​MqttProbe.Core.Tests/​Services/​Mqtt/​MqttManagedClientCancellationTests.cs Tests cancellation and stale callbacks.
tests/​MqttProbe.Core.Tests/​Services/​Mqtt/​MqttManagedClientActivityGateTests.cs Updates revocation and token tests.
tests/​MqttProbe.Core.Tests/​Services/​Mqtt/​FirstConnectRetainedMessageRaceTests.cs Updates the managed-client test double.
src/​MqttProbe.UI/​Components/​Browser/​Connection/​ConnectionDialog.razor.css Styles the connecting indicator and Cancel row.
src/​MqttProbe.UI/​Components/​Browser/​Connection/​ConnectionDialog.razor Adds Cancel controls and disposal handling.
src/​MqttProbe.UI/​Components/​Browser/​Connection/​ConnectionDialog.ConnectionAttempt.cs Tracks startup, cancellation, and outcomes.
src/​MqttProbe.Core/​Services/​Mqtt/​MqttManagedClient.Teardown.cs Adds cleanup helpers and stop notifications.
src/​MqttProbe.Core/​Services/​Mqtt/​MqttManagedClient.Events.cs Adds generation-aware event and queue handling.
src/​MqttProbe.Core/​Services/​Mqtt/​MqttManagedClient.cs Refactors startup, stopping, and client ownership.
src/​MqttProbe.Core/​Services/​Mqtt/​MqttManagedClient.Attempts.cs Implements connection generations and reconnect handling.
src/​MqttProbe.Core/​Services/​Mqtt/​MqttConnectAttempt.cs Defines attempt identifiers and outcomes.
src/​MqttProbe.Core/​Services/​Mqtt/​IMqttManagedClient.cs Exposes attempt-based startup and completion events.
src/​MqttProbe.Core/​Services/​Mqtt/​IMqttClientFactory.cs Provides raw-client creation and ownership contracts.
AGENTS.md Tightens guidance on code comments.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Attempts.cs Outdated
Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Events.cs Outdated
Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Attempts.cs Outdated
Copilot AI balanced review requested due to automatic review settings October 16, 2026 04:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Teardown.cs Outdated
Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Attempts.cs
Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Attempts.cs Outdated
Copilot AI balanced review requested due to automatic review settings October 16, 2026 04:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Remaining ownership gaps allow disposed-dialog startup to disconnect an active session and superseded stop notifications to resume.

1 open finding
4 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Disposed dialog can disconnect the active session after save

src/​MqttProbe.UI/​Components/​Browser/​Connection/​ConnectionDialog.ConnectionAttempt.cs:107

If the dialog is disposed while Save & Connect awaits AddConnectionAsync, disposal cancels and releases the operation, but the completed save still proceeds into StopActiveConnectionGracefullyAsync before checking ownership. The closed dialog can therefore disconnect the active session. Check IsCurrent(operation) after validation and the save prompt, before starting shutdown. Add a regression test that disposes the dialog during a blocked save.

🧠 Review effort: Balanced

Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Teardown.cs Outdated
Copilot AI balanced review requested due to automatic review settings October 16, 2026 04:59
Comment thread src/MqttProbe.Core/Services/Mqtt/IMqttClientFactory.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The lifecycle rewrite needs human review of callback ordering and resource ownership, with unresolved concurrency issues.

1 open finding
1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Disposed dialog can disconnect the active session after cancellation

src/​MqttProbe.UI/​Components/​Browser/​Connection/​ConnectionDialog.ConnectionAttempt.cs:108

If the dialog is disposed while validation or the unsaved-changes prompt is pending, startup can resume and call StopActiveConnectionGracefullyAsync after its operation has been canceled and released. The ownership check after shutdown is too late to prevent disconnecting the active session. Recheck IsCurrent(operation) after each earlier await, and add a regression test that disposes the dialog while the prompt is pending, then resolves it with “Connect anyway.”

🧠 Review effort: Balanced

Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Attempts.cs Outdated
Copilot AI balanced review requested due to automatic review settings October 16, 2026 05:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The cross-cutting lifecycle refactor warrants human review, and a disposed-dialog startup continuation can still stop the active session.

0 open findings

1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Disposed dialog can disconnect the existing session during save

src/​MqttProbe.UI/​Components/​Browser/​Connection/​ConnectionDialog.ConnectionAttempt.cs:107

If the dialog is disposed while “Save & Connect” awaits certificate import or AddConnectionAsync, disposal cancels and releases the operation. When the save finishes, startup still calls StopActiveConnectionGracefullyAsync before checking ownership, so the closed dialog can disconnect the existing session. Check IsCurrent after validation and the save prompt, before stopping the session. Add a regression test that disposes the dialog during a blocked save and verifies that session shutdown is not called.

🧠 Review effort: Balanced

@bluegrassiot

Copy link
Copy Markdown
Owner Author

Addressed all three body-only findings in Copilot review 5547811617 in 17622e1:

  • Disconnect notification after successful connection cancellation: caller cancellation now retires only its own generation, waits for its resource cleanup, and uses the existing exactly-once, supersession-aware deliberate disconnect dispatcher. Regression coverage verifies raw disconnect before managed disconnect/state, never-connected silence, supersession suppression, and a disconnect subscriber awaiting Stop. The original regression tests failed before the fix; the strengthened main test blocks a connected-state subscriber with the raw client connected.
  • Cancel during reconnect cleanup: the connecting footer appears before graceful shutdown is awaited, so Cancel cancels startup in place rather than closing the dialog. Cancel_DuringBlockedGracefulShutdown_CancelsStartupInPlace failed before the fix and now verifies the dialog and entered values remain, no discard prompt appears, and no new MQTT attempt or broker reset occurs.
  • Blocked fake-connect test cleanup: both lifecycle tests now release their gates in finally, retaining their assertions while ensuring an assertion failure cannot hang teardown. The new UI regression also releases its gate in finally.

Validation: full solution build with zero warnings/errors; unfiltered Core 1,787 passed / 1 skipped, UI 1,736 passed / 1 skipped, integration 59 passed; 89% line coverage; full-tree formatting, security, comment, blocking-await and file-length gates pass. CI-script tests: 361 run, 2 skipped, successful. Commit and push hooks enabled and passing. Independent core review found no blocker.

These findings exist only in the review body, so there are no corresponding threads to resolve. Fresh reviews of 17622e1 are required; no merge performed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The cross-layer changes to concurrent callbacks, reconnects, and resource teardown warrant final human review.

0 open findings

🧠 Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 16, 2026 18:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The cross-layer lifecycle rewrite has unresolved cancellation and disposal races and needs final human review.

2 open findings

🧠 Review effort: Balanced

Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Attempts.cs
Comment thread src/MqttProbe.UI/Components/Browser/Connection/ConnectionDialog.razor Outdated
Copilot AI balanced review requested due to automatic review settings October 16, 2026 21:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Cancellation-registration lifetime races can lose requested cancellation or tear down a session after startup returns.

2 open issues
2 resolved since last review

Review effort: Balanced

Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Attempts.cs Outdated
Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Attempts.cs Outdated
Copilot AI balanced review requested due to automatic review settings October 16, 2026 22:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The concurrency-sensitive lifecycle rewrite has unresolved cancellation and teardown issues that require correction and final human review.

1 open issue
1 previously missed issue

In code that hasn't changed since last review

Medium severity Canceled connect failure incorrectly emits disconnect notifications

src/​MqttProbe.Core/​Services/​Mqtt/​MqttManagedClient.Teardown.cs:13

If the raw Connected handler runs before cancellation and ConnectAsync then throws (including a wrapped cancellation), CompleteFailure settles Canceled but leaves WasConnected true. This condition therefore emits managed disconnect and state notifications for an attempt whose success was never reported. Unlike CompleteSuccess, the exception path does not clear that flag. Exclude canceled outcomes here, while preserving notifications for established connections and successful reconnects. Add an exception-path variant of the raw-connected cancellation race test.

2 resolved since last review

Review effort: Balanced

@bluegrassiot

Copy link
Copy Markdown
Owner Author

Addressed the additional body-only finding in Copilot review 5554506653 in 7842cf9: NotifyDeliberateStopAsync now excludes settled Canceled outcomes, including direct and wrapped exception paths after the raw Connected handler. Regression tests prove raw cleanup occurs without managed connected/failure/disconnect/state notifications; established cancellation and successful reconnect notifications remain covered. This commit also scopes dialog teardown and certificate ownership to its attempt (inline thread reply has details). Validation passed: full solution build, unfiltered Core/UI/integration suites, 89.2% line coverage, browser checks, formatting, security, and repository gates.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The concurrency and certificate-lifetime refactor needs human review, particularly around the unresolved reconnect cleanup race.

2 open issues
1 resolved since last review

Review effort: Balanced

Comment thread src/MqttProbe.Core/Services/Mqtt/MqttManagedClient.Attempts.cs Outdated
Comment thread tests/MqttProbe.Core.Tests/Services/Mqtt/MqttManagedClientScopedStopTests.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The cross-cutting concurrency and certificate-lifetime changes warrant human review, and an unresolved test cleanup path can hang on failure.

1 open issue
2 resolved since last review

Review effort: Balanced

@bluegrassiot
bluegrassiot requested a balanced review from Copilot October 17, 2026 00:55
@bluegrassiot
bluegrassiot removed the request for review from Copilot October 17, 2026 00:57
@bluegrassiot
bluegrassiot merged commit fec175c into release/v1.0.10 Oct 17, 2026
9 checks passed
@bluegrassiot
bluegrassiot deleted the fix/150-cancel-connection branch October 17, 2026 02:36
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.

2 participants