Skip to content

Commit 53fda60

Browse files
Chinmay KabiChinmay Kabi
authored andcommitted
Remove legacy interaction formats
1 parent 48b4988 commit 53fda60

35 files changed

Lines changed: 824 additions & 4976 deletions

docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ should be verified in their own repositories.
2929
## Current compatibility
3030

3131
- package version: `0.8.0`;
32-
- session JSON schema: `9`;
32+
- session JSON schema: `10`;
3333
- fingerprint schema: `6`;
3434
- minimum Dart SDK: `3.9.2`;
3535
- minimum Flutter SDK: `3.35.0`.

docs/design/capture-and-fingerprint.md

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ current controller and keeps future calls to `wrapApp` inert. Runtime
6363
requiring a host rebuild. `deactivate()` tears capture down through the same
6464
gate. Pause/hidden flush pending delivery; detach ends the session once.
6565

66-
Identity fields (session schema **v10**; compatibility readers accept v6–v10):
66+
Identity fields (session schema **v10** only):
6767

6868
- `activationRequestId` — host request correlation
6969
- `captureSessionId` — SDK-emitted session (`session.id`)
@@ -76,8 +76,7 @@ emits exact build and fingerprint-schema provenance only.
7676
## Session and event model
7777

7878
The controller owns one bounded, in-memory `TugboatSession`. Serialized session
79-
JSON is schema version `10`. Compatibility readers accept schema versions
80-
`6` through `10`. Schema v9 stopped writing `controlValue`,
79+
JSON is schema version `10` only. Schema v9 stopped writing `controlValue`,
8180
`controlValueTransition`, or `semanticAnnotation` in event `data`; those fields
8281
are optional historic data in older sessions only. Schema v10 removes
8382
serialized state identity and adds the `interaction` frame trigger.
@@ -99,8 +98,6 @@ The event stream currently includes:
9998
- lifecycle: `session_start`, `session_end`;
10099
- canonical gestures: `interaction` (`gesture`: `tap`, `swipe`, `scroll`,
101100
`cancelled`) with nested `payload` facts;
102-
- deprecated legacy gesture peers (`dualWrite` / `legacyOnly` only): `tap`,
103-
`tap_settled`, `swipe`, `tap_outside_tree`;
104101
- navigation: `route_change`;
105102
- exploration control: `scene_inventory`, `action_window_set`,
106103
`action_window_cleared`;
@@ -119,11 +116,10 @@ route dictionary.
119116

120117
`wrapApp` starts a session only after its repaint boundary has a non-zero
121118
viewport. The session begins with `session_start` and an initial capture
122-
request. Pointer-down records `tap` plus a compatible pre-interaction frame,
123-
then pointer-up either records a swipe or creates one `tap_settled` outcome.
124-
The settled event refers to the initial tap through `relatedEventId` and is
125-
intended to attach an after-frame only when that frame's provenance matches the
126-
observed route epoch. A capture that is unavailable, cancelled, superseded, or
119+
request. Pointer-down freezes a compatible pre-interaction frame. Pointer-up
120+
publishes one canonical `interaction` after gesture classification. Its
121+
after-frame attaches only when the frame provenance matches the observed route
122+
epoch. A capture that is unavailable, cancelled, superseded, or
127123
timed out is represented by bounded capture/attachment diagnostics instead of
128124
borrowing the latest frame from another screen.
129125

@@ -338,8 +334,9 @@ The package test suite covers deterministic fingerprints, list-length and
338334
scroll stability, dynamic-label exclusion, static list discriminators, tag
339335
transparency, route separation, modal/visibility filtering, generated widget
340336
names, actionable `InkWell` paths, dormant activation without rebuild,
341-
screenshot mask defaults, route payloads, scroll/swipe attribution, schema-v7
342-
JSON with v6 read compatibility, semantic modes, sink factories/mailboxes,
337+
screenshot mask defaults, route payloads, scroll/swipe attribution, schema-v10
338+
JSON round trips and rejection of unsupported schema versions, semantic modes,
339+
sink factories/mailboxes,
343340
outbox restart recovery, health diagnostics, lifecycle ordering, retry bounds,
344341
and stale session/frame protection.
345342

packages/tugboat/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## Unreleased
2+
3+
### Breaking changes
4+
5+
- The SDK now publishes only schema-v2 canonical `interaction` gesture events.
6+
Removed legacy gesture projections, publication modes, session aliases, and
7+
compatibility constructors.
8+
19
## 0.8.0
210

311
### Changed

packages/tugboat/README.md

Lines changed: 16 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ checkpoints around meaningful interactions, compact structural anchors, route
55
transitions, scrolling evidence, and optional viewport semantic maps. Capture
66
can be sent to the local exploration WebSocket, the HTTP collector, or both.
77

8-
The current package version is `0.8.0`. Session JSON writers emit schema
9-
version `10`; compatibility readers should accept versions `6` through
10-
`10`. Structural fingerprints use fingerprint schema version `6`.
8+
The current package version is `0.8.0`. Session JSON writers and readers use
9+
schema version `10` only. Structural fingerprints use fingerprint schema
10+
version `6`.
1111

1212
## 0.8.0 raw-event compatibility
1313

@@ -182,7 +182,7 @@ Navigator/repaint-boundary contract.
182182
Each visible route change creates a route epoch and waits for the transition
183183
plus the configured settle delay before taking the destination capture. A newer
184184
visible route supersedes an older pending capture. Consequently, a route event
185-
and a related `tap_settled` event are intended to reference a frame compatible
185+
and a related canonical `interaction` are intended to reference a frame compatible
186186
with that route epoch, or report bounded degraded/capture diagnostics rather
187187
than attach an origin-route frame merely because it was the latest frame.
188188

@@ -191,30 +191,12 @@ Production acceptance for #13/#14 remains open: rapid or nested modal chains
191191
and programmatic/automatic navigation can still be absent or degraded. Treat
192192
those cases as an SDK capture gap, not as coherent replay evidence.
193193

194-
### Deferred taps and interaction claims
194+
### Interaction claims
195195

196-
`tap` is sampled at pointer-down but emitted only after gesture classification
197-
(or when a claimed `route_change` must publish a cause). Consumers must read:
198-
199-
| Field | Meaning |
200-
| --- | --- |
201-
| `data.replayRole` | `interaction` (real user tap) or `causal_only` (cause id for a route; not a replayable action) |
202-
| `data.gestureFinal` | `tap`, `swipe`, `cancelled`, `superseded`, `session_end`, or `unresolved` |
203-
| `data.sampledAtMs` | Pointer-down sample time when the event was published later |
204-
| `data.captureCoordinate` | Boundary-local / normalized / raster transform for the before-frame |
205-
| `route_change.data.navigationOrigin` | `interaction` or `automatic_or_unknown` |
206-
| `route_change.data.causeEventId` | Claimed tap id when origin is `interaction` |
207-
| `route_change.data.interactionAttribution` | Always `same_turn` when claimed |
208-
| `swipe` (legacy dual-write) `.data.invalidatesRelatedTap` | Boolean `true` when a related causal tap should be ignored for playback |
209-
| `tap_gesture_resolved` | Promotes `relatedEventId` from `causal_only` → interaction (`promotesRelatedTap`) |
210-
211-
Released pointer-up claims attribute a route only through the pointer-up turn
212-
(sync `onTap` → `Navigator.push`). Timer/auth redirects after that turn stay
213-
`automatic_or_unknown`. Backgrounding and `session_end` drop pending claims
214-
without minting orphan `causal_only` taps for cancelled routes.
215-
216-
**Downstream contract:** Context Graph and PMKit CLI must not treat
217-
`replayRole: causal_only` taps as real actions or step openers.
196+
Pointer-down freezes interaction origin data. Pointer-up classifies the gesture
197+
and publishes one canonical `interaction`. A claimed `route_change` uses that
198+
interaction ID as `causeEventId`. Released pointer-up claims apply only through
199+
the pointer-up turn. Timer or auth redirects stay `automatic_or_unknown`.
218200

219201
## Capture profiles and runtime state
220202

@@ -250,9 +232,8 @@ Identity contract:
250232
- `explorationRunId` — exploration control-plane ID from config
251233
- `traitsId` — collector-issued traits dictionary id after `setTraits` / session responses
252234

253-
`TugboatReplay.activeSessionId` remains as a deprecated alias for
254-
`activationRequestId`. Inspect `TugboatReplay.health` for sink/outbox/screenshot
255-
budget pressure without reading protected content.
235+
Inspect `TugboatReplay.health` for sink, outbox, and screenshot-budget pressure
236+
without reading protected content.
256237

257238
### User traits and user id
258239

@@ -300,7 +281,6 @@ Call `TugboatReplay.clearDurableOutbox()` on logout/consent revocation.
300281
| `profile` | `dormant` | capture cost and exploration-only behavior |
301282
| `settleDelay` | 1 second | delay before post-interaction and post-route capture |
302283
| `interactionClaimWindow` | 1,250 ms | released-tap window for delayed route/modal attribution; `Duration.zero` keeps microtask-only same-turn claims |
303-
| `interactionPublishMode` | `canonicalOnly` | how finalized gestures are published; new recordings emit one canonical `interaction`. `legacyOnly` and `dualWrite` are deprecated compatibility modes |
304284
| `maxFrames` | 500 | in-memory frame bound |
305285
| `maxEvents` | 5000 | in-memory event bound |
306286
| `scrollCaptureInterval` | 2 seconds | interval for scroll checkpoint capture |
@@ -321,46 +301,6 @@ Call `TugboatReplay.clearDurableOutbox()` on logout/consent revocation.
321301
| `outbox` | disabled | durable HTTP outbox configuration |
322302
| `screenshotBudget` | 60ms / 5s window | degraded-capture skip window / budget |
323303

324-
### Legacy gesture projection deprecation
325-
326-
New recordings default to `TugboatInteractionPublishMode.canonicalOnly`. One
327-
completed physical gesture produces one semantic `interaction` event containing
328-
its immutable origin, finalized gesture, result, attribution, and evidence IDs.
329-
The SDK no longer emits separate `tap`, `tap_settled`, or `swipe` rows unless an
330-
integration explicitly opts into a legacy mode.
331-
332-
`dualWrite` and `legacyOnly` remain available temporarily so older collectors,
333-
Context Graph revisions, dashboards, and replay fixtures can be migrated without
334-
making historical recordings unreadable:
335-
336-
- `canonicalOnly` — supported default for all new recordings;
337-
- `dualWrite` — deprecated migration override that adds legacy peers on
338-
`stream: legacy_projection` with `enrichmentCandidate: false`;
339-
- `legacyOnly` — deprecated emergency compatibility override for consumers that
340-
cannot yet read canonical `interaction` records.
341-
342-
Do not enable either legacy mode in a new application integration. Consumers
343-
must use `interaction` as the user action and treat route/frame records as
344-
linked evidence. Historical `tap` and `tap_settled` rows may still be read and
345-
correlated through `interactionId` / `relatedEventId`, but must not be counted as
346-
additional user actions.
347-
348-
The code marker `TODO(tugboat-legacy-projection-removal)` tracks final removal.
349-
Remove the legacy enum values and emission branches in a future breaking SDK
350-
release only after all of the following are true:
351-
352-
1. Supported Collector and Context Graph versions consume canonical
353-
`interaction` records and ignore legacy projections by default.
354-
2. Dashboard, insight, rage-tap, and replay queries no longer depend on
355-
`tap_settled` or legacy `swipe` rows.
356-
3. Production telemetry confirms that current SDK versions are recording
357-
canonical interactions successfully across representative tap, navigation,
358-
scroll, cancellation, and lifecycle cases.
359-
4. Retained dual-write fixtures remain available to test historical replay
360-
compatibility after the emitters are deleted.
361-
5. Release notes announce the removal and the SDK schema/breaking version is
362-
advanced deliberately.
363-
364304
### Resolver and exploration events
365305

366306
When exploration is active, the controller may emit:
@@ -371,10 +311,6 @@ When exploration is active, the controller may emit:
371311
| `viewport_semantic_map` | Bounded semantic node map (mode-dependent) |
372312
| `scroll_semantic_snapshot` | Semantic snapshot tied to scroll checkpoints |
373313
| `action_window_set` / `action_window_cleared` | CLI exploration action-window fencing |
374-
| `tap_outside_tree` | Pointer resolved no target; carries the same `replayRole` / `gestureFinal` as the paired tap when deferred |
375-
| `tap_gesture_resolved` | Promotes a prior `causal_only` tap (`promotesRelatedTap: true`, `relatedEventId`) after the gesture finalizes as a real tap |
376-
377-
Consumers that filter `replayRole: causal_only` must honor `tap_gesture_resolved` (or the patched in-memory tap) before suppressing genuine navigations.
378314

379315
## Privacy and payload boundary
380316

@@ -427,9 +363,6 @@ Emitted inferred event types currently include:
427363
- canonical: `interaction` (`stream: semantic`) — one finalized gesture
428364
(`tap`, `swipe`, `scroll`, or `cancelled`) with gesture-specific facts under
429365
`payload` (omitted for `cancelled`);
430-
- deprecated legacy gesture peers (emitted only when an integration explicitly
431-
selects `dualWrite` or `legacyOnly`): `tap`, `tap_settled`, `swipe`,
432-
`tap_outside_tree`, `tap_gesture_resolved`;
433366
- lifecycle: `session_start`, `session_identify`, `session_end`;
434367
- navigation evidence (`stream: evidence`): `route_change`;
435368
- diagnostics: `capture_diagnostic` (`stream: diagnostic`);
@@ -442,7 +375,7 @@ Default enrichment and insight selection should use inferred events:
442375
`stream: semantic` `interaction` records (`enrichmentCandidate: true` on
443376
collector payloads).
444377
Rage-tap style insights must count finalized `gesture=tap` interactions;
445-
exclude scrolls, swipes, cancellations, evidence, legacy projections, and
378+
exclude scrolls, swipes, cancellations, evidence, and
446379
diagnostics.
447380

448381
Frames can be triggered by initial startup, interactions, routes, lifecycle,
@@ -457,25 +390,17 @@ local-WebSocket suppression, paint-generation reuse, dHash reuse, or
457390
content-hash reuse. A fresh route capture can satisfy only the interaction that
458391
causally claimed it.
459392

460-
Pointer coordinates in event data (`x`, `y`, and swipe `startX`/`startY`) are
461-
Flutter global logical-pixel coordinates from the pointer event. The SDK
462-
converts a copy into its capture boundary's local space only for hit-testing and
463-
normalizing target/viewport-semantic bounds; stored event coordinates are not
464-
capture-boundary-normalized for replay playback. Do not interpret them as
465-
physical pixels or as coordinates relative to an individual widget. Fractional
466-
overlay drift is therefore still possible.
393+
Interaction payload coordinates use normalized capture-boundary space. Do not
394+
interpret them as physical pixels or as coordinates relative to a widget.
467395

468396
For a tap, origin context (target, `beforeFrame`,
469397
`captureCoordinate`, route/navigator identity) is frozen at pointer-down into
470398
an `InteractionTransaction`. After pointer-up, settlement waits for either the
471399
first eligible visible successor inside `interactionClaimWindow` (default
472400
1,250 ms) or the deadline. The canonical `interaction` event retains that
473401
frozen origin and attaches destination/result fields when a successor claims.
474-
When deprecated dual-write compatibility is explicitly enabled, legacy `tap` +
475-
`tap_settled` records link via `relatedEventId` / `interactionId`. They are not
476-
additional semantic actions. A missing attachment is explicit in
477-
`frameAttachment`/settle diagnostics rather than a fallback to an unrelated
478-
frame.
402+
A missing attachment remains unavailable. The SDK does not attach an unrelated
403+
frame as a fallback.
479404

480405
During local WebSocket exploration, connecting without an HTTP collector
481406
suppresses only non-interaction Flutter screenshot capture for UI-thread

packages/tugboat/lib/src/collector_config.dart

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,6 @@ class TugboatCollectorAppInfo {
88
required this.appId,
99
});
1010

11-
@Deprecated('Use TugboatCollectorAppInfo(appId: ...) instead.')
12-
const TugboatCollectorAppInfo.legacyPackageName({
13-
required this.name,
14-
required this.version,
15-
required this.buildNumber,
16-
required this.installationId,
17-
required String packageName,
18-
}) : appId = packageName;
19-
2011
final String name;
2112
final String version;
2213
final String buildNumber;

0 commit comments

Comments
 (0)