PERF GUARD: detect paint barriers, repeated layout reads and local blur (#663) #794

Open
opened 2026-10-02 13:10:55 +00:00 by kayg · 1 comment
Owner

PERF GUARD: detect paint barriers, repeated layout reads and local blur (#663)

Finding and context

origin/dev MailView.svelte:328 clears previous rows; :330–376 then waits for
several reads. Shared flip.ts:68,76,180,335 reads layout for motion. Shared
ProgressiveBlur.svelte:37 uses Safari-compatible literal blur. These examples
need semantic detection and scoped exceptions, not blanket grep bans. Mail
fixes belong to #672/#686 and queued #640. Preserve #611 keyboard animation.

Exact detection

Parse TS and Svelte scripts/templates and CSS, including style attributes and
utility classes. Resolve handler aliases and local calls. For registered list
selection/next/previous keyboard handlers, reject async handlers, await, and
Promise.then continuations that perform the visible selection change. The
selection must commit synchronously. Async persistence scheduled after that
commit is allowed through the shared mutation helper.

In registered route initialization/cache-return paths, reject an await or
Promise continuation on counts, statistics, refresh, animation completion or
tick before the cached snapshot is assigned. Require a contract test that
blocks those promises and proves cached content and input are available. Cold
primary-data reads can await; flag secondary-data dependencies rather than
all route awaits. Reject {#await}/loading branches that replace an available
snapshot with a spinner or blank. Source detection is a candidate check;
the blocked-promise test establishes behavior where dataflow is unresolved.

Reject layout reads (getBoundingClientRect, getClientRects, Range rects,
getComputedStyle, offsetWidth/Height/Top/Left, scrollWidth/Height) inside loops
or map/forEach/reduce callbacks, including local helper calls from a loop.
Reject canvas measureText and per-row Resize/MutationObservers used for icon,
label or text alignment. Registered virtualization, resizable separators and
anchored-popover behavior may have exact capability exceptions with bounded
work tests. Shared FLIP needs an exception on specific symbols and its bounded
caller contract; never allow the whole UI directory. A single anchor read is
not a loop violation. Tests/benchmark DOM inspection is a separate class.

Reject active backdrop-filter/-webkit-backdrop-filter outside exact shared
material declarations, including Tailwind backdrop utilities and inline styles.
Values none and inherited/shared material application are valid. Allow the
specific ProgressiveBlur and glass primitives; no file-wide permission for
future selectors. Reduced-transparency fallbacks still work. Runtime computed
alignment is forbidden even if its own benchmark is fast.

Tests and gates

Fixture tests cover alias calls, loops, CSS comments, none, Tailwind filters,
shared FLIP exceptions and valid anchor reads. Block refresh/count promises in
component tests; cached rows paint and keyboard movement commits without
settlement. Release them and check update/error states. Run from bun run check
and existing component tests. No test disables shared keyboard motion.

Scope and exception contract

Specification only, from #663 perf-guards. Implement in a later job. Reuse the
shared guard registry and ledger described in the coverage issue. Until it
exists, use this exact contract: an exception has rule ID, repo-relative file,
symbol or route/surface ID, normalized syntax hash (or exact build metric),
owner issue, reason, replacement test and UTC expiry date. No directory globs,
line-number-only entries or ignore all comments. Fail on expired, changed,
duplicate and unused exceptions. Existing debt needs an explicit entry and a
non-growing limit. Print exceptions in CI. An exception does not waive access,
session clearing or accessibility tests. Generated and test fixtures are
excluded by explicit source class, not substring matching. Tests must prove
both violation detection and valid exceptions. No product fix belongs here.

The static/contract command exits 1 for violations, 2 for invalid configuration
or parse/build errors, and 0 only for complete passing coverage. Output includes
rule ID, file:line, symbol/route, actual value and required value. No User content.
Measured host timing stays periodic under CLAUDE.md. Deterministic guards fail
required CI; the dedicated perf-budget runner reports measured budget failures
with nonzero status but is not a required merge check.

# PERF GUARD: detect paint barriers, repeated layout reads and local blur (#663) ## Finding and context origin/dev MailView.svelte:328 clears previous rows; :330–376 then waits for several reads. Shared flip.ts:68,76,180,335 reads layout for motion. Shared ProgressiveBlur.svelte:37 uses Safari-compatible literal blur. These examples need semantic detection and scoped exceptions, not blanket grep bans. Mail fixes belong to #672/#686 and queued #640. Preserve #611 keyboard animation. ## Exact detection Parse TS and Svelte scripts/templates and CSS, including style attributes and utility classes. Resolve handler aliases and local calls. For registered list selection/next/previous keyboard handlers, reject async handlers, await, and Promise.then continuations that perform the visible selection change. The selection must commit synchronously. Async persistence scheduled after that commit is allowed through the shared mutation helper. In registered route initialization/cache-return paths, reject an await or Promise continuation on counts, statistics, refresh, animation completion or `tick` before the cached snapshot is assigned. Require a contract test that blocks those promises and proves cached content and input are available. Cold primary-data reads can await; flag secondary-data dependencies rather than all route awaits. Reject `{#await}`/loading branches that replace an available snapshot with a spinner or blank. Source detection is a candidate check; the blocked-promise test establishes behavior where dataflow is unresolved. Reject layout reads (getBoundingClientRect, getClientRects, Range rects, getComputedStyle, offsetWidth/Height/Top/Left, scrollWidth/Height) inside loops or map/forEach/reduce callbacks, including local helper calls from a loop. Reject canvas measureText and per-row Resize/MutationObservers used for icon, label or text alignment. Registered virtualization, resizable separators and anchored-popover behavior may have exact capability exceptions with bounded work tests. Shared FLIP needs an exception on specific symbols and its bounded caller contract; never allow the whole UI directory. A single anchor read is not a loop violation. Tests/benchmark DOM inspection is a separate class. Reject active backdrop-filter/-webkit-backdrop-filter outside exact shared material declarations, including Tailwind backdrop utilities and inline styles. Values `none` and inherited/shared material application are valid. Allow the specific ProgressiveBlur and glass primitives; no file-wide permission for future selectors. Reduced-transparency fallbacks still work. Runtime computed alignment is forbidden even if its own benchmark is fast. ## Tests and gates Fixture tests cover alias calls, loops, CSS comments, `none`, Tailwind filters, shared FLIP exceptions and valid anchor reads. Block refresh/count promises in component tests; cached rows paint and keyboard movement commits without settlement. Release them and check update/error states. Run from bun run check and existing component tests. No test disables shared keyboard motion. ## Scope and exception contract Specification only, from #663 perf-guards. Implement in a later job. Reuse the shared guard registry and ledger described in the coverage issue. Until it exists, use this exact contract: an exception has rule ID, repo-relative file, symbol or route/surface ID, normalized syntax hash (or exact build metric), owner issue, reason, replacement test and UTC expiry date. No directory globs, line-number-only entries or `ignore all` comments. Fail on expired, changed, duplicate and unused exceptions. Existing debt needs an explicit entry and a non-growing limit. Print exceptions in CI. An exception does not waive access, session clearing or accessibility tests. Generated and test fixtures are excluded by explicit source class, not substring matching. Tests must prove both violation detection and valid exceptions. No product fix belongs here. The static/contract command exits 1 for violations, 2 for invalid configuration or parse/build errors, and 0 only for complete passing coverage. Output includes rule ID, file:line, symbol/route, actual value and required value. No User content. Measured host timing stays periodic under CLAUDE.md. Deterministic guards fail required CI; the dedicated perf-budget runner reports measured budget failures with nonzero status but is not a required merge check.
Author
Owner

F2 — P2: animation waits on ordinary render paths have no source check

Evidence: scripts/perf_guards/rules.py:176 checks awaits only in functions
whose source contains ArrowDown, ArrowUp, PageDown or PageUp. The Svelte
exporter checks AwaitBlock nodes at apps/web/scripts/perf-source.mjs:108.
Neither check inspects an ordinary open or cache-return function that waits
for animation completion before it assigns available content.

Issue #794 requires this check. The owner rule requires loading to proceed as
soon as data exists. Resolve registered initialization and cache-return calls.
Require a blocked-animation contract when dataflow is unknown. Add a fixture
where an open function awaits animation.finished before it assigns a retained
snapshot. Add a valid fixture where the assignment occurs first. Owner: #794.

## F2 — P2: animation waits on ordinary render paths have no source check Evidence: `scripts/perf_guards/rules.py:176` checks awaits only in functions whose source contains ArrowDown, ArrowUp, PageDown or PageUp. The Svelte exporter checks AwaitBlock nodes at `apps/web/scripts/perf-source.mjs:108`. Neither check inspects an ordinary open or cache-return function that waits for animation completion before it assigns available content. Issue #794 requires this check. The owner rule requires loading to proceed as soon as data exists. Resolve registered initialization and cache-return calls. Require a blocked-animation contract when dataflow is unknown. Add a fixture where an open function awaits `animation.finished` before it assigns a retained snapshot. Add a valid fixture where the assignment occurs first. Owner: #794.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#794
No description provided.