fix: captured variables stay readable after re-enabling a pause point while paused#1734
Conversation
…ile paused Re-enabling the same pause-point id while Unity is still paused (for example to refresh its timeout during a step session) cleared the raw capture holder too, so UloopPausePoint.TryGetCapturedValue reported Found=false even though Unity never resumed. Enable() shared the same helper Clear() used to drop the hit snapshot, which unconditionally clears UloopPausePointRawCaptureHolder. A re-enable does not resume Unity, so the holder's documented paused-window invariant is not actually violated by keeping the previous hit's live references across a same-id re-enable. Split ClearLatestHitSnapshotIfMatches into ForgetHitSnapshotForId (drops only the hit-history entry / latest-hit pointer, used by Enable) and ClearHitSnapshotAndRawCaptureForId (also clears the raw capture holder, used by Clear). Add a regression test that hits a pause point, re-enables the same id while paused, and asserts the captured value is still retrievable. Update the pause-point skill doc (source and its two generated copies) to document the re-enable behavior. Manually verified against a running Unity Editor (6000.3.15f1): hit -> re-enable -> Step x5 keeps the capture, and a subsequent resume still clears it.
… paused "while Unity is both playing and paused" was ambiguous enough that a real usage report misread it as "watches also evaluate while the game is running," and reported that behavior as an implementation bug. The actual implementation (from PR #1729/#1733) only evaluates a watch once per changed Time.frameCount when Play Mode is running AND the Editor is paused (on each hit pause and each Step); nothing is recorded while the game runs unpaused. Reword the sentence in the pause-point skill doc (source and its two generated copies) so it cannot be misread as "playing" meaning "running unpaused."
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe pause-point registry now preserves raw captures when the same pause point is re-enabled while paused, while clear and lifecycle operations still remove them appropriately. Editor tests and synchronized skill documentation cover capture and watch-expression semantics. ChangesPause-point capture behavior
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
Assets/Tests/Editor/PausePointTests.cs (1)
581-598: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winLGTM for the re-enable-preserves-raw-capture test. Consider adding a companion test for the re-enable-then-clear path to guard against the
ClearHitSnapshotAndRawCaptureForIdissue flagged onUloopPausePointRegistry.cs.🧪 Suggested companion test
[Test] public void TryGetCapturedValue_WhenSamePausePointIsReenabledThenCleared_StillClearsRawCapture() { // Verifies Clear(id) after a same-id re-enable still drops the raw capture holder, // even though _latestHitSnapshot was nulled by the re-enable. UloopPausePointRegistry.Enable("jump", 30); UloopPausePointCapturedVariableFrame frame = new( new[] { new UloopPausePointCapturedVariableEntry("speed", UloopCapturedVariableScope.Local, 1) }, false); UloopPausePointRegistry.HitWithCapturedFrame("jump", frame, Array.Empty<UloopCapturedVariable>(), false); UloopPausePointRegistry.Enable("jump", 30); UloopPausePointRegistry.Clear("jump"); (bool found, object value) = UloopPausePoint.TryGetCapturedValue("speed"); Assert.That(found, Is.False); Assert.That(value, Is.Null); Assert.That(UloopPausePoint.GetCapturedPausePointId(), Is.Empty); }🤖 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 `@Assets/Tests/Editor/PausePointTests.cs` around lines 581 - 598, Add a companion test near TryGetCapturedValue_WhenSamePausePointIsReenabledWhilePaused_KeepsLatestHitRawCapture that re-enables the same pause point, calls UloopPausePointRegistry.Clear("jump"), and verifies TryGetCapturedValue("speed") returns false with a null value and GetCapturedPausePointId() is empty. This should cover the re-enable-then-clear path and ensure ClearHitSnapshotAndRawCaptureForId removes the held raw capture.
🤖 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.
Inline comments:
In `@Packages/src/Runtime/PausePoints/UloopPausePointRegistry.cs`:
- Around line 262-271: Update ClearHitSnapshotAndRawCaptureForId to determine
ownership using UloopPausePointRawCaptureHolder.GetCapturedPausePointId() rather
than _latestHitSnapshot. Clear the holder when its captured pause-point ID
matches id, including after re-enable has removed the latest snapshot, while
preserving the existing ForgetHitSnapshotForId behavior.
---
Nitpick comments:
In `@Assets/Tests/Editor/PausePointTests.cs`:
- Around line 581-598: Add a companion test near
TryGetCapturedValue_WhenSamePausePointIsReenabledWhilePaused_KeepsLatestHitRawCapture
that re-enables the same pause point, calls
UloopPausePointRegistry.Clear("jump"), and verifies TryGetCapturedValue("speed")
returns false with a null value and GetCapturedPausePointId() is empty. This
should cover the re-enable-then-clear path and ensure
ClearHitSnapshotAndRawCaptureForId removes the held raw capture.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3fb1a08f-c037-43e9-a435-c1c549ae804a
📒 Files selected for processing (5)
.agents/skills/uloop-pause-point/SKILL.md.claude/skills/uloop-pause-point/SKILL.mdAssets/Tests/Editor/PausePointTests.csPackages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.mdPackages/src/Runtime/PausePoints/UloopPausePointRegistry.cs
CodeRabbit flagged that ClearHitSnapshotAndRawCaptureForId judged raw capture ownership from _latestHitSnapshot, but Enable() already nulls that field for the same id via ForgetHitSnapshotForId as part of a same-id re-enable. So the sequence hit -> re-enable -> Clear(id) left the raw capture holder alive, contradicting the documented contract that an explicit Clear drops captures. Check ownership against UloopPausePointRawCaptureHolder's own captured id instead: the holder already tracks which pause point last stored into it and is updated in lockstep with every hit, so this is equivalent to the old semantics on all other paths and immune to _latestHitSnapshot being cleared early by a re-enable. Added a regression test verifying Clear(id) after a same-id re-enable still clears the holder.
|
Addressed the nitpick as well: added the suggested companion test |
Summary
UloopPausePoint.TryGetCapturedValue; the captured variables of the current hit stay readable.User Impact
enable-pause-pointagain on the same id (a natural move in a longcontrol-play-mode --action Stepsession, because the enable-time timeout does not extend after hits) silently cleared the raw capture holder.TryGetCapturedValuereturnedFound=falseand watch expressions built on it recordednullfrom that point on. This was reported from real usage as a suspected Step bug.Changes
EnableandClear:Enablenow only resets the hit bookkeeping (ForgetHitSnapshotForId), whileClearkeeps dropping the raw capture holder (ClearHitSnapshotAndRawCaptureForId).ClearAll, the simulate-* snapshot reset, and the resume/PlayMode-exit lifecycle are unchanged.Verification
found=false) before the fix and passes after.uloop compile: 0 errors / 0 warnings.PausePointTests: 48/48 green, including existing clear/replace/resume regressions.Found=true→ same-id re-enable → stillFound=true→Step×5 → stillFound=true; resume still clears the holder.