Skip to content

fix(editor): survive domain reloads in HTTP auto-start and reload-resume (#1229)#1234

Open
Scriptwonder wants to merge 2 commits into
CoplayDev:betafrom
Scriptwonder:fix/issue-1229-reload-resume-race
Open

fix(editor): survive domain reloads in HTTP auto-start and reload-resume (#1229)#1234
Scriptwonder wants to merge 2 commits into
CoplayDev:betafrom
Scriptwonder:fix/issue-1229-reload-resume-race

Conversation

@Scriptwonder

@Scriptwonder Scriptwonder commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1229. Full credit to @jtpistella for the precise two-part root-cause diagnosis in the issue — both mechanisms verified line-for-line before writing this.

Root causes (verified at beta HEAD)

  1. Auto-start dead for the whole session when startup includes a compile: HttpAutoStartHandler's cctor set the reload-surviving SessionState latch before registering the EditorApplication.delayCall — a domain reload wipes the pending delegate, and the latch then short-circuits every later domain load.
  2. Reload-resume permanently lost at multi-pass compiles (e.g. importing a script-bearing asset package): HttpBridgeReloadHandler consumed the one-shot resume flag before the deferred (delayCall) resume ran, and the next beforeAssemblyReload deleted it again because the bridge wasn't running.

Fix

  • Update ticks instead of delayCall: the [InitializeOnLoad] cctor re-arms an EditorApplication.update tick on every domain load, and the latch is written only when the deferred work actually dispatches. Services-not-ready frames retry (bounded per domain); the common case (auto-start off, nothing pending) skips the subscription entirely.
  • Resume flag moved EditorPrefs → SessionState and it now persists until the resume succeeds, is cancelled, or exhausts its retries — instead of being consumed at boundaries where the bridge is down. EditorPrefs was also per-user machine-global (two concurrently open editors could consume/delete each other's pending resume) and survived crashes (a stale flag could resurrect the bridge on the next launch without opt-in). A once-per-session migration deletes the legacy key.
  • TransportManager.StartAsync serialized per mode: concurrent starts coalesce onto one in-flight attempt. Manual Connect, reload-resume, and auto-start all flow through it, and WebSocketTransportClient.StartAsync tears down a live connection first — previously two interleaved starts could bounce a just-established session.
  • Connect-pending marker: a reload that kills the in-flight connect phase no longer strands auto-start; the next domain load finishes connect-only (never re-spawns — StartLocalHttpServer stops a still-booting server first). IServerManagementService.HasManagedServerLaunchHandle answers the "can we watch the launch process die?" question live; without a handle the wait polls to the 5-minute hard cap instead of fail-fasting.
  • Busy gate uses EditorStateCache.GetActualIsCompiling (now internal, reflection bound once as a delegate): raw EditorApplication.isCompiling stays true for a whole play session under Recompile-After-Finished-Playing (EditorApplication.isCompiling False Positive in Play Mode #549), which would otherwise block resume until play exits.
  • User-intent cancel paths (Connect, End Session, transport switch, orphan cleanup) go through named seams (CancelPendingResume / CancelPendingReconnect) that also abort an in-flight retry loop.

Intended behavior changes

  • The HTTP bridge no longer auto-resumes across an editor restart unless Auto-Start on Load is enabled (the old EditorPrefs flag could trigger an unrequested launch-time resume after a crash).
  • An invalid resume no longer replays a ~49s retry loop at every later reload: exhaustion erases the flag (matches the stdio sibling).
  • A reload-interrupted auto-start now recovers (connect-only) instead of silently dying.

Verification

  • Compile-only matrix via tools/check-unity-versions.sh (2021.3.45f2 floor passes locally; CI covers the rest — the floor caught that CompilationPipeline.isCompiling needs reflection there).
  • 23 EditMode tests added (HttpBridgeReloadHandlerTests, HttpAutoStartHandlerTests, TransportManagerTests), including a scenario test replaying the exact multi-pass-compile sequence from the issue, and a pin on the StartAsync coalescing contract. Full EditMode suite: 1002 passed / 0 failed locally.
  • Note: the seam tests cover the decision/flag logic; the "delegate re-arms across a real domain reload" wiring can't be unit-tested on UTF 1.1 — verified manually. A repro pass on Windows per the issue's setup would be a welcome review step.

Relationship to other open PRs (checked before filing)

Known follow-ups (deliberately out of scope)

  • stdio sibling has the same defect class: StdioBridgeReloadHandler.cs:65 (flag deleted at a bridge-down boundary — primary) and :133 (delayCall deferral — secondary). Kept out to respect fix: harden localhost resolution and reload transport resilience on Windows #688's centralized stdio teardown sequencing; same pattern applies.
  • Remaining stdio copies of the isCompiling reflection probe can now consolidate onto EditorStateCache.GetActualIsCompiling.
  • The manual Start Server path (McpConnectionSection.TryAutoStartSessionAsync) lacks the same reload survival; candidate for the same connect-pending pattern.

Summary by CodeRabbit

  • New Features

    • Added reload-safe handling for HTTP auto-start, reconnect resumption, and reload-resume tracking.
    • Improved transport startup so repeated start requests for the same transport share the same in-flight attempt.
  • Bug Fixes

    • Prevented stale resume/reconnect flags from persisting across editor reloads and user transport/session changes (migrated resume state to session-level storage).
    • More accurate “is compiling” detection during Play Mode and compilation pipeline activity.
    • Exposed managed server launch-handle availability for clearer state reporting.
  • Tests

    • Added edit-mode coverage for auto-start/reconnect decisions, reload-resume behavior, and concurrent transport start coalescing.

…ume (CoplayDev#1229)

Auto-start died for the whole session whenever startup included a compile:
the ctor latched SessionState before the delayCall ran, and the reload wiped
the delayCall. Reload-resume died at multi-pass compiles: the one-shot flag
was consumed before the deferred (delayCall) resume ran, and the next
boundary deleted it again.

- Replace delayCall with EditorApplication.update ticks that the
  [InitializeOnLoad] ctor re-arms on every domain load; latch only when the
  deferred work actually dispatches, retry (bounded per domain) while editor
  services are still initializing, and skip the subscription entirely in the
  common case where auto-start is off and nothing is pending.
- Move the resume flag from EditorPrefs (per-user machine-global, survives
  crashes, leaks across concurrently open editors) to SessionState; keep it
  until the resume succeeds, is cancelled, or exhausts its retries, instead
  of consuming it at boundaries where the bridge is down. Manual Connect,
  End Session, transport switch, and orphan cleanup cancel a pending resume
  through a named seam (CancelPendingResume), which also aborts an in-flight
  retry loop; exhaustion erases the flag so later reloads don't replay 49s
  failure loops.
- Serialize TransportManager.StartAsync per mode: concurrent starts coalesce
  onto one in-flight attempt, so a manual Connect can no longer race the
  resume/auto-start loops into bouncing a just-established session
  (WebSocketTransportClient.StartAsync tears down a live connection first).
- A SessionState connect-pending marker lets the next domain load finish an
  auto-start whose connect phase a reload killed — connect-only, never
  re-spawning (StartLocalHttpServer stops a still-booting server first).
  Whether a launch-process handle exists is now answered live by
  ServerManagementService.HasManagedServerLaunchHandle; without one (post-
  reload, or an externally started server) the wait polls to the 5-minute
  hard cap instead of fail-fasting.
- Busy gate uses EditorStateCache.GetActualIsCompiling (now internal, with
  the CompilationPipeline reflection bound once as a delegate): raw
  isCompiling stays true all play session under
  Recompile-After-Finished-Playing (CoplayDev#549).
- One-time (per session) migration deletes the legacy EditorPrefs flag.

The stdio sibling has the same defect class (StdioBridgeReloadHandler.cs:65
delete-when-not-running, :133 delayCall) — follow-up, kept out of scope here,
along with the remaining stdio copies of the isCompiling probe.
@Scriptwonder Scriptwonder added the bug Something isn't working label Jul 4, 2026
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 050ac575-68a8-433d-9b5e-4f02fecb9ed1

📥 Commits

Reviewing files that changed from the base of the PR and between 3015117 and d42c7d3.

📒 Files selected for processing (3)
  • MCPForUnity/Editor/Services/EditorStateCache.cs
  • MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs
  • MCPForUnity/Editor/Services/Transport/TransportManager.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • MCPForUnity/Editor/Services/Transport/TransportManager.cs
  • MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs

📝 Walkthrough

Walkthrough

Reworks HTTP auto-start and reload-resume handling to survive Unity domain reloads by moving state from EditorPrefs to SessionState and replacing delayCall scheduling with update-driven ticks. Adds a managed-launch-handle signal, coalesces TransportManager start calls, updates connection cancellation wiring, and adds tests.

Changes

Reload-safe auto-start and resume

Layer / File(s) Summary
Managed launch handle and compile-state signals
MCPForUnity/Editor/Services/IServerManagementService.cs, MCPForUnity/Editor/Services/ServerManagementService.cs, MCPForUnity/Editor/Services/EditorStateCache.cs
Adds HasManagedServerLaunchHandle and updates compile-state tracking in EditorStateCache for play-mode compilation checks.
HttpAutoStartHandler tick state machine
MCPForUnity/Editor/Services/HttpAutoStartHandler.cs
Replaces delayCall-based startup with update ticks, session latch/pending keys, reconnect handling, and managed-launch-handle-aware waiting.
HttpBridgeReloadHandler SessionState-based resume
MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs
Moves reload-resume state to SessionState, adds migration/cancel helpers, and updates retry behavior and editor-busy gating.
TransportManager start coalescing
MCPForUnity/Editor/Services/Transport/TransportManager.cs
Caches in-flight per-mode start tasks so concurrent StartAsync calls share one attempt.
Connection UI cancellation wiring and prefs cleanup
MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs, MCPForUnity/Editor/Constants/EditorPrefKeys.cs, MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs
Cancels pending resume/reconnect work from connection UI flows and removes the obsolete HTTP resume pref wiring.
Tests for tick decisions, resume flow, and transport coalescing
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/HttpAutoStartHandlerTests.cs, .../HttpBridgeReloadHandlerTests.cs, .../TransportManagerTests.cs, .../TestUtilities.cs, .../Windows_Characterization.cs, *.meta
Adds EditMode coverage for auto-start, reconnect, reload-resume, transport coalescing, and supporting test utilities.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant EditorApplication
  participant HttpAutoStartHandler
  participant SessionState
  participant HttpBridgeReloadHandler
  participant TransportManager

  EditorApplication->>HttpAutoStartHandler: update tick
  HttpAutoStartHandler->>SessionState: read SessionInitKey / ConnectPendingKey
  HttpAutoStartHandler->>HttpBridgeReloadHandler: check IsResumePending
  alt resume pending
    HttpBridgeReloadHandler->>TransportManager: ResumeHttpWithRetriesAsync
    HttpBridgeReloadHandler->>SessionState: erase ResumeSessionKey on completion/failure
  else auto-start or reconnect
    HttpAutoStartHandler->>TransportManager: StartAsync / ReconnectAsync
    HttpAutoStartHandler->>SessionState: set or clear ConnectPendingKey
  end
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 and omits several mandated sections. Reformat it to the repo template and add Type of Change, 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 HTTP domain-reload fix.
Linked Issues check ✅ Passed The changes address #1229's auto-start and reload-resume races with update ticks, SessionState, reconnect recovery, and tests.
Out of Scope Changes check ✅ Passed The transport coalescing, busy-gate cleanup, and test utilities are supporting the #1229 fix, with no clear unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 added a commit to Scriptwonder/unity-mcp that referenced this pull request Jul 4, 2026
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.
@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.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (2)
MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs (1)

164-187: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Keep service-race failures inside the retry loop.

OnAfterAssemblyReloadCore() intentionally schedules resume when config reads race reload, but these pre-start checks can still throw before the attempt try/catch. That can kill the fire-and-forget task and leave ResumeSessionKey set with no retry scheduled.

Suggested fix
-                // Abort retries if the user switched transports while we were waiting.
-                if (!EditorConfigurationCache.Instance.UseHttpTransport)
-                {
-                    SessionState.EraseBool(ResumeSessionKey);
-                    return;
-                }
-
-                // Never bounce a session someone else established while we were waiting
-                // (WebSocketTransportClient.StartAsync tears down a live connection first).
-                if (MCPServiceLocator.TransportManager.IsRunning(TransportMode.Http))
-                {
-                    SessionState.EraseBool(ResumeSessionKey);
-                    return;
-                }
-
                 try
                 {
+                    // Abort retries if the user switched transports while we were waiting.
+                    if (!EditorConfigurationCache.Instance.UseHttpTransport)
+                    {
+                        SessionState.EraseBool(ResumeSessionKey);
+                        return;
+                    }
+
+                    // Never bounce a session someone else established while we were waiting.
+                    if (MCPServiceLocator.TransportManager.IsRunning(TransportMode.Http))
+                    {
+                        SessionState.EraseBool(ResumeSessionKey);
+                        return;
+                    }
+
                     bool started = await MCPServiceLocator.TransportManager.StartAsync(TransportMode.Http);
🤖 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/Services/HttpBridgeReloadHandler.cs` around lines 164 -
187, Keep service-race failures inside the retry loop: in
OnAfterAssemblyReloadCore, move the early transport/state checks that can throw
(such as UseHttpTransport and TransportManager.IsRunning) into the existing
try/catch around TransportManager.StartAsync, or wrap the whole resume attempt
block so any transient service lookup/race exception is caught and triggers the
retry scheduling path. Make sure SessionState.EraseBool(ResumeSessionKey) only
happens on confirmed success or intentional abort, so a thrown pre-start check
doesn’t cancel the fire-and-forget resume task without rescheduling.
MCPForUnity/Editor/Services/EditorStateCache.cs (1)

532-569: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Don’t reflect CompilationPipeline.isCompiling as a public property. Unity 6000.3 doesn’t expose that member publicly, so this delegate will stay null and Play mode will fall back to EditorApplication.isCompiling, bringing back the false-positive busy state. Use the compilation started/finished events, or switch to a non-public lookup only if that member is present on the supported Unity range.

🤖 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/Services/EditorStateCache.cs` around lines 532 - 569, The
GetActualIsCompiling/PipelineIsCompiling path is reflecting
CompilationPipeline.isCompiling as a public property, but that member isn’t
publicly available on the newer Unity range so the delegate never binds. Update
CreatePipelineIsCompilingDelegate to use a supported signal instead, such as the
compilation started/finished events, or only fall back to a non-public lookup
when the member exists on the target Unity versions. Keep the change localized
to EditorStateCache.GetActualIsCompiling and its helper delegate setup.
🤖 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.

Inline comments:
In `@MCPForUnity/Editor/Services/Transport/TransportManager.cs`:
- Around line 53-61: Validate the transport mode before touching the coalescing
tasks in `TransportManager.StartAsync`; the current ternary treats every
non-`Http` value as `Stdio`, which can incorrectly reuse `_stdioStartTask` for
unsupported enum values. Refactor the lookup and assignment logic into a
`switch` on `mode` so only `TransportMode.Http` and `TransportMode.Stdio` map to
`_httpStartTask` and `_stdioStartTask`, and let invalid modes preserve the
unsupported-mode contract instead of returning an unrelated task.

---

Outside diff comments:
In `@MCPForUnity/Editor/Services/EditorStateCache.cs`:
- Around line 532-569: The GetActualIsCompiling/PipelineIsCompiling path is
reflecting CompilationPipeline.isCompiling as a public property, but that member
isn’t publicly available on the newer Unity range so the delegate never binds.
Update CreatePipelineIsCompilingDelegate to use a supported signal instead, such
as the compilation started/finished events, or only fall back to a non-public
lookup when the member exists on the target Unity versions. Keep the change
localized to EditorStateCache.GetActualIsCompiling and its helper delegate
setup.

In `@MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs`:
- Around line 164-187: Keep service-race failures inside the retry loop: in
OnAfterAssemblyReloadCore, move the early transport/state checks that can throw
(such as UseHttpTransport and TransportManager.IsRunning) into the existing
try/catch around TransportManager.StartAsync, or wrap the whole resume attempt
block so any transient service lookup/race exception is caught and triggers the
retry scheduling path. Make sure SessionState.EraseBool(ResumeSessionKey) only
happens on confirmed success or intentional abort, so a thrown pre-start check
doesn’t cancel the fire-and-forget resume task without rescheduling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f277cbf6-2f05-45fe-89e0-1ebe64b90915

📥 Commits

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

📒 Files selected for processing (17)
  • MCPForUnity/Editor/Constants/EditorPrefKeys.cs
  • MCPForUnity/Editor/Services/EditorStateCache.cs
  • MCPForUnity/Editor/Services/HttpAutoStartHandler.cs
  • MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs
  • MCPForUnity/Editor/Services/IServerManagementService.cs
  • MCPForUnity/Editor/Services/ServerManagementService.cs
  • MCPForUnity/Editor/Services/Transport/TransportManager.cs
  • MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs
  • MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/HttpAutoStartHandlerTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/HttpAutoStartHandlerTests.cs.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/HttpBridgeReloadHandlerTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/HttpBridgeReloadHandlerTests.cs.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TransportManagerTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TransportManagerTests.cs.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Windows/Characterization/Windows_Characterization.cs
💤 Files with no reviewable changes (2)
  • MCPForUnity/Editor/Constants/EditorPrefKeys.cs
  • MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs

Comment thread MCPForUnity/Editor/Services/Transport/TransportManager.cs Outdated
… mode + retry-loop races

Review follow-ups:
- CompilationPipeline.isCompiling does not exist on the supported Unity range
  (reflection probe on 2021.3 and 6000.4: neither public nor non-public), so
  the reflected play-mode double-check never resolved and GetActualIsCompiling
  silently fell back to the raw signal — the CoplayDev#549 false positive was never
  actually mitigated. Track compilationStarted/compilationFinished events
  instead (public across the range); in Play mode trust the event-tracked
  state, outside it the raw signal is reliable.
- TransportManager.StartAsync validates the mode before touching the
  coalescing slots, matching the class's unsupported-mode contract instead of
  routing unknown values to the stdio slot.
- The resume retry loop's transport/IsRunning pre-checks moved inside the
  attempt try: a service read racing the reload boundary now burns a retry
  instead of killing the fire-and-forget task with the flag still set.
@Scriptwonder Scriptwonder added the full-matrix Enable full-matrix CI test label Jul 6, 2026
@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 hardens the Unity Editor-side HTTP bridge lifecycle to survive domain reloads during compilation, ensuring auto-start and reload-resume behave reliably across multi-pass compiles and interrupted startup sequences (Fixes #1229).

Changes:

  • Reworked HTTP auto-start and reload-resume deferral to use reload-safe EditorApplication.update ticks and SessionState-backed flags.
  • Added coalescing in TransportManager.StartAsync to prevent concurrent start races from bouncing live connections.
  • Added EditMode tests covering the new retry/flag semantics and the StartAsync coalescing contract.

Reviewed changes

Copilot reviewed 14 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Windows/Characterization/Windows_Characterization.cs Updates characterization text to reflect the new HTTP resume flag storage semantics.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs Adds a synchronous fake transport client to support transport-lifecycle tests without async test support.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TransportManagerTests.cs.meta Adds Unity meta for the new test script.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TransportManagerTests.cs Adds tests pinning StartAsync coalescing behavior and restart-after-complete behavior.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/HttpBridgeReloadHandlerTests.cs.meta Adds Unity meta for the new test script.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/HttpBridgeReloadHandlerTests.cs Adds tests validating reload-resume flag persistence/consumption across multi-pass reload boundaries.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/HttpAutoStartHandlerTests.cs.meta Adds Unity meta for the new test script.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/HttpAutoStartHandlerTests.cs Adds tests for tick decision logic, latch semantics, and reconnect-pending behavior.
MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs Removes the legacy HTTP resume EditorPrefs key from the prefs window listing.
MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs Routes user-intent cancel paths through CancelPendingResume / CancelPendingReconnect seams.
MCPForUnity/Editor/Services/Transport/TransportManager.cs Coalesces concurrent StartAsync calls per transport mode onto one in-flight attempt.
MCPForUnity/Editor/Services/ServerManagementService.cs Exposes whether the current domain holds a managed server Process handle.
MCPForUnity/Editor/Services/IServerManagementService.cs Adds HasManagedServerLaunchHandle to support post-reload reconnect wait behavior.
MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs Moves HTTP resume flag to SessionState, adds migration cleanup, and defers resume via update tick + bounded retry semantics.
MCPForUnity/Editor/Services/HttpAutoStartHandler.cs Replaces delayCall-based auto-start with reload-safe update tick + bounded “services not ready” retries and connect-pending reconnect.
MCPForUnity/Editor/Services/EditorStateCache.cs Reworks the “actual compiling” signal to use compilation start/finish events for play-mode false positives (#549).
MCPForUnity/Editor/Constants/EditorPrefKeys.cs Removes the legacy HTTP resume EditorPrefs key constant.
Files not reviewed (3)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/HttpAutoStartHandlerTests.cs.meta: Generated file
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/HttpBridgeReloadHandlerTests.cs.meta: Generated file
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/TransportManagerTests.cs.meta: Generated file

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

Comment on lines +33 to +38
/// <summary>
/// True when this domain launched the local HTTP server and still holds its Process
/// handle. Handles do not survive domain reloads, so false also means "unknown" —
/// callers should wait rather than fail fast.
/// </summary>
bool HasManagedServerLaunchHandle { get; }
Comment on lines +40 to +45
const string migratedKey = "MCPForUnity.ResumeHttpAfterReload.Migrated";
if (!SessionState.GetBool(migratedKey, false))
{
EditorPrefs.DeleteKey(ResumeSessionKey);
SessionState.SetBool(migratedKey, true);
}
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.

[Bug] v10.0.0: HTTP auto-start and reload-resume both lost to delayCall/domain-reload race (Windows)

2 participants