Skip to content

Drop a stale active-instance pin instead of failing forever - #1266

Open
lgarczyn wants to merge 2 commits into
CoplayDev:betafrom
lgarczyn:fix/stale-instance-pin
Open

Drop a stale active-instance pin instead of failing forever#1266
lgarczyn wants to merge 2 commits into
CoplayDev:betafrom
lgarczyn:fix/stale-instance-pin

Conversation

@lgarczyn

@lgarczyn lgarczyn commented Jul 13, 2026

Copy link
Copy Markdown

A pin outlives the editor it names.
While it stayed pinned it also suppressed auto-select. Every later call then failed with no_unity_session. Neither waiting nor relaunching the editor recovered. The editor re-registers under its own name, not the name the pin holds. The pin is now dropped once another instance is registered. An empty registry means a domain reload is in flight, so the pin is kept.

Description

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)
  • Test update

Changes Made

Compatibility / Package Source

  • Unity version(s) tested:
  • Package source used (#beta, #main, tag, branch, or file:):
  • Resolved commit hash from Packages/packages-lock.json (if using a Git package URL):

Testing/Screenshots/Recordings

  • Python tests (cd Server && uv run pytest tests/ -v)
  • Unity EditMode tests
  • Unity PlayMode tests
  • Package import/compile check
  • Not applicable (explain why in Additional Notes)

Documentation Updates

  • I have added/removed/modified tools or resources
  • If yes, I have updated all documentation files using:
    • The LLM prompt at tools/UPDATE_DOCS_PROMPT.md (recommended)
    • Manual review of the generated changes

Related Issues

Additional Notes

Summary by CodeRabbit

  • Bug Fixes
    • Detects when a selected Unity instance is no longer available and clears the stale selection.
    • Reports a clear error instead of silently switching to another project.
    • Preserves selections during temporary periods when no instances are registered or while an instance is reconnecting.

@coderabbitai

coderabbitai Bot commented Jul 13, 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: Team

Run ID: 7673d43b-e7f1-44c9-91d8-02a006d3f475

📥 Commits

Reviewing files that changed from the base of the PR and between a8cd30a and 0dc7d20.

📒 Files selected for processing (3)
  • Server/src/transport/unity_instance_middleware.py
  • Server/tests/integration/test_instance_autoselect.py
  • Server/tests/test_stale_instance_pin.py

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

UnityInstanceMiddleware now validates HTTP session-pinned Unity instances before auto-selection. It polls during the reconnect window, preserves pins during empty discovery results, and clears and rejects pins that remain absent. New tests cover these cases and prevent silent retargeting.

Changes

Stale pin cleanup

Layer / File(s) Summary
Detect and reject stale pins
Server/src/transport/unity_instance_middleware.py
The middleware re-discovers pinned instances, polls for their return, preserves pins when discovery is empty, and raises ValueError after clearing pins that remain absent.
Validate pin lifecycle in tests
Server/tests/test_stale_instance_pin.py, Server/tests/integration/test_instance_autoselect.py
Tests cover stale-pin rejection, valid-pin retention, empty-registry retention, reconnect polling, prevention of auto-selection retargeting, and bounded-wait stubs.

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

Merge Risk: 🔵 Low · up to 0dc7d

The change adds bounded recovery for stale editor pins and prevents the current request from silently switching projects, but cleanup may leave an obsolete routing target or clear a newer pin when requests overlap in the same session. This is a bounded merge-readiness risk that should have explicit owner follow-up.

Sequence Diagram(s)

sequenceDiagram
  participant Request
  participant UnityInstanceMiddleware
  participant PluginHub
  participant SessionState
  Request->>UnityInstanceMiddleware: inject Unity instance
  UnityInstanceMiddleware->>PluginHub: discover registered instances
  PluginHub-->>UnityInstanceMiddleware: return registry
  UnityInstanceMiddleware->>PluginHub: poll during reconnect window
  UnityInstanceMiddleware->>SessionState: clear stale active-instance state
  UnityInstanceMiddleware-->>Request: raise ValueError for absent pin
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the problem and intended behavior, but most required template information is missing. The change list, change type, compatibility details, testing status, documentation status… Complete the required template sections. Select the applicable change types, list the implementation and test changes, provide Unity and package-source details or mark them not applicable, record executed tests, document documentation impac…
Docstring Coverage ⚠️ Warning Docstring coverage is 41.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 24 functions across 3 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: handling stale active-instance pins without indefinite failure.
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.
Full details: Description check

Explanation

The description explains the problem and intended behavior, but most required template information is missing. The change list, change type, compatibility details, testing status, documentation status, related issues, and additional notes are not completed.

Resolution

Complete the required template sections. Select the applicable change types, list the implementation and test changes, provide Unity and package-source details or mark them not applicable, record executed tests, document documentation impact, add related issue references if applicable, and include relevant additional notes.

  • Fix all pre-merge checks with AI
✨ 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.

@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/unity_instance_middleware.py (1)

215-243: 🚀 Performance & Scalability | 🔵 Trivial

Stale-pin detection adds a PluginHub.get_sessions() round-trip to every HTTP request with a pinned instance.

The logic is correct — empty registry preserves the pin, a registered pin is kept, and a stale pin is dropped and cleared. However, _drop_stale_pin calls _discover_instances (which calls PluginHub.get_sessions) on every request that has a non-null active_instance, even when the pin is valid. This adds a network round-trip to the hot path alongside the existing PluginHub._resolve_session_id call at line 403.

Consider caching the discovery result for a short TTL (similar to the _tool_visibility_refresh_interval_seconds pattern already used in this class) or reusing the session data fetched here to avoid the redundant _resolve_session_id call when the pin is confirmed valid.
[medium_effort_and_high_reward]

🤖 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/unity_instance_middleware.py` around lines 215 - 243,
The _drop_stale_pin path performs an extra PluginHub.get_sessions round-trip on
every request with an active pin. Add short-TTL caching for _discover_instances
using the class’s existing _tool_visibility_refresh_interval_seconds pattern, or
reuse the fetched session data through the subsequent request flow to avoid
redundant discovery/resolution when the pin is valid; preserve empty-registry
and stale-pin clearing behavior.
🤖 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/unity_instance_middleware.py`:
- Around line 215-243: The _drop_stale_pin path performs an extra
PluginHub.get_sessions round-trip on every request with an active pin. Add
short-TTL caching for _discover_instances using the class’s existing
_tool_visibility_refresh_interval_seconds pattern, or reuse the fetched session
data through the subsequent request flow to avoid redundant discovery/resolution
when the pin is valid; preserve empty-registry and stale-pin clearing behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5adc114b-949f-4895-b72a-f095dd01dd9d

📥 Commits

Reviewing files that changed from the base of the PR and between dcf74e1 and a8cd30a.

📒 Files selected for processing (2)
  • Server/src/transport/unity_instance_middleware.py
  • Server/tests/test_stale_instance_pin.py

singam96 added a commit to singam96/unity-mcp that referenced this pull request Aug 30, 2026
…ailing forever (resolved conflict with CoplayDev#1194 launch dir)
singam96 added a commit to singam96/unity-mcp that referenced this pull request Aug 30, 2026
…n with explicit routing (keep HEAD stale-pin + middleware fixes)

- Server/src/transport/unity_instance_middleware.py: keep HEAD's _file_uri_to_path + _strip_assets (HEAD) + pr/981's _get_http_request_for_binding; keep both imports (parse_qs, unquote, urlparse); resolve conflict in _inject_unity_instance to keep both _drop_stale_pin (HEAD, CoplayDev#1266) and allow_autoselect (pr/981) — now checks stale pin then autoselect with allow_autoselect flag
- Server/src/transport/legacy/unity_connection.py: keep HEAD's CoplayDev#1023 available_ids error (more recent than pr/981's suggestions dict)
- New file Server/src/services/registry/unity_targeting.py kept from pr/981
- Other Server files auto-merged (resource_registry, tool_registry, plugin_hub, etc.)
@Scriptwonder

Copy link
Copy Markdown
Collaborator

Hi there, I think this might need to be rebased as well first. Thanks for the PR and let me know if you can resolve these comments!

Two changes are needed, and website/docs/architecture/instance-routing.md:181-183 specifies the shape: "Clearing the pin automatically is not the fix. Clearing it and then selecting the one remaining Editor is #1023's exact harm arriving through a different door. The acceptable shape is to clear only after the reconnect wait has expired for that specific hash, and never to retarget silently."
So first, gate the drop on the per-hash reconnect wait that _resolve_session_id already performs (UNITY_MCP_SESSION_RESOLVE_MAX_WAIT_S, default 20s — plugin_hub.py:886-887); _drop_stale_pin currently runs upstream of that poll, so a routine domain reload trips it before the wait ever gets a chance. Second, after dropping, raise an error naming the departed instance instead of returning None and falling through to _maybe_autoselect_instance — as written, a call pinned to project A lands in project B, returns success, and re-pins B.

lgarczyn added a commit to lgarczyn/unity-mcp that referenced this pull request Sep 1, 2026
Review feedback on CoplayDev#1266. instance-routing.md is explicit that clearing a pin
and then selecting the one remaining Editor is CoplayDev#1023's harm through another
door, and that the acceptable shape is to clear only after the reconnect wait
has expired for that hash, never retargeting silently.

_drop_stale_pin ran upstream of the poll in _resolve_session_id, so a routine
domain reload read as a departure. It now gives the pinned hash the same
UNITY_MCP_SESSION_RESOLVE_MAX_WAIT_S window before deciding.

Returning None also let a call pinned to project A fall through to
_maybe_autoselect_instance, land in project B, report success and re-pin B.
A departure now raises and names the instance that went away.

The autoselect tests stub transport.plugin_hub, so their stub gains the
wait helper the middleware now reads.
@lgarczyn
lgarczyn force-pushed the fix/stale-instance-pin branch from a8cd30a to 0dc7d20 Compare September 1, 2026 23:28
@lgarczyn

lgarczyn commented Sep 1, 2026

Copy link
Copy Markdown
Author

Hi there, I think this might need to be rebased as well first. Thanks for the PR and let me know if you can resolve these comments!

Two changes are needed, and website/docs/architecture/instance-routing.md:181-183 specifies the shape: "Clearing the pin automatically is not the fix. Clearing it and then selecting the one remaining Editor is #1023's exact harm arriving through a different door. The acceptable shape is to clear only after the reconnect wait has expired for that specific hash, and never to retarget silently." So first, gate the drop on the per-hash reconnect wait that _resolve_session_id already performs (UNITY_MCP_SESSION_RESOLVE_MAX_WAIT_S, default 20s — plugin_hub.py:886-887); _drop_stale_pin currently runs upstream of that poll, so a routine domain reload trips it before the wait ever gets a chance. Second, after dropping, raise an error naming the departed instance instead of returning None and falling through to _maybe_autoselect_instance — as written, a call pinned to project A lands in project B, returns success, and re-pins B.

All done

A pin outlives the editor it names.
While it stayed pinned it also suppressed auto-select.
Every later call then failed with no_unity_session.
Neither waiting nor relaunching the editor recovered.
The editor re-registers under its own name, not the name the pin holds.
The pin is now dropped once another instance is registered.
An empty registry means a domain reload is in flight, so the pin is kept.
Review feedback on CoplayDev#1266. instance-routing.md is explicit that clearing a pin
and then selecting the one remaining Editor is CoplayDev#1023's harm through another
door, and that the acceptable shape is to clear only after the reconnect wait
has expired for that hash, never retargeting silently.

_drop_stale_pin ran upstream of the poll in _resolve_session_id, so a routine
domain reload read as a departure. It now gives the pinned hash the same
UNITY_MCP_SESSION_RESOLVE_MAX_WAIT_S window before deciding.

Returning None also let a call pinned to project A fall through to
_maybe_autoselect_instance, land in project B, report success and re-pin B.
A departure now raises and names the instance that went away.

The autoselect tests stub transport.plugin_hub, so their stub gains the
wait helper the middleware now reads.
@lgarczyn
lgarczyn force-pushed the fix/stale-instance-pin branch from 0dc7d20 to cdbcb21 Compare September 2, 2026 00:20
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