Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
37673bd
fix: Hot reload no longer fails to resolve plugin DLLs that only a re…
hatayama Oct 6, 2026
7c1cf59
feat: Report the hot-reload outcome and per-kind totals as fields and…
hatayama Oct 6, 2026
eff5a47
fix: Hot reload applies edits to methods using types from another fil…
hatayama Oct 6, 2026
61c1764
chore: Align two hot-reload test pins with the unresolved signature t…
hatayama Oct 6, 2026
5f86f0e
chore: Hot reload and run-tests skills now say whether live patches s…
hatayama Oct 6, 2026
c847dc7
fix: The hot-reload warning about edits outside method bodies now say…
hatayama Oct 6, 2026
f1101b3
feat: Hot reload responses now show how long each phase of a run took…
hatayama Oct 6, 2026
36876a6
fix: Hot reload no longer slows down with the number of unchanged met…
hatayama Oct 6, 2026
0c8e841
feat: Hot reload now applies method edits on partial types (#3184)
hatayama Oct 6, 2026
f37cf75
chore: Hot reload skill now says to read Outcome to tell whether an e…
hatayama Oct 6, 2026
654fee5
fix: Hot reload reads each edited assembly's debug symbols once per c…
hatayama Oct 6, 2026
370b6ee
feat: Hot reload responses now report time outside the phases, name s…
hatayama Oct 6, 2026
e084a23
fix: uloop skills install without a target flag now refreshes existin…
hatayama Oct 6, 2026
8bd459d
fix: Explain that hot reload cannot patch a Multiplayer Play Mode Vir…
hatayama Oct 6, 2026
f2b48b5
fix: Hot reload now applies static methods that use nameof on an inst…
hatayama Oct 6, 2026
7c0e77d
fix: Hot reload now says to run uloop compile when an added member us…
hatayama Oct 6, 2026
927db18
fix: Send the compile again when Unity lost the request or rejected i…
hatayama Oct 6, 2026
1917310
fix: Hot reload no longer patches unchanged methods when it runs righ…
hatayama Oct 6, 2026
3ee9b85
fix: A command that reached Unity after a busy retry no longer report…
hatayama Oct 6, 2026
aee6702
fix: Hot reload now patches partial-type bodies that use an internal …
hatayama Oct 6, 2026
ef68b94
fix: Hot reload no longer patches method bodies that would throw on a…
hatayama Oct 6, 2026
7950674
perf: Hot reload no longer re-reads the compiled assembly for every m…
hatayama Oct 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/uloop-compile/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Returns JSON:
- `WarningCount`: number or null
- `Warning` (string, optional): set when the compile was requested during Play Mode (the Play session is discarded), while pause points were armed in either Play Mode or Edit Mode (the domain reload drops every pause point patch; those enabled with `--persist` are counted separately and re-armed from their saved enable request after the reload), or while hot-reload changes were active (a successful compile drops every patch; the edited sources are compiled in).
- `Message`: string
- `ErrorCode`: string or null. `COMPILE_ALREADY_IN_PROGRESS` when Unity is already compiling, `COMPILE_EDITOR_UPDATING` when the editor is updating, `COMPILE_RESULT_UNKNOWN` after a forced recompile that did not return a definitive result.
- `ErrorCode`: string or null. `COMPILE_ALREADY_IN_PROGRESS` / `COMPILE_EDITOR_UPDATING` when Unity was still compiling or updating after the CLI waited for it and sent the compile again (it does so twice before giving up; run `uloop compile` again), `COMPILE_RESULT_UNKNOWN` after a forced recompile that did not return a definitive result.
- `NextActions`: string array or null. Corrective steps derived from the errors, e.g. the assembly that declares an unresolved namespace (CS0234), or, for CS0246 in a script under an asmdef, a reminder to check that asmdef's references (the type may instead be a typo — decide from the error).

When Unity stops compiling before the finish callback (`Success: null`, indeterminate), `Message` keeps the get-logs pointer and appends `Recent Console errors:` with the last few Console errors (typically the asmdef or compiler error that aborted the compile), so fix from that list before reaching for `uloop get-logs`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ uloop --project-path <PROJECT_ROOT>/Library/VP/mppm<id> simulate-keyboard --acti
uloop's one-command-at-a-time rule is per Editor, so commands to different players (or to the
main Editor) do not block each other.

## Hot reload

- A hot-reload patch lives in the Editor process it was applied to. A patch applied to the
main Editor does not reach the Virtual Players, and a player's own
`uloop --project-path <PROJECT_ROOT>/Library/VP/mppm<id> hot-reload --status` reports no
active patch.
- Hot reload cannot patch a Virtual Player yet: a player loads the main project's
`Library/ScriptAssemblies` and has none under its own root. `hot-reload --files ...` sent
to a player reports the file as `Failed`. Whether the CLI then compiles in that player
follows `--compile-on-skip`, as for any unapplied edit: when it compiles, the edit comes in
(`Outcome` is `ReplacedByCompile`); when the compile is held (`CompileFallback` is
`HeldForPlayMode`: `auto`, the default, while that player is in Play Mode), the edit has not
reached the player.

## Known limitations

- `Stop` sent while Virtual Players are still starting (the main Editor is not yet in Play Mode)
Expand Down
4 changes: 3 additions & 1 deletion .agents/skills/uloop-hot-reload/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ member access, static methods, return values, async methods, and iterators all w
the limits below, including private access inside async, iterator, lambda, local-function,
and LINQ-query bodies. Methods that cannot be patched are reported as `Skipped` or `Failed`;
one unpatchable method never aborts the rest of the run.
`Outcome` says whether the edit is live (`ReplacedByCompile`: a fallback compile succeeded, so
it is compiled in and no patch is active).

## Usage

Expand Down Expand Up @@ -106,4 +108,4 @@ All files live in `references/` beside this skill; read the one whose trigger ma
- `references/pause-point-interaction.md` — how patches re-target or suppress armed pause points; one-way reachability checks.
- `references/introduced-types.md` — types a reload can introduce: supported shapes, internal access, refusal wording, identity and lifetime, why a new file is never selected automatically.
- `references/added-field-wiring.md` — putting a value into an added field without a compile.
- `references/output.md` — every response field: `ErrorCode`, `NextActions`, `Methods` rows, `Warnings`, totals.
- `references/output.md` — "Is my edit live?" (`Outcome`), `Timing`, every response field: `ErrorCode`, `NextActions`, `Methods` rows, `Warnings`, totals.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## How a Reload Applies

1. Resolves each file to its compiled assembly via `CompilationPipeline`, then groups the files by that assembly. Each group runs the worker once and produces one shim assembly, so the members one file adds are visible to the bodies edited in its siblings; groups are processed one after another and never affect each other.
2. Rewrites each editable method body of the group into a static shim in an out-of-process Roslyn worker. When an async, iterator, lambda, local-function, or LINQ-query body touches private/internal members, those accesses are rewritten to accessor delegates so the body can compile and run from the shim assembly (the delegation shape in step 4).
2. Rewrites each editable method body of the group into a static shim in an out-of-process Roslyn worker. When an async, iterator, lambda, local-function, or LINQ-query body touches private/internal members, those accesses are rewritten to accessor delegates so the body can compile and run from the shim assembly (the delegation shape in step 4). An `internal` member of a type the reload was not given cannot be rewritten this way, so such a body is `Skipped` (`scope-and-limits.md`).
3. Compiles the group's shims into one assembly against publicized reference copies, loads the result into the Editor domain, and binds every shim type's accessor delegates (`__BindAccessors`) before any patch is applied.
4. Patches each original method with a Harmony transpiler (ID `io.github.hatayama.uloop.hot-reload`) in one of two shapes: transplant copies the shim's IL into the original method, while delegation rewrites the original to forward its arguments to the shim, which runs as normally compiled code.

Expand All @@ -24,7 +24,7 @@ patch binds to the newest shim. Edit the file and reload again to apply new chan
behavior converges by construction.
- Patches and loaded shim assemblies are static Editor state and disappear on the next
domain reload — that includes entering Play Mode with Domain Reload enabled (the
default for projects created before Unity 6.6), `uloop compile`, and `uloop run-tests`. `uloop control-play-mode --action Play` warns with
default for projects created before Unity 6.6), `uloop compile`, and the compile `uloop run-tests` runs first unless given `--skip-compile`. `uloop control-play-mode --action Play` warns with
the counts when it is about to drop patches or pause points. There is no persistence
and no automatic re-apply.
- Never reflected by hot reload: initializer changes on compiled fields and new
Expand All @@ -42,6 +42,19 @@ patch binds to the newest shim. Edit the file and reload again to apply new chan
before any method of that assembly is transformed, so the run applies no method body from any
file of that assembly; files in other assemblies still apply.

## Running tests while patches are live

- `uloop run-tests` compiles first by default. That compile reloads the domain and drops every live
patch; the edited source is compiled in, so the code changes stay without re-applying them, but
values wired into fields that hot reload added are not kept (they return to their initializer or
default values). The response's `CompileNote` carries the count, in the form
`1 active hot-reload change(s) were live when this compile was requested.`
- `--skip-compile` runs the tests against the live patches; added `[Test]` methods are not enumerated
that way, so compile first for those. Its `Warning` is a fixed notice that changes were live when the
run started and does not say whether they survived; read `uloop hot-reload --status` for that. While
hot reload holds Auto Refresh (the state an apply leaves), a verified run kept the patches live.
- There is no default switch: choose per call.

## Editor-Code Iteration Without PlayMode

Hot reload also patches static methods in Editor assemblies. Combined with
Expand Down
Loading
Loading