Repository navigation
feat: Hot reload keeps one run's assemblies cached between runs, uloop compile returns at once without raising the Editor, and pause point timeouts report their own timeout - #3236
Merged
Conversation
Contributor
|
Important Review skippedToo many files! This PR contains 189 files, which is 39 over the limit of 150. To get a review, reduce the PR to 150 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configuration
⛔ Files ignored due to path filters (69)
📒 Files selected for processing (189)
You can disable this status message by setting the
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. Comment |
…up preference, not that it reverts (#3237)
…ading the main project's compiled assemblies (#3238)
…once per domain (#3240)
… the file's length or write time changed (#3241)
…l Error before uloop loads (#3243)
… a package source (#3244)
… snapshot, so the first run after a domain reload does not read unchanged siblings (#3245)
…t that follows a compile's domain reload (#3246)
…fore walking it, so assemblies that name none of the edited methods are not loaded (#3247)
… so the first caller scan after a domain reload reads only assemblies that name an edited method (#3248)
…Metadata from the PDB and the dll's debug directory, so a rebuilt assembly's first run does not walk its dll (#3249)
…Y_NOT_REACHABLE and retryable instead of an internal error (#3251)
… and shim-compile phases into worker start, request steps, shim references, publicized copies, and compiler time (#3250)
… reuse a cached compiler worker instead of rebuilding it (#3252)
…emblies' call sites, referenced-method sets and PDB documents already loaded (#3253)
… second rewriting the edited assembly's reference copy (#3254)
…or compiled source (#3255)
…edits while hot reload holds Auto Refresh (#3257)
…e shared Roslyn worker already running with its references loaded (#3258)
…ps its own System.Reflection.Metadata (#3260)
hatayama
marked this pull request as ready for review
October 9, 2026 14:45
This was referenced Oct 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This collects 28 pull requests (fix: uloop compile returns as soon as the Editor answers instead of warming execute-dynamic-code first #3227 to fix: A request that Unity keeps answering busy no longer brings the Editor to the front #3231, fix: Hot reload's call-site cache is bounded by bytes instead of a count, so one run's assemblies stay cached #3235, fix: The Debug-switch warning says the setting goes back to the startup preference, not that it reverts #3237, feat: Hot reload patches a Multiplayer Play Mode Virtual Player by reading the main project's compiled assemblies #3238, perf: Hot reload asks the compilation pipeline for the assembly list once per domain #3240 to perf: The first hot reload shim compile after a server reset finds the shared Roslyn worker already running with its references loaded #3258 and fix: The package compiles on Unity 6000.5 and newer, whose Editor ships its own System.Reflection.Metadata #3260) that came out of the third batch of findings from using hot reload on a large multi-assembly project: the floor of the time outside the timed phases and the three steps below it, five steps of the first run after a domain reload, vibe entries that split that run's two remaining phases, two cases where
uloopraised the Editor window, the leftover warm-up ofuloop compile, four details of the pause-point timeout response and the busy vibe entries, the wording of the Debug-switch warning, hot reload on a Multiplayer Play Mode Virtual Player, a note on a player that quits before uloop loads, the baseline of a package source in such a player, the error envelope of a request that Unity never acknowledged, a cache of the shared compiler worker, a background warm-up after a domain reload that also writes the edited assembly's publicized copy, the baseline of a file edited right after or during a compile, a background start of the shared Roslyn worker after a server reset, and a note on edits that another package's ownAssetDatabase.Refresh()compiles.Hot reload keeps every assembly one run reads cached for the next run. On the trial project the caller lookup of an apply went from 2.0–2.5 s to 53–59 ms, the time outside the phases from 3.1–3.7 s to 0.7–0.9 s, and one apply from 3.7–4.4 s to 1.4–1.7 s.
Of the time that remained, hot reload now asks the compilation pipeline for the assembly list once per domain instead of on every run (
resolve_inputs, 0.34–0.37 s per run on the trial project), and compares a source with its baseline again only when the file's length or write time changed (sibling_detect, 0.23–0.46 s per run there). The first run after a domain reload that did not rebuild the edited assembly reads that assembly's PDB document list from a file underLibrary/UloopHotReload/instead of walking every sequence point again (snapshot_group_state, 0.8–1.2 s cold there). That first run also keeps the assembly list across the import that follows the reload (perf: Hot reload keeps its compilation assembly list across the import that follows a compile's domain reload #3246), compares unchanged siblings from the length and write time recorded with the snapshot instead of reading them (perf: Hot reload records each source's length and write time with its snapshot, so the first run after a domain reload does not read unchanged siblings #3245), and reads a referencing assembly's MemberRef table before walking it, so assemblies that name none of the edited methods are not loaded (perf: Hot reload's caller scan reads an assembly's MemberRef table before walking it, so assemblies that name none of the edited methods are not loaded #3247), and keeps each assembly's set on disk so that the first scan after a domain reload does not read those tables again (perf: Hot reload keeps each assembly's referenced-method set on disk, so the first caller scan after a domain reload reads only assemblies that name an edited method #3248). The first run after a compile that rebuilt the edited assembly builds the PDB document list from the PDB alone with System.Reflection.Metadata, opening the dll only for its CodeView GUID, instead of reading the whole dll with Cecil (perf: Hot reload builds the PDB document list with System.Reflection.Metadata from the PDB and the dll's debug directory, so a rebuilt assembly's first run does not walk its dll #3249;snapshot_group_state1774 ms to 288 ms on that run on the trial project, 413–465 ms to 60–62 ms on the development project).uloop --project-path <main>/Library/VP/mppm<id> hot-reload --files ...patches a Multiplayer Play Mode Virtual Player by reading the main project's compiled assemblies, where it used to fail with "Compiled assembly not found ... hot reload cannot patch it yet".uloop compilereturns as soon as the Editor answers and never brings the Editor to the front while it waits for compilation to start. A request that Unity keeps answering busy no longer brings the Editor to the front either.A timed-out
await-pause-pointreports its own timeout and the marker id the Editor uses; busy vibe entries name the running tool and the requests sent again.With
ULOOP_DEBUG, one hot reload run also writes entries that split a cold run's analysis and shim-compile phases: transform worker start and request steps, the worker's own stages, the shim reference list, publicized copies, the shim compiler and the shared Roslyn worker start (feat: Hot reload writes vibe entries that split a cold run's analysis and shim-compile phases into worker start, request steps, shim references, publicized copies, and compiler time #3250). Behaviour is unchanged; the entries are the evidence for the next cold-run work.After a domain reload, the first
execute-dynamic-coderun or hot reload copies the shared Roslyn compiler worker from a cache instead of building it with csc (perf: The first dynamic code run and hot reload after a domain reload reuse a cached compiler worker instead of rebuilding it #3252;assemblyEnsureMs424 ms to 2 ms on the development project). Hot reload also loads, in the background on the first Editor update tick, the call sites, referenced-method sets and PDB document lists of the assemblies earlier runs edited, so the first run finds them loaded (perf: The first hot reload after a domain reload finds the edited assemblies' call sites, referenced-method sets and PDB documents already loaded #3253; on the development project the first run after a compile went from 3024 ms to 2484 ms,caller_notesfrom 462 ms to 8 ms). The warm-up first writes the publicized copy of each of those assemblies that the shim compile references, so the first run after a compile no longer rewrites it (perf: The first hot reload after a compile no longer spends most of a second rewriting the edited assembly's reference copy #3254;targetCopyMs747 ms to 1 ms on the development project, 1012 ms to 5 ms on the trial project). A run that arrives during that warm-up waits only for the dll being read or the copy being written.A tool request that reached Unity but was not acknowledged within the attempt's 10-second window is reported as
UNITY_NOT_REACHABLEwithRetryable: trueandSafeToRetry: false, where it used to beINTERNAL_ERRORwithRetryable: falseand a hint to fix the environment (fix: A tool request that Unity never acknowledged is reported as UNITY_NOT_REACHABLE and retryable instead of an internal error #3251).Hot reload no longer takes an edit made right after
uloop compilereturned for compiled source: the snapshot of the compiled sources is captured while the new domain loads, before the Editor answers any command (fix: Hot reload no longer mistakes an edit made right after compile for compiled source #3255). Before, on the trial project, such an edit made the next run patch every method of the file ("No verified source snapshot"), and a run without--filesdid not select the file. A file saved while the compile ran, after the compiler had read it, is now checked against the PDB when the snapshot is captured and treated as changed when it does not match (fix: Hot reload no longer silently skips a file saved while the compile was running #3256), where it used to be dropped until the next compile.After each server reset, when hot reload's ledger names a compiled assembly, the shared Roslyn worker starts in the background and compiles once against the references the shim compile binds, so the first shim compile finds it running (perf: The first hot reload shim compile after a server reset finds the shared Roslyn worker already running with its references loaded #3258; the first run's shim
compileMswent from 801–807 ms to 72–80 ms on the development project, and from 439–493 ms to 118–129 ms on the trial project).Merge status
084e1d13: five of the six checks behaved as described, and a source inside a local package patched every method of its file instead of the edited one, a defect in the baseline lookup that fix: Hot reload in a Virtual Player patches only the edited method of a package source #3244 fixes; the re-check on headc328223fconfirmed the fix on the real player. See "Verification".62b1236c, and the caller-scan changes perf: Hot reload's caller scan reads an assembly's MemberRef table before walking it, so assemblies that name none of the edited methods are not loaded #3247 and perf: Hot reload keeps each assembly's referenced-method set on disk, so the first caller scan after a domain reload reads only assemblies that name an edited method #3248 on head1ed19803: the caller scan of the first run after a domain reload went from 4.4–5.9 s to 1.0–1.7 s there (see "Verification"). The PDB read of perf: Hot reload builds the PDB document list with System.Reflection.Metadata from the PDB and the dll's debug directory, so a rebuilt assembly's first run does not walk its dll #3249 was re-measured by the reporting agent on head7daed1af: the first run after a rebuild of the edited assembly went from 9.8 s to 6.1 s there, withsnapshot_group_statefrom 1774 ms to 288 ms (see "Verification").server_busyinstead of staying silent.4fa78e1bandb200c1ab, each switched off and on in the same session (see "Verification"). fix: Hot reload no longer mistakes an edit made right after compile for compiled source #3255 and fix: Hot reload no longer silently skips a file saved while the compile was running #3256 were verified on the development project by their authors and re-checked by the reporting agent on head57bbb472(see "Verification"). docs: Note that a package's own AssetDatabase.Refresh compiles saved edits while hot reload holds Auto Refresh #3257 (documentation) was checked on the development project by its author, and perf: The first hot reload shim compile after a server reset finds the shared Roslyn worker already running with its references loaded #3258 by its author there and by the reporting agent on the trial project on head88d0ff2c(see "Verification").User Impact
Speed
ULOOP_DEBUGset, the time outside the response's phases (OtherMs) was one numberhot_reload_timing_detailentry per run that breaksOtherMsinto named steps, with the unaccounted remainder, so the next floor can be read from the log instead of instrumented againCompilationPipeline.GetAssemblies()for the project's assembly list, from five places; on the trial project (558 assemblies) one call took 342–622 ms, still 340 ms warm, andresolve_inputs(337–366 ms per run) was almost entirely that callcompilationStartedorprojectChangedfires; the startup capture fills it, so the first run after a domain reload is fast too. On the development project (111 assemblies)resolve_inputswent from 62–149 ms to 1–2 ms per runsibling_detecttook 228–457 ms per runcompilationStarted. Astatof the same files takes 16–19 ms thereuloop compile, a Play Mode reload, an Editor restart) dropped it and the next run read the dll and the PDB with Cecil and walked every type, method and sequence point again, even when the edited assembly had not been rebuilt; on the trial projectsnapshot_group_statetook 0.8–1.2 s on that first runLibrary/UloopHotReload/PdbDocuments/fmt1/<assemblyName>.txtwith the same five-value stamp (dll length, write time and MVID; PDB length and write time); the first run after a domain reload reads it when the stamp still matches. On the development project that first run went from 28–30 ms to 3 ms. The run right after recompiling the edited assembly itself still walks, because its stamp changedprojectChanged, which dropped the assembly list the startup capture had just filled, so the first run after every compile askedCompilationPipeline.GetAssemblies()again (resolve_inputs0.6–0.7 s on that run on the trial project)compilationStartedand by an import batch that touches a.cs,.asmdef,.asmrefor.dll; the batch that follows a domain reload keeps it. On the trial projectresolve_inputstakes 25–53 ms on the first run after a compilesibling_detect0.75–1.3 s on that run on the trial project)source-stamps.txtbeside it when it is captured; the first run after a reload compares stamps and reads only the sources whose stamp changed. A snapshot captured before this change has no stamp file and keeps the byte comparison until the assembly is rebuilt and snapshotted againLibrary/UloopHotReload/ReferencedMethods/fmt1/<assemblyName>.txtwith the dll's length, write time and MVID, and the first scan after a reload reads the file instead of the dll while they match. A missing, truncated, differently stamped or otherwise malformed file is ignored as a whole and rewritten after the dll is readInMemory,ReadSymbols) and walking every type, method and sequence point:snapshot_group_state1.2–1.8 s on that run on the trial project (a 10 MB dll with about 1,300 sources)execute-dynamic-codeand the hot reload shim compile use was built again with csc, because shutdown deletes its per-process directory:assemblyEnsureMs400–500 ms on the development project, paid by whichever came first<OS temp>/UnityCliLoopCompilation/RoslynWorkerCache/<key>/, keyed by the worker source and the compiler paths, and copied from there:assemblyEnsureMs424 ms on the first build, 2 ms after each later reloadcaller_notes462 ms andsnapshot_group_state95 ms on the development project, 1.5 s and 0.3 s on the trial projectLibrary/UloopHotReload/WarmUp/targets.txt. On the first update tick after a domain reload the Editor loads those three for up to 8 of them in the background. On the development project the first run after a compile took 2484 ms instead of 3024 ms, withcaller_notes8 ms andsnapshot_group_state2 mstargetCopyMs747 ms on the development project, 858 ms on the trial projecttargetCopyMs1 ms on the development project, 5 ms on the trial project (1012 ms with the warm-up switched off in the same session)compileMs801–807 ms on the development project and 439–493 ms on the trial projectcompileMswas 72–80 ms and 118–129 ms thereEdits made around a compile
uloop compilereturns. An edit saved in that window was copied as compiled source: the next run warned "No verified source snapshot" and patched every method of the file, and a run without--filesdid not select it, until the next compileuloop compileran, after the compiler had read it, was copied into the snapshot with its new content, so the default file selection, the sibling scan and the skip check compared it as unchanged and the edit was dropped until the next compilesource-stamps.txt, and the three readers treat a marked file as changedMultiplayer Play Mode
uloop --project-path <main>/Library/VP/mppm<id> hot-reload --files ...failed withCompiled assembly not found ... hot reload cannot patch it yet: hot reload looked for the player's DLLs under the player's ownLibrary/ScriptAssemblies, which does not exist, and resolved relative assembly references against the process's current directoryLibrary/ScriptAssemblies, while the player keeps its own hot reload state (patch ledger, source snapshots, publicized copies, worker cache) under its ownLibrary/UloopHotReload/. When the main project has never compiled the assembly, the failure says to compile the main Editor's project firstuloop compileget-versionand then anexecute-dynamic-codeprobe before returning, showed aWarming execute-dynamic-code after compile...spinner, and printedwarning: post-compile warmup skipped: ...when the probe did not go throughexecute-dynamic-codeafter a compile pays its own settle timeEditor focus
server_busyfor five seconds, because another command was running, brought the Editor to the frontPause points
await-pause-point --timeout-seconds 5reportedDetails.TimeoutSeconds: 30(the marker's window) while its message said 5 sDetails.TimeoutSecondsis the wait's own timeout; the marker's window moves toDetails.MarkerTimeoutSecondsDetails.Idof that failure echoed the typed path form (./Assets/Foo.cs:42), which differs from the id the status command reportsAssets/Foo.cs:42)Logs (with
ULOOP_DEBUG)cli_hot_reload_busy_wait_completeentry did not say which requests were sent again during the waitresend_correlation_ids, an empty array when none)cli_tool_request_failedentry did not say which tool held the Editorrunning_tool_name)hot_reload_call_site_cache_evictedentry with the evicted count and bytes, the bytes still cached, and the budgetAnalysisMsandShimCompileMswere two numbers, with nothing inside them measuredhot_reload_worker_request_timing(one per transform worker request: gate wait, launch target resolution, input write, process spawn, response wait, output read, whether the request started the worker, and the worker's own stages),hot_reload_signature_gate_timing,hot_reload_shim_first_compile_timing,hot_reload_shim_references_timing,hot_reload_shim_compiler_timing; and only when they happen,hot_reload_publicized_copy_writtenanddynamic_code_shared_worker_started(readyfalse when the start failed)dynamic_code_shared_worker_starteddid not say where the worker assembly came fromworkerAssemblySource(existing,cacheorbuilt),cachePublishandcachePublishErrorhot_reload_timing_detailshowed the first run's loads insidecaller_notesandsnapshot_group_stateonlyhot_reload_warm_up_complete(the target assemblies, each item's outcome and time in the orderpublicized_targets,call_sites,referenced_method_sets,pdb_documents, andcancelledBy) orhot_reload_warm_up_skipped(its reason), and a run's steps gainwarm_up_yieldandwarm_up_targets. A copy the warm-up writes also appears ashot_reload_publicized_copy_writtenhot_reload_source_snapshot_captured, once per domain, withtrigger(domain_load,first_update_tickorapply) andcaptureMshot_reload_source_snapshot_checked, per assembly whose capture checked any source against the PDB, withsuspect,editedAfterCompileandcheckMsdynamic_code_shared_worker_warm_up_skippedwith itsreason, ordynamic_code_shared_worker_warm_up_completewithms,referencesMs,mainThreadMs,warmMs,referenceCount,outcome(answered,already_running,start_failed,request_failedorfailed),failureReasonanderrorCountError envelopes
ErrorCode: INTERNAL_ERROR,Phase: execution,Retryable: false,SafeToRetry: false, with the rawi/o timeoutmessage and the hint to fix the local environment or command inputErrorCode: UNITY_NOT_REACHABLE,Phase: response_waiting,Retryable: true,SafeToRetry: false,Details.Causecarrying the timeout, and NextActions that say Unity may have received the request and to retry only after confirming the previous command did not run or has finishedBehaviour changes to weigh before merging
uloop compileno longer probesexecute-dynamic-codeafter a successful compile (fix: uloop compile returns as soon as the Editor answers instead of warming execute-dynamic-code first #3227). A script that runsuloop execute-dynamic-coderight afteruloop compilesees the settle time of the domain reload in that command instead of incompile.Two of the focus rescues are gone (fix: uloop compile no longer brings the Editor to the front while it waits for compilation to start #3228, fix: A request that Unity keeps answering busy no longer brings the Editor to the front #3231).
uloop compilenever raises the Editor while waiting for compilation to start, and a request that Unity keeps answering busy never raises it. If the Editor is genuinely stuck behind a dialog, the remaining reasons (pre-accept timeout, main-thread stall, heartbeat silence) still bring it forward.Details.TimeoutSecondsof a timed-outawait-pause-pointchanged meaning (fix: Pause point timeout details carry the wait's timeout and the marker id, and busy vibe entries name the running tool and the resent requests #3229). It is now the wait's--timeout-seconds; anything that read the marker's window from it must readDetails.MarkerTimeoutSeconds.The call-site cache may hold up to 256 MB of dll bytes in the Editor process between runs (fix: Hot reload's call-site cache is bounded by bytes instead of a count, so one run's assemblies stay cached #3235), where it held 64 entries before. The bound counts file lengths, not the exact in-memory size, and nothing is evicted while a run is in progress, as before.
With
ULOOP_DEBUGset, the Editor-side vibe log gets one more entry per hot-reload run (feat: Hot reload writes a timing detail vibe entry that breaks down the time outside the response's phases #3230) and, only when the cache had to evict, onehot_reload_call_site_cache_evicted(fix: Hot reload's call-site cache is bounded by bytes instead of a count, so one run's assemblies stay cached #3235). Nothing is written without the define.Hot reload resolves a project's compiled assemblies through one layout (feat: Hot reload patches a Multiplayer Play Mode Virtual Player by reading the main project's compiled assemblies #3238). A Virtual Player root (
<main>/Library/VP/<player>) reads the main project'sLibrary/ScriptAssemblies; an ordinary root reads its own, as before. Relative entries of an assembly'sallReferencesare resolved against the project root instead of the process's current directory; for an ordinary project, whose Editor runs with the project root as its current directory, the files are the same. An assembly whose reportedoutputPathdoes not lead to the DLL the layout built now fails as a missing compiled assembly that names both paths.The compilation assembly list is kept per domain (perf: Hot reload asks the compilation pipeline for the assembly list once per domain #3240, perf: Hot reload keeps its compilation assembly list across the import that follows a compile's domain reload #3246).
CompilationPipeline.GetAssemblies()is called once per domain and the answer reused untilcompilationStartedfires or an import batch touches a.cs,.asmdef,.asmrefor.dll; an empty answer is never kept. perf: Hot reload keeps its compilation assembly list across the import that follows a compile's domain reload #3246 replaced theprojectChangedsubscription with that import check, because the import that follows a compile's domain reload firesprojectChangedafter the startup capture and dropped the list on every first run. An import of other assets only, and the batch that follows a domain reload, keep the list; a change that fires no import of those files leaves hot reload with the earlier list until the next compile.A source compared with its baseline once is compared again only when its length or last write time changes (perf: Hot reload compares a sibling with its snapshot again only when the file's length or write time changed #3241). This applies wherever a source is compared with its baseline: finding drifted siblings, deciding which live patches to re-apply after a skip, and selecting files when
--filesis omitted. A rewrite that keeps both, for example a copy that preserves timestamps, is noticed only after the next domain reload.One small file per assembly appears under
Library/UloopHotReload/PdbDocuments/fmt1/(perf: Hot reload keeps the PDB document list of an assembly across domain reloads #3242). A missing, truncated, hand-edited or differently stamped file is treated as absent: the PDB is walked and the file rewritten (temp file, then move). A walk that throws leaves no entry and no file. A failure to read or write that file (a locked file, an unwritableLibrary) surfaces as the run's error instead of being ignored, like the other files hot reload keeps underLibrary/UloopHotReload/.Hot reload in a Virtual Player finds the baseline of a package source (fix: Hot reload in a Virtual Player patches only the edited method of a package source #3244). The physical path of a package source is made relative to the root of the project that compiled the assembly, which for a Virtual Player is the main project, instead of the player's own root. The lookup key now matches the
./-prefixed relative path the main Editor's compile records, so only the edited method is patched. The match rule stays one-directional and the snapshot key is unchanged. An ordinary project resolves to its own root as before.Each source snapshot directory gets a
source-stamps.txt(perf: Hot reload records each source's length and write time with its snapshot, so the first run after a domain reload does not read unchanged siblings #3245), written with the snapshot and read on the first run after a domain reload. A missing, truncated or differently formed file means the byte comparison of perf: Hot reload compares a sibling with its snapshot again only when the file's length or write time changed #3241's cold path, never a stale verdict. A source rewritten with the same length and write time is read as unchanged until the next snapshot, the same limit as perf: Hot reload compares a sibling with its snapshot again only when the file's length or write time changed #3241's memo.The caller scan no longer reads an assembly whose MemberRef table names none of the edited methods (perf: Hot reload's caller scan reads an assembly's MemberRef table before walking it, so assemblies that name none of the edited methods are not loaded #3247). The scan result gains
SkippedScanAssemblyNames(diagnostic, likeExaminedCallSiteCount);MissingScanAssemblyNameskeeps its meaning, so the lifecycle notes and the signature-change closure behave as before. The Editor holds one more static memo (one key set per dll, dropped with the domain). Keys are kept for every assembly scope on purpose: limiting them to project assemblies would build an empty set while Unity compiles and hide real callers.Hot reload keeps one more file per referencing assembly under
Library/UloopHotReload/ReferencedMethods/fmt1/(perf: Hot reload keeps each assembly's referenced-method set on disk, so the first caller scan after a domain reload reads only assemblies that name an edited method #3248), written with a temp file and a move after the dll is read, and read only when its stamp matches the dll. A set is never used in part: any malformed file means the dll is read and the file rewritten. A file that cannot be written surfaces as the run's error, like the other files underLibrary/UloopHotReload/. The trial project's 67 referencing assemblies are estimated at 5–8 MB of such files.The shared hot reload assembly now runs System.Reflection.Metadata in the Editor domain (perf: Hot reload builds the PDB document list with System.Reflection.Metadata from the PDB and the dll's debug directory, so a rebuilt assembly's first run does not walk its dll #3249). It references the bundled
UnityCliLoop.System.Reflection.Metadata,UnityCliLoop.System.Collections.ImmutableandUnityCliLoop.System.Runtime.CompilerServices.Unsafeplugins that the dynamic code assembly already lists; before, only the out-of-process workers used them. The PDB document list keeps its meaning (documents with at least one visible sequence point, once each), but the documents now come in method token order instead of type-then-method order, and a PDB of another build throwsInvalidOperationExceptionwhere Cecil threwSymbolsNotMatchingException; no caller catches either. A dll without a CodeView debug directory entry throws too (Unity's compiler always writes one).With
ULOOP_DEBUGset, the Editor-side vibe log gets up to seven more entries per hot reload run (feat: Hot reload writes vibe entries that split a cold run's analysis and shim-compile phases into worker start, request steps, shim references, publicized copies, and compiler time #3250), and the transform worker's output JSON gains atimingsarray (read only by that entry; output of an older worker leaves it null). Nothing is written without the define, andhot_reload_timing_detailkeeps its shape, sounaccountedMskeeps its meaning.The project runner wraps a dispatched, unacknowledged timeout in
UnityServerNotRespondingError(fix: A tool request that Unity never acknowledged is reported as UNITY_NOT_REACHABLE and retryable instead of an internal error #3251). The wrap happens in the retry loop after the focus handling, for thepre_accept_timeoutfocus reason only; it does not depend on whether a Unity process was found, and it is applied even when the caller has cancelled (that path never checked cancellation before either). The raw timeout stays in the error's unwrap chain, soIsFinalResponseTimeoutErrorand the CLI vibe log'serror_kind(final_response_timeout) are unchanged. An accepted timeout, a disconnect, an RPC error, an editor-unresponsive error and an undispatched failure are reported as before.cli/commonis untouched, so the shared release inputs are not stamped.The shared Roslyn compiler worker assembly is cached in OS temp across domain reloads and Editor restarts (perf: The first dynamic code run and hot reload after a domain reload reuse a cached compiler worker instead of rebuilding it #3252). One file per key under
<OS temp>/UnityCliLoopCompilation/RoslynWorkerCache/<key>/, written with a temp file and a move and never cleaned up; a Unity upgrade or a changed worker source makes a new key. A worker assembly already in the per-process directory is used as is and never published. A cache copy that fails with an IO error falls back to the build, and a publish that fails still starts the worker and records the error incachePublishError; this is the one place in the branch where an IO error is not the command's error, because the assembly is already built. A toolchain whose files change while their paths stay the same is not detected.Hot reload works in the background after every domain reload (perf: The first hot reload after a domain reload finds the edited assemblies' call sites, referenced-method sets and PDB documents already loaded #3253, perf: The first hot reload after a compile no longer spends most of a second rewriting the edited assembly's reference copy #3254). On the first Editor update tick after a reload, unless the Editor is compiling or importing, it works on a pool thread for up to 8 assemblies from
Library/UloopHotReload/WarmUp/targets.txtwhose dll and PDB exist, one kind of work after another: it writes their publicized copies underLibrary/UloopHotReload/PublicizedRefs/(perf: The first hot reload after a compile no longer spends most of a second rewriting the edited assembly's reference copy #3254), then reads their call sites, the referenced-method sets of the referencing assemblies and their PDB document lists. A project that never ran hot reload has no such file and reads nothing. A domain reload, a compile start and a service replacement stop it. A hot reload run first stops it before its next dll and waits for the dll being read (stepwarm_up_yield), whose result it reuses; an item stopped that way is reportedcancelled. Every run records its edited assemblies in that file (stepwarm_up_targets, most recent first, up to 32 names), and a failure to write it fails the run, like the other files underLibrary/UloopHotReload/. No macOS activity is held while it runs. Writes of rewritten reference copies go through one lock per Editor process (perf: The first hot reload after a compile no longer spends most of a second rewriting the edited assembly's reference copy #3254); a run never overlaps the warm-up, so the lock only orders a test run that starts while the warm-up still writes. A Cecil resolution or IO error while writing a copy marks that item failed and the later items still run; any other error stops the remaining items and is logged to the Console, and a run on the same assembly fails the same way. A transform worker request that failed on parse errors now keeps the worker's own stage times (workerSteps).The source snapshot is captured in the Editor's startup, before the domain answers its first command (fix: Hot reload no longer mistakes an edit made right after compile for compiled source #3255). The capture moved from the first update tick to the last statement of the
[InitializeOnLoadMethod]startup, so its time (431 ms on the development project, 1.2–1.9 s on the trial project) is spent before the IPC server answers instead of right after, and auloop compilereturns that much later; the same capture ran on the first update tick before, so the main thread is busy for the same time in total. It does not run in an Asset Import Worker. A capture that throws is logged by Unity as a startup exception and retried on the first update tick; a capture that finds no compilation assembly is no longer marked done, so a later reader captures again.The snapshot's
source-stamps.txtmoves to format version 2 (fix: Hot reload no longer silently skips a file saved while the compile was running #3256). Each line gains a fourth field,0or1; a version 1 file reads as empty, so the first run after updating compares sources by bytes until the next compile rewrites it. The compile start is kept in SessionState fromcompilationStarted; with no record (the first compile after an Editor start), only sources written after the dll are checked. A mark stays until the next compile, even if the edit is reverted. A file with no method bodies (an interface, an enum, constants) has no PDB document, so when it is saved after a compile started it is marked even if the compile read the saved content, and a run without--filesselects it. The method-diff baseline is unchanged; its mismatch warning now names the usual cause ("most often the file was saved after the compile had read it").The shared Roslyn worker starts after every server reset in a project whose hot-reload ledger names a compiled assembly (perf: The first hot reload shim compile after a server reset finds the shared Roslyn worker already running with its references loaded #3258). It is a
dotnetchild process that stays running until the next reset, domain reload or quit, as it does after a first compile today; before, it started only at the first compile. The warm-up compiles a one-class source in the background, holds the worker's compile gate for that compile (about 0.8 s on the development project), and reports a failed start or request only to the vibe log, leaving the Console error to the next real compile; an unexpected exception is logged to the Console. Projects without a ledger are unchanged.Not covered
7daed1af(5.7–9.8 s on1ed19803; warm runs 1.0–1.3 s).caller_noteswas down to 1.3–1.5 s, of which the edited assembly's own walk (about 1 s there) stayed, because its call sites are MethodDef rows that no MemberRef table names; perf: The first hot reload after a domain reload finds the edited assemblies' call sites, referenced-method sets and PDB documents already loaded #3253 moves that walk and the 288 ms ofsnapshot_group_stateafter a rebuild (perf: Hot reload builds the PDB document list with System.Reflection.Metadata from the PDB and the dll's debug directory, so a rebuilt assembly's first run does not walk its dll #3249) to the background warm-up, when the run comes after it. The three phases themselves (AnalysisMs1.3–1.5 s,ShimCompileMs2.2–2.4 s,PatchMs0.07–0.13 s, against 0.57 s, 0.13 s and 0.09 s warm) were the largest part of that run; perf: The first dynamic code run and hot reload after a domain reload reuse a cached compiler worker instead of rebuilding it #3252 takes the worker assembly build out ofShimCompileMs. Reading each file's MVID (ReadAssemblyMvid, the whole dll) is unchanged.<project root>/Library/ScriptAssemblies; the Debug switch and recompile need their own design there (feat: Hot reload patches a Multiplayer Play Mode Virtual Player by reading the main project's compiled assemblies #3238). A new source added in a Virtual Player was checked on a real player and applied as an introduced type. A source inside a local package lost its baseline in a Virtual Player (every method of the file was patched, see "Verification"); fix: Hot reload in a Virtual Player patches only the edited method of a package source #3244 fixes it; the re-check on headc328223fapplied only the edited method in the player, and the player reports the same normalized absolutePackageInfo.resolvedPathas the main Editor.INTERNAL_ERRORwithRetryable: false; the same request sent again a few seconds later succeeded. fix: A tool request that Unity never acknowledged is reported as UNITY_NOT_REACHABLE and retryable instead of an internal error #3251 fixes the classification. The stall itself coincided with a machine load average above 100; the reporting agent tried to reproduce it on head7daed1afat a load average below 32 (one player, five applies; then three players at once, three applies plus one with the original check sequence, each followed by 60 s of two-second probes) and saw no stall and no failed probe.Library/ScriptAssembliescan fail on the item's read handle: the call-site reader opens the file withFileShare.Read. The window is one dll, because a compile start keeps the warm-up from reading the next one (perf: The first hot reload after a domain reload finds the edited assemblies' call sites, referenced-method sets and PDB documents already loaded #3253). Not verified on Windows.compilationStartedfires in a Multiplayer Play Mode Virtual Player is not verified; if it does not, the check there starts at the dll's write time.Assets/after every domain reload and then callsAssetDatabase.Refresh()itself. That refresh imports any script saved since the last compile, so an edit saved a few seconds after a compile's domain reload was compiled by the Editor on its own (4 of 4 tries, with and without the warm-up; an edit saved 35 s after was not). Patches applied before that compile end at its domain reload, and the compiled code contains the edit. A hot reload that meets the compile is refused and sent again once the Editor settles. Nothing inuloopstarts that refresh. The Auto Refresh hold does not stop such an explicit call: on the development project anAssetDatabase.Refresh()sent while the hold was armed compiled a saved edit and reloaded, and the hold was released only after that reload. The skill's lifecycle reference now says so (docs: Note that a package's own AssetDatabase.Refresh compiles saved edits while hot reload holds Auto Refresh #3257).Included pull requests
Each pull request's description holds its own behaviour changes, mutation checks and "Not changed" list.
Changes
HotReloadCompiledCallSiteCache, byte budget and eviction entry), the timing detail entry (HotReloadRunTiming,HotReloadTimingDetailPayload,HotReloadOrchestratorLog), the group file-list helper split out of the group processor for file length, the compiled-assembly layout (CompiledAssemblyLayoutin Common.ScriptPath, replacingHotReloadVirtualPlayerProject.IsVirtualPlayerProjectRoot; the eightLibrary/ScriptAssembliesjoins of hot reload go through it, and HotReload.Patching now references Common.ScriptPath), theoutputPathcheck (HotReloadCompiledAssemblyPathCheck), the baseline loader's lookup root (HotReloadSourceBaselinerelativizes a package source's physical path against the layout's main project root), the per-domain assembly list (HotReloadCompilationAssembliesoverHotReloadCompilationAssemblyCache; the five call sites read it instead ofCompilationPipeline.GetAssemblies()), and the baseline verdict memo (HotReloadSiblingVerdictCache, read and written byHotReloadChangedSiblingSourceDetector), the persisted PDB document lists (HotReloadPdbDocumentIndextakes its persistence directory;HotReloadConstants.PdbDocumentsRelativeDirectory), the source stamp manifest (HotReloadSourceStampManifest, written byHotReloadSourceSnapshotterand read byHotReloadChangedSiblingSourceDetector, perf: Hot reload records each source's length and write time with its snapshot, so the first run after a domain reload does not read unchanged siblings #3245), the import-batch check that replaces theprojectChangedsubscription (HotReloadCompilationAssemblyListPostprocessor, perf: Hot reload keeps its compilation assembly list across the import that follows a compile's domain reload #3246), the referenced-method index (HotReloadReferencedMethodIndex, consulted byHotReloadCallSiteScanner;HotReloadCallSiteScanResult.SkippedScanAssemblyNames;HotReloadCompiledCallSiteCache.ReadFingerprintshared, perf: Hot reload's caller scan reads an assembly's MemberRef table before walking it, so assemblies that name none of the edited methods are not loaded #3247; the index takes its persistence directory and gainsPersistedLoadCount,HotReloadConstants.ReferencedMethodsRelativeDirectory, perf: Hot reload keeps each assembly's referenced-method set on disk, so the first caller scan after a domain reload reads only assemblies that name an edited method #3248), and the PDB read of the document index (HotReloadPdbDocumentIndex.ReadDocumentsreads the PDB withMetadataReaderProviderand the dll's debug directory withPEReader; the shared hot reload asmdef adds the three System.Reflection.Metadata plugins to its precompiled references, perf: Hot reload builds the PDB document list with System.Reflection.Metadata from the PDB and the dll's debug directory, so a rebuilt assembly's first run does not walk its dll #3249), and the cold-run breakdown entries (TransformWorkerRequestTimingattached to everyTransformWorkerHostResultat the single exit ofTransformWorkerHost.RunAsync;WorkerRequestTimingsand thetimingsoutput field in the transform worker; timing entries inHotReloadGroupProcessor,HotReloadShimFirstCompile,HotReloadShimReferenceBuilder,ReferencePublicizer,HotReloadShimCompilerandSharedRoslynCompilerWorkerHostProcess, feat: Hot reload writes vibe entries that split a cold run's analysis and shim-compile phases into worker start, request steps, shim references, publicized copies, and compiler time #3250), the shared Roslyn compiler worker's assembly cache (SharedRoslynCompilerWorkerAssemblyCache, used bySharedRoslynCompilerWorkerSession.EnsureWorkerAssemblyBuiltAsync, and the new fields ofdynamic_code_shared_worker_started, perf: The first dynamic code run and hot reload after a domain reload reuse a cached compiler worker instead of rebuilding it #3252), and the warm-up after a domain reload (theHotReloadWarmUpfacade and its call-site, referenced-method and PDB document items, the target ledgerHotReloadWarmUpTargetLedger, the context source, the editor hooks that stop it,Preloadon the referenced-method and PDB document indexes, and the orchestrator'swarm_up_yieldandwarm_up_targetssteps, perf: The first hot reload after a domain reload finds the edited assemblies' call sites, referenced-method sets and PDB documents already loaded #3253), and the warm-up's first item, which writes the edited assemblies' publicized copies (HotReloadPublicizedTargetWarmUpItem), with the lock that serializesReferencePublicizer's copy writes (perf: The first hot reload after a compile no longer spends most of a second rewriting the edited assembly's reference copy #3254), the snapshot capture at the end of the Editor startup (FirstPartyToolsEditorStartup.PrepareBeforeServingCommands, fix: Hot reload no longer mistakes an edit made right after compile for compiled source #3255), and the compile start record, the PDB check at capture and the manifest's mark (HotReloadCompileStartRecord,HotReloadSnapshotSourceCheck,HotReloadSourceStampManifestformat 2, fix: Hot reload no longer silently skips a file saved while the compile was running #3256), and the shared-worker warm-up (SharedRoslynCompilerWorkerWarmUp, started byDynamicCodeServicesRegistry.ResetServerScopedServices; the host'sWarmUpAsyncandSharedWorkerWarmUpOutcome; the ToolContracts portSharedCompilerWarmUpCoordination, set by the hot-reload startup toHotReloadSharedCompilerWarmUpReferenceSource;HotReloadShimReferenceBuilder.ClassifyCompileReferences, shared with the run;ExternalCompilerPathResolversplit into resolving and reporting, withResolveWithoutReporting, perf: The first hot reload shim compile after a server reset finds the shared Roslyn worker already running with its references loaded #3258).finishNonRetryableConnectionAttempt(fix: A tool request that Unity never acknowledged is reported as UNITY_NOT_REACHABLE and retryable instead of an internal error #3251).docs/vibe-logs.md(new entries and fields), ADR 0012 (the removed busy-stall focus),docs/soak-testing.mdand the pause-point troubleshooting reference (the startup preference), the Multiplayer Play Mode reference's hot reload section and the hot reload scope table (feat: Hot reload patches a Multiplayer Play Mode Virtual Player by reading the main project's compiled assemblies #3238), and the scope reference's baseline paragraph (when a source is compared with its baseline again, perf: Hot reload compares a sibling with its snapshot again only when the file's length or write time changed #3241), anddocs/hot-reload.md's list of what hot reload keeps underLibrary/UloopHotReload/(perf: Hot reload keeps the PDB document list of an assembly across domain reloads #3242, perf: Hot reload records each source's length and write time with its snapshot, so the first run after a domain reload does not read unchanged siblings #3245, perf: Hot reload keeps each assembly's referenced-method set on disk, so the first caller scan after a domain reload reads only assemblies that name an edited method #3248),docs/vibe-logs.md's list of the breakdown entries (feat: Hot reload writes vibe entries that split a cold run's analysis and shim-compile phases into worker start, request steps, shim references, publicized copies, and compiler time #3250), and a section of the Multiplayer Play Mode reference on a player that quits a few seconds after activation withFatal Error! Compilation Pipeline: Could not read file Packages/<package>/<path>.asmdef(docs: Explain why a Multiplayer Play Mode player can quit with a Fatal Error before uloop loads #3243): the player restores packages from the main project'sLibrary/PackageManager/ProjectCache, which is absent when the main Editor's own package resolution failed, so uloop never loads in it; the fix is on the main project's dependencies.docs/hot-reload.mdgains the warm-up paragraph anddocs/vibe-logs.mdthe warm-up entries, the order of the warm-up items and the worker cache fields (perf: The first dynamic code run and hot reload after a domain reload reuse a cached compiler worker instead of rebuilding it #3252, perf: The first hot reload after a domain reload finds the edited assemblies' call sites, referenced-method sets and PDB documents already loaded #3253, perf: The first hot reload after a compile no longer spends most of a second rewriting the edited assembly's reference copy #3254).docs/vibe-logs.mdgains the snapshot capture and check entries (fix: Hot reload no longer mistakes an edit made right after compile for compiled source #3255, fix: Hot reload no longer silently skips a file saved while the compile was running #3256);docs/hot-reload.mdand the hot reload scope reference describe the mark of a file saved during a compile (fix: Hot reload no longer silently skips a file saved while the compile was running #3256). The hot reload lifecycle reference says that the Auto Refresh hold does not stop a package's ownAssetDatabase.Refresh()(docs: Note that a package's own AssetDatabase.Refresh compiles saved edits while hot reload holds Auto Refresh #3257).docs/hot-reload.mdgains the shared-worker warm-up anddocs/vibe-logs.mdits two entries (perf: The first hot reload shim compile after a server reset finds the shared Roslyn worker already running with its references loaded #3258).Verification
f3daae7e(fix: uloop compile returns as soon as the Editor answers instead of warming execute-dynamic-code first #3227 to feat: Hot reload writes a timing detail vibe entry that breaks down the time outside the response's phases #3230): the timing detail entry matched the response'sTimingto within 5–21 ms per run and showed the caller lookup at 2.0–2.5 s of a 3.1–3.7 sOtherMs. A diagnostic in the Editor confirmed the cause: 68 assemblies to scan against a 64-entry cache, 9 of them (29 MB) read again on every run, the fingerprint pass itself 1–3 ms.d872b6d6(adds fix: A request that Unity keeps answering busy no longer brings the Editor to the front #3231 and fix: Hot reload's call-site cache is bounded by bytes instead of a count, so one run's assemblies stay cached #3235): after a warm run, no assembly of the scan set was missing from the cache, a run read no dll again (load count unchanged), the end of the run evicted nothing and wrote no eviction entry; the caller lookup took 53, 53 and 59 ms over three applies,OtherMs935, 805 and 725 ms, and one apply 1676, 1364 and 1367 ms.d872b6d6, behaviour checks:uloop compileprinted no warm-up spinner or warning and anexecute-dynamic-codesent right after it succeeded; with another application in front, neither acompilenor a plain tool request that the busy Editor kept refusing (BUSY after the 10-second window) brought the Editor forward, and the CLI vibe log had nobusy_stallorcompile_start_stallfocus entry; a timed-outawait-pause-point --timeout-seconds 5reportedTimeoutSeconds5,MarkerTimeoutSeconds30 and the asset-pathId; the busycli_tool_request_failedentry named the running tool, and thecli_hot_reload_busy_wait_completeentry carriedresend_correlation_ids(an empty array, as no request had to be sent again during that wait).084e1d13. Its first attempt on head00b4a896did not reach hot reload: the player's Editor exited during its initial asset database refresh withCompilation Pipeline: Could not read file Packages/<package>/Editor/<name>.asmdef. The cause was on the project's side: afile:dependency pointed into a git submodule that was not initialized, so the main Editor's package resolution had failed and left noLibrary/PackageManager/ProjectCachefor the player to restore from (docs: Explain why a Multiplayer Play Mode player can quit with a Fatal Error before uloop loads #3243 documents this). After initializing the submodule the player started anduloopreached Ready in it after about 1.5 minutes. Results: (1) an edit sent to the player is Applied with the player'sActivePatchTotalat 1 and the main Editor's at 0; (2) the patched method logs in the player only; (3) auloop compileon the main Editor reloads the player and itsActivePatchTotalreturns to 0; (4) with the main project's DLL renamed away, the player fails with the "Compile the main Editor's project first" wording and the Virtual PlayerRecommendedNextAction; (5) the main project'sLibrary/UloopHotReloadis not written by the player's apply, and the player's ownSourceSnapshotholds the snapshots; (6) a new source with one new type is applied as an introduced type without any sentence pointing at the player's ownLibrary(the first send of this check timed out after 10 seconds with no record on the Editor side and succeeded when resent; seen once, not explained); (7) a source inside afile:local package came back PartiallyApplied with every method of the file patched and the warning "has no compiled method body, so there is no baseline for edited-method detection", although the player's snapshot of the assembly exists. The cause is inHotReloadSourceBaseline: the PDB document is looked up by the physical path made relative to the player's root, and a package folder under the main project is outside that root, so the lookup uses an absolute path that the relative document the main Editor's compile recorded never matches. fix: Hot reload in a Virtual Player patches only the edited method of a package source #3244 fixes it by making the physical path relative to the main project's root, which compiled the assembly. Re-checked on headc328223f: the same edit sent to the player is Applied with 1 patched and 19 unchanged methods, no skipped accessor and no warning, exactly as when sent to the main Editor;PackageInfo.resolvedPathreported inside the player is identical to the main Editor's, a normalized absolute path under the main project rather than under the player's root, so the case the change assumes away does not occur there.1ed19803: an edit sent to one player patched that player only, the same edit sent to both at once patched both independently, auloop compileon the main Editor reloaded both players and cleared their patches, and one edit sent to the main Editor and both players at once was Applied on all three (6.6–11.8 s each, against 1.6–1.7 s when sent to one player at a time; the extra time is outside the phases). Right after that last apply one player did not answer tool requests for about 30 s (see "Not covered").resolve_inputsandsibling_detect(perf: Hot reload asks the compilation pipeline for the assembly list once per domain #3240, perf: Hot reload compares a sibling with its snapshot again only when the file's length or write time changed #3241), re-measured by the reporting agent on head084e1d13with three consecutive runs of the same one-method edit at a one-minute load average of 22 to 27: run 1, the first apply after a compile that rebuilt the edited assembly, tookresolve_inputs594 ms,sibling_detect749 ms,snapshot_group_state1106 ms,caller_notes3810 ms, 9266 ms in total; runs 2 and 3 took 5 ms, 50 to 52 ms, 2 to 4 ms, 66 to 190 ms, and 1092 to 1286 ms in total. The same runs at a load average of 71 to 94 showed 86 ms and 198 ms on run 2, so the two steps still scale with machine load on their first warm run. The persisted PDB document list (perf: Hot reload keeps the PDB document list of an assembly across domain reloads #3242) was measured in this repository's Editor only (first run after a reload that keptAssembly-CSharp.dll: 30 and 28 ms without the file, 3 and 3 ms with it); on headc328223fthe reporting agent measured it: after a compile of an unrelated Editor-only assembly that left the edited assembly's DLL untouched, the first run'ssnapshot_group_statefell from 1165 ms (on084e1d13, without the file) to 65 ms with the persisted list, which that run read without rewriting it; the other first-run steps (resolve_inputs704 ms,sibling_detect655 ms,caller_notes3654 ms) are rebuilt per domain and are unchanged by perf: Hot reload keeps the PDB document list of an assembly across domain reloads #3242. That project runs with Domain Reload off, so there the saving applies to compiles of other assemblies and Editor restarts.62b1236cat a one-minute load average of 4 to 12, two ways. With the edited assembly's dll unchanged by the reload (a checkout of the edited file followed byuloop compile):resolve_inputs53 ms (611 ms on084e1d13),sibling_detect752 ms because that snapshot predated perf: Hot reload records each source's length and write time with its snapshot, so the first run after a domain reload does not read unchanged siblings #3245 and had no stamp file,snapshot_group_state49 ms,caller_notes5897 ms, 9657 ms in total. With the dll rebuilt by the compile: 25 ms, 72 ms (the snapshot and its stamp file had been captured by the startup capture that follows that compile's reload), 1298 ms (the PDB walk after a rebuild, unchanged), 4438 ms, 9677 ms in total. The warm runs that followed took 1078 and 1678 ms. A third attempt, meant to reload without rebuilding the dll, ended up rebuilding it (the edit left on disk was compiled when the Auto Refresh hold was released after the reload) and measuredsibling_detect95 ms on a stamped snapshot. Every run was Applied with one patched method and no warning, and the Editor never came to the front.1ed19803at a one-minute load average of 5 to 17, three ways. After a compile that rebuilt the edited assembly's dll and none of the 559 other dlls, the first run read the MemberRef tables of the 72 referencing assemblies and wrote their 72 set files (3.7 MB):caller_notes1719 ms (4438–5897 ms on62b1236c),snapshot_group_state1774 ms, 9757 ms in total. The next compile-triggered first run read the sets from the files and wrote none:caller_notes983 ms,snapshot_group_state1195 ms, 6564 ms in total. A reload without a rebuild (EditorUtility.RequestScriptReload()with the source checked out to its compiled content; no second reload, dll unchanged, stamped snapshot):resolve_inputs37 ms,sibling_detect172 ms,snapshot_group_state46 ms,caller_notes1353 ms, 5666 ms in total (AnalysisMs1946,ShimCompileMs1714,PatchMs152), against 1085 ms warm. Every run was Applied with one patched method and no warning, and the Editor never came to the front.snapshot_group_state413 and 465 ms on the base, 60, 61 and 62 ms on the head, 1 ms on the next run. Every run applied with no warning. The 22 existing document-index tests and the snapshot tests passed unchanged, and the first Editor-domain load of the bundled System.Reflection.Metadata plugin raised no error. Re-measured by the reporting agent on head7daed1afat a one-minute load average of 12 to 24, two ways. After a compile that rebuilt the edited assembly's dll and none of the 559 others:resolve_inputs120 ms,sibling_detect72 ms,snapshot_group_state288 ms (1774 ms on1ed19803),caller_notes1521 ms, 6097 ms in total (9757 ms on1ed19803), withAnalysisMs1524,ShimCompileMs2360 andPatchMs71; that run rewrote the persisted document list. A reload without a rebuild (EditorUtility.RequestScriptReload()after a checkout and a compile; dll unchanged, stamped snapshot): 22 ms, 91 ms, 111 ms (46 ms on1ed19803; one measurement each, and this path reads the persisted list, which perf: Hot reload builds the PDB document list with System.Reflection.Metadata from the PDB and the dll's debug directory, so a rebuilt assembly's first run does not walk its dll #3249 does not touch), 1310 ms, 5392 ms in total. The warm runs that followed took 979–986 ms. Every run was Applied with one patched method and no warning, and the Editor never came to the front.AnalysisMs827,ShimCompileMs1972,unaccountedMs18) against a warm run of 738 ms (280, 305, 6). The prepare request to the transform worker reportedworkerStartedtrue,process_start54 ms,response_wait341 ms and 285 ms of worker stages (72 ms warm); the shared Roslyn worker start reportedassemblyEnsureMs487 andprocessStartMs35; the publicized copy of the rebuilt assembly took 73 ms to rewrite and 615 ms to write; the shim compiler reportedcompileMs1086 cold and 15 warm on the shared worker backend. All seven entries appeared on the cold run, the two conditional ones were absent on the warm run, and both runs were Applied with no warning.Unwrap) each fail the tests meant to catch them. A live reproduction was attempted in this repository's Editor by blocking the main thread for 25 s and sendingclear-console: the server answeredserver_busy, so the pre-accept timeout itself could not be provoked that way; it is reproduced only by a silent server in the tests.assemblyEnsureMs424); anexecute-dynamic-coderight after a compile and a hot reload right after another compile copied it from the cache (assemblyEnsureMs2 each), and the hot reload applied on the shared worker backend. Unit tests pin the cache key, the copy, the publish (temp file and move, an existing file kept, a blocked directory and a failed move reported asFailedwith no temp file left), and nine mutations each fail the tests meant to catch them.hot_reload_warm_up_completereported call_sites 635 ms, referenced_method_sets 897 ms and pdb_documents 309 ms (1857 ms in total, not cancelled), and the first run took 2484 ms withcaller_notes8 ms,snapshot_group_state2 ms andwarm_up_yield0 ms. Without the ledger the warm-up was skipped (no_targets) and the first run took 3024 ms withcaller_notes462 ms andsnapshot_group_state95 ms. A hot reload sent right after a compile waited 2 ms (warm_up_yield): call_sites had finished (880 ms), referenced_method_sets stopped between its dlls and was reportedcancelledafter 1377 ms, and pdb_documents never started. Unit and end-to-end tests pin the hand-off to a run (before the start, inside a unit, between units, after completion), the stops on a reload, a compile start and a service replacement, the per-item failure boundary and the ledger. Each mutation listed in the pull request fails the tests meant to catch it, except one equivalent mutation and three that EditMode cannot detect, which are guarded by review.publicized_targetsfinished in 1693 ms (rewrite 57 ms, write 1460 ms) with all four items done, and the first run after the compile did not write the copy (targetCopyMs1 ms). With the ledger emptied, the warm-up was skipped and the run wrote the copy itself (rewrite 49 ms, write 542 ms;targetCopyMs747 ms). Unit and end-to-end tests pin that the item writes the copy the run then reuses, leaves an existing copy untouched, skips a name that is no compilation assembly, and stops before the first unit and between units when cancelled; each listed mutation fails them. The lock has no deterministic test.4fa78e1bafter an Editor restart, at a one-minute load average of 10 to 34: the first run after a compile took 2793 ms with the warm-up's ledger present and 3760 ms with it removed (caller_notes143 against 867 ms,snapshot_group_state2 against 101 ms); warm runs took 851–952 ms. The worker assembly was built once (assemblyEnsureMs480) and copied from the cache after every later reload, in the main Editor and in two Virtual Players (0–3 ms). With the main Editor and two players, the highest one-minute load average in the minute after a compile was 20.7 with the warm-up and 23.5 without.b200c1ab: with the ledger present, the warm-up wrote the copy (rewrite 416 ms, write 1234 ms) and the first run after the compile took 2793 ms withtargetCopyMs5 ms andShimCompileMs721; with the ledger removed, the run wrote the copy itself (rewrite 137 ms, write 863 ms) and took 4968 ms withtargetCopyMs1012 ms andShimCompileMs1612. Runs sent while the warm-up was working stopped it within 4–113 ms (warm_up_yield); the two that applied found the copy written (targetCopyMs4 and 7 ms). With the main Editor and two Virtual Players, each Editor's first run after a compile took 2.1–2.7 s and wrote no copy.ULOOP_DEBUG: after a compile,hot_reload_source_snapshot_capturedreportedtriggerdomain_loadandcaptureMs431, logged 4.7 s beforedomain_reload_complete, with no capture on the first update tick. A file saved duringuloop compilewas checked at capture (suspect1,editedAfterCompile1,checkMs28); the next run without--filesselected it, warned that the snapshot does not match the compiled file and applied. An ordinary edit followed by a compile checked nothing and marked no line.AutoRefreshHeldtrue), a second edit was saved andAssetDatabase.Refresh()was called throughexecute-dynamic-code; the Editor reloaded 3 s later with no compile request pending,hot_reload_auto_refresh_hold_releasedappeared only after that reload, and--statusthen reported no active patch.uloop compile, a 15 s wait and a hot reload of one edited method body, with the ledger present and moved aside on the same head: with the ledger, the warm-up answered every time (warmMs764–819,mainThreadMs69–94, 228 references), the run did not start the worker, and the shimcompileMswas 72–80 ms; without it, the warm-up was skipped (no_targets), the run started the worker, and the shimcompileMswas 801–807 ms. Unit tests pin that the reset starts the warm-up without running it inline, that the package path is read on the main thread and the compiler paths off it, the skip reasons, the outcomes, that a failed start logs nothing to the Console and leaves the error to the next real compile, and the reference list; each of the 16 listed mutations fails the tests meant to catch it.88d0ff2c, three rounds each with the ledger present and removed, alternating, editing 42 s afteruloop compilereturned: with the ledger, the warm-up answered every time 2.6–3.3 s after the compile returned (warmMs433–473,mainThreadMs0, 328 references), the worker started inside the warm-up and not in the hot reload, and the shimcompileMswas 118–129 ms; without it, the warm-up was skipped (no_targets), the worker started inside the hot reload, and the shimcompileMswas 439–493 ms. No round saw an unplanned compile or domain reload, and the Console had no error or exception.57bbb472: every domain load captured the snapshot before the server answered (captureMs1182–1903, logged 1.2–2.5 s beforedomain_reload_complete). An edit saved 0.12 s afteruloop compilereturned was applied with 1 patched and 14 unchanged methods and no snapshot warning. An edit saved during a compile, 2.4 s before its domain reload, was marked (suspect1,editedAfterCompile1, the line's fourth field 1); the next run treated all 15 methods of that file as changed, was refused when an Editor compile started by the package refresh described in "Not covered" began, and, sent again after that compile, had nothing to apply because the edit was compiled by then.