Skip to content

fix: stop tearing down healthy HTTP sessions on transient probe misses (#1207)#1237

Merged
Scriptwonder merged 3 commits into
CoplayDev:betafrom
Scriptwonder:fix/issue-1207-orphan-detector-debounce
Jul 11, 2026
Merged

fix: stop tearing down healthy HTTP sessions on transient probe misses (#1207)#1237
Scriptwonder merged 3 commits into
CoplayDev:betafrom
Scriptwonder:fix/issue-1207-orphan-detector-debounce

Conversation

@Scriptwonder

@Scriptwonder Scriptwonder commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Addresses the dominant churn source in #1207, following the fix order @dsarno proposed from the evidence bundle.

Problem

Two amplifiers turned ordinary reload/test boundaries into no_unity_session wedges:

  1. Hair-trigger orphan detector. McpConnectionSection.UpdateConnectionStatus ends an active session the moment one cached reachability reading is false — and that reading is a lone TCP connect with a 50 ms wait, cached 0.75 s. On a machine busy with a test run or domain reload the probe times out against a perfectly healthy server; the detector then force-stops the bridge (13 teardowns / 147 socket closures in one session in the macOS / HTTP transport: bridge WebSocket repeatedly drops (close 1005) on reload/test boundaries → no_unity_session #1207 evidence bundle), feeding the reconnect churn that outruns the server's session-resolve window.
  2. Neutered escape hatch. UNITY_MCP_SESSION_RESOLVE_MAX_WAIT_S is documented as configurable but was clamped to a ceiling equal to its default (20 s), so users whose reloads legitimately exceed 20 s could not actually extend the window.

Fix

  • Orphan teardown now requires 3 consecutive failed polls at the 0.75 s cadence (~2.25 s of continuous unreachability); readings taken while the editor is compiling/importing don't count, and detection is skipped while busy. The decision is an internal static predicate with tests.
  • Probe connect wait raised 50 ms → 250 ms, implemented as an overall budget shared across candidate hosts so the worst-case main-thread wait cannot multiply (a refused connect still fails in ~1 ms; the budget only bites when packets are dropped or the stack is starved).
  • UNITY_MCP_SESSION_RESOLVE_MAX_WAIT_S ceiling raised to 120 s (default unchanged at 20 s); UNITY_MCP_SESSION_READY_WAIT_SECONDS likewise (default 6 s). Both now share one bounded env-read helper with tests.

What this deliberately does not do

Verification

  • cd Server && uv run pytest tests/ -q1313 passed, 3 skipped (5 new env-clamp tests).
  • 7 new EditMode tests covering the teardown predicate matrix (threshold, below-threshold, busy-editor deferral, toggle-in-progress, non-HTTP-local, not-running).
  • Compile-only matrix via tools/check-unity-versions.sh (2021.3 floor passes locally).

Summary by CodeRabbit

  • Bug Fixes
    • Improved local server reachability checks with more tolerant timing and a single overall timeout budget.
    • Reduced unexpected teardown of HTTP Local sessions by requiring multiple consecutive “server down” polls and deferring when the editor is busy or a connection toggle is underway.
    • Hardened wait-time configuration from environment variables, safely clamping values up to 120 seconds and falling back on invalid inputs.
  • Tests
    • Added automated coverage for the wait-time configuration handling and orphaned-session detection logic.

CoplayDev#1207)

The orphaned-session detector ended an active session on a SINGLE stale
reachability reading, and the reading came from a lone 50ms TCP connect
cached for 0.75s — trivially false-negative on a machine busy with test
runs or domain reloads. Evidence bundles in CoplayDev#1207 show 13 teardowns and
147 socket closures in one session from exactly this loop, wedging the
bridge in no_unity_session churn until manual recovery.

- Require 3 consecutive failed polls (0.75s cadence) before declaring a
  session orphaned; probe readings taken while the editor is compiling or
  importing don't count toward teardown, and detection is skipped entirely
  while busy.
- Raise the probe's connect wait 50ms -> 250ms, as an overall budget shared
  across candidate hosts so the worst-case main-thread wait cannot multiply.
- Honor UNITY_MCP_SESSION_RESOLVE_MAX_WAIT_S above 20s (ceiling now 120s;
  default unchanged): the old ceiling equalled the default, silently
  neutering the documented escape hatch for projects whose reloads or test
  boundaries legitimately exceed 20s. Same treatment for
  UNITY_MCP_SESSION_READY_WAIT_SECONDS, and both now share one bounded
  env-read helper.

The remaining piece of CoplayDev#1207 (keepalive reload-awareness in
WebSocketTransportClient) is untouched here: the resume machinery reworked
in CoplayDev#1234 already covers reload boundaries, and the detector debounce
removes the dominant churn source dsarno identified.
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Unity local HTTP reachability probing now uses a longer shared timeout budget, and orphaned session teardown waits for repeated down probes. Server-side wait-time environment parsing is centralized with a higher clamp ceiling and new tests.

Changes

Unity Editor: Reachability probing and orphan-session teardown

Layer / File(s) Summary
Local port reachability timeout budget
MCPForUnity/Editor/Services/ServerManagementService.cs
Increases the overall probe timeout from 50ms to 250ms and shares that budget across candidate hosts.
Orphan-session teardown policy
MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs, TestProjects/UnityMCPTests/Assets/Tests/EditMode/Windows/McpConnectionSectionOrphanDetectionTests.cs, TestProjects/UnityMCPTests/Assets/Tests/EditMode/Windows/McpConnectionSectionOrphanDetectionTests.cs.meta
Adds consecutive down-poll tracking and threshold-based teardown checks, with NUnit coverage for session, editor, toggle, and transport states.

Server-side bounded wait-time environment handling

Layer / File(s) Summary
Bounded env wait-time helper and call sites
Server/src/transport/plugin_hub.py, Server/tests/test_plugin_hub_wait_env.py
Centralizes parsing and clamping for session resolve and readiness waits, raising the ceiling from 20s to 120s and testing default, override, invalid, negative, and ceiling cases.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant UpdateStartHttpButtonState
  participant UpdateConnectionStatus
  participant ShouldEndOrphanedSession

  UpdateStartHttpButtonState->>UpdateStartHttpButtonState: probe local HTTP reachability
  UpdateStartHttpButtonState->>UpdateStartHttpButtonState: update consecutiveServerDownPolls
  UpdateConnectionStatus->>ShouldEndOrphanedSession: evaluate session and editor state
  ShouldEndOrphanedSession-->>UpdateConnectionStatus: return teardown decision
  UpdateConnectionStatus->>UpdateConnectionStatus: end orphaned session when allowed
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: dsarno

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed but does not follow the required template sections for type, changes made, compatibility, testing, and related issues. Add the missing template sections: Description, Type of Change, Changes Made, Compatibility/Package Source, Testing, Documentation Updates, Related Issues, and Additional Notes.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main fix to HTTP session teardown behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Scriptwonder Scriptwonder added the bug Something isn't working label Jul 4, 2026
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 91.66667% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
Server/src/transport/plugin_hub.py 91.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@Scriptwonder Scriptwonder added the safe-to-test Triggers CI checks label Jul 4, 2026
@Scriptwonder

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs (1)

342-366: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reset the orphaned-poll counter when the session starts
consecutiveServerDownPolls only clears after UpdateStartHttpButtonState() sees the server up again, so a stale count can survive a successful StartAsync() and immediately satisfy orphan detection before the next fresh probe. Clear it when the session transitions to running.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs`
around lines 342 - 366, The orphaned-session detection in UpdateConnectionStatus
can be triggered by a stale consecutiveServerDownPolls count after the session
transitions to running. Reset consecutiveServerDownPolls when the connection
successfully starts, and make sure the reset happens in the start-path tied to
StartAsync/UpdateStartHttpButtonState so ShouldEndOrphanedSession only uses
fresh probes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs`:
- Around line 342-366: The orphaned-session detection in UpdateConnectionStatus
can be triggered by a stale consecutiveServerDownPolls count after the session
transitions to running. Reset consecutiveServerDownPolls when the connection
successfully starts, and make sure the reset happens in the start-path tied to
StartAsync/UpdateStartHttpButtonState so ShouldEndOrphanedSession only uses
fresh probes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 39ada2ce-19ef-4b3b-955c-1851ab24acdd

📥 Commits

Reviewing files that changed from the base of the PR and between d87ca19 and 18158fa.

📒 Files selected for processing (6)
  • MCPForUnity/Editor/Services/ServerManagementService.cs
  • MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs
  • Server/src/transport/plugin_hub.py
  • Server/tests/test_plugin_hub_wait_env.py
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Windows/McpConnectionSectionOrphanDetectionTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Windows/McpConnectionSectionOrphanDetectionTests.cs.meta

Review follow-up: a streak accumulated while no session was running (detector
inert, counter still counting) survived into a freshly started session and
could satisfy orphan detection before the first post-start probe refreshed.
Reset on the not-running -> running transition, which covers every start path
(manual Connect, auto-start, resume) since UpdateConnectionStatus runs on the
UI tick.
@Scriptwonder Scriptwonder marked this pull request as ready for review July 7, 2026 00:30
Copilot AI review requested due to automatic review settings July 7, 2026 00:30

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.

Pull request overview

This PR reduces unnecessary HTTP transport session churn by debouncing the Unity-side “orphaned session” teardown logic, making local reachability probing less hair-trigger, and by allowing the server-side session resolve/ready wait env overrides to extend beyond the previous hard 20s ceiling.

Changes:

  • Debounce orphaned-session teardown to require 3 consecutive failed reachability polls, and skip/defer teardown while the editor is busy (compile/import) or a toggle is in progress.
  • Increase local HTTP reachability probe connect budget (50ms → 250ms) and share that budget across candidate hosts to cap worst-case UI-thread blocking.
  • Add a bounded env-read helper for PluginHub waits and extend max clamp to 120s, with new unit tests.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Windows/McpConnectionSectionOrphanDetectionTests.cs.meta Adds Unity asset metadata for the new EditMode regression test.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Windows/McpConnectionSectionOrphanDetectionTests.cs Adds EditMode regression tests for the orphaned-session teardown predicate.
Server/tests/test_plugin_hub_wait_env.py Adds tests for bounded/clamped env overrides for session resolve waits.
Server/src/transport/plugin_hub.py Introduces shared bounded env parsing for resolve/ready waits and raises the clamp ceiling.
MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs Implements debounced orphan detection with busy-editor deferral and a testable predicate.
MCPForUnity/Editor/Services/ServerManagementService.cs Increases local probe timeout and applies a shared overall timeout budget across probe hosts.
Files not reviewed (1)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Windows/McpConnectionSectionOrphanDetectionTests.cs.meta: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +49 to +54
try:
value = float(raw)
except ValueError as e:
logger.warning("Invalid %s=%r, using default %s: %s", name, raw, default_s, e)
value = default_s
return max(0.0, min(value, max_s))
Comment on lines +33 to +35
def test_invalid_falls_back_to_default(monkeypatch):
monkeypatch.setenv(ENV, "not-a-number")
assert _read_bounded_wait_env(ENV, default_s=20.0, max_s=120.0) == 20.0
@Scriptwonder Scriptwonder added the full-matrix Enable full-matrix CI test label Jul 11, 2026
Semantic coexistence resolution in McpConnectionSection.cs: keep both
sibling debounce mechanisms intact — beta's health-check verification
debounce (UnhealthyVerificationThreshold / ShouldReportUnhealthy /
consecutiveVerifyFailures, c344625) and this branch's orphaned-session
down-poll debounce (OrphanedSessionDownPollThreshold /
ShouldEndOrphanedSession / consecutiveServerDownPolls + reset on
session start). Both EditMode test files
(McpConnectionSectionHealthDebounceTests, McpConnectionSectionOrphanDetectionTests)
are preserved.

Claude-Session: https://claude.ai/code/session_01C8TU8ibk8gv3h4LqBxPbQi

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
Server/src/transport/plugin_hub.py (1)

928-935: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider narrowing the broad exception catch in _available_instance_ids.

Ruff flags line 934 as BLE001 (blind except Exception). This is an error-path fallback returning [], so broad catching is defensible. If feasible, narrowing to (RegistryError, KeyError, RuntimeError) or at minimum adding a logger.debug would improve diagnosability without changing behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Server/src/transport/plugin_hub.py` around lines 928 - 935, In
`_available_instance_ids`, avoid the blind `except Exception` by catching the
specific registry-related exceptions that `_registry.list_sessions` can raise,
such as `RegistryError`, `KeyError`, and `RuntimeError`; if narrowing is not
feasible, retain the fallback behavior but add a `logger.debug` call with
exception context before returning `[]`.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@Server/src/transport/plugin_hub.py`:
- Around line 928-935: In `_available_instance_ids`, avoid the blind `except
Exception` by catching the specific registry-related exceptions that
`_registry.list_sessions` can raise, such as `RegistryError`, `KeyError`, and
`RuntimeError`; if narrowing is not feasible, retain the fallback behavior but
add a `logger.debug` call with exception context before returning `[]`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aff63db1-8218-4cc6-9360-af3f81e5d8b4

📥 Commits

Reviewing files that changed from the base of the PR and between 2c8ae3e and 9eaadd8.

📒 Files selected for processing (2)
  • MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs
  • Server/src/transport/plugin_hub.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs

@Scriptwonder Scriptwonder merged commit 6ce0ff1 into CoplayDev:beta Jul 11, 2026
8 checks passed
@Scriptwonder Scriptwonder deleted the fix/issue-1207-orphan-detector-debounce branch July 11, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working full-matrix Enable full-matrix CI test safe-to-test Triggers CI checks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants