Skip to content

fix(replay): align frames with interactions and navigation - #26

Merged
Chinmay-KB merged 14 commits into
mainfrom
feat/replay-coherence-u8-u15
Jul 29, 2026
Merged

fix(replay): align frames with interactions and navigation#26
Chinmay-KB merged 14 commits into
mainfrom
feat/replay-coherence-u8-u15

Conversation

@Chinmay-KB

@Chinmay-KB Chinmay-KB commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

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:

  • real-pixel dialog, bottom-sheet, nested-navigation, and dismissal matrices
  • explicit navigator, route-instance, navigation-origin, and causal-link metadata
  • versioned capture-coordinate payloads and frame provenance
  • the 0.4.12 canary version, changelog, integration guidance, and production acceptance checklist

Validation

  • dart format --output=none --set-exit-if-changed . — 84 files, no changes
  • flutter analyze — no issues
  • flutter test --concurrency=1 --reporter compact — all 264 Tugboat tests passed

The 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

Chinmay-KB and others added 14 commits July 26, 2026 23:14
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>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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/causeEventId causality fencing so only tap-caused navigations can be attributed to taps.
  • Introduce TugboatCaptureCoordinate (captureCoordinate nested 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.12 with 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 replace case causes the switch to fall through into pop/remove handling, which can incorrectly pop the stack and compute the wrong destination/instance IDs. Add a break; after resolving fromRouteInstanceId for 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;
}
@Chinmay-KB
Chinmay-KB merged commit 427f3ef into main Jul 29, 2026
2 checks passed
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.

2 participants