Skip to content

feat(cli): a line_unread finding carries the frames it measured around where the line was first read - #7294

Merged
miguel-heygen merged 3 commits into
mainfrom
hficp/line-unread
Nov 6, 2026
Merged

miguel-heygen merged 3 commits into
mainfrom
hficp/line-unread

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

What

A line_unread finding now carries measured: the samples at both ends of the line's run (three either side of its first and last read), each with its time, the block's opacity and its height in pixels (null when nothing of the line was painted there).

Why

line_unread on a film's closing line has been reported as a false alarm on films of 65 to 85 s whose end card is a sub-composition: the check says the line is on screen 0.4 to 1 s while a snapshot shows it for about 3.2 s, and the number differs between runs. The audit's own arithmetic holds up (it measures a 3.2 s card correctly for every thinned sample grid, stride 1 to 12 at every offset, with and without the refine probe), and I could not reproduce the report on 0.9.16 or 0.9.18 with a 10 s, 67 s or 85 s film, a scaled host, an exit fade, per-word stagger, forced thinning, or a late-loading font. So the wrong number has to come from the browser frames, and the finding gave no way to see them. With measured, the next report shows whether the frames before the first read painted nothing, painted the line too faint, or too small.

Related work

Refs #7183 (the thinned craft samples, ruled out as the sole cause here).

How

measuredAround in craftAudit.ts reads the frames the audit already holds: three samples either side of the first and of the last read, filtered to the line's block, so a line that is read late and one that drops out early both show their frames. No new browser work and no change to what is flagged.

Size

78 changed lines (3 files), under the usual 100: a lone change, since the diagnostic is one self-contained field on one finding and no open PR touches the check's craft audit to carry it.

Test plan

  • Unit tests added/updated: the 7-word line read for 0.7 s carries twelve measured samples: none painted before or after it, opacity 1 and a 115 px block while it shows.
  • fails without the fix: the new test fails on main's craftAudit.ts (no measured) and passes here; the file's other 23 tests pass (24 of 24 on a remote box, with oxfmt and oxlint clean on the three files).
  • Manual testing performed: no real check shows the false alarm, so none was possible; a clean film still reports nothing.
  • Documentation updated: not needed, the finding gains a field only.
  • Comments follow CONTRIBUTING.md "Comments".

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

Requesting changes at c281ac35: the Comments workflow fails on this head (run 42158474673, "A package file's comment share may not rise").

RAISED comment share: packages/cli/src/utils/checkTypes.ts is 8.4% (39/466), at the base 8.2% (38/464)
RAISED comment share: packages/cli/src/utils/craftAudit.ts is 4.6% (27/587), at the base 4.5% (25/551)

Shortening the comment in the second commit wasn't enough, because the ratchet counts comment lines, not their length. Here is one way to get both files back to or under the base share:

  • checkTypes.ts: drop the JSDoc on measured. A named type can carry the meaning instead, e.g. type MeasuredFrame = { time: number; opacity: number | null; heightPx: number | null }, with null meaning nothing was painted.
  • craftAudit.ts: shorten the measuredAround doc to one line, or drop it. The function name and the MEASURED_BEFORE/MEASURED_AFTER constants already say most of it.

The rest looks right, no other blockers:

  • measuredAround reads the frames the audit already holds, and firstIndex is run[0] of the chosen run, an index into that same array. No new probe work, and nothing changes in what gets flagged.
  • It filters to part.block === key && !part.captions without the 0.5 opacity or read-height cut that readableBlocks applies. So frames that painted the line too faint or too small show up with their values instead of being dropped, which is the point of the diagnostic.
  • The "fails without the fix" line is present and holds. Without measured, the new test reads [] and the toEqual([null, null, null, 1, 1, 1]) fails.

Not blocking: the window covers only the start of the chosen run (3 samples before, 2 after). If the next false-alarm report turns out to be a line that drops out early rather than one read late, the end of the run won't be in measured. It might be worth adding the same few samples around run[1] now, so the next report doesn't need another round trip.

The other workflows on this head were still running when I posted this (CI was queued; CodeQL, preview-regression and Windows render verification were in progress).

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 141 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (8)

Unstable (1)

  • seqfastkeys-none-px-r0-root-z100: tracking 0.07, pressJump 0.05, drop 0, reload 0, render 0.02, renderKey -, undo false, teleport true / tracking 0.06, pressJump 0.05, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true / tracking 0.06, pressJump 0.05, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true

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

Approving at f87df1c9. The blocker from my last review is fixed, and so is the non-blocking note.

  • Comment ratchet: the Comments workflow passes on this head. The JSDoc on measured is gone, and measuredAround has a one-line doc.
  • Both ends: lastIndex: run[1] is now carried on the candidate, and measuredAround takes ±3 samples around the first and last read. A Set dedupes the two ranges when they overlap on a short run, and indices past either end of frames are skipped. The test's 12 contiguous samples (5.875 to 7.25 s, with opacity null, then 1, then null) match a 6-sample run at a 0.125 s step.
  • No behaviour change: it reads only the frames the audit already holds, adds no probe calls, and leaves the flag logic as it was.
  • "Fails without the fix" line: present, and it holds. On main there's no measured, so the opacity toEqual fails.
  • CI on this head passed: CI, Comments, regression, preview-regression, Windows render verification, PR captures, CodeQL and Player perf. The cancelled runs were superseded duplicates on the same SHA, and later runs of the same workflows passed.

Not blocking: the PR body's "Why" still talks only about "the frames before the first read". The "How" section has the both-ends wording right.

@miguel-heygen
miguel-heygen added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit a81b344 Nov 6, 2026
87 of 89 checks passed
@miguel-heygen
miguel-heygen deleted the hficp/line-unread branch November 6, 2026 04:28
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 6, 2026
…view

Adopts heygen-com#7294 (line_unread findings carry the measured frames), heygen-com#7271
(blur mutes discrete callbacks instead of undoing them), heygen-com#7293 (the
filter tier's svg keeps the last child), heygen-com#7292 (a mover holding a fixed
child stays sharp), heygen-com#7288 (auto motion blur draws with the filter tier)
and heygen-com#7298 (Shift no longer re-renders the timeline). The fork's seek
recovery in captureMotionBlur stays. Defers heygen-com#7134 (no packages/share)
and heygen-com#7295 (needs the heygen-com#7080 crop pipeline). Watermark advances to
66cbabc.

Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
Co-authored-by: Vance Ingalls <vance@heygen.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant