Skip to content

fix: hold Auto Refresh when Editor starts unfocused (external scene dialog/crash) - #1757

Merged
hatayama merged 2 commits into
feature/compile-consistencyfrom
feat/external-scene-unfocused-hold
Jul 13, 2026
Merged

hatayama merged 2 commits into
feature/compile-consistencyfrom
feat/external-scene-unfocused-hold

Conversation

@hatayama

@hatayama hatayama commented Jul 13, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Background / never-focused Editor startup now arms DisallowAutoRefresh immediately (not only on focusChanged(false))
  • Throttled update reconcile keeps focus ↔ held flag aligned; kCodeReload failures log and retry via reconcile (no delayCall retry chains)
  • SessionState held flag is set/cleared only after Disallow/Allow succeed

User Impact

  • Before: uloop launch in the background never fired focus-lost, so Auto Refresh stayed enabled. External Scene edits (e.g. git discard) then hit Unity’s native EditorSceneManager::HandleOpenScenesChangeOnDisk path → reload/ignore dialog, and in at least one local run a native SIGSEGV that killed the whole Editor
  • After: unfocused startup holds Auto Refresh; focus return uses the existing quiet resolve path instead of the native dialog/crash path
  • Out of scope (approved): discard while Unity stays focused (②) — mitigated only by reconcile + compile-time ResolveForCompile

Real-world crash evidence (pre-fix)

~/Library/Logs/Unity/Editor-prev.log (~22:27 session), after loading RaycastAnnotationPerspectiveDemoScene:

Native Crash Reporting
Got a segv while executing native code.
...
EditorSceneManager::HandleOpenScenesChangeOnDisk
EditorSceneManager::OnApplicationTick
...
EditorSceneManager::ReloadScene

This is the native external-scene-change path this PR bypasses by holding Auto Refresh until uloop can resolve quietly.

Design choices (from approved answers)

  • Retry strategy: no delayCall chains — catch logs + leave unheld; update reconcile retries
  • try-catch: Disallow/Allow boundary only (hatayama-approved); always logs; never swallows
  • Idempotent Hold covered by pure C# tests

Verification

  • uloop compile — 0 errors / 0 warnings
  • uloop run-tests filter ExternalSceneChangeResolverTests — 23 passed (incl. vibe hold log tests)
  • Manual clean gate: quit → CLI launch unfocused → held probe focused=False;held=True → discard → first focus → no dialog / no crash
    • Evidence: /tmp/claude-shared/pr-5-2-clean-gate-result.md (+ vibe timeline + Editor-prev.log Loaded scene before Refresh)
    • Post-fix held probe = ODE snippet reading EditorApplication.isFocused + SessionState ...AutoRefreshHeld; proves hold armed before discard/focus (not dialog absence alone)
  • Manual: focused discard (②) still may dialog — expected out of scope (approved)
  • ULOOP_DEBUG VibeLogger events on hold/focus/resolve/reconcile-repair (observability for gate correlation)

Test plan

  • CI green on this PR
  • Clean gate on patched build (see Verification)
  • Domain reload: held re-armed after unfocused compile path (vibe hold_armed at launch; no permanent Disallow desync observed)

Made with Cursor

Review in cubic

Background launch never fires focusChanged(false), so DisallowAutoRefresh
was never armed and native HandleOpenScenesChangeOnDisk could dialog or
segfault on external scene edits. Arm Hold from Initialize, reconcile on
throttled update, and only set SessionState after Disallow/Allow succeed.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jul 13, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0be17622-7350-4618-9127-c97b59ac24a6

📥 Commits

Reviewing files that changed from the base of the PR and between 8cdb54a and bf2aa9a.

📒 Files selected for processing (3)
  • Assets/Tests/Editor/ExternalSceneChangeResolverTests.cs
  • Packages/src/Editor/FirstPartyTools/Compile/ExternalAssetFocusReturnService.cs
  • Packages/src/Editor/FirstPartyTools/Compile/ExternalSceneChangeTracker.cs

📝 Walkthrough

Walkthrough

The focus return service now guards Unity auto-refresh hold and release operations, logs failures, and exposes focus reconciliation methods. The scene tracker performs throttled reconciliation during editor updates, handles initially unfocused startup states, and records richer focus-return diagnostics. Tests cover idempotence, retries, release ordering, and exceptions.

Changes

Focus-aware auto-refresh handling

Layer / File(s) Summary
Guarded focus and auto-refresh transitions
Packages/src/Editor/FirstPartyTools/Compile/ExternalAssetFocusReturnService.cs, Assets/Tests/Editor/ExternalSceneChangeResolverTests.cs
The service adds warning injection, focus reconciliation methods, guarded auto-refresh operations, and held-state preservation across failures. Tests cover idempotent holding, retry behavior, release ordering, vibe logs, and warning messages.
Periodic focus reconciliation
Packages/src/Editor/FirstPartyTools/Compile/ExternalSceneChangeTracker.cs
The tracker throttles update-based reconciliation, snapshots scene state during initialization, and immediately applies the unfocused hold behavior.
Focus-return diagnostics
Packages/src/Editor/FirstPartyTools/Compile/ExternalSceneChangeTracker.cs
Focus changes and focus-return resolution now log auto-refresh state and per-scene fingerprint differences, including early-exit and completion data.

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

Sequence Diagram(s)

sequenceDiagram
  participant EditorApplication
  participant ExternalSceneChangeTracker
  participant ExternalAssetFocusReturnService
  participant UnityAutoRefresh
  EditorApplication->>ExternalSceneChangeTracker: invoke throttled update callback
  ExternalSceneChangeTracker->>ExternalAssetFocusReturnService: reconcile hold with focus
  ExternalAssetFocusReturnService->>UnityAutoRefresh: disallow or allow auto-refresh
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Title check ✅ Passed The title clearly summarizes the main change: holding Auto Refresh when the Editor starts unfocused.
Description check ✅ Passed The description is directly related to the changeset and explains the startup, reconcile, and retry behavior.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/external-scene-unfocused-hold

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.

Clean-gate held probes previously ran blind when ODE was busy; structured
hold/focus/resolve events make dialog timing correlatable without relying on
Editor.log alone.

Co-authored-by: Cursor <cursoragent@cursor.com>
@hatayama

Copy link
Copy Markdown
Owner Author

LGTM (reviewed by Fable on behalf of the review flow; same-account approval unavailable).

Reviewed the full 345-line diff plus the observability commit bf2aa9a:

  • All 3 approved design constraints hold: no delayCall retry chains (reconcile-only retry), SessionState held flag set/cleared only after Disallow/Allow actually succeed, throttled + idempotent update reconcile (0.5s, no-op when aligned).
  • try-catch stays within the approved Disallow/Allow boundary, always logs, never swallows.
  • VibeLogger instrumentation is ULOOP_DEBUG-gated, injected on the service so pure C# tests stay logger-free, and reconcile logs only on actual repair.
  • Clean machine gate PASS with a non-blind held probe (focused=False, held=True before discard), vibe timeline hold_armed -> focus_changed -> resolve (fingerprint 63231 -> 63226) -> hold_released, no native dialog window, and no Asset Pipeline Refresh between discard and the quiet scene reload.

Follow-ups recorded in group5 umbrella notes: warning backoff if Disallow persistently fails, narrowing catch(Exception) once the real exception type is confirmed, and the group-3 residual (DynamicCodeExecutionScheduler slot stuck across domain reload with an idle Roslyn worker).

@hatayama
hatayama merged commit 55b681f into feature/compile-consistency Jul 13, 2026
1 of 2 checks passed
@hatayama
hatayama deleted the feat/external-scene-unfocused-hold branch July 13, 2026 14:17
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