Skip to content

fix: Improve Unity reload recovery and skill setup reliability#1150

Merged
hatayama merged 4 commits into
v3-betafrom
feature/hatayama/port-main-fixes-v3-beta
May 17, 2026
Merged

fix: Improve Unity reload recovery and skill setup reliability#1150
hatayama merged 4 commits into
v3-betafrom
feature/hatayama/port-main-fixes-v3-beta

Conversation

@hatayama
Copy link
Copy Markdown
Owner

Summary

  • Unity reload and restart recovery now avoids waiting synchronously for dynamic-code runtime shutdown.
  • Skill setup defaults to the flat folder layout, and runtime/test assemblies stay out of player builds.

User Impact

  • Unity is less likely to freeze when a domain reload starts while the editor-side server or dynamic-code runtime is still active.
  • Agents get the expected flat skill layout by default, reducing setup confusion across CLI targets.
  • Player builds avoid pulling in runtime test and demo assemblies.

Changes

  • Moves domain reload shutdown into the v3 server lifecycle and readiness flow, with non-blocking runtime shutdown signaling.
  • Keeps the bridge server stop path from disposing the whole server instance during reload recovery.
  • Updates skill target installation defaults and regenerates native CLI binaries.
  • Excludes demo and runtime test assemblies from player builds and covers the assembly boundaries with focused tests.

Verification

  • Packages/src/Cli~/dist/darwin-arm64/uloop compile --project-path /Users/a12115/ghq/hatayama/unity-cli-loop
  • Unity EditMode focused tests: 127 passed, 0 failed
  • Unity OnionAssemblyDependencyTests: 80 passed, 0 failed
  • scripts/check-go-cli.sh
  • codex-review v3-beta: no accepted/actionable findings

hatayama added 3 commits May 17, 2026 11:31
The v3 branch still treated grouped skill directories as the command default, which kept freshly installed skills outside the standard agent discovery path. Route list and install through the flat layout by default, and make public uninstall remove both managed layouts so stale grouped copies do not survive the migration.
Adapt the main-branch Unity fixes to the v3 server split so compiler discovery, Unity object identifiers, and runtime assembly targets match newer Unity layouts. Route domain-reload cleanup through the v3 lifecycle hook so dynamic-code shutdown is signaled without blocking reload teardown.
Mark the demo test-support assembly as Editor-only because it depends on package assemblies that are intentionally excluded from player builds. Add an assembly-definition guard so future non-test assemblies cannot reference Editor-only package code without also targeting the Editor platform.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 17, 2026

Review Change Stack

Warning

Rate limit exceeded

@hatayama has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 29 minutes and 14 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7014265c-60f1-46c5-bf8c-e403c161e477

📥 Commits

Reviewing files that changed from the base of the PR and between c088a90 and 6617c63.

⛔ Files ignored due to path filters (3)
  • Packages/src/Cli~/dist/darwin-amd64/uloop is excluded by !**/dist/** and included by none
  • Packages/src/Cli~/dist/darwin-arm64/uloop is excluded by !**/dist/** and included by none
  • Packages/src/Cli~/dist/windows-amd64/uloop.exe is excluded by !**/dist/**, !**/*.exe and included by none
📒 Files selected for processing (8)
  • Assets/Tests/Editor/DynamicCodeToolTests/ExternalCompilerPathResolverTests.cs
  • Assets/Tests/Editor/FindGameObjectsToolTests.cs
  • Assets/Tests/Editor/HierarchyServiceTests.cs
  • Packages/src/Cli~/internal/cli/skills.go
  • Packages/src/Cli~/internal/cli/skills_test.go
  • Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/ExternalCompilerPathResolver.cs
  • Packages/src/Editor/FirstPartyTools/FindGameObjects/GameObjectFinder/ComponentPropertySerializer.cs
  • Packages/src/Editor/FirstPartyTools/GetHierarchy/HierarchyAnalyzer/HierarchyService.cs
📝 Walkthrough

Walkthrough

This PR introduces a domain-reload lifecycle interface for server cleanup, migrates hierarchy node identifiers from integers to strings, switches object reference serialization from instanceId to entityId with culture-invariant formatting, refactors external compiler path resolution with multi-SDK support, and adds default flat-layout behavior for skills management along with assembly dependency constraint tests.

Changes

Domain Reload Lifecycle and Server Shutdown

Layer / File(s) Summary
Lifecycle interface contract
Packages/src/Editor/Application/UnityCliLoopServerApplicationService.cs
Introduces IUnityCliLoopServerDomainReloadLifecycle interface with PrepareForDomainReload() method for pre-domain-reload server cleanup.
Controller service lifecycle wiring
Packages/src/Editor/Infrastructure/Server/UnityCliLoopServerController.cs
Injects domain-reload lifecycle dependency into UnityCliLoopServerControllerService constructor; calls PrepareForDomainReload() before assembly reload persists reloading state.
First-party server lifecycle binding
Packages/src/Editor/CompositionRoot/UnityCliLoopFirstPartyServerLifecycleBinding.cs
Updates UnityCliLoopFirstPartyServerLifecycleBinding to implement the domain-reload lifecycle and delegate PrepareForDomainReload() to FirstPartyToolsEditorStartup.
Dependency composition registration
Packages/src/Editor/CompositionRoot/UnityCliLoopApplicationRegistration.cs
Updates registration to wire first-party server lifecycle instance for both readiness probing and domain-reload preparation dependencies.
Dynamic code services domain-reload reset
Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCodeServices.cs, Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/ExecuteDynamicCodeEditorStartup.cs, Packages/src/Editor/FirstPartyTools/FirstPartyToolsEditorStartup.cs
Introduces ResetServerScopedServicesBeforeDomainReload() in registry with shutdown signaling for shutdown-aware runtimes; exposes path through editor startup hooks.
Domain reload recovery shutdown sequence
Packages/src/Editor/Application/UseCases/DomainReloadRecoveryUseCase.cs
Changes server shutdown from Dispose() to StopServer() for orderly pre-reload shutdown without disposing instances.
Domain reload recovery and dynamic code tests
Assets/Tests/Editor/DomainReloadRecoveryUseCaseTests.cs, Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeExecutionFacadeTests.cs, Assets/Tests/Editor/UnityCliLoopServerControllerStartupLockTests.cs, Assets/Tests/Editor/UnityCliLoopServerStartupProtectionTests.cs
Extends test doubles with call counters; adds tests verifying StopServer() behavior, shutdown-aware runtime reset without disposal, and domain-reload lifecycle preparation.

Hierarchy Node Identifier Migration

Layer / File(s) Summary
HierarchyNode data model
Packages/src/Editor/FirstPartyTools/GetHierarchy/HierarchyAnalyzer/HierarchyNode.cs
Changes id and parent fields from int/int? to string; updates constructor to accept string identifiers and normalize null id to string.Empty.
HierarchySerializer node indexing
Packages/src/Editor/FirstPartyTools/GetHierarchy/HierarchyAnalyzer/HierarchySerializer.cs
Refactors BuildGroupForScene to use string-keyed Dictionary<string, HierarchyNodeNested> for node lookup; refactors component LUT heuristic to use explicit per-node loop.
HierarchyService traversal and ID generation
Packages/src/Editor/FirstPartyTools/GetHierarchy/HierarchyAnalyzer/HierarchyService.cs
Updates TraverseHierarchy to pass/store parentId as string; introduces GetObjectId() helper generating culture-invariant string identifiers from EntityId (Unity 6000.4+) or GetInstanceID().
Serializer tests with string identifiers
Assets/Tests/Editor/HierarchySerializerTests.cs
Updates HierarchySerializerTests to construct nodes with string IDs; verifies grouping and max-depth calculations with string-keyed node dictionaries.

Object Reference Serialization to EntityId

Layer / File(s) Summary
ComponentPropertySerializer entityId serialization
Packages/src/Editor/FirstPartyTools/FindGameObjects/GameObjectFinder/ComponentPropertySerializer.cs
Refactors object-reference serialization to use entityId (via GetEntityId() on Unity 6000.4+, otherwise GetInstanceID()) with culture-invariant string formatting; introduces GetObjectReferenceValue() helper.
Object reference serialization tests
Assets/Tests/Editor/FindGameObjectsToolTests.cs
Updates FindGameObjectsToolTests to validate entityId field instead of instanceId; introduces GetExpectedObjectId() helper with conditional compilation for version-aware assertions.

External Compiler Path Resolution

Layer / File(s) Summary
Compiler directory and file constants
Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/ExternalCompilerPathResolver.cs
Centralizes external compiler/runtime directory and file names as private constants; updates Resolve() to compute effective scripting root and resolve compiler directory.
Compiler directory resolution and detection
Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/ExternalCompilerPathResolver.cs
Introduces ResolveCompilerDirectoryPath() supporting legacy DotNetSdkRoslyn and dotnet-sdk versioned layouts; implements version-aware SDK directory comparison for highest-version selection.
Compiler path resolver tests
Assets/Tests/Editor/DynamicCodeToolTests/ExternalCompilerPathResolverTests.cs
Adds coverage for Resources/Scripting layout discovery, legacy DotNetSdkRoslyn resolution, and multi-version SDK selection with highest-version preference.

Skills Layout Management

Layer / File(s) Summary
Managed skills layout grouping logic
Packages/src/Cli~/internal/cli/skills.go
Introduces groupSkillsByDefault = false constant and groupManagedSkillsForOptions() helper to centralize grouping decision based on --flat flag.
Skills command refactoring
Packages/src/Cli~/internal/cli/skills.go
Updates runSkillsList and runSkillsInstall to use centralized grouping helper; extends runSkillsUninstall to branch between grouped and ungrouped removal paths.
Ungrouped uninstall from all layouts
Packages/src/Cli~/internal/cli/skills.go, Packages/src/Cli~/internal/cli/skills_targets.go
Introduces uninstallSkillsForTargetFromAllLayouts() to remove skills from both managed layouts; refactors removeSkillFromAllLayouts() to delegate to removeSkillFromAllLayoutsIfExists() helper.
Skills layout default behavior tests
Packages/src/Cli~/internal/cli/skills_test.go
Adds tests verifying default flat-layout behavior for install/list/uninstall commands with minimal options.

Assembly Dependency Constraints

Layer / File(s) Summary
Assembly dependency tests and helpers
Assets/Tests/Editor/OnionAssemblyDependencyTests.cs
Introduces NUnit tests enforcing runtime asmdef editor-only targeting and project asmdefs not referencing editor-only package assemblies; provides JSON helpers for asmdef inspection and filtering.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • hatayama/unity-cli-loop#1119: Overlaps on object/hierarchy identifier migration to entityId/string-based IDs and external compiler path resolution improvements with corresponding tests.
  • hatayama/unity-cli-loop#1121: Directly connected through domain-reload server shutdown sequencing and dynamic-code reset logic introduced in this PR.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: Improve Unity reload recovery and skill setup reliability' directly and clearly summarizes the main changes: improved domain reload recovery and skill setup defaults.
Description check ✅ Passed The description provides a detailed, structured overview of changes that are clearly related to the actual changeset, including summary, user impact, specific changes made, and verification steps.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/hatayama/port-main-fixes-v3-beta

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
Copy Markdown
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: 2

🧹 Nitpick comments (1)
Assets/Tests/Editor/DynamicCodeToolTests/ExternalCompilerPathResolverTests.cs (1)

151-163: ⚡ Quick win

Add a regression test for mixed-layout fallback (stale legacy + valid DotNetSdk).

This will protect the resolver behavior when DotNetSdkRoslyn exists as a directory but the usable compiler is only under DotNetSdk/sdk/.../Roslyn/bincore.

Suggested test
+        [Test]
+        public void ResolveCompilerDirectoryPath_WhenLegacyDirectoryExistsButCscIsMissing_ShouldFallbackToDotNetSdkLayout()
+        {
+            string scriptingRootPath = CreateDirectory("Scripting");
+            CreateDirectory(Path.Combine("Scripting", "DotNetSdkRoslyn")); // intentionally empty
+            string expectedCompilerDirectoryPath = CreateDirectory(Path.Combine("Scripting", "DotNetSdk", "sdk", "8.0.318", "Roslyn", "bincore"));
+
+            string resolvedCompilerDirectoryPath = ExternalCompilerPathResolver.ResolveCompilerDirectoryPath(scriptingRootPath);
+
+            Assert.That(resolvedCompilerDirectoryPath, Is.EqualTo(expectedCompilerDirectoryPath));
+        }
🤖 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
`@Assets/Tests/Editor/DynamicCodeToolTests/ExternalCompilerPathResolverTests.cs`
around lines 151 - 163, Add a regression test in
ExternalCompilerPathResolverTests that ensures
ExternalCompilerPathResolver.ResolveCompilerDirectoryPath falls back to the
highest-versioned sdk Roslyn bincore when a stale legacy DotNetSdkRoslyn
directory exists; create a test that (1) creates a "DotNetSdkRoslyn" legacy
directory alongside a "DotNetSdk/sdk/{olderVersion}/Roslyn/bincore" and a newer
"DotNetSdk/sdk/{newerVersion}/Roslyn/bincore" (and optionally a "current"
symlink/dir), (2) calls ResolveCompilerDirectoryPath(scriptingRootPath), and (3)
asserts the resolved path equals the newer sdk Roslyn bincore path so the
resolver prefers the highest sdk version over the stale DotNetSdkRoslyn
directory.
🤖 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
`@Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/ExternalCompilerPathResolver.cs`:
- Around line 201-207: The current fast path returns legacyCompilerDirectoryPath
when Directory.Exists(DotNetSdkRoslynDirectoryName) is true, but that can be a
stale/partial folder; change the check to validate the legacy folder is usable
(e.g., contains expected compiler artifacts such as csc.dll or the "bincore"
subdirectory) before returning it; if those expected files/subdirs are missing
or invalid, call ResolveDotNetSdkCompilerDirectoryPath(scriptingRootPath)
instead (use the symbols legacyCompilerDirectoryPath,
DotNetSdkRoslynDirectoryName, ResolveDotNetSdkCompilerDirectoryPath, and
Directory.Exists to locate and implement the check).

In
`@Packages/src/Editor/FirstPartyTools/GetHierarchy/HierarchyAnalyzer/HierarchyService.cs`:
- Around line 382-393: Replace the unsafe conversion using EntityId.ToULong in
GetObjectId: instead of calling UnityEngine.EntityId.ToULong(obj.GetEntityId()),
call ToString() on the EntityId returned by obj.GetEntityId() (e.g., use
obj.GetEntityId().ToString()) so you rely on the EntityId public API rather than
converting to integer types; keep the Assert and CultureInfo usage where
applicable but remove the ToULong conversion.

---

Nitpick comments:
In
`@Assets/Tests/Editor/DynamicCodeToolTests/ExternalCompilerPathResolverTests.cs`:
- Around line 151-163: Add a regression test in
ExternalCompilerPathResolverTests that ensures
ExternalCompilerPathResolver.ResolveCompilerDirectoryPath falls back to the
highest-versioned sdk Roslyn bincore when a stale legacy DotNetSdkRoslyn
directory exists; create a test that (1) creates a "DotNetSdkRoslyn" legacy
directory alongside a "DotNetSdk/sdk/{olderVersion}/Roslyn/bincore" and a newer
"DotNetSdk/sdk/{newerVersion}/Roslyn/bincore" (and optionally a "current"
symlink/dir), (2) calls ResolveCompilerDirectoryPath(scriptingRootPath), and (3)
asserts the resolved path equals the newer sdk Roslyn bincore path so the
resolver prefers the highest sdk version over the stale DotNetSdkRoslyn
directory.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bf04cc57-f6ab-4b52-b4e3-39421593b880

📥 Commits

Reviewing files that changed from the base of the PR and between c09a5af and c088a90.

⛔ Files ignored due to path filters (5)
  • Assets/Tests/Demo/uLoopMCP.Tests.Demo.asmdef is excluded by none and included by none
  • Packages/src/Cli~/dist/darwin-amd64/uloop is excluded by !**/dist/** and included by none
  • Packages/src/Cli~/dist/darwin-arm64/uloop is excluded by !**/dist/** and included by none
  • Packages/src/Cli~/dist/windows-amd64/uloop.exe is excluded by !**/dist/**, !**/*.exe and included by none
  • Packages/src/Runtime/uLoopMCP.Runtime.asmdef is excluded by none and included by none
📒 Files selected for processing (24)
  • Assets/Tests/Editor/DomainReloadRecoveryUseCaseTests.cs
  • Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeExecutionFacadeTests.cs
  • Assets/Tests/Editor/DynamicCodeToolTests/ExternalCompilerPathResolverTests.cs
  • Assets/Tests/Editor/FindGameObjectsToolTests.cs
  • Assets/Tests/Editor/HierarchySerializerTests.cs
  • Assets/Tests/Editor/OnionAssemblyDependencyTests.cs
  • Assets/Tests/Editor/UnityCliLoopServerControllerStartupLockTests.cs
  • Assets/Tests/Editor/UnityCliLoopServerStartupProtectionTests.cs
  • Packages/src/Cli~/internal/cli/skills.go
  • Packages/src/Cli~/internal/cli/skills_targets.go
  • Packages/src/Cli~/internal/cli/skills_test.go
  • Packages/src/Editor/Application/UnityCliLoopServerApplicationService.cs
  • Packages/src/Editor/Application/UseCases/DomainReloadRecoveryUseCase.cs
  • Packages/src/Editor/CompositionRoot/UnityCliLoopApplicationRegistration.cs
  • Packages/src/Editor/CompositionRoot/UnityCliLoopFirstPartyServerLifecycleBinding.cs
  • Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCodeServices.cs
  • Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/ExternalCompilerPathResolver.cs
  • Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/ExecuteDynamicCodeEditorStartup.cs
  • Packages/src/Editor/FirstPartyTools/FindGameObjects/GameObjectFinder/ComponentPropertySerializer.cs
  • Packages/src/Editor/FirstPartyTools/FirstPartyToolsEditorStartup.cs
  • Packages/src/Editor/FirstPartyTools/GetHierarchy/HierarchyAnalyzer/HierarchyNode.cs
  • Packages/src/Editor/FirstPartyTools/GetHierarchy/HierarchyAnalyzer/HierarchySerializer.cs
  • Packages/src/Editor/FirstPartyTools/GetHierarchy/HierarchyAnalyzer/HierarchyService.cs
  • Packages/src/Editor/Infrastructure/Server/UnityCliLoopServerController.cs

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-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.

1 issue found across 29 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Packages/src/Cli~/internal/cli/skills.go">

<violation number="1" location="Packages/src/Cli~/internal/cli/skills.go:302">
P1: Uninstall in flat mode is not layout-scoped; it removes skill directories from both flat and grouped layouts.

(Based on your team's feedback about scoping uninstall operations to the selected layout.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Re-trigger cubic

Comment thread Packages/src/Cli~/internal/cli/skills.go Outdated
Address PR review findings by validating legacy compiler folders before selecting them, using the Unity EntityId string API instead of integer conversion, and keeping skills uninstall scoped to the selected layout.
@hatayama hatayama merged commit 4556535 into v3-beta May 17, 2026
19 checks passed
@hatayama hatayama deleted the feature/hatayama/port-main-fixes-v3-beta branch May 17, 2026 09:37
@github-actions github-actions Bot mentioned this pull request May 17, 2026
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.

1 participant