Skip to content

Reduce Noise in Run Analytics Events#1516

Merged
DNR500 merged 10 commits into
mainfrom
1543-reduce-run-event-noise
Jul 2, 2026
Merged

Reduce Noise in Run Analytics Events#1516
DNR500 merged 10 commits into
mainfrom
1543-reduce-run-event-noise

Conversation

@DNR500

@DNR500 DNR500 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Issue 1543.

Investigation into duplicate Project - Ran code and Project - Run completed analytics events. I was unable to reproduce the kind of event timings reported in the ticket through normal UI interaction alone. The closest match came from running the following in the browser console on a project page:

let n = 0;
const burst = setInterval(() => {
  document.querySelector("editor-wc")?.runCode();
  if (++n >= 15) clearInterval(burst);
}, 50);

This PR takes a two-pronged approach: fix several edge cases in how run events are dispatched, and add gating so analytics only records runs that are likely to be meaningful. Should provide some fortification against abuse of the run button and over running the code.

Bug fixes

Briefly — see commit messages for detail:

  • Fire runCompleted once per run cycle, not again when error state updates after the run ends.
  • Remove leaked window.message listeners in HtmlRunner that could cause duplicate handling on re-render.
  • Keep runStarted stable across web-component remounts during an active run.

Run event dispatch gating

Commits have been used break this down a little

Code-change snapshot (editable projects)

Run analytics events (editor-runStarted / editor-runCompleted) are only dispatched when the project's code differs from the last counted run. Repeated runs of unchanged code are suppressed.

This assumes that a user hammering the run button without changing code does not tell us anything useful for analytics — as these do not reflect a new attempt or progress.

Read-only mode (e.g. a teacher viewing student work) bypasses the snapshot check so deliberate re-runs of the same code are still tracked.

Debounce (250ms quiet period)

A trailing debounce is also applied so bursts of run attempts collapse into a single counted dispatch after 250ms of quiet. This reduces noise on project views where code cannot be edited, or where a code-change snapshot is not practical (e.g. Scratch green-flag runs via ScratchContainer).

Together, snapshot gating handles the common student case; debounce handles read-only and Scratch paths where repeated runs without an easy code-diff are still possible.

DNR500 added 5 commits July 1, 2026 17:39
Run completed was dispatched whenever error, errorDetails, or
friendlyError changed after a run had already ended, so a single
Python run could produce multiple Project - Run completed events.
Dispatch completion only on the codeRunTriggered true-to-false
transition, matching how run started is tracked.
Preview iframe messages were handled by anonymous window listeners
that were never removed on unmount, and showModal registered more
listeners on each external-link error. Remounts could stack handlers
so one RELOAD message triggered multiple code runs and duplicate
Ran code events.
WebComponentProject tracked the previous codeRunTriggered value in a
ref that reset when the component remounted while a run was still
active, causing a second Project - Ran code event. Persist run edge
tracking in module state, reset it when the web component disconnects
or the project identifier changes.
Only dispatch run analytics events (editor-runStarted / editor-runCompleted)
when a project's code differs from the last counted run, so repeated runs of
unchanged code no longer generate noise.
- Add buildProjectCodeSnapshot to produce a deterministic, order-independent
  snapshot string from a project's components.
- Add runEventCodeSnapshot to gate each run cycle: suppress runStarted when the
  snapshot is unchanged, reset on project change, and pair runCompleted to the
  counted start via a per-cycle flag.
- Reset snapshot state alongside existing run-event tracking on web-component
  disconnect so remounts stay safe.
- Bypass the snapshot check in read-only mode (teachers viewing student work)
  so deliberate re-runs of the same code are still tracked.
Add trailing debounce so bursts of run attempts collapse into a single
counted analytics dispatch after 250ms of quiet, while deliberate runs
still emit once the debounce window has passed.

- Replace synchronous run-start gating with scheduleRunEventCycle, which
  resets a debounce timer on each run start and evaluates the snapshot
  check when the timer fires.
- Pair runCompleted with the debounced runStarted, including when the run
  finishes before the debounce fires.
- Apply the same debounce path to Scratch run-started events in
  ScratchContainer.
- Reset debounce state on project identifier change and web-component
  disconnect.
Copilot AI review requested due to automatic review settings July 1, 2026 19:40
@DNR500 DNR500 temporarily deployed to previews/1516/merge July 1, 2026 19:40 — with GitHub Actions Inactive
Comment thread src/components/WebComponentProject/WebComponentProject.jsx
Comment thread src/components/Editor/Project/ScratchContainer.jsx Outdated

Copilot AI 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.

Pull request overview

Reduces duplicate/noisy run analytics events in the web component editor by tightening run lifecycle dispatch (start/completed) and adding gating (code snapshot + debounce) to suppress repeated “meaningless” runs.

Changes:

  • Adds a debounced run-event cycle with optional per-run code snapshot gating to suppress repeated runs of unchanged code.
  • Makes runStarted/runCompleted emission more robust across rerenders/remounts and post-run error-state updates.
  • Fixes duplicate handling by ensuring HtmlRunner cleans up window.message listeners; adds/updates unit tests to cover new behavior.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/web-component.js Resets module-level run-event tracking state when the web component disconnects.
src/components/WebComponentProject/WebComponentProject.jsx Routes runStarted/runCompleted dispatch through new debounced + snapshot-gated run-cycle helpers.
src/components/WebComponentProject/WebComponentProject.test.js Updates/extends tests to account for debounced run-event dispatch and snapshot gating.
src/components/WebComponentProject/runEventTrackingState.js Introduces module-level tracking for previous codeRunTriggered and project sync/reset behavior.
src/components/WebComponentProject/runEventTrackingState.test.js Adds unit coverage for the new tracking-state module behavior.
src/components/WebComponentProject/runEventCodeSnapshot.js Implements debounce + “code changed since last counted run” snapshot gating for run events.
src/components/WebComponentProject/runEventCodeSnapshot.test.js Adds unit tests for debounce/snapshot gating behaviors and edge cases.
src/components/WebComponentProject/buildProjectCodeSnapshot.js Adds stable snapshot builder for project components to support gating.
src/components/WebComponentProject/buildProjectCodeSnapshot.test.js Adds unit tests ensuring snapshot stability and change detection.
src/components/Editor/Runners/HtmlRunner/HtmlRunner.jsx Reworks iframe message handling to avoid leaked listeners and duplicate handling across rerenders/unmounts.
src/components/Editor/Runners/HtmlRunner/HtmlRunner.test.js Adds tests asserting messages aren’t handled after unmount and aren’t duplicated after remount.
src/components/Editor/Project/ScratchContainer.jsx Debounces scratch runStarted analytics dispatch via the shared run-event cycle helper.
src/components/Editor/Project/ScratchContainer.test.js Updates tests to account for debounced scratch runStarted dispatch (including burst collapsing).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/components/WebComponentProject/WebComponentProject.test.js
Comment thread src/components/WebComponentProject/WebComponentProject.jsx Outdated
@DNR500 DNR500 temporarily deployed to previews/1516/merge July 1, 2026 19:53 — with GitHub Actions Inactive

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit be17ed8. Configure here.

Comment thread src/components/Editor/Project/ScratchContainer.jsx
@DNR500 DNR500 temporarily deployed to previews/1516/merge July 1, 2026 20:00 — with GitHub Actions Inactive
@DNR500 DNR500 merged commit 4871ed4 into main Jul 2, 2026
8 checks passed
@DNR500 DNR500 deleted the 1543-reduce-run-event-noise branch July 2, 2026 15:16
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.

3 participants