Skip to content

recorder: thumbnail pipeline never self-heals after a single failed frame-grab #174

Description

@morepriyam

Low priority. These findings surfaced during the #173 investigation, where they were the initial suspects before the real root cause was isolated. No field incident has ever been attributed to this path. Filing so the knowledge isn't lost, not as a call to action.

Context

A segment's cover thumbnail is a jpeg written at record/import/migration time; the DB row stores its relative path. Three writers persist thumbnail: null when the native frame-grab (getFrameAt) fails once:

  • src/features/recorder/use-recorder.ts, persistSegment (record time)
  • src/features/draft-transfer/unpack.ts, .pulse import (regenerates covers; 17 sequential native frame-grabs while the whole archive is held in RAM, making it the most stressed context)
  • src/db/legacy-migration.ts, migrated rows with no usable legacy thumb

A null row then degrades to the designed runtime fallback (src/hooks/use-thumbnail.tsgenerateThumbnail): spin up a full expo-video player, extract the first frame, and cache it in memory only.

The gaps

  1. A single transient failure is permanent. One failed getFrameAt means thumbnail: null forever. No retry, no later repair.
  2. The fallback's cost is permanent and recurring. Every null row spins up a whole video player on every draft open, for the lifetime of the row.
  3. The fallback never retries within a mount. whenReady resolves silently after its 5 s timeout, so a failed or slow extraction stays blank until remount. (Cosmetic; remount covers it in practice.)

Ranked remediation (if this is ever touched)

  1. Backfill on fallback success: when the runtime fallback extracts a frame, persist the jpeg to the row's thumb path and repair the column. This makes the entire failure class self-healing regardless of cause, subsumes the value of any retry, converts the recurring per-open player cost into a one-time cost, and heals legacy-migrated rows too. The only item with real merit.
  2. One retry in the import loop (unpack.ts), the only genuinely stressed context. Marginal.
  3. In-mount retry in useThumbnail: skip unless evidence appears.

Why this is low priority

Trigger to act: the first confirmed null-thumbnail row in the wild. Then implement item 1 only.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:recorderCamera, segments, recording lifecycletech-debtRefactor / cleanup / maintainability

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions