Skip to content

feat: add pause-point watch expressions#1733

Merged
hatayama merged 3 commits into
v3-betafrom
feat/pause-point-watch
Jul 12, 2026
Merged

feat: add pause-point watch expressions#1733
hatayama merged 3 commits into
v3-betafrom
feat/pause-point-watch

Conversation

@hatayama

@hatayama hatayama commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

Phase 2 base PR for pause-point watch expressions (feat/pause-point-watchv3-beta).

  • Add an Editor-side watch expression registry with compile-once caching, per-Step evaluation while playing and paused, bounded history, and typed evaluation errors
  • Expose enable-watch / clear-watch / get-watch-values as plain default-tools (schema-driven help/completion, shared C#/Go response contract)
  • Document watch workflows, duplicate-id rejection, raw pause-point capture access, and domain-reload lifetime in the pause-point skill

Included work

Validation

  • uloop compile: 0 errors / 0 warnings
  • Watch EditMode tests: green (24/24 after contract test)
  • scripts/check-go-cli.sh: green
  • E2E (dist/darwin-arm64/uloop, SimulateMouseDemoScene Update path):
    • baseline frame grew across Editor Steps
    • throwing expression recorded System.FormatException without stopping Step
    • domain reload cleared in-memory watches

Test plan

  • CI green on this base PR
  • /review / autoreview clean
  • Confirm watch tools remain discoverable via default-tools (not NativeCommands)
  • Spot-check skill copies stay byte-identical to the PausePoint skill source

Made with Cursor

Review in cubic

@coderabbitai

coderabbitai Bot commented Jul 12, 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: 36 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: 53996644-43f6-4a44-8a8c-f587c477660b

📥 Commits

Reviewing files that changed from the base of the PR and between ec19976 and dd92d61.

📒 Files selected for processing (2)
  • Assets/Tests/Editor/WatchResponseContractTests.cs
  • tests/contracts/watch_response_contract.json
📝 Walkthrough

Walkthrough

Adds watch expressions backed by dynamic compilation, baseline and per-frame evaluation, bounded history, Unity CLI tools, shared response contracts, tests, and usage documentation.

Changes

Watch Expressions

Layer / File(s) Summary
Watch compilation and evaluation contracts
Packages/src/Editor/FirstPartyTools/Watch/*, Assets/Tests/Editor/WatchExpressionCompilerTests.cs, Assets/Tests/Editor/WatchExpressionEvaluationTests.cs
Watch expressions are compiled into evaluators, and evaluation or compilation failures are represented through structured result types.
Frame evaluation and bounded history
Packages/src/Editor/FirstPartyTools/Watch/WatchExpressionEntry.cs, Packages/src/Editor/FirstPartyTools/Watch/WatchExpressionRegistry.cs, Packages/src/Editor/FirstPartyTools/Watch/WatchExpressionStepMonitor.cs, Assets/Tests/Editor/WatchExpressionRegistryTests.cs
Registered watches receive baseline and changed-frame evaluations while paused, retain bounded history, support clearing, and prevent re-entrant evaluation.
CLI tools and Unity service wiring
Packages/src/Editor/FirstPartyTools/Watch/WatchTools.cs, Packages/src/Editor/ToolContracts/UnityCliLoopConstants.cs, cli/common/tools/default-tools.json, .agents/skills/..., .claude/skills/..., Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md, Assets/Tests/Editor/WatchToolTests.cs, cli/dispatcher/internal/dispatcher/help_test.go
Adds enable-watch, clear-watch, and get-watch-values, including request validation, response mapping, service startup, CLI schemas, tests, and documentation.
Cross-language response contract validation
tests/contracts/watch_response_contract.json, cli/project-runner/internal/projectrunner/watch_types.go, cli/project-runner/internal/projectrunner/watch_response_contract_test.go, Assets/Tests/Editor/WatchResponseContractTests.cs
Unity and Go watch responses are checked against a shared JSON field-shape contract.

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

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant WatchUseCase
  participant WatchExpressionCompiler
  participant WatchExpressionRegistry
  participant UnityEditor
  CLI->>WatchUseCase: enable-watch
  WatchUseCase->>WatchExpressionCompiler: compile expression
  WatchExpressionCompiler-->>WatchUseCase: evaluator or diagnostics
  WatchUseCase->>WatchExpressionRegistry: register evaluator
  WatchExpressionRegistry-->>CLI: baseline WatchResponse
  UnityEditor->>WatchExpressionRegistry: editor update
  WatchExpressionRegistry-->>CLI: retained frame history via get-watch-values
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.84% 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 clearly summarizes the main change: adding pause-point watch expressions.
Description check ✅ Passed The description accurately covers the watch-expression registry, tools, docs, tests, and contracts in this changeset.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/pause-point-watch

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.

Actionable comments posted: 1

🧹 Nitpick comments (4)
Assets/Tests/Editor/WatchExpressionRegistryTests.cs (1)

19-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a test for duplicate ID rejection.

The PR objectives explicitly list "duplicate ID rejection" as a feature, and WatchExpressionRegistry.Register handles it by returning WatchRegistrationResult.FailureResult($"Watch expression '{id}' is already registered."). However, no test in this file covers that path. Similarly, maxHistory boundary validation (≤0 or > MaxHistoryLimit) is untested.

If these are already covered in WatchToolTests.cs or another file outside this cohort, feel free to disregard. Otherwise, a simple test would close the gap:

💚 Suggested test for duplicate ID rejection
+        /// <summary>
+        /// Verifies registering a duplicate ID is rejected.
+        /// </summary>
+        [Test]
+        public void Register_DuplicateId_ReturnsFailure()
+        {
+            FakeWatchEditorStateProvider stateProvider = new(10, true, true);
+            WatchExpressionRegistry registry = new(stateProvider);
+            registry.Register("speed", "speed", new SequenceWatchExpressionEvaluator(1), 20);
+
+            WatchRegistrationResult result = registry.Register(
+                "speed", "speed", new SequenceWatchExpressionEvaluator(2), 20);
+
+            Assert.That(result.Success, Is.False);
+            Assert.That(result.ErrorMessage, Does.Contain("already registered"));
+        }
🤖 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/WatchExpressionRegistryTests.cs` around lines 19 - 35,
Extend the tests for WatchExpressionRegistry.Register to cover duplicate ID
rejection, asserting the second registration fails with the expected message
while preserving the original registration. If not already covered elsewhere,
also add boundary tests verifying maxHistory values at or below zero and above
MaxHistoryLimit are rejected.
Assets/Tests/Editor/WatchToolTests.cs (1)

14-71: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a happy-path test for EnableAsync.

Current tests only cover validation failures and the empty GetValues case. A test that successfully compiles/registers a watch (e.g. Id="speed", Expression="1 + 2") and asserts on WatchResponse.Watches would also exercise the RegisterGetEntries().Single() path flagged in WatchTools.cs.

🤖 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/WatchToolTests.cs` around lines 14 - 71, Add a happy-path
test alongside the existing EnableAsync tests that calls
WatchUseCase.EnableAsync with a valid Id and expression, asserts the completed
response is successful, and verifies WatchResponse.Watches contains the
registered watch details. This should exercise the successful
Register-to-GetEntries().Single() flow in WatchTools.
cli/dispatcher/internal/dispatcher/help_test.go (1)

276-292: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extend help coverage to clear-watch and get-watch-values.

Only enable-watch --help is asserted here. Mirroring this test for clear-watch (--id, --all) and get-watch-values (--id) would close the coverage gap for the other two new default watch tools.

🤖 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 `@cli/dispatcher/internal/dispatcher/help_test.go` around lines 276 - 292, The
help coverage currently only validates enable-watch; extend
TestCommandHelpUsesWatchToolSchemaForDefaultWatchCommands to also invoke
clear-watch and get-watch-values, asserting successful handling and that
clear-watch exposes --id and --all while get-watch-values exposes --id.
Assets/Tests/Editor/WatchResponseContractTests.cs (1)

105-116: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Handle JTokenType.Date in NormalizeScalarType to avoid imprecise type normalization.

JObject.Parse with default settings parses ISO 8601 strings (like EvaluatedAtUtc) as JTokenType.Date, which falls through to the "unknown" default. Both sides currently match because both are parsed identically, but the normalization is semantically incorrect for date-valued fields. Adding JTokenType.Date => "string" makes the intent explicit.

♻️ Proposed fix
             JTokenType.Boolean => "boolean",
             JTokenType.Float => "number",
             JTokenType.Integer => "number",
+            JTokenType.Date => "string",
             JTokenType.Null => "null",
             JTokenType.String => "string",
             _ => "unknown"
🤖 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/WatchResponseContractTests.cs` around lines 105 - 116,
Update NormalizeScalarType to explicitly map JTokenType.Date to "string",
preserving the existing mappings and unknown fallback for all other token types.
🤖 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 `@Assets/Tests/Editor/WatchResponseContractTests.cs`:
- Around line 57-58: Update the test response fixture in
WatchResponseContractTests so CompilationErrors contains at least one populated
WatchCompilationErrorResponse instance. Set its fields using the actual
WatchCompilationErrorResponse type definition, ensuring the shared contract
comparison validates the nested entry shape.

---

Nitpick comments:
In `@Assets/Tests/Editor/WatchExpressionRegistryTests.cs`:
- Around line 19-35: Extend the tests for WatchExpressionRegistry.Register to
cover duplicate ID rejection, asserting the second registration fails with the
expected message while preserving the original registration. If not already
covered elsewhere, also add boundary tests verifying maxHistory values at or
below zero and above MaxHistoryLimit are rejected.

In `@Assets/Tests/Editor/WatchResponseContractTests.cs`:
- Around line 105-116: Update NormalizeScalarType to explicitly map
JTokenType.Date to "string", preserving the existing mappings and unknown
fallback for all other token types.

In `@Assets/Tests/Editor/WatchToolTests.cs`:
- Around line 14-71: Add a happy-path test alongside the existing EnableAsync
tests that calls WatchUseCase.EnableAsync with a valid Id and expression,
asserts the completed response is successful, and verifies WatchResponse.Watches
contains the registered watch details. This should exercise the successful
Register-to-GetEntries().Single() flow in WatchTools.

In `@cli/dispatcher/internal/dispatcher/help_test.go`:
- Around line 276-292: The help coverage currently only validates enable-watch;
extend TestCommandHelpUsesWatchToolSchemaForDefaultWatchCommands to also invoke
clear-watch and get-watch-values, asserting successful handling and that
clear-watch exposes --id and --all while get-watch-values exposes --id.
🪄 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: e4d8266f-37d7-427d-b09e-ee98e66ff39e

📥 Commits

Reviewing files that changed from the base of the PR and between 0b5e72e and ec19976.

⛔ Files ignored due to path filters (24)
  • Assets/Tests/Editor/UnityCLILoop.Tests.Editor.asmdef is excluded by none and included by none
  • Assets/Tests/Editor/WatchExpressionCompilerTests.cs.meta is excluded by none and included by none
  • Assets/Tests/Editor/WatchExpressionEvaluationTests.cs.meta is excluded by none and included by none
  • Assets/Tests/Editor/WatchExpressionRegistryTests.cs.meta is excluded by none and included by none
  • Assets/Tests/Editor/WatchResponseContractTests.cs.meta is excluded by none and included by none
  • Assets/Tests/Editor/WatchToolTests.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/UnityCLILoop.FirstPartyTools.Editor.asmdef is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/AssemblyInfo.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/CompiledWatchExpressionEvaluator.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/IWatchEditorStateProvider.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/IWatchExpressionEvaluator.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/UnityCLILoop.FirstPartyTools.Watch.Editor.asmdef is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/UnityCLILoop.FirstPartyTools.Watch.Editor.asmdef.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/UnityWatchEditorStateProvider.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/WatchCompilationResult.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/WatchEvaluationResult.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/WatchExpressionCompiler.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/WatchExpressionEntry.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/WatchExpressionHistoryEntry.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/WatchExpressionRegistry.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/WatchExpressionStepMonitor.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/WatchRegistrationResult.cs.meta is excluded by none and included by none
  • Packages/src/Editor/FirstPartyTools/Watch/WatchTools.cs.meta is excluded by none and included by none
📒 Files selected for processing (29)
  • .agents/skills/uloop-pause-point/SKILL.md
  • .claude/skills/uloop-pause-point/SKILL.md
  • Assets/Tests/Editor/WatchExpressionCompilerTests.cs
  • Assets/Tests/Editor/WatchExpressionEvaluationTests.cs
  • Assets/Tests/Editor/WatchExpressionRegistryTests.cs
  • Assets/Tests/Editor/WatchResponseContractTests.cs
  • Assets/Tests/Editor/WatchToolTests.cs
  • Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md
  • Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/AssemblyInfo.cs
  • Packages/src/Editor/FirstPartyTools/Watch/AssemblyInfo.cs
  • Packages/src/Editor/FirstPartyTools/Watch/CompiledWatchExpressionEvaluator.cs
  • Packages/src/Editor/FirstPartyTools/Watch/IWatchEditorStateProvider.cs
  • Packages/src/Editor/FirstPartyTools/Watch/IWatchExpressionEvaluator.cs
  • Packages/src/Editor/FirstPartyTools/Watch/UnityWatchEditorStateProvider.cs
  • Packages/src/Editor/FirstPartyTools/Watch/WatchCompilationResult.cs
  • Packages/src/Editor/FirstPartyTools/Watch/WatchEvaluationResult.cs
  • Packages/src/Editor/FirstPartyTools/Watch/WatchExpressionCompiler.cs
  • Packages/src/Editor/FirstPartyTools/Watch/WatchExpressionEntry.cs
  • Packages/src/Editor/FirstPartyTools/Watch/WatchExpressionHistoryEntry.cs
  • Packages/src/Editor/FirstPartyTools/Watch/WatchExpressionRegistry.cs
  • Packages/src/Editor/FirstPartyTools/Watch/WatchExpressionStepMonitor.cs
  • Packages/src/Editor/FirstPartyTools/Watch/WatchRegistrationResult.cs
  • Packages/src/Editor/FirstPartyTools/Watch/WatchTools.cs
  • Packages/src/Editor/ToolContracts/UnityCliLoopConstants.cs
  • cli/common/tools/default-tools.json
  • cli/dispatcher/internal/dispatcher/help_test.go
  • cli/project-runner/internal/projectrunner/watch_response_contract_test.go
  • cli/project-runner/internal/projectrunner/watch_types.go
  • tests/contracts/watch_response_contract.json

Comment thread Assets/Tests/Editor/WatchResponseContractTests.cs Outdated
…rage

An empty CompilationErrors array left WatchCompilationErrorResponse unverified on both C# and Go contract tests. Use the real Line/Column/Message/ErrorCode fields so the shared fixture exercises the nested shape.

Co-authored-by: Cursor <cursoragent@cursor.com>
@hatayama hatayama merged commit 875835f into v3-beta Jul 12, 2026
10 checks passed
@hatayama hatayama deleted the feat/pause-point-watch branch July 12, 2026 12:40
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