Skip to content

Fix video-seek, leak, and correctness issues found in codebase review - #106

Merged
veillette merged 1 commit into
mainfrom
claude/tracklab-codebase-review-cmijn0
Jul 25, 2026
Merged

Fix video-seek, leak, and correctness issues found in codebase review#106
veillette merged 1 commit into
mainfrom
claude/tracklab-codebase-review-cmijn0

Conversation

@veillette

Copy link
Copy Markdown
Collaborator

Functional bugs:

  • Clicking a data-table row did not seek the video. The only model -> video
    time sync was gated on the scrubbing flag, which is set only by the
    slider's pointer DragListener, so the row-click handler moved the model
    clock while the displayed frame stayed put.

  • Keyboard scrubbing had the same root cause: Scenery's Slider routes
    keyboard input through AccessibleValueHandler's startInput/endInput, not
    the startDrag/endDrag that set scrubbing.

    Both are fixed by centralizing the sync: VideoPlayerNode now links
    currentTimeProperty -> videoElement.currentTime, guarded by the scrubbing
    flag and a half-frame deadband so it does not fight timeupdate during
    playback. startInput/endInput are wired on the scrubber as well.

  • PlaybackControlsNode orphaned an HSlider at construction. The duration
    link fired synchronously before this.children was assigned, so
    replaceScrubber()'s indexOf() returned -1 and the new slider was dropped
    undisposed, permanently retaining listeners on currentTimeProperty
    (measured: +4 per construct/dispose cycle). Fixed by using lazyLink and by
    disposing an uninsertable replacement instead of dropping it.

  • The OpenCV worker leaked a cv.Mat on every template capture: the
    intermediate header from blurred.roi() was never deleted. WASM heap, so it
    is never reclaimed by the JS GC.

  • Changing the frame rate after digitizing left stored points labelled with
    indices from the old rate, so everything keyed on frame addressed the
    wrong point. TrackingModel.retimeTrackPoints() re-derives each index from
    the point's authoritative timestamp; frame collisions from a lowered rate
    keep the earlier point, matching addPointToTrack()'s first-wins policy.

Correctness and robustness:

  • TableRenderer.update() claimed to rebuild on colour/locale changes but
    only compared unit and track IDs, relying on DataTableNode observing one
    representative colour and one representative string. Those fire while
    their siblings still hold old values. The renderer now compares the full
    colour/label snapshot, and DataTableNode observes every contributing
    property.
  • OpenCVTracker.send() silently overwrote the pending resolve/reject pair,
    stranding the earlier Promise forever; a superseded request is now
    rejected.
  • The worker's no-template branch posted x/y as null against a response type
    declaring numbers.
  • calculateTickSpacing() clamped to rangeLength / 20, which could override
    the 1-2-5 "nice number" result at targetTicks = 15 (the scrubber path).
    Spacing now escalates through the nice sequence instead.

i18n and accessibility:

  • The compile-time locale parity check covered only en/fr, leaving Spanish
    free to drift even though the language picker exposes it.
  • Four accessible names were snapshotted with .value, so they did not
    re-translate under supportsDynamicLocale.
  • KinematicsGraphNode leaked the VBox wrapping the track checkboxes on
    every rebuild.
  • CSV export revoked the object URL synchronously after click(), which
    cancels the download in some browsers.

Tests: added specs for TrackExporter, ModelViewTransformFactory (including
the retransform round-trip invariant), VideoPlaybackModel, frame retiming,
and tick spacing; extended the memory-leak suite with listener-count
regressions for PlaybackControlsNode. Each new regression test was verified
to fail against the unfixed code. 22 -> 61 tests.

Docs: corrected the sample-video path, the stale FFmpeg references in the
CSP rationale, and the shipped-locale list.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01Vg2oNMWc4fmuQWmHh6Bxqo

Functional bugs:

- Clicking a data-table row did not seek the video. The only model -> video
  time sync was gated on the `scrubbing` flag, which is set only by the
  slider's pointer DragListener, so the row-click handler moved the model
  clock while the displayed frame stayed put.
- Keyboard scrubbing had the same root cause: Scenery's Slider routes
  keyboard input through AccessibleValueHandler's startInput/endInput, not
  the startDrag/endDrag that set `scrubbing`.

  Both are fixed by centralizing the sync: VideoPlayerNode now links
  currentTimeProperty -> videoElement.currentTime, guarded by the scrubbing
  flag and a half-frame deadband so it does not fight timeupdate during
  playback. startInput/endInput are wired on the scrubber as well.

- PlaybackControlsNode orphaned an HSlider at construction. The duration
  link fired synchronously before this.children was assigned, so
  replaceScrubber()'s indexOf() returned -1 and the new slider was dropped
  undisposed, permanently retaining listeners on currentTimeProperty
  (measured: +4 per construct/dispose cycle). Fixed by using lazyLink and by
  disposing an uninsertable replacement instead of dropping it.

- The OpenCV worker leaked a cv.Mat on every template capture: the
  intermediate header from blurred.roi() was never deleted. WASM heap, so it
  is never reclaimed by the JS GC.

- Changing the frame rate after digitizing left stored points labelled with
  indices from the old rate, so everything keyed on `frame` addressed the
  wrong point. TrackingModel.retimeTrackPoints() re-derives each index from
  the point's authoritative timestamp; frame collisions from a lowered rate
  keep the earlier point, matching addPointToTrack()'s first-wins policy.

Correctness and robustness:

- TableRenderer.update() claimed to rebuild on colour/locale changes but
  only compared unit and track IDs, relying on DataTableNode observing one
  representative colour and one representative string. Those fire while
  their siblings still hold old values. The renderer now compares the full
  colour/label snapshot, and DataTableNode observes every contributing
  property.
- OpenCVTracker.send() silently overwrote the pending resolve/reject pair,
  stranding the earlier Promise forever; a superseded request is now
  rejected.
- The worker's no-template branch posted x/y as null against a response type
  declaring numbers.
- calculateTickSpacing() clamped to rangeLength / 20, which could override
  the 1-2-5 "nice number" result at targetTicks = 15 (the scrubber path).
  Spacing now escalates through the nice sequence instead.

i18n and accessibility:

- The compile-time locale parity check covered only en/fr, leaving Spanish
  free to drift even though the language picker exposes it.
- Four accessible names were snapshotted with .value, so they did not
  re-translate under supportsDynamicLocale.
- KinematicsGraphNode leaked the VBox wrapping the track checkboxes on
  every rebuild.
- CSV export revoked the object URL synchronously after click(), which
  cancels the download in some browsers.

Tests: added specs for TrackExporter, ModelViewTransformFactory (including
the retransform round-trip invariant), VideoPlaybackModel, frame retiming,
and tick spacing; extended the memory-leak suite with listener-count
regressions for PlaybackControlsNode. Each new regression test was verified
to fail against the unfixed code. 22 -> 61 tests.

Docs: corrected the sample-video path, the stale FFmpeg references in the
CSP rationale, and the shipped-locale list.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vg2oNMWc4fmuQWmHh6Bxqo
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@veillette
veillette merged commit 94b234c into main Jul 25, 2026
7 of 8 checks passed
@veillette
veillette deleted the claude/tracklab-codebase-review-cmijn0 branch July 27, 2026 18:58
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