fix(replay): align frames with interactions and navigation - #26
Merged
Conversation
Add a widget-backed matrix that asserts encoded RenderRepaintBoundary pixels for named/anonymous sheets, draggable extent, dismiss paths, and nested Navigators. Mark navigator/route-instance ownership gaps for U9 without changing capture behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
Assign session-local opaque navigator and route-instance IDs from observer callbacks, scope route-capture supersession per Navigator, and emit ownership fields on route_change events and diagnostics. Flip U8 GAP assertions for stacked, nested, and repeated modals. Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce observer-time single-use pending-interaction claims so only a verified tap can set navigationOrigin=interaction with causeEventId. Automatic, cancelled, swipe, multi-touch, and post-settlement routes emit automatic_or_unknown without fabricating tap causality. Co-authored-by: Cursor <cursoragent@cursor.com>
Prove timer/service/auth-style Navigator transitions emit coherent route evidence with automatic_or_unknown origin and never fabricate tap causality after settlement or during stack cleanup. Co-authored-by: Cursor <cursoragent@cursor.com>
Define an immutable captureCoordinate payload with boundary-local, normalized, and raster fields while preserving legacy global x/y. Freeze the golden projection contract for dashboard consumers. Co-authored-by: Cursor <cursoragent@cursor.com>
Convert global logical pointer positions through the active capture boundary at pointer-down, bind raster dimensions from the compatible before-frame, and emit captureCoordinate (or an unavailable reason) on every tap without mutating legacy x/y fields. Co-authored-by: Cursor <cursoragent@cursor.com>
Bump tugboat to 0.4.11, document nested-observer and captureCoordinate contracts, extend the release compatibility matrix for modal ownership, navigation origin, and coordinate projection, and update the production acceptance entry gate. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
This PR tightens Tugboat replay correctness by making route/frame ownership and navigation causality explicit (including nested navigators), and by introducing a versioned, frame-owned tap coordinate transform that degrades to explicit unavailability instead of producing plausible-but-wrong projections.
Changes:
- Add navigator/route-instance ownership metadata plus
navigationOrigin/causeEventIdcausality fencing so only tap-caused navigations can be attributed to taps. - Introduce
TugboatCaptureCoordinate(captureCoordinatenested under tap data) with boundary-geometry provenance and transform-generation mismatch handling. - Expand replay correctness test coverage (modal visual matrix, navigation matrices, coordinate transform contracts) and bump SDK version to
0.4.12with updated docs/changelog.
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/tugboat/test/semantics_flags_compat_test.dart | Formatting-only adjustments to an existing compatibility test. |
| packages/tugboat/test/replay/tap_coordinate_transform_test.dart | New widget tests validating tap-to-frame coordinate projection and unavailability reasons. |
| packages/tugboat/test/replay/replay_programmatic_navigation_matrix_test.dart | New matrix tests ensuring automatic navigation remains independent of taps. |
| packages/tugboat/test/replay/replay_navigation_race_matrix_test.dart | Updates race-matrix expectations to reflect the new causality fence/settle observations. |
| packages/tugboat/test/replay/navigation_origin_contract_test.dart | New contract tests for navigationOrigin and causeEventId serialization behavior. |
| packages/tugboat/test/replay/modal_capture_visual_test.dart | New real-pixel visual characterization tests for modal/sheet ownership and capture. |
| packages/tugboat/test/replay_coherence_characterization_test.dart | Updates characterization expectations for tap/route independence and successor behavior. |
| packages/tugboat/test/outbox/tugboat_outbox_test.dart | Formatting-only change in outbox test setup. |
| packages/tugboat/test/outbox/tugboat_outbox_recovery_test.dart | Formatting-only change in outbox recovery test. |
| packages/tugboat/test/integration/release_compatibility_matrix_test.dart | Adds integration assertions for coordinate contract + masking + modal ownership composition. |
| packages/tugboat/test/coordinate_space_test.dart | New unit tests for coordinate transform round-tripping and consumer contract “golden” JSON. |
| packages/tugboat/README.md | Version bump + guidance for nested navigator observer installation. |
| packages/tugboat/pubspec.yaml | Version bump to 0.4.12. |
| packages/tugboat/lib/tugboat.dart | Expose the new coordinate-space API surface. |
| packages/tugboat/lib/src/tugboat.dart | Add createNavigatorObserver() and update observer emission to include navigator/route context. |
| packages/tugboat/lib/src/sdk_version.dart | Keep SDK version constant in sync with pubspec (0.4.12). |
| packages/tugboat/lib/src/screenshot_capturer.dart | Persist capture-boundary logical rect into capture results for provenance/transform checks. |
| packages/tugboat/lib/src/outbox/outbox.dart | Minor formatting; no functional changes. |
| packages/tugboat/lib/src/outbox/outbox_sink.dart | Minor formatting; no functional changes. |
| packages/tugboat/lib/src/lifecycle.dart | Minor formatting; no functional changes. |
| packages/tugboat/lib/src/coordinate_space.dart | New versioned coordinate transform model + JSON + projection logic. |
| packages/tugboat/lib/src/controller.dart | Core logic: route/tap causality claims, navigator/route-instance ownership, boundary transform generation, tap coordinate sampling. |
| packages/tugboat/lib/src/collector_mapper.dart | Minor formatting. |
| packages/tugboat/CHANGELOG.md | Release notes for 0.4.12 covering ownership/causality/coordinates. |
| docs/README.md | Version bump to 0.4.12. |
| docs/integration/production-replay-acceptance.md | Strengthen acceptance prerequisites to include the new test suites + dashboard support for captureCoordinate. |
Comments suppressed due to low confidence (1)
packages/tugboat/lib/src/controller.dart:3644
- Missing a terminating statement after the
replacecase causes the switch to fall through intopop/removehandling, which can incorrectly pop the stack and compute the wrong destination/instance IDs. Add abreak;after resolvingfromRouteInstanceIdfor replace.
fromRouteInstanceId =
_surfaces.peekRouteId(departingRoute) ?? _currentRouteInstanceId;
case _RouteNavigationKind.pop:
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+3636
to
+3637
| fromRouteInstanceId = _currentRouteInstanceId; | ||
| case _RouteNavigationKind.replace: |
Comment on lines
+212
to
+215
| if (localX < 0 || | ||
| localY < 0 || | ||
| localX > boundaryWidth || | ||
| localY > boundaryHeight) { |
Comment on lines
+156
to
+160
| // Prefer the root navigator for pointer/anchor context; nested observers | ||
| // still report their own NavigatorState into route ownership. | ||
| if (identical(this, TugboatReplay.navigatorObserver)) { | ||
| TugboatReplay.controller?.navigatorContext = navigator?.context; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replay events now stay attached to the screen that actually produced them. Taps can only inherit a navigation frame when that exact interaction caused the transition; automatic redirects remain independent, while Flutter navigation callbacks delivered immediately after pointer-up still retain the tap claim.
The SDK also projects taps through the geometry owned by the referenced frame. A resize or boundary change therefore produces an explicit unavailable coordinate instead of a plausible-looking tap at the wrong place.
This stack also adds the runtime coverage needed for the first replay-correctness release:
Validation
dart format --output=none --set-exit-if-changed .— 84 files, no changesflutter analyze— no issuesflutter test --concurrency=1 --reporter compact— all 264 Tugboat tests passedThe remaining acceptance gate is a manual Blend production replay after publishing the canary; local tests do not claim production ingestion quality.
Related
Related: #14
Related: #13