Skip to content

fix: Physics pause points that miss a pre-existing GameObject now log diagnostics for future investigation - #1922

Merged
hatayama merged 7 commits into
feature/round7-pause-point-improvementsfrom
investigate/physics-callback-existing-instance
Jul 21, 2026
Merged

hatayama merged 7 commits into
feature/round7-pause-point-improvementsfrom
investigate/physics-callback-existing-instance

Conversation

@hatayama

@hatayama hatayama commented Jul 21, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Physics-callback pause points (OnCollisionEnter2D/OnTriggerEnter2D/etc.) that miss a GameObject which already existed before the pause point was enabled now log diagnostics to help pin down a future recurrence.
  • No lighter workaround than destroying and recreating the GameObject (or a manual UloopPausePoint.Pause marker) was found to be valid; an enabled-toggle workaround investigated during this work was rejected as a false positive (see Investigation summary).

User Impact

  • Before: if the pause point never hit a pre-existing physics-callback instance, the only documented workaround was destroying and recreating the GameObject (or embedding a manual UloopPausePoint.Pause marker), and there were no diagnostics to help investigate a recurrence.
  • After: the workaround guidance is unchanged, but a future recurrence now carries diagnostic evidence (Play Mode state, seconds since the last domain reload, the declaring type, and its instance count) instead of just a symptom.

Changes

  • Add physics-callback dispatch diagnostics: PausePointTools logs a pause_point_physics_dispatch_diagnostics VibeLogger entry whenever a physics-flagged pause point is enabled, and a pause_point_expired_without_hit_physics entry if it later expires without ever hitting.
  • Add a permanent regression-harness scene and driver script (Assets/RegressionHarness/PhysicsCallbackExistingInstance/, scripts/regression-harness-physics-callback-existing-instance.sh) covering three call shapes: direct (OnCollisionEnter2D), indirect (a one-hop callee, primed with a prior contact), and OnTriggerEnter2D. Each scenario arms the pause point, triggers a fresh contact, and classifies the result from the component's own hit counter plus IsHit -- a miss is only counted as genuine when the counter incremented (proof the method body ran) while IsHit stayed false; a counter that did not increment means the harness itself failed to produce a contact.

Investigation summary

An enabled-toggle workaround (toggle a physics-callback component's enabled off/on once, claimed to re-resolve Unity's cached message dispatch for the whole component type) was investigated as a lighter alternative to destroy-and-recreate. Every local observation that seemed to support it was a false positive: the harness's baseline check never triggered a fresh contact after arming, so IsHit=false at baseline only meant "no new collision occurred in the check window" (the ball had already settled from a fall that happened before arming), not that dispatch was missed. The apparent "hit after toggle" was simply the first-ever fresh collision since arming, unrelated to the toggle. The same flaw explained an earlier single miss observation recorded in this environment's VibeLogger output. The Warning text (SourcePausePointConstants.PhysicalCallbackMayMissExistingInstanceWarning / PhysicalCallbackIndirectCallMayMissExistingInstanceWarning) and docs/regression-harness.md have been corrected to remove the toggle-workaround claim, and the harness now requires a counter increment alongside IsHit=false before calling something a genuine miss.

Verification

  • uloop compile -- 0 errors, 0 warnings.
  • uloop run-tests --filter-type regex --filter-value "SourcePausePointPatcherTests|PausePointPhysicsDispatchDiagnosticsTests" --test-mode EditMode -- 29/29 passed.
  • sh scripts/regression-harness-physics-callback-existing-instance.sh -- all three scenarios (direct/indirect/trigger) passed as "did not reproduce" (each fresh contact was captured by the pause point; no genuine miss and no harness self-failure).
  • Confirmed via .uloop/outputs/VibeLogs/ that pause_point_physics_dispatch_diagnostics entries are emitted with the expected fields (Play Mode state, declaring type, instance count, seconds since domain reload).

…round

Pause points armed on OnCollisionEnter2D/OnTriggerEnter2D miss GameObjects
that already existed before the pause point was enabled, because Unity
caches physics message dispatch per component type at first registration.
Confirmed the enabled-toggle workaround (disable/enable the component once)
re-resolves dispatch for the whole component type for the rest of the
Editor session, fixing every instance, not just the toggled one -- verified
via a controlled two-instance experiment (one toggled, one never touched,
both received the fix).

- Update the physics-callback Warning constants with the lightest-known
  workaround, its type-wide/session-wide scope, and its OnDisable/OnEnable
  side effect
- Add a permanent regression-harness scene and driver script covering both
  OnCollisionEnter2D and OnTriggerEnter2D
- The harness's baseline-miss check is informational only (not asserted):
  a best-effort domain reload before arming did not reliably reproduce a
  fresh miss on every run, so only the post-workaround hit is a strict
  assertion
@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@hatayama, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 45e43bd9-67c1-4738-a029-055e270c04c0

📥 Commits

Reviewing files that changed from the base of the PR and between 5e83593 and 848aa50.

📒 Files selected for processing (1)
  • Packages/src/Editor/FirstPartyTools/PausePoint/PausePointTools.cs
📝 Walkthrough

Walkthrough

Adds physics callback fixtures, structured warning metadata, dispatch diagnostics, domain-reload tracking, and a regression harness covering existing-instance collision and trigger pause points.

Changes

Physics callback diagnostics

Layer / File(s) Summary
Callback fixtures and warning metadata
Assets/RegressionHarness/PhysicsCallbackExistingInstance/*, Packages/src/Editor/FirstPartyTools/PausePoint/SourcePausePointPatchResult.cs, Packages/src/Editor/FirstPartyTools/PausePoint/SourcePausePointPatcher.cs, Packages/src/Editor/FirstPartyTools/PausePoint/SourcePausePointConstants.cs
Adds collision and trigger counters, reload markers, and structured declaring-type and physics-warning metadata for direct and indirect callbacks.
Dispatch diagnostics and lifecycle tracking
Packages/src/Editor/FirstPartyTools/PausePoint/PausePointTools.cs, Packages/src/Editor/FirstPartyTools/PausePoint/PausePointPhysicsDispatchDiagnostics.cs, Packages/src/Editor/FirstPartyTools/PausePoint/PausePointDomainReloadTracker.cs, Assets/Tests/Editor/SourcePausePointPatcher/PausePointPhysicsDispatchDiagnosticsTests.cs, Packages/src/Editor/Infrastructure/Api/PausePointStatusBridgeCommand.cs
Tracks flagged pause points, logs instance counts and domain-reload timing, reports expired dispatch misses, and tests instance-count resolution.
Existing-instance regression validation
scripts/regression-harness-physics-callback-existing-instance.sh, docs/regression-harness.md
Adds a documented harness that reloads the domain, primes contacts, tests direct/indirect collision and trigger callbacks, and classifies hit, miss, and harness-failure outcomes.

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

Sequence Diagram(s)

sequenceDiagram
  participant Script as Regression script
  participant Unity as Unity Editor
  participant Physics as Physics callback components
  participant Tools as Pause-point tools
  participant Status as Pause-point status
  Script->>Unity: Request domain reload and start Play Mode
  Script->>Physics: Prime and reset contacts
  Script->>Tools: Arm pause point on existing instance
  Physics->>Tools: Invoke callback and update counter
  Script->>Status: Read IsHit and counter delta
  Tools->>Tools: Log dispatch diagnostics on warning or expiry
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title is concise and accurately highlights the new diagnostic logging for missed physics pause points.
Description check ✅ Passed The description is clearly related and matches the harness, diagnostics, and warning updates in the changeset.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch investigate/physics-callback-existing-instance

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.

hatayama added 4 commits July 21, 2026 22:52
…lling

EditorUtility.RequestScriptReload() queues the reload for a later editor
update rather than running it inline, so polling for bare IPC
responsiveness is not proof the reload actually happened -- both a
still-loading domain and the still-alive pre-reload domain can answer a
lightweight command successfully. Add HarnessDomainMarker, a static
readonly value that is only re-initialized when the AppDomain actually
reloads, and have the harness poll for that value changing instead.

This still does not make the harness's baseline-miss check reproducible
(see the investigation in PR #1922) -- left as-is pending further
investigation.
…d an indirect-callee scenario

A confirmed-fresh domain reload (verified via a static marker that only
changes when the AppDomain actually reloads) did not reliably reproduce the
pre-existing-instance dispatch miss on every run -- reproduction is
environment-dependent. The harness now treats the baseline-miss check as
conditional: if a miss is observed, the enabled-toggle workaround is
strictly asserted to fix it; if not, that is logged and the run passes
without exercising the workaround assertion.

Also add an indirect-callee scenario (a one-hop method called from
OnCollisionEnter2D, primed with one prior contact before arming), matching
the dominant real-world pattern where the pause point marker sits in a
helper method rather than the physics callback itself.
The existing-instance physics-callback dispatch miss investigated in this
PR turned out to be environment-dependent with no deterministic
reproduction found (see docs/regression-harness.md for the ruled-out
hypotheses). Since the miss is real but rare, add diagnostics so a future
recurrence carries evidence instead of just a symptom:

- SourcePausePointPatcher now reports whether a patch carries a physics-
  callback warning, plus the declaring type, alongside the existing warning
  text (SourcePausePointPatchResult.HasPhysicsCallbackWarning/DeclaringType)
- PausePointDomainReloadTracker captures this AppDomain's load time via
  [InitializeOnLoad], so diagnostics can report how long the domain has
  been alive without a reload
- PausePointTools logs a pause_point_physics_dispatch_diagnostics
  VibeLogger entry whenever a physics-flagged pause point is enabled
  (Play Mode state, seconds since domain reload, declaring type, and --
  for MonoBehaviour-derived types -- current scene instance count), and a
  pause_point_expired_without_hit_physics entry if it later expires
  without ever hitting
- Update the Warning constants: the enabled-toggle workaround's effect was
  previously described as lasting "until the next domain reload" based on
  a reload-timing theory that further investigation ruled out; it now
  says the miss has not been observed to recur once re-resolved, without
  claiming a specific expiry condition
- PausePointPhysicsDispatchDiagnostics.ResolveInstanceCount is unit tested
  as the one pure piece of this diagnostics logic (whether counting
  applies to a non-MonoBehaviour declaring type)
…ng-instance miss

The harness's baseline-miss check never triggered a fresh contact after
arming a pause point, so every "miss" it reported was a false positive:
IsHit=false at baseline only meant no new collision occurred in the check
window (the ball had already settled from a fall before arming), not that
dispatch was missed. The apparent "toggle fixes it" result was simply the
first-ever fresh collision since arming.

- Revert the Warning text to only recommend destroy-and-recreate or a
  manual UloopPausePoint.Pause marker; document why the toggle workaround
  was rejected.
- Rewrite the harness to require a component hit-counter increment
  alongside IsHit=false before calling something a genuine miss, and to
  distinguish that from a harness self-failure (no fresh contact produced
  at all). An enabled-toggle probe still runs informationally on a genuine
  miss but is no longer asserted.
- Update docs/regression-harness.md to match.
@hatayama hatayama changed the title fix: Suggest a lighter workaround when a physics pause point misses an existing GameObject fix: Physics pause points that miss a pre-existing GameObject now log diagnostics for future investigation Jul 21, 2026
… and cover the clear --all path

pause_point_expired_without_hit_physics was logged for any Expired marker,
including one that hit before expiring, which pollutes the primary
evidence a future recurrence would rely on. It also never fired via
clear-pause-point --all, the dominant field path (an agent cleaning up
after an await timeout), so the common case lost this diagnostic
entirely.

- Gate both the single-id Clear path and a new ClearAll path on
  HitCount == 0 in addition to Status == Expired.
- Snapshot each physics-flagged tracked id via GetStatus before ClearAll
  resolves it away, so the ClearAll path also gets the diagnostic.
- Inline PausePointPhysicsDispatchDiagnostics.ResolveInstanceCount into
  its only caller and delete its dedicated test; the ternary it wrapped
  needed no separate coverage.
coderabbitai[bot]
coderabbitai Bot previously requested changes Jul 21, 2026

@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.

Actionable comments posted: 1

🤖 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/Editor/FirstPartyTools/PausePoint/PausePointTools.cs`:
- Line 458: Update the diagnostic payload construction near DeclaringType to
access declaringType.FullName safely when declaringType is null, using the
null-conditional access so global methods produce a null fallback instead of
throwing.
🪄 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: e815be1c-bb5b-46e6-87ff-5f15d7cbde64

📥 Commits

Reviewing files that changed from the base of the PR and between 3b5b9cf and 5e83593.

⛔ Files ignored due to path filters (4)
  • Assets/RegressionHarness/PhysicsCallbackExistingInstance/HarnessDomainMarker.cs.meta is excluded by none and included by none
  • Assets/Tests/Editor/SourcePausePointPatcher/PausePointPhysicsDispatchDiagnosticsTests.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/PausePoint/PausePointDomainReloadTracker.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/PausePoint/PausePointPhysicsDispatchDiagnostics.cs.meta is excluded by none and included by none
📒 Files selected for processing (12)
  • Assets/RegressionHarness/PhysicsCallbackExistingInstance/HarnessDomainMarker.cs
  • Assets/RegressionHarness/PhysicsCallbackExistingInstance/PhysicsCallbackFloor.cs
  • Assets/Tests/Editor/SourcePausePointPatcher/PausePointPhysicsDispatchDiagnosticsTests.cs
  • Packages/src/Editor/FirstPartyTools/PausePoint/PausePointDomainReloadTracker.cs
  • Packages/src/Editor/FirstPartyTools/PausePoint/PausePointPhysicsDispatchDiagnostics.cs
  • Packages/src/Editor/FirstPartyTools/PausePoint/PausePointTools.cs
  • Packages/src/Editor/FirstPartyTools/PausePoint/SourcePausePointConstants.cs
  • Packages/src/Editor/FirstPartyTools/PausePoint/SourcePausePointPatchResult.cs
  • Packages/src/Editor/FirstPartyTools/PausePoint/SourcePausePointPatcher.cs
  • Packages/src/Editor/Infrastructure/Api/PausePointStatusBridgeCommand.cs
  • docs/regression-harness.md
  • scripts/regression-harness-physics-callback-existing-instance.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • Assets/RegressionHarness/PhysicsCallbackExistingInstance/PhysicsCallbackFloor.cs

Comment thread Packages/src/Editor/FirstPartyTools/PausePoint/PausePointTools.cs
…gType

CodeRabbit flagged declaringType.FullName as an NRE risk if the parameter
were ever null. It is only reachable via PhysicsFlaggedDeclaringTypesById,
populated solely from a successful patch's method.DeclaringType, which a
C#-sourced method always has -- so a null-conditional would silently mask
a contract violation instead of surfacing it. Document that contract with
a Debug.Assert precondition instead, per this repo's Fail Fast /
contract-programming exception policy.
@hatayama
hatayama dismissed coderabbitai[bot]’s stale review July 21, 2026 14:57

Addressed: unreachable-null contract documented with a precondition assert (848aa50); CodeRabbit re-review unavailable due to rate limiting.

@hatayama
hatayama merged commit de474f4 into feature/round7-pause-point-improvements Jul 21, 2026
2 checks passed
@hatayama
hatayama deleted the investigate/physics-callback-existing-instance branch July 21, 2026 14:58
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