Skip to content

feat: Enable pause points by source file and line#1684

Merged
hatayama merged 5 commits into
v3-betafrom
feat/enable-pause-point-by-source-line
Jul 11, 2026
Merged

feat: Enable pause points by source file and line#1684
hatayama merged 5 commits into
v3-betafrom
feat/enable-pause-point-by-source-line

Conversation

@hatayama

@hatayama hatayama commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

  • enable-pause-point now accepts File+Line in addition to the existing Id marker mode, resolving a source location and patching a pause point into it without any source edit.
  • Clearing a source pause point (single or ClearAll) automatically removes the underlying Harmony patch, so no injected instrumentation is left behind after the marker reports Cleared.
  • When a pause point hits, the response now includes CapturedVariables (locals, parameters, and instance fields captured at that line) and a CapturedVariablesTruncated flag.

User Impact

  • AI agents can now pause execution at any source line (e.g. enable-pause-point --file Assets/Scripts/Enemy.cs --line 42) instead of requiring a hand-written UloopPausePoint.Pause("id") marker in code.
  • Pausing at a Release-optimized build is rejected up front with a clear message to switch the Editor to Debug code optimization, instead of silently patching the wrong instruction.
  • Waiting on a pause point (wait-for-pause-point) now surfaces captured variable values directly in the response, so agents can inspect state at the pause point without a separate step.

Changes

  • PausePointTools.EnablePausePointSchema/PausePointUseCase.Enable: validates that exactly one of Id or File+Line is provided, rejects Release code optimization for the source-line path, resolves the location via the existing Resolver, patches via SourcePausePointPatcher, and merges any resolver/patcher warning with the existing enable warning.
  • UloopPausePointRegistry gains OnCleared/OnClearedAll hooks invoked from Clear/ClearAll; SourcePausePointPatcher"'s static constructor wires Unpatch/UnpatchAll` into these hooks. This keeps the onion-architecture dependency direction intact: the Runtime registry and Infrastructure bridge never reference the Editor-only patching assembly directly.
  • PausePointStatusBridgeCommand/Go pause_point_wait.go: both response DTOs gain CapturedVariables/CapturedVariablesTruncated, backed by a new shared JSON contract fixture (tests/contracts/pause_point_status_response_contract.json) with matching Go and C# field-shape tests.
  • default-tools.json: enable-pause-point's schema documents the new File/Line properties as mutually exclusive with Id.

Verification

  • uloop compile — 0 errors, 0 warnings
  • uloop run-tests --filter-value "PausePoint" — 98/98 passed
  • scripts/check-go-cli.sh — all green (fmt, vet, lint, tests, build)

Review in cubic

hatayama added 5 commits July 11, 2026 11:21
Add File/Line parameters to enable-pause-point alongside the existing
Id marker path. PausePointUseCase.Enable now validates that exactly
one of Id or File+Line is provided, rejects the File/Line path when
CompilationPipeline.codeOptimization is Release (Debug symbols are
required to resolve a patch location), resolves File/Line via
SourcePausePointResolver, patches the resolved method via
SourcePausePointPatcher, derives the pause point id as "<file>:<line>"
from the originally requested line (not the resolved/rounded line) so
repeated calls at the same location stay idempotent, and returns
ResolvedLine/ResolvedMethod plus any Patcher warning merged with the
existing domain-reload warning.

cli/common/tools/default-tools.json is updated in the same commit so
DefaultToolsCatalogDriftTests keeps passing. A new frozen fixture file
backs a full Resolver -> Patcher -> Registry integration test driven
through the public tool surface.

This is commit 1 of PR 5 in the source pause point plan; Clear() is
intentionally unchanged here (Unpatch-on-clear wiring is commit 2).
Wire SourcePausePointPatcher.Unpatch/UnpatchAll into both clear paths
so a source pause point's Harmony injection is actually removed, not
just its registry entry:

- PausePointUseCase.Clear now calls Unpatch(id) for a specific id and
  UnpatchAll() for --all (a safe no-op for marker-only ids that were
  never Harmony-patched).
- PausePointStatusBridgeCommand.Clear (the path Go's
  wait-for-pause-point timeout auto-clear and clear-pause-point-status
  hit) now calls Unpatch(id) too, so a source pause point left armed
  past its timeout does not leave a dangling injection after Unity
  reports it Cleared.

Infrastructure needs a new reference to the PausePoint editor assembly
plus an InternalsVisibleTo grant to call the internal
SourcePausePointPatcher; Tests.Editor gets the same grant so
PausePointTests can prove Unpatch actually ran (re-Patch-ing the same
id with a deliberately stale Mvid only reaches the Patcher's
stale-assembly gate once the id is no longer in its ledger, since an
already-patched id short-circuits before that gate runs).
The previous commit (feat(package): Unpatch source pause points on
clear) wired SourcePausePointPatcher.Unpatch/UnpatchAll into
PausePointStatusBridgeCommand.Clear by adding a direct asmdef
reference and InternalsVisibleTo grant from the Infrastructure asmdef
to the FirstPartyTools.PausePoint.Editor asmdef. Running the full
Unity EditMode suite revealed this broke
OnionAssemblyDependencyTests.InfrastructureAsmdef_WhenLoaded_DependsOnApplicationRuntimeAndDoesNotReferencePresentation,
which asserts Infrastructure's asmdef reference set is a fixed,
closed list that excludes individual FirstPartyTools implementation
asmdefs; Infrastructure may only depend on the shared
PausePointsRuntime assembly, InternalApiBridge, Application, Domain,
and ToolContracts.

Fix the root cause instead of adjusting the test: move the unpatch
wiring out of Infrastructure entirely by adding an Action<string>
OnCleared and an Action OnClearedAll hook to UloopPausePointRegistry
(Runtime assembly, already an allowed Infrastructure dependency).
SourcePausePointPatcher's static constructor wires
Unpatch/UnpatchAll into these hooks; Registry.Clear/ClearAll invoke
them internally. Both PausePointUseCase.Clear and
PausePointStatusBridgeCommand.Clear go back to calling only
UloopPausePointRegistry.Clear/ClearAll, with neither referencing
SourcePausePointPatcher directly. The now-unneeded Infrastructure
asmdef reference and InternalsVisibleTo grant are removed.
Enabling a pause point by File/Line already captures local
variables, parameters, and this fields into the registry snapshot,
but the CLI's wait-for-pause-point/pause-point-status commands only
surfaced marker bookkeeping fields (Status, IsHit, EditorState,
etc.). A user hitting a source pause point had no way to see what
values were actually captured.

Add CapturedVariables and CapturedVariablesTruncated to both the C#
PausePointStatusResponse (Infrastructure bridge) and the Go
pausePointStatusResponse, mirroring UloopCapturedVariable's fields
field-for-field so the Go CLI passes Unity's captured values straight
through to its JSON stdout. normalizePausePointStatusResponse is left
untouched since it only derives Expired/RemainingMilliseconds for
older Unity packages.
Extends the get-logs contract pattern to the pause-point status
response so the CapturedVariables/CapturedVariablesTruncated fields
added for source file:line pause points stay in sync between the Go
CLI and the Unity package: a shared JSON fixture plus a Go test and a
C# test that each round-trip it through their own DTO and compare the
normalized field shape.
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Pause points can now be enabled by source file and line, automatically unpatched when cleared, and reported with captured-variable metadata. Unity and Go tests validate the new behavior against a shared JSON response contract.

Changes

Pause-point pipeline

Layer / File(s) Summary
Source-location enablement and cleanup
Packages/src/Editor/FirstPartyTools/PausePoint/*, Packages/src/Runtime/PausePoints/UloopPausePointRegistry.cs
Adds File/Line resolution, patching, resolved-target response fields, Release-mode validation, and registry-driven unpatching.
Captured-variable status mapping
Packages/src/Editor/Infrastructure/Api/PausePointStatusBridgeCommand.cs, cli/project-runner/internal/projectrunner/pause_point_wait.go
Adds captured-variable metadata and truncation fields to editor and CLI status responses.
CLI input and response schema
cli/common/tools/default-tools.json
Documents mutually exclusive Id and File/Line enablement modes and their validation rules.
End-to-end validation and shared contracts
Assets/Tests/Editor/*, cli/project-runner/internal/projectrunner/*_test.go, tests/contracts/*
Tests source-location enablement, captured variables, cleanup/repatch behavior, and cross-language JSON field-shape compatibility.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant EnablePausePointTool
  participant SourcePausePointResolver
  participant SourcePausePointPatcher
  participant UloopPausePointRegistry
  CLI->>EnablePausePointTool: Send File and Line
  EnablePausePointTool->>SourcePausePointResolver: Resolve source location
  SourcePausePointResolver-->>EnablePausePointTool: Return method and line
  EnablePausePointTool->>SourcePausePointPatcher: Patch resolved method
  SourcePausePointPatcher-->>EnablePausePointTool: Return patch result
  EnablePausePointTool->>UloopPausePointRegistry: Register pause point
  UloopPausePointRegistry-->>SourcePausePointPatcher: Unpatch on clear
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: enabling pause points by source file and line.
Description check ✅ Passed The description accurately describes the pause-point source-location, capture, and contract changes in the PR.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/enable-pause-point-by-source-line

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Packages/src/Runtime/PausePoints/UloopPausePointRegistry.cs (1)

217-226: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

ResetForTests should trigger OnClearedAll to unpatch stale source pause points.

ResetForTests clears Entries and snapshots but never invokes OnClearedAll, so Harmony patches from source pause points enabled in a previous test survive the reset. Subsequent tests that exercise the patched method will hit stale injected IL. Calling OnClearedAll?.Invoke() before clearing entries ensures the patcher's UnpatchAll runs and removes all transpilers.

🛡️ Proposed fix
         public static void ResetForTests()
         {
+            OnClearedAll?.Invoke();
             Entries.Clear();
             _nextGeneration = 0;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/src/Runtime/PausePoints/UloopPausePointRegistry.cs` around lines 217
- 226, ResetForTests currently clears registry state without removing Harmony
patches from previously registered source pause points. Invoke OnClearedAll
before clearing Entries so the patcher’s UnpatchAll logic runs, then continue
resetting counters, snapshots, and providers as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@Packages/src/Runtime/PausePoints/UloopPausePointRegistry.cs`:
- Around line 217-226: ResetForTests currently clears registry state without
removing Harmony patches from previously registered source pause points. Invoke
OnClearedAll before clearing Entries so the patcher’s UnpatchAll logic runs,
then continue resetting counters, snapshots, and providers as before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 346a263d-f592-400f-b37a-bf5ee994702e

📥 Commits

Reviewing files that changed from the base of the PR and between 0159711 and 4b7b664.

⛔ Files ignored due to path filters (2)
  • Assets/Tests/Editor/PausePointStatusResponseContractTests.cs.meta is excluded by none and included by none
  • Assets/Tests/Editor/PausePointToolsFixture.cs.meta is excluded by none and included by none
📒 Files selected for processing (14)
  • Assets/Tests/Editor/PausePointStatusResponseContractTests.cs
  • Assets/Tests/Editor/PausePointTests.cs
  • Assets/Tests/Editor/PausePointToolsFixture.cs
  • Packages/src/Editor/FirstPartyTools/PausePoint/AssemblyInfo.cs
  • Packages/src/Editor/FirstPartyTools/PausePoint/PausePointTools.cs
  • Packages/src/Editor/FirstPartyTools/PausePoint/SourcePausePointConstants.cs
  • Packages/src/Editor/FirstPartyTools/PausePoint/SourcePausePointPatcher.cs
  • Packages/src/Editor/Infrastructure/Api/PausePointStatusBridgeCommand.cs
  • Packages/src/Runtime/PausePoints/UloopPausePointRegistry.cs
  • cli/common/tools/default-tools.json
  • cli/project-runner/internal/projectrunner/pause_point_status_response_contract_test.go
  • cli/project-runner/internal/projectrunner/pause_point_wait.go
  • cli/project-runner/internal/projectrunner/pause_point_wait_test.go
  • tests/contracts/pause_point_status_response_contract.json

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