Summary
When the Unity Editor is unfocused, Unity CLI Loop holds Auto Refresh with
AssetDatabase.DisallowAutoRefresh(). In the reproduction below, an external git pull
changed existing C# files during that interval. Returning focus to Unity released the hold,
but the pending script changes were not imported. No compilation or Domain Reload occurred
until an explicit refresh through uloop compile was run.
This reproduced with Unity's Auto Refresh and Domain Reload enabled, no active uloop
hot-reload patches, and no other hot-reload package installed.
I understand that Unity CLI Loop is primarily designed around an LLM-driven Unity workflow.
In practice, however, the same Editor session is often shared by an agent and a developer
working manually. It would be helpful if external IDE edits, git pull, git checkout, or
similar human-driven operations could still flow through Unity's normal refresh behavior on
focus return, without the developer needing to know that a later uloop compile is required.
Environment
- macOS 26.5.1 (25F80)
- Unity 6000.3.23f1
- Installed Unity CLI Loop package: 3.1.0, Git dependency lock
d6fa8bf679cc31563c7b58e7da95a15f3dd07d14
- Edit Mode (
EditorApplication.isPlaying == false)
kAutoRefreshMode=1
AssetDatabase.IsDirectoryMonitoringEnabled() == false (Directory Monitoring is not
available on macOS)
uloop hot-reload --status: ActivePatchTotal: 0
I also checked current main at d0340b3b577b86f71ccb05139ddaf062d3998f9d
(package version 3.2.1). The relevant hold/release implementation is unchanged there, and
still does not request an AssetDatabase refresh after releasing the hold.
Reproduction
-
Open a Unity project containing an existing C# type, focus the Editor once, then move
focus to Finder or a terminal. The loaded Scenes and Prefab Stage were clean in this
reproduction.
-
Confirm the focus-return tracker has armed its hold:
uloop execute-dynamic-code --code 'using UnityEditor; return $"focused={EditorApplication.isFocused};held={SessionState.GetBool("io.github.hatayama.UnityCliLoop.ExternalSceneChangeTracker.AutoRefreshHeld", false)}";'
Observed: focused=False;held=True.
-
Change the existing C# file outside Unity. In the observed case, git pull changed two
existing scripts and added a private static field to each type.
-
Bring Unity to the foreground once. I used uloop focus-window, which reported that it
focused the real Editor process, and then waited for the normal Auto Refresh/compilation.
-
Inspect EditorApplication.isCompiling, the loaded type through reflection, the
Assembly-CSharp.dll timestamp, and Editor.log.
Actual result
Eight seconds after focus return:
focused=True
held=False
compiling=False
updating=False
new fields present in loaded types=False
- The source files had a modification time of
22:56:16, while
Library/ScriptAssemblies/Assembly-CSharp.dll remained at 20:04:12.
Editor.log contained no new script import, compilation, or Domain Reload after focus
return.
- Therefore, the hold was released successfully, but the pending C# changes remained
unimported and Unity kept running the previous assembly.
Running plain uloop compile immediately afterward produced zero errors and warnings,
imported both scripts, updated Assembly-CSharp.dll, logged domain reloads=1, and made both
new fields visible in the loaded types. This confirms the files were valid and that an
explicit refresh resolves the stale state.
I reproduced the same failure independently with a newly created C# probe under Assets/.
Forty-five seconds after focus return, AssetDatabase.AssetPathToGUID(path) was still empty,
no .meta file had been created, and Editor.log showed no import, compilation, or Domain
Reload. This second reproduction did not depend on Git or on the contents of the pulled
commit.
Expected result
After the focus-return preflight safely handles open Scene/Prefab changes and releases its
Auto Refresh hold, Unity should resume the normal Auto Refresh behavior for changes that
accumulated while the hold was active. Pending C# changes should be imported, compiled, and
cause the normal Domain Reload without requiring an agent-specific uloop compile command.
This matters in mixed human/agent workflows where source changes arrive through Git or an
external IDE while Unity is running in the background. Although uloop's primary use case is
LLM-driven operation, it would be helpful if a developer working manually in the same project
could continue to receive Unity's normal Auto Refresh behavior simply by returning to the
Editor.
Suspected cause
ExternalSceneChangeTracker supplies AssetDatabase.DisallowAutoRefresh and
AssetDatabase.AllowAutoRefresh to ExternalAssetFocusReturnService. On focus gain,
HandleFocusChanged(true) performs the preflight and only then calls
ReleaseAutoRefreshIfHeld().
Unity documents focus regain as an Auto Refresh trigger, while it documents
AllowAutoRefresh() as decrementing the internal hold counter; it does not document that the
call schedules AssetDatabase.Refresh().
This timing suggests, but does not yet prove, that Unity processes the focus-triggered refresh
while the hold is still active and that releasing the hold later in the callback does not
queue another refresh. The hold/release path was introduced in #1757 and shipped through
#1760. I have not run an A/B test without that tracker, and #2480 observed a changed Scene
being imported on focus return with Unity 6000.3.15f1, so this may be script-specific or
Unity-version-dependent.
The current unit tests verify the call order preflight -> allow, but do not verify the
end-to-end result of an external .cs change being imported and compiled after real focus
return.
Documentation and related issues
Possible fix and regression coverage
Possible direction, pending confirmation: after the Scene/Prefab preflight and successful
hold release, ensure that changes accumulated while the hold was active are handed back to
Unity's refresh pipeline. This might require scheduling one refresh, but the exact mechanism
should preserve the existing native-dialog/crash protection and avoid unnecessary refreshes.
If reliably restoring the automatic focus-return behavior is difficult because of Unity's
refresh or callback constraints, a useful fallback would be a clearly visible button in the
Unity CLI Loop Editor UI that lets a developer manually refresh assets and compile pending
scripts. Automatic handling would still be preferable, but an Editor button would support
mixed human/agent workflows without requiring the developer to open a terminal or know the
uloop compile command.
Please add regression coverage for the hold/release-to-refresh handoff:
- Editor unfocused and Auto Refresh held.
- Existing
.cs file changed externally.
- Editor receives focus and the hold is released.
- The changed script is imported, compilation completes, and a Domain Reload occurs without
calling uloop compile.
If a real focus/import/Domain Reload integration test is unsafe in Edit Mode, cover the
refresh-scheduling decision with a pure unit test and retain a documented manual or external
E2E gate for the complete sequence.
Summary
When the Unity Editor is unfocused, Unity CLI Loop holds Auto Refresh with
AssetDatabase.DisallowAutoRefresh(). In the reproduction below, an externalgit pullchanged existing C# files during that interval. Returning focus to Unity released the hold,
but the pending script changes were not imported. No compilation or Domain Reload occurred
until an explicit refresh through
uloop compilewas run.This reproduced with Unity's Auto Refresh and Domain Reload enabled, no active uloop
hot-reload patches, and no other hot-reload package installed.
I understand that Unity CLI Loop is primarily designed around an LLM-driven Unity workflow.
In practice, however, the same Editor session is often shared by an agent and a developer
working manually. It would be helpful if external IDE edits,
git pull,git checkout, orsimilar human-driven operations could still flow through Unity's normal refresh behavior on
focus return, without the developer needing to know that a later
uloop compileis required.Environment
d6fa8bf679cc31563c7b58e7da95a15f3dd07d14EditorApplication.isPlaying == false)kAutoRefreshMode=1AssetDatabase.IsDirectoryMonitoringEnabled() == false(Directory Monitoring is notavailable on macOS)
uloop hot-reload --status:ActivePatchTotal: 0I also checked current
mainatd0340b3b577b86f71ccb05139ddaf062d3998f9d(package version 3.2.1). The relevant hold/release implementation is unchanged there, and
still does not request an AssetDatabase refresh after releasing the hold.
Reproduction
Open a Unity project containing an existing C# type, focus the Editor once, then move
focus to Finder or a terminal. The loaded Scenes and Prefab Stage were clean in this
reproduction.
Confirm the focus-return tracker has armed its hold:
uloop execute-dynamic-code --code 'using UnityEditor; return $"focused={EditorApplication.isFocused};held={SessionState.GetBool("io.github.hatayama.UnityCliLoop.ExternalSceneChangeTracker.AutoRefreshHeld", false)}";'Observed:
focused=False;held=True.Change the existing C# file outside Unity. In the observed case,
git pullchanged twoexisting scripts and added a private static field to each type.
Bring Unity to the foreground once. I used
uloop focus-window, which reported that itfocused the real Editor process, and then waited for the normal Auto Refresh/compilation.
Inspect
EditorApplication.isCompiling, the loaded type through reflection, theAssembly-CSharp.dlltimestamp, andEditor.log.Actual result
Eight seconds after focus return:
22:56:16, whileLibrary/ScriptAssemblies/Assembly-CSharp.dllremained at20:04:12.Editor.logcontained no new script import, compilation, or Domain Reload after focusreturn.
unimported and Unity kept running the previous assembly.
Running plain
uloop compileimmediately afterward produced zero errors and warnings,imported both scripts, updated
Assembly-CSharp.dll, loggeddomain reloads=1, and made bothnew fields visible in the loaded types. This confirms the files were valid and that an
explicit refresh resolves the stale state.
I reproduced the same failure independently with a newly created C# probe under
Assets/.Forty-five seconds after focus return,
AssetDatabase.AssetPathToGUID(path)was still empty,no
.metafile had been created, andEditor.logshowed no import, compilation, or DomainReload. This second reproduction did not depend on Git or on the contents of the pulled
commit.
Expected result
After the focus-return preflight safely handles open Scene/Prefab changes and releases its
Auto Refresh hold, Unity should resume the normal Auto Refresh behavior for changes that
accumulated while the hold was active. Pending C# changes should be imported, compiled, and
cause the normal Domain Reload without requiring an agent-specific
uloop compilecommand.This matters in mixed human/agent workflows where source changes arrive through Git or an
external IDE while Unity is running in the background. Although uloop's primary use case is
LLM-driven operation, it would be helpful if a developer working manually in the same project
could continue to receive Unity's normal Auto Refresh behavior simply by returning to the
Editor.
Suspected cause
ExternalSceneChangeTrackersuppliesAssetDatabase.DisallowAutoRefreshandAssetDatabase.AllowAutoRefreshtoExternalAssetFocusReturnService. On focus gain,HandleFocusChanged(true)performs the preflight and only then callsReleaseAutoRefreshIfHeld().Unity documents focus regain as an Auto Refresh trigger, while it documents
AllowAutoRefresh()as decrementing the internal hold counter; it does not document that thecall schedules
AssetDatabase.Refresh().This timing suggests, but does not yet prove, that Unity processes the focus-triggered refresh
while the hold is still active and that releasing the hold later in the callback does not
queue another refresh. The hold/release path was introduced in #1757 and shipped through
#1760. I have not run an A/B test without that tracker, and #2480 observed a changed Scene
being imported on focus return with Unity 6000.3.15f1, so this may be script-specific or
Unity-version-dependent.
The current unit tests verify the call order
preflight -> allow, but do not verify theend-to-end result of an external
.cschange being imported and compiled after real focusreturn.
Documentation and related issues
docs/focus-return-asset-handling.mddocuments the hold and focus-return preflight.uloop compileexplicitly refreshes externallyedited files.
changes pending, or that
uloop compileis mandatory after such changes.run-testspath; notest command is involved here.
run-tests --skip-compileand active hot-reload patches, which were absenthere.
follow-up observed a changed Scene being imported after focus return on Unity 6000.3.15f1,
unlike the pending
.csbehavior reported here.Possible fix and regression coverage
Possible direction, pending confirmation: after the Scene/Prefab preflight and successful
hold release, ensure that changes accumulated while the hold was active are handed back to
Unity's refresh pipeline. This might require scheduling one refresh, but the exact mechanism
should preserve the existing native-dialog/crash protection and avoid unnecessary refreshes.
If reliably restoring the automatic focus-return behavior is difficult because of Unity's
refresh or callback constraints, a useful fallback would be a clearly visible button in the
Unity CLI Loop Editor UI that lets a developer manually refresh assets and compile pending
scripts. Automatic handling would still be preferable, but an Editor button would support
mixed human/agent workflows without requiring the developer to open a terminal or know the
uloop compilecommand.Please add regression coverage for the hold/release-to-refresh handoff:
.csfile changed externally.calling
uloop compile.If a real focus/import/Domain Reload integration test is unsafe in Edit Mode, cover the
refresh-scheduling decision with a pure unit test and retain a documented manual or external
E2E gate for the complete sequence.