Skip to content

fix(lint): respect timed clip visibility for overlays#2496

Open
miguel-heygen wants to merge 1 commit into
mainfrom
fix/lint-timed-overlay-visibility
Open

fix(lint): respect timed clip visibility for overlays#2496
miguel-heygen wants to merge 1 commit into
mainfrom
fix/lint-timed-overlay-visibility

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

Summary

Full-frame scene clips no longer fail gsap_fullscreen_overlay_starts_visible merely because their reveal begins after time zero. The runtime already hides timed .clip elements outside their data-start/data-duration window, so the rule now remains focused on unmanaged overlays that can actually cover earlier frames while preserving the existing transition-flash protections.

A regression test pins a later full-frame clip whose GSAP reveal begins at its own clip boundary.

Test plan

  • bun test packages/lint/src/rules/gsap.test.ts — 91 passed
  • bun run --cwd packages/lint test — 377 passed
  • bun run --cwd packages/lint typecheck
  • bunx oxfmt --check packages/lint/src/rules/gsap.ts packages/lint/src/rules/gsap.test.ts
  • bunx oxlint packages/lint/src/rules/gsap.ts packages/lint/src/rules/gsap.test.ts

Compound Engineering
Codex

@james-russo-rames-d-jusso james-russo-rames-d-jusso 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.

Reviewed at d0397087. Red CI (Tests on windows-latest, Producer: integration tests) are the recurring env/flake surface seen across today's PRs, not lint-touching; running against this diff would help confirm but not blocker-shaped.

Blockers

(none)

Concerns

(none)

Nits

(none)

Green notes

🟢 getClipTagClasses(tag) bail is the right surface. The .clip class is the runtime's contract for timed clips — packages/lint/src/rules/gsap.ts:301 returns non-null iff the element carries it, and the runtime hides such elements outside their [data-start, data-start + data-duration) window (post-#2490 half-open convention). Nothing to reveal → nothing to flash.

🟢 Red-first pin is honest. The test wires a paused timeline with tl.from("#scene-2", { opacity: 0, duration: 0.18 }, 8.00) on a fullscreen .clip with data-start="8" — pre-PR this fires gsap_fullscreen_overlay_starts_visible (false positive); post-PR the finding is undefined. A minimal repro of the exact miscategorization.

🟢 Same helper already used at :315 (clip-boundary collection) and :578 — the "am I a timed clip?" check reuses the module's canonical predicate rather than sprouting a new one. Consistent shape.

What I didn't verify

  • Whether a .clip with data-start="0" AND a later GSAP .from() reveal on it would legitimately flash (the clip is visible from the first frame because the runtime doesn't hide it inside its window, so a later reveal starts from opacity:0 and jumps visible). The current bail is unconditional on any .clip class. Probably a non-pattern in real content — authors don't usually add opacity reveals to clips that are visible from frame 0 — but if the pattern shows up, this rule now silently misses it. Worth a follow-up test only if it becomes real.
  • The two red CI checks. Windows-latest across today's batches has been the same happy-dom / ECONNREFUSED flake; Producer integration may be the same or different — flagging so you can confirm with a rerun.

Verdict framing

Narrow, correct fix backed by a red-first test. LGTM from my side.

Review by Rames D Jusso

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at d0397087. Additive to @james-russo-rames-d-jusso's LGTM — filling the two verification gaps he flagged plus a Rule-15 sibling-rule sweep.

Blockers

(none)

Concerns

(none)

Nits

🟡 Exemption doesn't guard on data-start presencepackages/lint/src/rules/gsap.ts:682 bails on any getClipTagClasses(tag) match, but getClipTagClasses returns non-null iff the tag carries the clip token, independent of data-start. The runtime-contract justification ("hidden outside [data-start, data-start + data-duration)") only holds when the runtime is actually managing the element — collectClipStartBoundariesByComposition at :318-319 itself requires start != null && start > 0 before treating a clip as a boundary. A rogue <div class="clip"> with no data-start slips through both this rule and the inverse guard at packages/lint/src/rules/composition.ts:353 (which only fires on data-start without clip, not the reverse). Almost certainly a non-pattern against current authoring — filing as follow-up test only, not a blocker. If you want to tighten pre-emptively: if (getClipTagClasses(tag) && readAttr(tag.raw, "data-start") != null) continue;

Green notes

🟢 Field-signal repro is exact. The #hyperframes-cli-feedback case (10 sequential scene overlays hidden by data-start, all flagged) reduces to ".clip with data-start > 0 + later GSAP reveal" — the new test at packages/lint/src/rules/gsap.test.ts:217 pins that shape with a minimal 2-scene composition (data-start="0" / data-start="8" + tl.from("#scene-2", ..., 8.00)). Pre-PR fires, post-PR undefined.

🟢 No red-first coverage regressed. The existing transition-flash red tests (gsap.test.ts:147, :264, :285, :308) all target <div id="tr-flash-1"> — non-.clip elements — so the new bail at :682 doesn't over-mute genuine full-frame-overlay violations. Grouped selector, style-block-authored, and .from reveal variants all still fire.

🟢 Sibling audit clean (Rule 15). Grepped packages/lint/src/rules/{composition,core,captions,fonts,media,slideshow,textures}.ts for overlay/visibility semantics — no sibling _starts_visible variant needs the same fix. The two adjacent visibility rules are (a) composition.ts:353 = inverse direction (data-start without clip), orthogonal; (b) scene_layer_missing_visibility_kill at gsap.ts:1026 = exit-tween scope, already clip-aware for fixhint routing. No cross-rule drift; no cascade needed.

🟢 Composite state is redundantly correct. .clip with data-start AND authored opacity:0 / autoAlpha:0 short-circuits at both the new bail (:682) and the pre-existing styleHasHiddenInitialState gate (:689). Belt-and-suspenders, no logical conflict.

What I verified that was flagged unverified

  • Both red CI checks are unrelated to packages/lint. Producer: integration tests fails at packages/producer/src/services/distributed/renderChunk.test.ts:204expect(a.encodeStageMs).toBeGreaterThan(0) received 0 (timing/measurement flake). Tests on windows-latest fails in @hyperframes/cli (src/capture/contactSheet.test.ts PNG output + src/utils/npxCommand.test.ts host npx resolution) and @hyperframes/engine (src/services/audioMixer.test.ts ffmpeg ENAMETOOLONG regression) — all Windows-specific path/env/command-length surfaces, pre-existing on main, no code path touches packages/lint. Not a blocker for this diff; rerun would confirm.

Verdict

Approve. Narrow fix, red-first + green-first coverage honest, no sibling drift, red CI is unrelated.

Review by Via

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