Skip to content

perf: Skip the hot reload source snapshot capture when the hot-reload and pause-point tools are both disabled - #3276

Merged
hatayama merged 6 commits into
perf/hot-reload-snapshot-capturefrom
perf/hot-reload-snapshot-skip-when-tools-disabled
Oct 11, 2026
Merged

hatayama merged 6 commits into
perf/hot-reload-snapshot-capturefrom
perf/hot-reload-snapshot-skip-when-tools-disabled

Conversation

@hatayama

@hatayama hatayama commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • When both the hot-reload and pause-point tools are disabled in the tool settings, a domain reload no longer captures hot reload's source snapshot, so a compile no longer pays for it on the main thread. Hot reload's background warm-up does not start while the hot-reload tool is disabled.
  • A pause point now captures the snapshot through the same gate as a hot reload run before reading it. That way a tool enabled after the capture was skipped still has a snapshot to read.

User Impact

  • Before: every domain reload copied the sources of the changed assemblies into the snapshot before the Editor answered its first command. This happened even for users who had turned off every tool that reads the snapshot.
  • After: with both tools disabled, the capture at domain load and its retry at the first update tick are skipped and logged. The first hot reload run or pause point after either tool is enabled takes the capture. Any source written since the compile started is then checked against the PDB, as it is today. A late capture never records an edited file as compiled source.
  • Nothing changes while either tool is enabled.

Changes

  • Commit 1: the pause-point side of hot reload asks the capture gate before it reads a verified snapshot source. An empty path still returns no source without asking. A capture that throws reaches the pause-point command, as it already does for a hot reload run. The gate stays unmarked, so the next read tries again. New trigger name pause_point.
  • Commit 2: the two startup captures ask the tool settings. If neither reader tool is enabled, they log hot_reload_source_snapshot_skipped (trigger, reason = tools_disabled) and leave the gate unmarked. The warm-up is not started when the hot-reload tool is disabled; it logs hot_reload_warm_up_skipped with reason = tool_disabled. The settings check is handed in from the composition root as a function, because the tool settings are not visible to the tool assemblies.
  • Commit 3: docs/vibe-logs.md and docs/hot-reload.md.
  • Commit 4: a composition root test that the installed pause-point side captures through the installed gate with the pause_point trigger. The port tests hand the capture in directly, so they could not catch a wrong wiring.

Why the compile-start record and the sweep still run when the tools are disabled

The capture decides which sources it must check against the PDB by comparing their write time with the moment the last compile started. If the compile start were not recorded while the tools are disabled, a capture taken after a tool is enabled would fall back to the dll's write time. A source edited during the compile would then be recorded as compiled source. The sweep only removes artifacts of earlier domains, and skipping it would leave them behind for the next run. So only the two startup captures and the warm-up look at the tool settings.

Input space covered

Entry point hot-reload pause-point Before After Test
Capture at domain load off off captures skips, logs, gate unmarked ..._WhenNeitherReaderToolIsEnabled_SkipsAndLeavesTheCaptureForAReader, ..._AsksBothReaderToolsBySettingsName
Capture at domain load on / off, off / on, on / on captures captures ..._WhenOnlyOneReaderToolIsEnabled_Captures (2 cases), existing tests
Capture at the first update tick off off captures skips with that trigger ..._AtTheFirstUpdateTick_WhenNeitherIsEnabled_SkipsWithThatTrigger
Either startup capture, gate already marked nothing nothing existing HotReloadSourceSnapshotCaptureTests
Hot reload run (apply) through the gate unchanged existing
Pause point reads the snapshot read without the gate captures through the gate first GetVerifiedSnapshotSource_EnsuresTheSnapshotIsCaptured
Pause point, capture throws (not reachable) exception to the command, gate unmarked GetVerifiedSnapshotSource_WhenTheCaptureThrows_PassesTheExceptionOn
Pause point, empty path null null, gate not asked GetVerifiedSnapshotSource_WithAnEmptyPath_DoesNotEnsureTheCapture
Warm-up start off starts does not start, logs StartWarmUpUnlessHotReloadIsDisabled_WhenHotReloadIsDisabled_...
Warm-up start on starts starts StartWarmUpUnlessHotReloadIsDisabled_WhenHotReloadIsEnabled_StartsOnce

Import worker processes return before the check, as before. A missing or blank settings file, or one with disabledTools: null, reads as every tool enabled, so it adds no branch. A settings file that is not valid JSON makes IsToolEnabled throw, as it already does for every tool command; the two startup captures and the warm-up start then throw too and Unity logs each, but nothing else in the startup is skipped, because the domain-load capture runs last and each first-update-tick callback unsubscribes itself before doing its work.

Verification

Local, Unity 2022.3 Editor via the dev uloop binary.

  • uloop compile: ErrorCount 0. uloop compile-check --all reports the same 7 warnings, all from existing test fixtures, with this branch's files and with the base branch's versions of them.
  • scripts/check-file-length.sh and scripts/check-code-complexity.sh: no findings.
  • Tests, one class at a time with a class filter, all passed:
    • HotReloadEditorStartupTests: 9 (7 added).
    • HotReloadSourceSnapshotCaptureTests: 6.
    • HotReloadDomainTests: 49 (3 added).
    • HotReloadCompositionRootTests: 7 (1 added).
  • Mutation check. Each mutation was applied on top of the committed tests, then reverted:
    • Startup skip condition replaced with if (false): only the two "neither tool enabled" skip tests failed (domain load and first update tick).
    • The pause point's gate call removed: GetVerifiedSnapshotSource_EnsuresTheSnapshotIsCaptured and ..._WhenTheCaptureThrows_PassesTheExceptionOn failed.
    • The composition root's capture for the pause-point side replaced with a no-op, or given the apply trigger: Install_PausePointSide_CapturesThroughTheInstalledGateWithThePausePointTrigger failed each time.
  • Live check in the development project. --force-recompile does not return a definitive compile result, so each reload was followed up with a plain uloop compile.
    • With hot-reload and pause-point both in disabledTools, the reload logged hot_reload_source_snapshot_skipped twice (domain_load, first_update_tick, both tools_disabled) and hot_reload_warm_up_skipped once (tool_disabled). It logged no hot_reload_source_snapshot_captured.
    • With only hot-reload disabled, the reload logged hot_reload_source_snapshot_captured once (domain_load) and hot_reload_warm_up_skipped once (tool_disabled).
    • The settings file did not exist before the check. It was deleted afterwards, and the next reload logged hot_reload_source_snapshot_captured (domain_load) and a completed warm-up again.
  • The path where a tool is enabled partway through a domain (trigger pause_point) cannot be toggled from the CLI. The port tests cover it.

…ds it

A pause point read the snapshot without asking the gate, so once the
startup captures can be skipped it could read a snapshot that was never
taken in this domain. The port now asks the gate first; a capture that
throws reaches the pause-point command, as it does for a hot reload run,
and the gate stays unmarked so the next read tries again.
…ds them

The capture after a domain reload blocks the main thread on every
compile even when the user disabled both tools that read the snapshot.
The two startup captures now ask the tool settings and log a skip when
hot-reload and pause-point are both disabled, leaving the gate unmarked
so the first run or pause point after a tool is enabled captures. The
warm-up is skipped when hot-reload is disabled. The compile-start record
and the artifact sweep still run, because a later capture needs the
compile start to tell compiled sources from edits made during the compile.
@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: hatayama/unity-cli-loop/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7d0ef30d-5335-4f35-a811-69e243aae48e

📥 Commits

Reviewing files that changed from the base of the PR and between 3fec3ae and 087b28f.


📒 Files selected for processing (11)
  • Assets/Tests/Editor/HotReload/HotReloadCompositionRootTests.cs
  • Assets/Tests/Editor/HotReload/HotReloadDomainTests.cs
  • Assets/Tests/Editor/HotReload/HotReloadEditorStartupTests.cs
  • Packages/src/Editor/CompositionRoot/UnityCliLoopEditorBootstrapper.cs
  • Packages/src/Editor/FirstPartyTools/FirstPartyToolsEditorStartup.cs
  • Packages/src/Editor/FirstPartyTools/HotReload/HotReloadCompositionRoot.cs
  • Packages/src/Editor/FirstPartyTools/HotReload/HotReloadEditorStartup.cs
  • Packages/src/Editor/FirstPartyTools/HotReload/Patching/HotReloadPausePointPort.cs
  • Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadConstants.cs
  • docs/hot-reload.md
  • docs/vibe-logs.md

🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/hot-reload.md

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 0 remain after this review.



📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough

Walkthrough

Startup snapshot capture and warm-up now depend on tool settings. Pause-point source reads capture a snapshot before loading verified source. Tests and documentation cover the updated behavior.

Changes

Source snapshot capture

Layer / File(s) Summary
Gate startup capture and warm-up
Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadConstants.cs, Packages/src/Editor/FirstPartyTools/FirstPartyToolsEditorStartup.cs, Packages/src/Editor/CompositionRoot/UnityCliLoopEditorBootstrapper.cs, Packages/src/Editor/FirstPartyTools/HotReload/HotReloadEditorStartup.cs, Assets/Tests/Editor/HotReload/HotReloadEditorStartupTests.cs, docs/hot-reload.md, docs/vibe-logs.md
Startup receives a tool-enabled callback. Snapshot capture is skipped and logged when both reader tools are disabled, while warm-up is skipped and logged when Hot Reload is disabled. Tests and documentation cover these checks and skip reasons.
Capture before pause-point source reads
Packages/src/Editor/FirstPartyTools/HotReload/Patching/HotReloadPausePointPort.cs, Packages/src/Editor/FirstPartyTools/HotReload/HotReloadCompositionRoot.cs, Assets/Tests/Editor/HotReload/HotReloadDomainTests.cs
The pause-point port calls an injected capture callback before loading verified source for valid paths. The composition root supplies the callback, and tests cover callback behavior.

Priority: ➖ Normal

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

Change: Other

Sequence Diagram(s)

sequenceDiagram
  participant FirstPartyToolsEditorStartup
  participant HotReloadEditorStartup
  participant isToolEnabled
  participant SourceSnapshotCapture
  participant VibeLog
  FirstPartyToolsEditorStartup->>HotReloadEditorStartup: Initialize(isToolEnabled)
  HotReloadEditorStartup->>isToolEnabled: Check Hot Reload and Pause Point
  alt Either reader tool is enabled
    HotReloadEditorStartup->>SourceSnapshotCapture: EnsureCaptured(trigger)
  else Both reader tools are disabled
    HotReloadEditorStartup->>VibeLog: Log skipped capture
  end
Loading





























Merge Risk | ⚪ Minimal · up to 087b2

Merge Risk: ⚪ Minimal · up to 087b2

Snapshot capture is deferred when neither reader tool is enabled and remains available when a reader runs. No actionable issue in the supplied evidence prevents merging after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 087b2

The change is limited to when source snapshots are prepared. Existing source-verification and command security checks remain in place, and no introduced security vulnerability was established. Some uncertainty remains around concurrent access and recovery from incomplete captures.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — The new pause-point prerequisite can initiate capture across eligible compilation assemblies, not just the requested source file. Capture derives its snapshot-owning root from the current Editor project and enumerates Unity's compilation assemblies.

Trust Boundaries and Controls

  • observed — Startup settings checks govern preparatory work, not command authorization. The unchanged execution-session entry separately evaluates enabled-tool settings and security policy before granting execution. The new capture callback only precedes verified-source loading and does not itself apply patches.

Resilience and Maintainability Implications

  • observed — A pre-existing recovery limitation remains: caught per-assembly failures can coexist with a completed gate, preventing another capture attempt in that domain. This affects snapshot availability, but missing or mismatched snapshots still yield no verified source. The gate is documented main-thread-only and has no synchronization.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 63.64% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 33 functions across 9 files. (2 skipped: … 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 clearly and concisely describes the primary performance change: skipping hot reload source snapshot capture when both reader tools are disabled.
Description check Passed The description directly explains the startup capture changes, pause-point behavior, warm-up gating, logging, tests, and verification results.
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.

Full details: Docstring Coverage

Explanation

Docstring coverage is 63.64% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 33 functions across 9 files. (2 skipped: 2 unsupported.)


  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR









🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

…' into perf/hot-reload-snapshot-skip-when-tools-disabled

# Conflicts:
#	docs/vibe-logs.md
…h its own trigger

The port tests hand the capture in directly, so replacing the composition
root's capture with a no-op, or passing another trigger, passed every
test.
…' into perf/hot-reload-snapshot-skip-when-tools-disabled

# Conflicts:
#	docs/hot-reload.md
@hatayama
hatayama merged commit 974a2b5 into perf/hot-reload-snapshot-capture Oct 11, 2026
5 checks passed
@hatayama
hatayama deleted the perf/hot-reload-snapshot-skip-when-tools-disabled branch October 11, 2026 05:44
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