Skip to content

test(root): compare the running effect against what the stylesheet declares - #867

Merged
mobeenabdullah merged 4 commits into
mainfrom
fix/live-effect-timing-vs-declaration
Aug 16, 2026
Merged

mobeenabdullah merged 4 commits into
mainfrom
fix/live-effect-timing-vs-declaration

Conversation

@mobeenabdullah

Copy link
Copy Markdown
Collaborator

Closes the last known UNDER-reporter in the canvas settle guard — the direction that lets an allowance certify a wait shorter than the movement.

The gap

effect.updateTiming({ duration }) retimes a declared animation and leaves every value this probe reads unchanged: the object is still a CSSAnimation, playbackRate is still 1, and every computed longhand still describes the stylesheet. All three existing refusals — scripted effect, changed playback rate, non-document timeline — are blind to it, and the probe charges the declaration while the edge travels longer.

Why a comparison rather than a fourth special case

Three findings so far are one shape: the live effect disagrees with what the stylesheet says. Enumerating routes means finding the fourth in review and the fifth in production.

getComputedTiming().activeDuration already folds duration × iterations and any override, so no effect has to be matched back to the rule that produced it. One comparison covers the routes nobody has found yet:

declared = longest span from the computed longhands   (as before)
live     = max(activeDuration + delay) over the element's own geometry effects
refuse when live > declared + tolerance

Only the under-reporting direction is refused. A live span shorter than the declaration makes the probe wait longer than the edge moves, which is safe.

Two corrections found by running it, both worth reading

1. The live side must match the declared side's DOMAIN, not just its expression. My first version counted every owned effect and immediately failed four existing cases with "claims 100ms while its declaration reads 0ms" — the canvas's real background .1s transition. That is a paint transition that moves no edge, and charging it reinstates exactly the ~100ms false floor #825 removed. The live side now counts only geometry-moving effects: a transition names its property, an animation's computed keyframes carry theirs, and an effect whose keyframes cannot be read is charged rather than skipped.

2. activeDuration === 0 has two causes and they charge differently. Zero ITERATIONS runs nothing, so it holds the edge for nothing — not even its delay. Zero DURATION waits out its delay and then moves instantly, so the delay is the span. activeDuration alone cannot tell them apart; the declared path already separates them, so the live path reads the iteration count to agree. Caught by a zero-iteration animation charges neither duration nor DELAY, which failed on the first version.

The control, and its failure

A CSSAnimation retimed from a declared 20000ms to 60000ms. The control asserts the three readings that make it the case it claims to be — playbackRate is 1, animation-duration is still 0.2s, animation-iteration-count is still 100 — and that the effect reports 60000ms. Without those the fixture could be refused by some other clause and prove nothing.

Finite and still running, for the same reason the playback-rate control was rewritten from infinite to .2s 100: an endless animation is already refused for being endless, so an endless fixture passes with the comparison removed.

Break verified. Comparison disabled → only the new control fails, with Received promise resolved instead of rejected; the other 24 pass. Restored → 25 pass.

Verification

  • Guard suite: 25 passed, including the new control.
  • Full canvas suite: 94 passed, 0 failed.
  • check-types + lint green for @nextlyhq/e2e, turbo --force.

One run was discarded rather than reported: a worker was SIGKILLed and every case failed on ECONNREFUSED :3100, which is a server that did not boot, not evidence.

No changeset: test-only.

…clares

Three refusals already exist for effects whose real timing is not in any
longhand: a scripted animation, a declared one whose playback rate was
changed, and a non-document timeline. `updateTiming` is a fourth route to
the same place and it defeats all three — the object stays a
`CSSAnimation`, the playback rate stays 1, and every computed longhand
still describes the stylesheet.

So this compares rather than enumerating. `activeDuration` already folds
duration by iterations and any override, so no effect has to be matched
back to the rule that produced it, and the comparison covers routes
nobody has found yet.

Only the under-reporting direction is refused. A live span shorter than
the declaration makes the probe wait longer than the edge moves, which
is safe.

The live side counts only geometry-moving effects, because the declared
side does. Counting every owned effect compares two populations rather
than two numbers, and the canvas has a real background transition that
moves no edge.
@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@codex please review this PR

@coderabbitai

coderabbitai Bot commented Aug 16, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@mobeenabdullah, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Limit details: You’ve used all 1 included review currently available under your plan.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fc9ba5f9-a54e-4d1d-9b9d-c05d53e023b1

📥 Commits

Reviewing files that changed from the base of the PR and between fcbecc0 and 02e3248.

📒 Files selected for processing (1)
  • e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Aug 16, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@nextlyhq/adapter-drizzle

npm i https://pkg.pr.new/@nextlyhq/adapter-drizzle@02e3248

@nextlyhq/adapter-mysql

npm i https://pkg.pr.new/@nextlyhq/adapter-mysql@02e3248

@nextlyhq/adapter-postgres

npm i https://pkg.pr.new/@nextlyhq/adapter-postgres@02e3248

@nextlyhq/adapter-sqlite

npm i https://pkg.pr.new/@nextlyhq/adapter-sqlite@02e3248

@nextlyhq/admin

npm i https://pkg.pr.new/@nextlyhq/admin@02e3248

@nextlyhq/admin-css

npm i https://pkg.pr.new/@nextlyhq/admin-css@02e3248

@nextlyhq/blocks-engine

npm i https://pkg.pr.new/@nextlyhq/blocks-engine@02e3248

@nextlyhq/blocks-react

npm i https://pkg.pr.new/@nextlyhq/blocks-react@02e3248

@nextlyhq/builder

npm i https://pkg.pr.new/@nextlyhq/builder@02e3248

create-nextly-app

npm i https://pkg.pr.new/create-nextly-app@02e3248

nextly

npm i https://pkg.pr.new/nextly@02e3248

@nextlyhq/plugin-form-builder

npm i https://pkg.pr.new/@nextlyhq/plugin-form-builder@02e3248

@nextlyhq/plugin-page-builder

npm i https://pkg.pr.new/@nextlyhq/plugin-page-builder@02e3248

@nextlyhq/plugin-sdk

npm i https://pkg.pr.new/@nextlyhq/plugin-sdk@02e3248

@nextlyhq/plugin-seo

npm i https://pkg.pr.new/@nextlyhq/plugin-seo@02e3248

@nextlyhq/storage-s3

npm i https://pkg.pr.new/@nextlyhq/storage-s3@02e3248

@nextlyhq/storage-uploadthing

npm i https://pkg.pr.new/@nextlyhq/storage-uploadthing@02e3248

@nextlyhq/storage-vercel-blob

npm i https://pkg.pr.new/@nextlyhq/storage-vercel-blob@02e3248

@nextlyhq/ui

npm i https://pkg.pr.new/@nextlyhq/ui@02e3248

commit: 02e3248

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 30e80efa1d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts Outdated
Comment thread e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts Outdated
…ect is

Two holes in the comparison, both of which let it report the declared
span while the edge keeps moving.

The classification had two states where the declared path has three. A
property on neither list can move an edge — `border-bottom-width` does —
so reading "not geometry" as "does not move geometry" dropped the effect
from the comparison entirely, and retiming such an effect was invisible.
Unknown now refuses through the same channel the declared path uses.

And the span is measured from where the effect currently is. Moving a
schedule rather than a duration leaves `activeDuration` and `delay`
untouched, so their sum equals the declaration while an animation rewound
to a negative current time keeps moving for a second longer. `endTime`
minus `currentTime` covers both, and can only come out shorter as an
effect progresses, which is the safe direction.
@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@codex please review this PR

@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@nextly-bot review

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 41ba5ccab8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts Outdated
Comment thread e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts Outdated
Comment thread e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts Outdated
Comment thread e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts Outdated
@github-actions

github-actions Bot commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @mobeenabdullah's task in 36m 12s —— View job


Review round 1 — not posted (superseded)

  • Pre-flight: PR open, head 41ba5cca matched the invocation SHA at start
  • Load repo law (AGENTS.md, ARCHITECTURE.md, rules, skills — fetched at base SHA)
  • Round awareness: no prior github-actions[bot] rounds; 6 Codex threads pulled (2 resolved, 4 open)
  • Read the full diff + the whole enclosing file at head and base
  • Adversarial verification of candidates (see findings below)
  • Compose review payload
  • Post review — refused by the gateway: the branch moved to 1a6b2e4b while this run was in flight

The review was written and submitted against 41ba5cca, the commit this run was triggered for. The gateway refused to post it because the PR head has since moved to 1a6b2e4b6c18bf6faa2f4a43d2648bfbcc53674a — a review landing against a commit nobody is looking at any more reads as current, so it was correctly withheld. This is a superseded round, not a clean one. The newer push gets its own run.

What the withheld review would have said, for the record against 41ba5cca only:

No new findings (0 P0/P1/P2/P3). I attacked the PR's stated guarantee along four routes Codex did not name, and each is covered:

  • iterationStart — activeDuration and endTime already integrate it, so a shifted phase shows up in the same comparison.
  • startTime shift — requires a non-default rate first, which the pre-existing playbackRate !== 1 refusal at e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts:233 already catches.
  • rate-vs-currentTime product — currentTime is the animation's own clock and already integrates the playback rate, so endTime - currentTime stays correct where endTime - timelineTime would not.
  • finished effect replayed via startTime = null — every path to that state produces a finite live far above the declared span, and the finite divergences fire first.

The two resolved Codex threads are genuinely fixed at head, and their controls are load-bearing: the three-state classify routes unknown properties through the same unclassified channel the declared path uses, and endTime - currentTime is verified by a control asserting activeDuration and delay still hold their declared values after a rewind.

Four Codex threads remain open and unanswered (paused effects, empty-but-readable keyframes, positive endDelay, setKeyframes() on a transition). I did not duplicate them; they stand or fall on their own.

Process checks passed: test-only diff (1 file, +308), root scope is in the allowlist, correctly no changeset, no as any/@ts-expect-error added, no AI attribution. The suite could not be executed here — the checkout is git-sparse with no node_modules — so the verdict is from reading head and base side by side, not from a run.

…nimates

Four holes, two that let the comparison under-report and two where it
refused a canvas that was fine.

A PAUSED effect leaves the playback rate at 1 while its clock stops, so
every span computed from it is a remainder that never shrinks and the
edge moves again whenever something resumes it. Refused, like the
rate-zero case it reaches by a different API.

A transition's `transitionProperty` names what CREATED it, not what it
animates now. `setKeyframes` can replace that, and a paint transition
rewritten to move height was classified safe while the declared path
skipped the paint longhand — so nothing charged it at all. The keyframes
are read alongside the name.

An empty keyframe list is a SUCCESSFUL read proving the effect moves
nothing, so it is no longer treated as unreadable and refused.

And `endDelay` is a post-active tail rather than movement, so the span is
measured over the active interval instead of to `endTime`.
@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@codex please review this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a6b2e4b6c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts
Comment thread e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts Outdated
Comment thread e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts Outdated
@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@nextly-bot review

@github-actions

github-actions Bot commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @mobeenabdullah's task in 35m 12s —— View job


Review posted — round 1

The review round ran to completion and was posted against 1a6b2e4 as pullrequestreview-4946038726.

  • Phase 0 — pre-flight: PR open, head SHA matches the invocation, not merged/draft.
  • Phase 1 — loaded AGENTS.md, pr-title.yml, the e2e driver and canvas sources (poc-driver.ts, IframeCanvas.tsx, DropZone.tsx). e2e/AGENTS.md is absent from the checkout.
  • Phase 2 — round awareness: no prior pr-review-agent round exists (this is round 1). All 6 resolved Codex threads re-verified at head; the 3 unresolved ones checked for overlap.
  • Phase 3 — task understood: test-only PR adding a live-effect vs declared-stylesheet comparison to geometrySpanMs, with 7 new controls.
  • Phase 4 — context expansion: read the full file at head and base, the caller (poc-driver.ts), the canvas CSS the fixture rides on, and the real drop-zone rules.
  • Phase 5/6 — hunt and adversarial verification: attacked the stated guarantee across the Web Animations surface (updateTiming, currentTime, pause, endDelay, fill, play states); killed several candidates (pre-existing on main, deliberately documented, non-separating).
  • Phase 7/8 — composed and posted: 1 P1, 1 P2, 1 P3, all inline-anchored, one review call.

Findings that survived:

  1. P1 — a pre-start/delayed effect has currentTime === null, which Number(a.currentTime ?? 0) reads as 0; the live span comes out as the declared number and the comparison misses an effect that still has its whole delay to travel.
  2. P2 — negative endDelay truncates the active interval at endTime, but the formula charges the full delay + activeDuration, over-refusing a correct canvas.
  3. P3 — the zones === -1 refusal message still enumerates three WAAPI routes; the PR routes paused effects and unreadable keyframes into it without extending the sentence.

Not executed: the Playwright suite — node_modules is absent here and building the playground on :3100 was out of scope. Every finding is argued from the API contract in the code, not from a run.

@github-actions github-actions 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.

Nextly Review Bot: round 1 - findings on the live-effect comparison

Verdict: The live-vs-declared comparison is the right shape and the six controls are genuinely separating, but two states still let an owned effect out-run its declaration without the comparison seeing it (playState: "idle" with a delay, and a negative endDelay that truncates the active interval). Both are the same direction the PR says it refuses, so they belong in this PR rather than a follow-up.

New findings: 1 P1, 1 P2, 1 P3 (inline below)

Prior rounds: none by this bot (no pr-review-agent review exists on this PR). The six resolved Codex threads were each re-verified at head: unclassified routing (three-state classify), the currentTime subtraction, the paused clause, the empty-keyframes read, the active-interval-not-endTime span, and the keyframes-read-for-both-kinds union are all present and each is backed by a control. The three unresolved Codex threads (negative endDelay, zero-iteration fill, pause-before-classify) overlap the findings below; see the duplicate notes on each.

Not inline-anchorable: none

Pre-existing (not this PR): none noted. The e2e AGENTS.md files are absent from this checkout (git show HEAD:e2e/AGENTS.md and .claude/AGENTS.md both fail), so package-local rules could not be read.

Checked:

  • Attacked the stated guarantee ("refuse when live > declared") against every Web Animations state reachable through updateTiming/currentTime/pause on an owned CSSAnimation; two bypasses survived.
  • Re-derived the live span by hand for each of the seven new controls and confirmed each assertion is the separating property, not a precondition (AGENTS.md L194-L204).
  • Verified the three prior-round fixes against the head code path, not the resolution click.
  • Did not execute the suite: node_modules is absent in this runner and installing the workspace plus building the playground on :3100 was out of scope for this run. Nothing below depends on a run; each is argued from the API contract in the code.
  • Changeset: correctly omitted for a test-only PR (AGENTS.md L306). Title test(root): ... is conventional, root is in the pr-title.yml scope allowlist, subject is lowercase imperative with no trailing period. No AI attribution in the three commits or the PR body.

Comment thread e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts Outdated
Comment thread e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts
Comment thread e2e/tests/canvas/geometry-settle-matches-the-canvas.test.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 02e3248ccb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +320 to +321
return [
...declared,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Trust readable transition keyframes over the original property

When a geometry CSSTransition is replaced with paint-only or empty keyframes via setKeyframes() and then given a longer duration, this unconditional inclusion of declared still classifies the effect as geometry-moving. The live/declaration comparison therefore refuses even though the readable effect can no longer move an edge; this is inconsistent with the empty-animation handling immediately above. Use the original transition property only as the unreadable fallback, and classify successfully read transitions from their actual keyframes.

AGENTS.md reference: AGENTS.md:L198-L202

Useful? React with 👍 / 👎.

@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@codex please review this PR

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 02e3248ccb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mobeenabdullah
mobeenabdullah merged commit 88b44fc into main Aug 16, 2026
30 of 31 checks passed
mobeenabdullah added a commit that referenced this pull request Aug 16, 2026
test(root): recover the clock-bound commit stranded by #867
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