Skip to content

Harden transcript recording/replay system #619

@paddymul

Description

@paddymul

Summary

The transcript recording system (window._buckarooTranscript, record_transcript trait, PinnedRowsTranscriptReplayer story) has gaps that limit its reliability as a testing foundation. This issue tracks hardening it.

Current state

Recording lives in packages/js/widget.tsx (setupTranscriptRecording()). Replay lives in PinnedRowsTranscriptReplayer.stories.tsx.

What's captured:

Captured Event Content
Yes custom_msg message type, key, buffers_len (count only)
Yes infinite_resp_parsed key, parsed parquet rows as JSON, row/total counts
Yes all_stats_update summary stats from df_data_dict change

What's NOT captured:

Missing Event Impact
Trait syncs df_display_args, buckaroo_state, column_config Rendering config changes not recorded
JS→Python requests infinite_request (scroll-triggered data fetches) Can't verify correct data ranges requested
Ghost events dfi_cols_fields, dfi_grid_ready, dfi_first_data_rendered, dfi_pinned_*, ds_request, ds_success, user_operation Defined in TS type but never pushed — misleading

The five gaps

1. Missing trait sync events

Only change:df_data_dict is listened to. df_display_args (column config, displayers, formatting), buckaroo_state (widget mode, active tab), and column_config (per-column styling) are not recorded. If these change during a session, replay can't reproduce the state.

2. Missing request side (JS→Python)

model.send({ type: 'infinite_request', payload_args }) in BuckarooWidgetInfinite.tsx:58 is never captured. Transcripts have responses without requests. A bug where the widget requests wrong scroll offsets wouldn't be caught.

3. Ghost event types

Six event types are defined in the TranscriptEvent union type but never actually pushed to the transcript array. Either wire them up or remove them from the type.

4. Replay path differs from real path

The replayer sets React state directly (setCfg(), setSummary(), setRawRows()). The real widget receives data through the model/comm channel → DFViewerInfinite datasource → AG-Grid getRows(). A rendering bug in the real datasource path wouldn't be caught by replay.

5. No schema version

Transcripts have no version field. Format changes silently produce wrong replays with no error signal.

Proposed hardening (tiered)

Tier 1 — Low effort, high value

  • Schema version — add schema_version: 1 to every event. Replayer checks on load, throws on mismatch.
  • Record trait syncs — add model.on("change:df_display_args", ...), change:buckaroo_state, change:column_config listeners in setupTranscriptRecording().
  • Clean up ghost events — either implement the dfi_* / ds_* / user_operation recording or remove them from the type.

Tier 2 — Medium effort

  • Capture JS→Python requests — wrap model.send() to record outgoing messages as outgoing_request events.
  • Replay through real data path — replay should feed data through the model/comm path, not set React state directly, so the datasource → AG-Grid pipeline is exercised.

Tier 3 — Design needed

  • Transcript completeness assertion — validate that a recorded transcript contains minimum expected events before trusting it.
  • Round-trip validation — record a live transcript, replay it, record the replay output, compare. Catches replay path divergence.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions