Skip to content

Plugin eviction, CLI camera/graphics, minor fixes#911

Merged
Scriptwonder merged 2 commits intoCoplayDev:betafrom
Scriptwonder:bug-fix-and-doc-update
Mar 10, 2026
Merged

Plugin eviction, CLI camera/graphics, minor fixes#911
Scriptwonder merged 2 commits intoCoplayDev:betafrom
Scriptwonder:bug-fix-and-doc-update

Conversation

@Scriptwonder
Copy link
Collaborator

@Scriptwonder Scriptwonder commented Mar 10, 2026

Handle plugin session evictions and extend CLI/docs; misc Unity tooling fixes.

  • Server: PluginRegistry.register now returns (session, evicted_session_id). PluginHub consumes the evicted id to cancel ping tasks, clear pending commands, remove state, and close the old WebSocket to avoid orphaned connections after reconnection races. Tests updated to unpack the returned tuple.
  • CLI/docs: Add extensive camera, graphics, packages, and texture command examples and raw command snippets across CLI guides and examples; update command reference tables.
  • Unity editor tools: SkyboxOps adds a CustomReflectionTexture accessor to handle Unity API differences (RenderSettings.customReflection vs customReflectionTexture). ManageScript now ignores C# keywords when detecting duplicate methods to prevent false positives; added unit test to cover keyword cases.
  • Misc: Small docstring tweaks in services/tools/utils.py and skill docs updated to reference manage_camera screenshots.

Type of Change

Save your change type

  • Bug fix (non-breaking change that fixes an issue)
  • Documentation update- Test update

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 updates following the guide at tools/UPDATE_DOCS.md

Related Issues

Fixes #910

Summary by Sourcery

Handle plugin session eviction on re-registration, expand CLI camera/graphics/packages/texture documentation and examples, and improve Unity editor tooling robustness.

New Features:

  • Document and expose comprehensive CLI usage patterns for camera, graphics, packages, textures, and raw commands, including updated command reference tables.

Bug Fixes:

  • Ensure plugin re-registration evicts any previous session, cancelling ping tasks, clearing pending commands, and closing old WebSocket connections to avoid orphaned sessions.
  • Prevent Unity ManageScript duplicate-method detection from treating C# control-flow keywords as methods, eliminating false-positive errors.
  • Make Unity skybox reflection handling compatible across Unity versions by centralizing access to the custom reflection texture API.

Enhancements:

  • Adjust common screenshot utility docstrings and skill documentation to reflect manage_camera-based screenshot workflows.
  • Update plugin registry tests to account for the new register() return shape including evicted session IDs.
  • Extend CLI example and guide docs with higher-level camera, graphics, packages, and texture operation recipes, and refine feature counts in command tables.

Documentation:

  • Augment CLI usage, guides, and examples with detailed camera/graphics/package/texture command sections and raw command snippets, and update command reference tables and skill docs for screenshot usage.

Tests:

  • Add unit coverage ensuring C# keyword usage inside methods does not trigger duplicate-method errors in ManageScript validation, and update existing tests for the changed plugin registry API.

Summary by CodeRabbit

  • New Features

    • Expanded CLI documentation with comprehensive command examples for Camera, Graphics, Package, and Texture operations.
    • Enhanced duplicate method detection to properly filter C# keywords.
  • Bug Fixes

    • Improved session connection management and cleanup handling.
  • Documentation

    • Updated quick-start guides with current command examples and workflows.
    • Added detailed CLI usage examples across multiple command categories.

Handle plugin session evictions and extend CLI/docs; misc Unity tooling fixes.

- Server: PluginRegistry.register now returns (session, evicted_session_id). PluginHub consumes the evicted id to cancel ping tasks, clear pending commands, remove state, and close the old WebSocket to avoid orphaned connections after reconnection races. Tests updated to unpack the returned tuple.
- CLI/docs: Add extensive camera, graphics, packages, and texture command examples and raw command snippets across CLI guides and examples; update command reference tables.
- Unity editor tools: SkyboxOps adds a CustomReflectionTexture accessor to handle Unity API differences (RenderSettings.customReflection vs customReflectionTexture). ManageScript now ignores C# keywords when detecting duplicate methods to prevent false positives; added unit test to cover keyword cases.
- Misc: Small docstring tweaks in services/tools/utils.py and skill docs updated to reference manage_camera screenshots.
Copilot AI review requested due to automatic review settings March 10, 2026 14:38
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Mar 10, 2026

Reviewer's Guide

Implements proper cleanup of evicted Unity plugin sessions during registration, expands CLI/docs with detailed camera/graphics/package/texture command examples and raw usages, and hardens Unity editor tooling for skybox reflection handling and script duplicate-method detection (with new tests).

Sequence diagram for plugin session registration and eviction handling

sequenceDiagram
    actor UnityPlugin
    participant PluginHub
    participant PluginRegistry
    participant Connections
    participant PingTasks
    participant PendingCommands
    participant EvictedWS

    UnityPlugin->>PluginHub: RegisterMessage(session_id, project_hash, user_id, ...)
    PluginHub->>UnityPlugin: RegisteredMessage(session_id)

    PluginHub->>PluginRegistry: register(session_id, project_hash, user_id, ...)
    PluginRegistry-->>PluginHub: session, evicted_session_id

    rect rgb(235,235,255)
        alt evicted_session_id is not None
            PluginHub->>Connections: pop(evicted_session_id) as evicted_ws
            PluginHub->>PingTasks: pop(evicted_session_id) as old_ping
            alt old_ping exists and not done
                PluginHub->>PingTasks: old_ping.cancel()
            end
            PluginHub->>PluginHub: remove last_pong[evicted_session_id]
            PluginHub->>PendingCommands: find entries where session_id == evicted_session_id
            loop each pending command for evicted_session_id
                PluginHub->>PendingCommands: future.set_exception(PluginDisconnectedError)
                PluginHub->>PendingCommands: remove(command_id)
            end
        else no previous session
            PluginHub->>PluginHub: skip eviction cleanup
        end
    end

    PluginHub->>Connections: connections[session.session_id] = websocket
    PluginHub->>PluginHub: last_pong[session_id] = now
    PluginHub->>PingTasks: ping_tasks[session_id] = create_task(ping_loop)

    alt evicted_ws is not None
        PluginHub->>EvictedWS: close(evicted_ws, code=1001)
    end
Loading

Class diagram for PluginRegistry, PluginHub, SkyboxOps, and ManageScript changes

classDiagram

    class PluginSession {
        +session_id : str
        +project_name : str
        +project_hash : str
        +unity_version : str
        +project_path : str
        +user_id : str
        +connected_at : datetime
    }

    class PluginRegistry {
        -_sessions : dict~str, PluginSession~
        -_hash_to_session : dict~str, str~
        -_user_hash_to_session : dict~tuple~str, str~, str~
        +async register(session_id, project_name, project_hash, unity_version, project_path, user_id) : (PluginSession, str)
        +async touch(session_id) : None
    }

    class PluginHub {
        <<async server>>
        +_connections : dict~str, WebSocket~
        +_ping_tasks : dict~str, Task~
        +_last_pong : dict~str, float~
        +_pending : dict~str, dict~str, any~~
        +async _handle_register(websocket, payload) : None
        +async _ping_loop(session_id, websocket) : None
    }

    class PluginDisconnectedError {
        +message : str
    }

    PluginRegistry --> PluginSession : manages
    PluginHub ..> PluginRegistry : uses register
    PluginHub ..> PluginDisconnectedError : raises on eviction

    class SkyboxOps {
        <<static>>
        +Texture CustomReflectionTexture
        +object GetEnvironment(JObject params)
        +object SetReflection(JObject params)
    }

    SkyboxOps ..> Texture : uses
    SkyboxOps ..> RenderSettings : reads_writes
    SkyboxOps ..> AssetDatabase : uses

    class ManageScript {
        +void CheckDuplicateMethodSignatures(string contents, IList warnings)
        -static HashSet~string~ CSharpKeywords
        -static bool IsCSharpKeyword(string name)
        -static int CountTopLevelParams(string paramList)
        -static string ExtractParamTypes(string paramList)
    }

    ManageScript ..> System_Collections_Generic_HashSet : uses
Loading

File-Level Changes

Change Details Files
Handle plugin session eviction on re-register to avoid orphaned connections and stale pending commands.
  • PluginRegistry.register now returns a (session, evicted_session_id) tuple and tracks the evicted session ID when replacing an existing mapping for a project_hash/user_id pair.
  • PluginHub._handle_register consumes the returned evicted_session_id to remove the old connection from internal maps, cancel its ping task, clear last-pong timestamps, fail outstanding pending commands for that session with PluginDisconnectedError, and close the old WebSocket outside the lock.
  • Existing server tests that call PluginRegistry.register were updated to unpack the new tuple return type.
Server/src/transport/plugin_registry.py
Server/src/transport/plugin_hub.py
Server/tests/integration/test_plugin_registry_user_isolation.py
Server/tests/test_transport_characterization.py
Extend CLI guides and examples with camera, graphics, packages, and texture command usage plus raw tool snippets and updated command reference tables.
  • CLI_USAGE_GUIDE.md gains detailed camera/graphics/packages/texture sections, including concrete command invocations and screenshot/batch examples, along with additional raw manage_* snippets.
  • docs/guides/CLI_USAGE.md and CLI_EXAMPLE.md are expanded with focused "Operations" sections for camera, graphics, packages, and textures, and the command reference table is updated to document the new subcommand sets and updated probuilder capabilities.
  • Skill docs are updated to reference manage_camera-based screenshots instead of manage_scene, aligning guidance with the current screenshot tooling.
Server/src/cli/CLI_USAGE_GUIDE.md
docs/guides/CLI_USAGE.md
docs/guides/CLI_EXAMPLE.md
unity-mcp-skill/SKILL.md
Improve Unity editor tooling for skybox reflection API compatibility and script duplicate-method detection, with tests for keyword false positives.
  • SkyboxOps introduces a CustomReflectionTexture property to abstract over RenderSettings.customReflection/customReflectionTexture differences across Unity versions and uses it wherever custom reflection cubemaps are read or set.
  • ManageScript.CheckDuplicateMethodSignatures now ignores C# keywords when interpreting method-like matches by maintaining a HashSet of language keywords and skipping them during duplicate detection.
  • A new ManageScriptValidation test ensures duplicate detection does not treat control flow constructs and other keywords as duplicate methods, reducing false positives.
MCPForUnity/Editor/Tools/Graphics/SkyboxOps.cs
MCPForUnity/Editor/Tools/ManageScript.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptValidationTests.cs
Tighten shared screenshot utilities docstrings to reflect current usage pattern.
  • Updated comments in screenshot utility helpers to describe that sharing is currently for manage_camera rather than both manage_scene and manage_camera.
Server/src/services/tools/utils.py

Assessment against linked issues

Issue Objective Addressed Explanation
#910 Update SkyboxOps to avoid using RenderSettings.customReflectionTexture on Unity versions (e.g., 2021.3.45f2) where that API does not exist, restoring compilation compatibility.

Possibly linked issues

  • #(not specified): PR’s new eviction logic cleans up superseded plugin sessions, fixing beta HTTP reconnection “no session” behavior described in issue.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 10, 2026

📝 Walkthrough

Walkthrough

The PR introduces cross-version compatibility for Unity reflection texture access in SkyboxOps.cs, enhances duplicate method detection by filtering C# keywords, implements session eviction tracking in the plugin registry with cleanup procedures, and significantly expands CLI/documentation with command examples for camera, graphics, package, and texture operations.

Changes

Cohort / File(s) Summary
Cross-version Compatibility
MCPForUnity/Editor/Tools/Graphics/SkyboxOps.cs
Introduces private static CustomReflectionTexture property via conditional compilation to unify reflection texture access across Unity versions, replacing direct RenderSettings.customReflectionTexture usage in GetEnvironment and SetReflection methods. Addresses Unity 2021.3.45f2 compatibility issue.
Duplicate Method Signature Filtering
MCPForUnity/Editor/Tools/ManageScript.cs, TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptValidationTests.cs
Adds C# keyword filtering in duplicate method detection via new CSharpKeywords set and IsCSharpKeyword() helper method. Introduces test case validating that keyword-like constructs (if, for, while, etc.) are not flagged as duplicate methods.
Session Eviction & Registry Management
Server/src/transport/plugin_hub.py, Server/src/transport/plugin_registry.py, Server/tests/integration/test_plugin_registry_user_isolation.py, Server/tests/test_transport_characterization.py
Updates registry.register() return signature to tuple (session, evicted_session_id). Plugin hub now handles eviction cleanup: closes previous WebSocket, cancels ping loop, clears pong state, and fails in-flight commands with PluginDisconnectedError. Test sites updated to unpack new tuple return value.
Minor Documentation Updates
Server/src/services/tools/utils.py
Docstring text updates in extract_screenshot_images and build_screenshot_params (cosmetic only, no functional changes).
CLI Usage Documentation
Server/src/cli/CLI_USAGE_GUIDE.md, docs/guides/CLI_EXAMPLE.md, docs/guides/CLI_USAGE.md
Expands documented command surface with extensive examples for Camera Operations (ping, list, create, set-target, set-lens, etc.), Graphics Operations (volumes, render pipeline, baking, URP), Package Operations (search, add/remove, registries), Texture Operations (create, modify, delete), and raw command access patterns.
Skill Documentation
unity-mcp-skill/SKILL.md
Updates Quick Start and agentic workflow examples to use manage_camera(action="screenshot") instead of manage_scene for screenshot capture, adding multiview screenshot example (manage_camera(action="screenshot_multiview")).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant PluginHub
    participant PluginRegistry
    participant OldSession
    participant NewSession
    participant Connections

    Client->>PluginHub: register(new_plugin)
    PluginHub->>PluginRegistry: register(new_plugin)
    PluginRegistry->>PluginRegistry: Check for existing session
    alt Existing session found
        PluginRegistry->>OldSession: Mark as evicted
        PluginRegistry->>PluginRegistry: Remove old session from _sessions
        PluginRegistry-->>PluginHub: (new_session, evicted_session_id)
    else No existing session
        PluginRegistry-->>PluginHub: (new_session, None)
    end
    
    PluginHub->>Connections: Get old WebSocket (if evicted)
    PluginHub->>OldSession: Cancel ping loop
    PluginHub->>OldSession: Clear last_pong
    PluginHub->>OldSession: Fail in-flight commands with PluginDisconnectedError
    PluginHub->>Connections: Close old WebSocket (outside lock)
    
    PluginHub->>NewSession: Start new ping loop
    PluginHub->>Connections: Track new session
    PluginHub-->>Client: Registration complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Feature/multi-view screenshot #840: Implements multi-view/contact-sheet screenshot feature with ManageScene/manage_scene orbit params and screenshot utilities, directly related to screenshot workflow changes.
  • Skill update #888: Modifies unity-mcp-skill SKILL.md to replace screenshot workflows with manage_camera-based examples, overlaps with camera/screenshot documentation updates.
  • [Feature] Manage_graphics #890: Modifies SkyboxOps.cs for graphics operations, directly related to the custom reflection texture compatibility fix.

Poem

🐰 A fix for Unity's version split,
Keywords now filter without a skip,
Sessions evict with graceful care,
Docs bloom with commands everywhere!
The warren's tools shine bright and clear.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive Plugin eviction, keyword filtering, and docstring updates appear scope-aligned with fixes; however, extensive CLI documentation additions lack explicit connection to the linked issue which only covers Unity API compatibility. Verify that CLI documentation additions in CLI_USAGE_GUIDE.md, CLI_EXAMPLE.md, and CLI_USAGE.md were part of the intended scope; consider linking to additional issues if these represent separate feature work beyond the #910 bug fix.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly summarizes the main changes: plugin eviction handling, CLI camera/graphics documentation, and minor Unity tooling fixes.
Description check ✅ Passed Description comprehensively covers all major changes including server plugin eviction, CLI/docs additions, Unity tool improvements, and testing updates with proper detail.
Linked Issues check ✅ Passed Issue #910 requirement for RenderSettings API compatibility across Unity versions is met: SkyboxOps now includes CustomReflectionTexture wrapper property with conditional compilation to handle API differences.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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 and usage tips.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 4 issues, and left some high level feedback:

  • When evicting a previous session in PluginHub._handle_register, consider logging the affected command_id values when setting exceptions on pending futures to make it easier to trace which in-flight commands were cancelled during reconnection races.
  • The new CLI camera/graphics/packages/texture examples are duplicated across CLI_USAGE_GUIDE.md, CLI_USAGE.md, and CLI_EXAMPLE.md with slight differences (e.g., PostFX vs PostProcess targets and option variations); it may be worth standardizing the examples or factoring common snippets to avoid drift and user confusion.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- When evicting a previous session in `PluginHub._handle_register`, consider logging the affected `command_id` values when setting exceptions on pending futures to make it easier to trace which in-flight commands were cancelled during reconnection races.
- The new CLI camera/graphics/packages/texture examples are duplicated across `CLI_USAGE_GUIDE.md`, `CLI_USAGE.md`, and `CLI_EXAMPLE.md` with slight differences (e.g., `PostFX` vs `PostProcess` targets and option variations); it may be worth standardizing the examples or factoring common snippets to avoid drift and user confusion.

## Individual Comments

### Comment 1
<location path="Server/src/transport/plugin_hub.py" line_range="440-442" />
<code_context>
+                if old_ping and not old_ping.done():
+                    old_ping.cancel()
+                cls._last_pong.pop(evicted_session_id, None)
+                for command_id, entry in list(cls._pending.items()):
+                    if entry.get("session_id") == evicted_session_id:
+                        future = entry.get("future") if isinstance(entry, dict) else None
+                        if future and not future.done():
+                            future.set_exception(
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard use of `entry.get` when `entry` may not be a dict

Here `entry.get("session_id")` is used before you check `isinstance(entry, dict)` for the `future` access. If `_pending` can contain non-dict entries, this first call to `.get()` will raise `AttributeError`. Either ensure `_pending` only stores dicts, or add the same `isinstance(entry, dict)` guard (or equivalent normalization/skip logic) around the `session_id` access.
</issue_to_address>

### Comment 2
<location path="Server/src/transport/plugin_hub.py" line_range="463-468" />
<code_context>
             ping_task = asyncio.create_task(cls._ping_loop(session_id, websocket))
             cls._ping_tasks[session_id] = ping_task

+        # Close evicted WebSocket outside the lock to avoid blocking
+        if evicted_ws is not None:
+            try:
+                await evicted_ws.close(code=1001)
+            except Exception:
+                pass
+
</code_context>
<issue_to_address>
**suggestion:** Consider logging failures when closing the evicted WebSocket

The broad `except Exception: pass` around `evicted_ws.close(...)` hides any errors when closing the previous connection, which makes diagnosing reconnection/eviction issues difficult. Consider logging the exception (e.g., at debug level) while still swallowing it so errors aren’t propagated to the caller.

```suggestion
        # Close evicted WebSocket outside the lock to avoid blocking
        if evicted_ws is not None:
            try:
                await evicted_ws.close(code=1001)
            except Exception:
                logger.debug(
                    "Failed to close evicted WebSocket for session %s",
                    evicted_session_id,
                    exc_info=True,
                )
```
</issue_to_address>

### Comment 3
<location path="Server/tests/integration/test_plugin_registry_user_isolation.py" line_range="13-16" />
<code_context>
     async def test_register_with_user_id_stores_composite_key(self):
         registry = PluginRegistry()
-        session = await registry.register(
+        session, _ = await registry.register(
             "sess-1", "MyProject", "hash1", "2022.3", user_id="user-A"
         )
</code_context>
<issue_to_address>
**suggestion (testing):** Add integration tests asserting `evicted_session_id` behavior for user-scoped registrations

Since `PluginRegistry.register` now returns `(session, evicted_session_id)`, this suite should assert the eviction semantics instead of discarding the second value:

- Same `user_id` and `project_hash`: the second `register` with a new `session_id` should return `evicted_session_id == first_session_id`.
- Different `user_id` values with the same `project_hash`: both calls should return `evicted_session_id is None`.

These expectations will lock in the user-scoped eviction behavior rather than leaving it untested.

Suggested implementation:

```python
    @pytest.mark.asyncio
    async def test_register_with_user_id_stores_composite_key(self):
        registry = PluginRegistry()
        session, _ = await registry.register(
            "sess-1", "MyProject", "hash1", "2022.3", user_id="user-A"
        )
        assert session.user_id == "user-A"

    @pytest.mark.asyncio
    async def test_register_same_user_same_hash_evicts_previous_session(self):
        """Same user + project_hash: second registration evicts the first session."""
        registry = PluginRegistry()

        first_session, first_evicted_session_id = await registry.register(
            "sess-1", "MyProject", "hash1", "2022.3", user_id="user-A"
        )
        assert first_session.session_id == "sess-1"
        assert first_evicted_session_id is None

        second_session, second_evicted_session_id = await registry.register(
            "sess-2", "MyProject", "hash1", "2022.3", user_id="user-A"
        )
        assert second_session.session_id == "sess-2"
        assert second_evicted_session_id == "sess-1"

    async def test_cross_user_isolation_same_hash(self):

```

```python
    async def test_cross_user_isolation_same_hash(self):
        """Two users registering with the same project_hash get independent sessions."""
        registry = PluginRegistry()
        sess_a, evicted_a = await registry.register(
            "sA", "Proj", "hash1", "2022", user_id="userA"
        )
        sess_b, evicted_b = await registry.register(
            "sB", "Proj", "hash1", "2022", user_id="userB"
        )

        assert sess_a.session_id == "sA"
        assert sess_b.session_id == "sB"
        # Different users should not evict each other's sessions
        assert evicted_a is None
        assert evicted_b is None

```
</issue_to_address>

### Comment 4
<location path="Server/src/cli/CLI_USAGE_GUIDE.md" line_range="816-820" />
<code_context>
 unity-mcp raw manage_components '{"action": "add", "target": "Test", "componentType": "Rigidbody"}'
 unity-mcp raw manage_editor '{"action": "play"}'
+unity-mcp raw manage_camera '{"action": "screenshot", "include_image": true}'
+unity-mcp raw manage_graphics '{"action": "volume_info", "target": "PostProcess"}'
+unity-mcp raw manage_packages '{"action": "list"}'
 ```
</code_context>
<issue_to_address>
**suggestion (typo):** Volume name in raw example appears inconsistent with earlier "PostProcessing" examples.

Above, the volume is created and used as `"PostProcessing"`, but this example uses `"PostProcess"`. If it’s the same volume, please update the target here to `"PostProcessing"` for consistency.

```suggestion
unity-mcp raw manage_components '{"action": "add", "target": "Test", "componentType": "Rigidbody"}'
 unity-mcp raw manage_editor '{"action": "play"}'
+unity-mcp raw manage_camera '{"action": "screenshot", "include_image": true}'
+unity-mcp raw manage_graphics '{"action": "volume_info", "target": "PostProcessing"}'
+unity-mcp raw manage_packages '{"action": "list"}'
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +440 to +442
for command_id, entry in list(cls._pending.items()):
if entry.get("session_id") == evicted_session_id:
future = entry.get("future") if isinstance(entry, dict) else None
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Guard use of entry.get when entry may not be a dict

Here entry.get("session_id") is used before you check isinstance(entry, dict) for the future access. If _pending can contain non-dict entries, this first call to .get() will raise AttributeError. Either ensure _pending only stores dicts, or add the same isinstance(entry, dict) guard (or equivalent normalization/skip logic) around the session_id access.

Comment on lines +13 to 16
session, _ = await registry.register(
"sess-1", "MyProject", "hash1", "2022.3", user_id="user-A"
)
assert session.user_id == "user-A"
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Add integration tests asserting evicted_session_id behavior for user-scoped registrations

Since PluginRegistry.register now returns (session, evicted_session_id), this suite should assert the eviction semantics instead of discarding the second value:

  • Same user_id and project_hash: the second register with a new session_id should return evicted_session_id == first_session_id.
  • Different user_id values with the same project_hash: both calls should return evicted_session_id is None.

These expectations will lock in the user-scoped eviction behavior rather than leaving it untested.

Suggested implementation:

    @pytest.mark.asyncio
    async def test_register_with_user_id_stores_composite_key(self):
        registry = PluginRegistry()
        session, _ = await registry.register(
            "sess-1", "MyProject", "hash1", "2022.3", user_id="user-A"
        )
        assert session.user_id == "user-A"

    @pytest.mark.asyncio
    async def test_register_same_user_same_hash_evicts_previous_session(self):
        """Same user + project_hash: second registration evicts the first session."""
        registry = PluginRegistry()

        first_session, first_evicted_session_id = await registry.register(
            "sess-1", "MyProject", "hash1", "2022.3", user_id="user-A"
        )
        assert first_session.session_id == "sess-1"
        assert first_evicted_session_id is None

        second_session, second_evicted_session_id = await registry.register(
            "sess-2", "MyProject", "hash1", "2022.3", user_id="user-A"
        )
        assert second_session.session_id == "sess-2"
        assert second_evicted_session_id == "sess-1"

    async def test_cross_user_isolation_same_hash(self):
    async def test_cross_user_isolation_same_hash(self):
        """Two users registering with the same project_hash get independent sessions."""
        registry = PluginRegistry()
        sess_a, evicted_a = await registry.register(
            "sA", "Proj", "hash1", "2022", user_id="userA"
        )
        sess_b, evicted_b = await registry.register(
            "sB", "Proj", "hash1", "2022", user_id="userB"
        )

        assert sess_a.session_id == "sA"
        assert sess_b.session_id == "sB"
        # Different users should not evict each other's sessions
        assert evicted_a is None
        assert evicted_b is None

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 (4)
docs/guides/CLI_USAGE.md (4)

819-820: ⚠️ Potential issue | 🔴 Critical

Incorrect action names in raw command examples

Lines 819-820 use incorrect internal action names:

  • Line 819: Uses "action": "volume_info" but the correct action name is "volume_get_info" (see MCPForUnity/Editor/Tools/Graphics/ManageGraphics.cs)
  • Line 820: Uses "action": "list" but the correct action name is "list_packages" (see MCPForUnity/Editor/Tools/ManagePackages.cs)

Raw commands bypass the CLI's action name normalization and must use the exact internal action names that the C# handlers expect.

🐛 Proposed fix
-unity-mcp raw manage_graphics '{"action": "volume_info", "target": "PostProcess"}'
-unity-mcp raw manage_packages '{"action": "list"}'
+unity-mcp raw manage_graphics '{"action": "volume_get_info", "target": "PostProcess"}'
+unity-mcp raw manage_packages '{"action": "list_packages"}'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/guides/CLI_USAGE.md` around lines 819 - 820, Update the raw JSON command
examples to use the exact internal action names expected by the C# handlers:
replace "action": "volume_info" with "action": "volume_get_info" (handler
referenced in ManageGraphics.cs) and replace "action": "list" with "action":
"list_packages" (handler referenced in ManagePackages.cs); ensure any raw
command examples elsewhere follow the same exact internal action name convention
so they match the C# action handlers.

74-74: ⚠️ Potential issue | 🟡 Minor

Parameter name inconsistency: --camera-ref vs --camera

Line 74 uses --camera-ref "SecondCamera" but the Screenshot Parameters table at line 190 documents the parameter as --camera, -c. Please use consistent parameter naming throughout the documentation.

📝 Suggested fix
-unity-mcp camera screenshot --camera-ref "SecondCamera" --include-image
+unity-mcp camera screenshot --camera "SecondCamera" --include-image
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/guides/CLI_USAGE.md` at line 74, The CLI example uses the parameter
--camera-ref while the Screenshot Parameters table documents --camera, -c; make
these consistent by either updating the example to use --camera (e.g., replace
--camera-ref "SecondCamera" with --camera "SecondCamera") or by updating the
Screenshot Parameters table to document --camera-ref (and include its short form
if applicable), and ensure every occurrence of the flag in the document (search
for --camera-ref and --camera) uses the chosen canonical name so examples and
the table match.

658-658: ⚠️ Potential issue | 🟡 Minor

Parameter name inconsistency: --camera-ref vs --camera

Line 658 uses --camera-ref "SecondCamera" but docs/guides/CLI_USAGE.md line 190 documents this parameter as --camera, -c. Please ensure consistent parameter naming across all documentation files.

📝 Suggested fix
-unity-mcp camera screenshot --camera-ref "SecondCamera" --include-image
+unity-mcp camera screenshot --camera "SecondCamera" --include-image
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/guides/CLI_USAGE.md` at line 658, Documentation uses two different CLI
flag names (--camera-ref vs --camera / -c); standardize to the documented flag
set (--camera, -c). Search for occurrences of "--camera-ref" and replace them
with "--camera" (and add the short form "-c" where examples show only the long
flag), and update example invocation text to match the flag description in the
CLI_USAGE.md CLI parameter section and any examples that reference
"SecondCamera" to use the unified flag name; ensure all README/examples and
guide references consistently use "--camera" and "-c".

233-234: ⚠️ Potential issue | 🔴 Critical

Incorrect action names in raw command examples

Lines 233-234 use incorrect internal action names:

  • Line 233: Uses "action":"volume_info" but the correct action name is "volume_get_info" (see MCPForUnity/Editor/Tools/Graphics/ManageGraphics.cs)
  • Line 234: Uses "action":"list" but the correct action name is "list_packages" (see MCPForUnity/Editor/Tools/ManagePackages.cs)

Raw commands must use the exact internal action names that the C# handlers expect.

🐛 Proposed fix
-unity-mcp raw manage_graphics '{"action":"volume_info","target":"PostFX"}'
-unity-mcp raw manage_packages '{"action":"list"}'
+unity-mcp raw manage_graphics '{"action":"volume_get_info","target":"PostFX"}'
+unity-mcp raw manage_packages '{"action":"list_packages"}'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/guides/CLI_USAGE.md` around lines 233 - 234, Update the raw command
examples to use the exact internal action names expected by the C# handlers:
replace any occurrences of "volume_info" with "volume_get_info" (handled by
ManageGraphics) and replace "list" with "list_packages" (handled by
ManagePackages); ensure the example command strings (e.g., the unity-mcp
examples under Particle systems) are updated so the action fields match
volume_get_info and list_packages exactly.
🧹 Nitpick comments (2)
Server/src/transport/plugin_hub.py (1)

463-468: Consider logging the exception instead of silently swallowing it.

While best-effort cleanup is appropriate here (the WebSocket may already be closed or in a bad state), silently ignoring exceptions can mask unexpected issues during debugging.

🔧 Suggested improvement
         # Close evicted WebSocket outside the lock to avoid blocking
         if evicted_ws is not None:
             try:
                 await evicted_ws.close(code=1001)
             except Exception:
-                pass
+                logger.debug(
+                    "Error closing evicted WebSocket for session %s (best-effort cleanup)",
+                    evicted_session_id,
+                    exc_info=True,
+                )

This matches the pattern used in _evict_connection at line 762 which also logs close errors at debug level.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Server/src/transport/plugin_hub.py` around lines 463 - 468, The close() call
for evicted_ws currently swallows all exceptions; update the except block to log
the exception at debug level (like the pattern in _evict_connection) rather than
silently ignoring it. Catch Exception as e and call the same logger used in
_evict_connection (e.g., logger.debug or self.logger.debug) with a message such
as "error closing evicted websocket" and include the exception info to preserve
traceback/context. Ensure the change only affects the small try/except around
evicted_ws.close and keeps the best-effort cleanup behavior.
unity-mcp-skill/SKILL.md (1)

93-96: Consider clarifying the relationship between action="screenshot_multiview" and action="screenshot", batch="surround".

Line 95 shows action="screenshot_multiview" for a 6-angle contact sheet, while lines 71-74 use action="screenshot" with batch="surround" to achieve the same result. Both approaches are valid—screenshot_multiview is a built-in shorthand for screenshot with batch='surround' and include_image=true.

For improved clarity, consider either:

  • Adding a note explaining that screenshot_multiview is equivalent to the batch parameter approach shown earlier, or
  • Using only one approach consistently throughout the document.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@unity-mcp-skill/SKILL.md` around lines 93 - 96, Clarify that
action="screenshot_multiview" is a shorthand for
manage_camera(action="screenshot", batch="surround", include_image=True) by
adding a short note near the examples (or pick one style and make the other a
footnote) so readers know both produce the same 6-angle contact sheet; reference
the examples using action="screenshot_multiview" and the earlier
manage_camera(action="screenshot", batch="surround") example to show
equivalence.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/guides/CLI_USAGE.md`:
- Around line 468-469: Update the raw CLI examples to use the exact internal
action names expected by the C# handlers: change the manage_graphics example to
use "action": "volume_get_info" (handler in ManageGraphics) instead of
"volume_info", and change the manage_packages example to use "action":
"list_packages" (handler in ManagePackages) instead of "list"; keep the
surrounding command syntax unchanged so the raw invocation matches the internal
action names.

---

Outside diff comments:
In `@docs/guides/CLI_USAGE.md`:
- Around line 819-820: Update the raw JSON command examples to use the exact
internal action names expected by the C# handlers: replace "action":
"volume_info" with "action": "volume_get_info" (handler referenced in
ManageGraphics.cs) and replace "action": "list" with "action": "list_packages"
(handler referenced in ManagePackages.cs); ensure any raw command examples
elsewhere follow the same exact internal action name convention so they match
the C# action handlers.
- Line 74: The CLI example uses the parameter --camera-ref while the Screenshot
Parameters table documents --camera, -c; make these consistent by either
updating the example to use --camera (e.g., replace --camera-ref "SecondCamera"
with --camera "SecondCamera") or by updating the Screenshot Parameters table to
document --camera-ref (and include its short form if applicable), and ensure
every occurrence of the flag in the document (search for --camera-ref and
--camera) uses the chosen canonical name so examples and the table match.
- Line 658: Documentation uses two different CLI flag names (--camera-ref vs
--camera / -c); standardize to the documented flag set (--camera, -c). Search
for occurrences of "--camera-ref" and replace them with "--camera" (and add the
short form "-c" where examples show only the long flag), and update example
invocation text to match the flag description in the CLI_USAGE.md CLI parameter
section and any examples that reference "SecondCamera" to use the unified flag
name; ensure all README/examples and guide references consistently use
"--camera" and "-c".
- Around line 233-234: Update the raw command examples to use the exact internal
action names expected by the C# handlers: replace any occurrences of
"volume_info" with "volume_get_info" (handled by ManageGraphics) and replace
"list" with "list_packages" (handled by ManagePackages); ensure the example
command strings (e.g., the unity-mcp examples under Particle systems) are
updated so the action fields match volume_get_info and list_packages exactly.

---

Nitpick comments:
In `@Server/src/transport/plugin_hub.py`:
- Around line 463-468: The close() call for evicted_ws currently swallows all
exceptions; update the except block to log the exception at debug level (like
the pattern in _evict_connection) rather than silently ignoring it. Catch
Exception as e and call the same logger used in _evict_connection (e.g.,
logger.debug or self.logger.debug) with a message such as "error closing evicted
websocket" and include the exception info to preserve traceback/context. Ensure
the change only affects the small try/except around evicted_ws.close and keeps
the best-effort cleanup behavior.

In `@unity-mcp-skill/SKILL.md`:
- Around line 93-96: Clarify that action="screenshot_multiview" is a shorthand
for manage_camera(action="screenshot", batch="surround", include_image=True) by
adding a short note near the examples (or pick one style and make the other a
footnote) so readers know both produce the same 6-angle contact sheet; reference
the examples using action="screenshot_multiview" and the earlier
manage_camera(action="screenshot", batch="surround") example to show
equivalence.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e1109109-7364-4efb-97b8-8a1afd0342ee

📥 Commits

Reviewing files that changed from the base of the PR and between ad998f1 and 8e122e3.

📒 Files selected for processing (12)
  • MCPForUnity/Editor/Tools/Graphics/SkyboxOps.cs
  • MCPForUnity/Editor/Tools/ManageScript.cs
  • Server/src/cli/CLI_USAGE_GUIDE.md
  • Server/src/services/tools/utils.py
  • Server/src/transport/plugin_hub.py
  • Server/src/transport/plugin_registry.py
  • Server/tests/integration/test_plugin_registry_user_isolation.py
  • Server/tests/test_transport_characterization.py
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptValidationTests.cs
  • docs/guides/CLI_EXAMPLE.md
  • docs/guides/CLI_USAGE.md
  • unity-mcp-skill/SKILL.md

Comment on lines +468 to +469
unity-mcp raw manage_graphics '{"action": "volume_info", "target": "PostFX"}'
unity-mcp raw manage_packages '{"action": "list"}'
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Incorrect action names in raw command examples

Lines 468-469 use incorrect internal action names:

  • Line 468: Uses "action": "volume_info" but the correct action name is "volume_get_info" (see MCPForUnity/Editor/Tools/Graphics/ManageGraphics.cs)
  • Line 469: Uses "action": "list" but the correct action name is "list_packages" (see MCPForUnity/Editor/Tools/ManagePackages.cs)

Raw commands bypass the CLI's action name normalization and must use the exact internal action names that the C# handlers expect.

🐛 Proposed fix
-unity-mcp raw manage_graphics '{"action": "volume_info", "target": "PostFX"}'
-unity-mcp raw manage_packages '{"action": "list"}'
+unity-mcp raw manage_graphics '{"action": "volume_get_info", "target": "PostFX"}'
+unity-mcp raw manage_packages '{"action": "list_packages"}'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
unity-mcp raw manage_graphics '{"action": "volume_info", "target": "PostFX"}'
unity-mcp raw manage_packages '{"action": "list"}'
unity-mcp raw manage_graphics '{"action": "volume_get_info", "target": "PostFX"}'
unity-mcp raw manage_packages '{"action": "list_packages"}'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/guides/CLI_USAGE.md` around lines 468 - 469, Update the raw CLI examples
to use the exact internal action names expected by the C# handlers: change the
manage_graphics example to use "action": "volume_get_info" (handler in
ManageGraphics) instead of "volume_info", and change the manage_packages example
to use "action": "list_packages" (handler in ManagePackages) instead of "list";
keep the surrounding command syntax unchanged so the raw invocation matches the
internal action names.

Copy link
Contributor

Copilot AI left a comment

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 addresses Unity plugin reconnection races by explicitly evicting superseded plugin sessions on the server, while also extending CLI/skill documentation with camera/graphics/packages/texture examples and applying a couple of Unity editor tooling fixes (Skybox reflection API compatibility + ManageScript duplicate-method detection improvements).

Changes:

  • Server transport: PluginRegistry.register() now returns (session, evicted_session_id), and PluginHub uses the evicted ID to cancel ping tasks, fail pending commands, remove old state, and close the old WebSocket.
  • Unity editor tools: add a Unity-version-compatible accessor for custom reflection textures and ignore C# keywords during duplicate-method signature detection (plus a unit test).
  • Docs/CLI: add/expand camera/graphics/packages/texture command examples and raw command snippets across guides.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
Server/src/transport/plugin_registry.py Returns (new_session, evicted_session_id) to enable upstream eviction cleanup.
Server/src/transport/plugin_hub.py Cleans up evicted session state/tasks/pending commands and closes old WebSocket.
Server/tests/test_transport_characterization.py Updates tests to unpack the new (session, evicted_id) return value where needed.
Server/tests/integration/test_plugin_registry_user_isolation.py Updates tests to unpack the new return value where needed.
MCPForUnity/Editor/Tools/Graphics/SkyboxOps.cs Adds CustomReflectionTexture shim for Unity API differences.
MCPForUnity/Editor/Tools/ManageScript.cs Skips C# keywords in duplicate-method signature detection.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptValidationTests.cs Adds test coverage for keyword-related duplicate detection behavior.
Server/src/services/tools/utils.py Docstring tweaks for shared screenshot helpers.
docs/guides/CLI_USAGE.md Adds camera/graphics/packages/texture sections + raw command examples + command table updates.
docs/guides/CLI_EXAMPLE.md Adds example snippets for camera/graphics/packages/texture + raw command examples.
Server/src/cli/CLI_USAGE_GUIDE.md Adds extended CLI usage examples for camera/graphics/packages/texture + raw command snippets.
unity-mcp-skill/SKILL.md Updates skill guidance to reference manage_camera(action="screenshot") for verification.

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

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +429 to 431
session, evicted_session_id = await registry.register(session_id, project_name, project_hash, unity_version, project_path, user_id=user_id)
evicted_ws = None
async with lock:
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

Potential deadlock due to lock-order inversion between PluginHub’s _lock and PluginRegistry’s internal _lock: _handle_register awaits registry.register(...) before acquiring PluginHub._lock, while on_disconnect acquires PluginHub._lock and then awaits registry.unregister(...) (which takes the registry lock). If these happen concurrently, each task can end up waiting on the other lock. To avoid this, enforce a consistent acquisition order (e.g., never await PluginRegistry methods while holding PluginHub._lock, or acquire PluginHub._lock first in _handle_register and keep registry calls within that order).

Suggested change
session, evicted_session_id = await registry.register(session_id, project_name, project_hash, unity_version, project_path, user_id=user_id)
evicted_ws = None
async with lock:
evicted_ws = None
async with lock:
session, evicted_session_id = await registry.register(
session_id,
project_name,
project_hash,
unity_version,
project_path,
user_id=user_id,
)

Copilot uses AI. Check for mistakes.
Comment on lines +441 to +442
if entry.get("session_id") == evicted_session_id:
future = entry.get("future") if isinstance(entry, dict) else None
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

In the eviction cleanup loop, entry.get("session_id") is accessed before the isinstance(entry, dict) check, so the type-guard is ineffective and can’t prevent an AttributeError if a non-dict ever ends up in _pending. Either drop the isinstance check (since _pending is typed/stored as dict entries) or move the type check before any .get(...) access for consistency with the intended safety.

Suggested change
if entry.get("session_id") == evicted_session_id:
future = entry.get("future") if isinstance(entry, dict) else None
if isinstance(entry, dict) and entry.get("session_id") == evicted_session_id:
future = entry.get("future")

Copilot uses AI. Check for mistakes.
unity-mcp graphics feature-list
unity-mcp graphics feature-add --type "ScreenSpaceAmbientOcclusion"
unity-mcp graphics feature-configure --name "SSAO" -p Intensity 1.5
unity-mcp graphics feature-toggle --name "SSAO" --active|--inactive
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

unity-mcp graphics feature-toggle --name "SSAO" --active|--inactive reads like a literal pipe in a single command, which isn’t valid CLI syntax. Consider splitting into two example lines (one with --active, one with --inactive) or using a clearer optional/alternatives notation so users don’t copy-paste an invalid command.

Suggested change
unity-mcp graphics feature-toggle --name "SSAO" --active|--inactive
unity-mcp graphics feature-toggle --name "SSAO" --active
unity-mcp graphics feature-toggle --name "SSAO" --inactive

Copilot uses AI. Check for mistakes.
Comment on lines +314 to +315
typeof(int);
typeof(string);
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

The snippet inside the new test includes typeof(int); / typeof(string); as standalone statements, which is not valid C# (expression statements can’t be plain typeof expressions). Even though the test currently only exercises ValidationLevel.Basic (so it won’t catch syntax errors), using valid C# here will make the test more robust if validation levels or checks change. Consider assigning the result (e.g., var _ = typeof(int);) or removing these lines.

Suggested change
typeof(int);
typeof(string);
var _ = typeof(int);
var __ = typeof(string);

Copilot uses AI. Check for mistakes.
@Scriptwonder Scriptwonder merged commit fa282b1 into CoplayDev:beta Mar 10, 2026
1 check passed
@Scriptwonder Scriptwonder deleted the bug-fix-and-doc-update branch March 10, 2026 14:58
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.

Version 9.5.3 gives the following error in Unity 2021.3.45f2:

2 participants