Skip to content

test(e2e): measure a dropped node against the copy it is welded to (#566) - #568

Merged
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame
Aug 23, 2026
Merged

test(e2e): measure a dropped node against the copy it is welded to (#566)#568
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame

Conversation

@AndresL230

@AndresL230AndresL230 commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Fixes the e2e (browser lane) red on main, failing since the landing-v5 port (#524) merged on 2026-08-16.

Closes#566.

The failure

frontend/e2e/landing-drag-field.spec.ts"a dropped node stays where it was put, and scrolls with the page" — fails its scroll-follow assertion:

Expected: < 12 Received: 98.40079543249837

Not flaky: three CI retries land within 0.2px of each other, and it reproduces identically across two CI runs six days apart and every local run.

Root cause

The product is right; the journey was wrong.

faq is the only section in DragField.tsx's TRACKS, and engine/sim.ts::syncClusters() positions a tracked cluster at field.top + a.top + dy where dy = (track.top - field.top) - trackTop. That collapses to cluster_y = track.top + const — the cluster is welded to the FAQ question column, which is sticky; top:110.

Instrumented over the journey's own 300px scroll:

measurementΔ
scrollY+300.00
#faq top−300.00
track top−202.81 ← hit its top:110 pin partway
cluster top−202.80
probe node y−202.60
error97.40 (CI: 98.2–98.4)

The column had exactly 202.81px of travel left before pinning, so ~97px of the scroll happened with the copy held still — and the cluster correctly held still with it. That weld is deliberate: 8bb34869 added it because the clusters were sliding 374px out from under the words they annotate.

The journey asserted raw scrollY on the single cluster where 1:1 page coupling is deliberately untrue. It picked cluster 4 for elbow room, not knowing faq is also the only tracked field.

The change

Two commits — the second is the response to review, and it matters more than the first.

The oracle belongs to the test. The first attempt read the reference out of the field's own data-drag-track and fell back to the field when absent. That was self-defeating: deleting faq from TRACKS would regress the product and re-frame the test to match, keeping it green on exactly the bug it exists to catch. Verified by mutation, not argument — emptying TRACKS leaves the first version passing; against the landed version the offset drifts 96.69px and it fails, while the unmutated page sits at 0.45px. The column is now named in the spec (FAQ_COPY) and resolved section-scoped, the way sim.ts:296 resolves it.

The invariant is an offset that must not change, reusing the idiom the act-tutor journey already uses, instead of a signed delta that only worked because the page scrolls downward. Also from review: the "did the reference move" guard is signed (< -100) rather than Math.abs(...) > 100; preconditions run before the guard so a page that failed to scroll says so; node and reference are sampled in one page.evaluate rather than two frames; toBeCloseTo(300, 0) rather than exact float equality on a browser-computed offset; and the dead section parameter is gone.

The 1:1 case is restored where it is true. Re-framing the file's only page-relative assertion would have left nothing tying a cluster to raw document scroll, while the file header still teaches that symptom 4 survived every earlier test "because they all measured a cluster against its own field rather than against the page". So a new journey covers cluster 6 in newsletter — no TRACKS entry, no sticky stage, and unlike cta it has room below for the scroll. It carries the original assertion verbatim; measured 1:1 error is 0.58px.

Verification

Rebased onto main at 7863210a.

Control, on unpatched main: 47 passed, 1 failed — and the single failure is this journey. The lane is still red on current main, so this is still the fix it needs.

runresult
control (no patch)47 passed, 1 failed ← this journey
full suite, patched, run 149 passed, 0 failed
full suite, patched, run 249 passed, 0 failed
landing-drag-field.spec.ts ×37/7, 7/7, 7/7

Mutation test — the assertion is load-bearing, not vacuous:

product stateoffset driftresult
weld intact0.45pxpasses
faq dropped from TRACKS96.69pxfails

The new newsletter journey measures 1:1 error of 0.58px. tsc --noEmit and eslint clean.

On the gradebook question raised against the previous revision:gradebook.spec.ts:35 failed 2 of 3 full-suite runs then, always on a tree carrying an in-flight #553, and the signature (the Exams category, which exists only on the fall-2025 enrollment) matched "resolved the wrong enrollment" as readily as "rendered slowly". #553 has since merged; across the three full suites above it passed every time. It has not reproduced on current main, so there is nothing to file.

Note the e2e (browser lane) workflow is main-only, so this PR's own checks cannot exercise the lane it fixes — hence the local gate above.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Expanded drag-and-drop coverage for FAQ and untracked sections.
    • Added checks confirming dropped elements maintain their position relative to FAQ content during scrolling.
    • Added validation that dropped elements in untracked sections move consistently with page scrolling.

@coderabbitai

coderabbitaiBot commented Aug 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 116adc67-32b7-4996-a550-5269781748f7

📥 Commits

Reviewing files that changed from the base of the PR and between 7863210 and 9da3e4c.

📒 Files selected for processing (1)
  • frontend/e2e/landing-drag-field.spec.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The landing drag-field E2E tests now compare dropped nodes with FAQ copies during scrolling and verify that nodes in untracked sections move with the document.

Changes

Landing drag-field scroll validation

Layer / File(s)Summary
Scroll probe and section definitions
frontend/e2e/landing-drag-field.spec.ts
The test defines FAQ and untracked-section identifiers. probeAgainstCopy measures the dropped node, its FAQ copy, and the scroll offset in one page evaluation.
Dropped-node scroll assertions
frontend/e2e/landing-drag-field.spec.ts
The FAQ test checks scroll and pre-pinning conditions and preserves the node-to-copy offset. A new test checks 1:1 movement for nodes in untracked sections.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk:⚪ Minimal · up to 9da3e

This change corrects the drag-field E2E assertions for welded FAQ content and restores direct scroll coverage for newsletter content; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers:darkest-teddy

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe changes address issue #566 by correcting the sticky FAQ assertion and preserving direct scroll coverage for an untracked newsletter cluster.
Out of Scope Changes check✅ PassedThe changes are limited to the landing drag-field E2E tests and directly support the linked issue objectives.
Title check✅ PassedThe title clearly describes the main change: updating the E2E test to measure a dropped node against its welded copy.
Description check✅ PassedThe description explains the failure, root cause, changes, related issue, testing, and reviewer context in sufficient detail.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/566-drag-field-weld-frame

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.

@supabase

supabaseBot commented Aug 22, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging9da3e4cCommit Preview URL

Branch Preview URL
Aug 22 2026, 07:32 PM

AndresL230and others added 2 commits August 22, 2026 15:17
)
The scroll-follow half of "a dropped node stays where it was put, and
scrolls with the page" has been red on main since the landing-v5 port
(#524) merged, taking the whole e2e browser lane with it. It is not
flaky: three CI retries and every local run land within 0.2px of each
other, ~98px short of where the assertion expects the node.
The product is right and the journey was wrong. `faq` is the one section
in DragField's `TRACKS`, and `engine/sim.ts::syncClusters()` positions a
tracked cluster at `field.top + a.top + dy`, where
`dy = (track.top - field.top) - trackTop`. That collapses to
`cluster_y = track.top + const`: the cluster is welded to the FAQ
question column, which is `sticky; top:110`. Instrumented over the
journey's own 300px scroll:
scrollY +300.00
#faq top -300.00
track top -202.81 <- pinned at top:110 partway through
cluster top -202.80
probe node y -202.60
error 97.40 (CI: 98.2-98.4)
The column had exactly 202.81px of travel left before its pin, so ~97px
of the scroll happened with the copy held still and the cluster
correctly held still with it. Welding those clusters to their copy is
deliberate — 8bb3486 added it because they slid 374px out from under
the words they belong to.
So the assertion was measuring the one coupling this section does not
have. The other two scroll journeys in this file already measure
relatively — a ring against its own cluster, a cluster against its act's
stage — and both pass; this one reached for raw scrollY, on the single
cluster where that is untrue. It picked cluster 4 for elbow room, not
knowing `faq` is also the only tracked field.
Re-frames it against the weld instead, read from the field's own
`data-drag-track` so the test follows the product rather than
duplicating it: drop the weld and `weldTop()` falls back to the field,
which is the plain 1:1 page-scroll reference. The intent is unchanged —
placed, not detached — and it keeps its teeth: a guard that the weld
target really moved (>100px) plus the exact 300px scroll, so a node
pinned to the screen over a page that never scrolled still fails.
Pointing the same assertion at the untracked section reproduces the
97.40px failure, so it is load-bearing, not vacuous.
Verified on the full local stack: this spec 6/6 twice, full suite 47/48
with one unrelated gradebook flake that passed 3/3 on re-run (retries
are CI-only).
Closes#566
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…566)
Review of the first commit found the fix had bought its elegance with the
thing the test is for.
`weldTop()` read the reference out of `data-drag-track` — the product's
own declaration — and fell back to the field when it was absent. So
deleting the `faq` entry from DragField's `TRACKS` would regress the
product (clusters slide 374px out from under the words again, the bug
8bb3486 fixed) while the test re-framed itself to match and stayed
green. A test that derives its expectation from the code under test
cannot fail when that code is wrong. Proven, not argued: emptying
`TRACKS` against the first version left it passing; against this one the
offset drifts 96.69px and it fails, while the unmutated page sits at
0.45px.
The oracle now belongs to the test — `FAQ_COPY` names the column — and
the reference is resolved section-scoped, the way `engine/sim.ts:296`
resolves it (`(field.closest('section') ?? document)`), so the journey
measures against the element the sim actually bound to instead of
whatever matches first document-wide.
Also from the review:
- The invariant is expressed as an offset that must not change, reusing
the idiom the act-tutor journey already uses, rather than a signed
delta that only worked out because the page scrolls downward.
- The "did the reference move" guard is signed (`< -100`), not
`Math.abs(...) > 100`: scrolling down must carry content up, and an
absolute value would bless a copy that translated the wrong way.
- Preconditions run before the guard, so a page that failed to scroll
says so instead of blaming the weld.
- Node and reference are sampled in ONE `page.evaluate`; two round trips
compared two different frames of a still-integrating sim.
- `toBeCloseTo(300, 0)` rather than exact float equality on a
browser-computed scroll offset.
- The dead `section` parameter is gone. It was never passed, and
`weldTop(page, CLUSTER)` — plausible, given the adjacent constant —
built `#4 .drag-field` and threw a raw SyntaxError.
The deeper finding was coverage: re-framing the only page-relative
assertion in the file left nothing tying a cluster to raw document
scroll, while the file header still teaches that symptom 4 survived
every earlier test because they measured against a field instead of the
page. So the plain case is restored where it is actually true — a new
journey on cluster 6 in `newsletter`, which has no `TRACKS` entry, no
sticky stage, and unlike `cta` has room below it for the scroll. It
carries the original assertion verbatim; measured 1:1 error is 0.58px.
Verified on the full local stack, rebased onto main at 7863210.
Control first, on UNPATCHED main: 47 passed, 1 failed — and the one
failure is this journey, so the lane is still red on current main and
this is still the fix it needs. With the patch applied, two consecutive
full suites: 49 passed, 0 failed. The spec alone, three consecutive
runs: 7/7 each time.
That also closes out the gradebook.spec.ts:35 question raised against
the previous revision. It failed 2 of 3 full-suite runs then, always on
a tree carrying an in-flight #553, and I could not tell a suite-context
flake from a real intermittent in enrollment resolution. #553 has since
merged; across the three full suites above it passed every time. It has
not reproduced on current main, so there is nothing to file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the fix/566-drag-field-weld-frame branch from 5774111 to 9da3e4cCompareAugust 22, 2026 19:30
@AndresL230
AndresL230 merged commit 752a82c into mainAug 23, 2026
7 checks passed
AndresL230 added a commit that referenced this pull request Aug 23, 2026
* Revert "test(e2e): measure a dropped node against the copy it is welded to (#566) (#568)"
This reverts commit 752a82c.
* Revert "Merge pull request #524 from SaplingLearn/feat/landing-v5-port"
This reverts commit 4f5a7f9, reversing
changes made to 0effc9e.
Sign up for freeto 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.

e2e lane red on main since #524: a dropped landing node doesn't scroll with the page

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
test(e2e): measure a dropped node against the copy it is welded to (#566) by AndresL230 · Pull Request #568 · SaplingLearn/Sapling · GitHub
Skip to content

test(e2e): measure a dropped node against the copy it is welded to (#566) - #568

Merged
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame
Aug 23, 2026
Merged

test(e2e): measure a dropped node against the copy it is welded to (#566)#568
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame

Conversation

@AndresL230

@AndresL230AndresL230 commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Fixes the e2e (browser lane) red on main, failing since the landing-v5 port (#524) merged on 2026-08-16.

Closes#566.

The failure

frontend/e2e/landing-drag-field.spec.ts"a dropped node stays where it was put, and scrolls with the page" — fails its scroll-follow assertion:

Expected: < 12 Received: 98.40079543249837

Not flaky: three CI retries land within 0.2px of each other, and it reproduces identically across two CI runs six days apart and every local run.

Root cause

The product is right; the journey was wrong.

faq is the only section in DragField.tsx's TRACKS, and engine/sim.ts::syncClusters() positions a tracked cluster at field.top + a.top + dy where dy = (track.top - field.top) - trackTop. That collapses to cluster_y = track.top + const — the cluster is welded to the FAQ question column, which is sticky; top:110.

Instrumented over the journey's own 300px scroll:

measurementΔ
scrollY+300.00
#faq top−300.00
track top−202.81 ← hit its top:110 pin partway
cluster top−202.80
probe node y−202.60
error97.40 (CI: 98.2–98.4)

The column had exactly 202.81px of travel left before pinning, so ~97px of the scroll happened with the copy held still — and the cluster correctly held still with it. That weld is deliberate: 8bb34869 added it because the clusters were sliding 374px out from under the words they annotate.

The journey asserted raw scrollY on the single cluster where 1:1 page coupling is deliberately untrue. It picked cluster 4 for elbow room, not knowing faq is also the only tracked field.

The change

Two commits — the second is the response to review, and it matters more than the first.

The oracle belongs to the test. The first attempt read the reference out of the field's own data-drag-track and fell back to the field when absent. That was self-defeating: deleting faq from TRACKS would regress the product and re-frame the test to match, keeping it green on exactly the bug it exists to catch. Verified by mutation, not argument — emptying TRACKS leaves the first version passing; against the landed version the offset drifts 96.69px and it fails, while the unmutated page sits at 0.45px. The column is now named in the spec (FAQ_COPY) and resolved section-scoped, the way sim.ts:296 resolves it.

The invariant is an offset that must not change, reusing the idiom the act-tutor journey already uses, instead of a signed delta that only worked because the page scrolls downward. Also from review: the "did the reference move" guard is signed (< -100) rather than Math.abs(...) > 100; preconditions run before the guard so a page that failed to scroll says so; node and reference are sampled in one page.evaluate rather than two frames; toBeCloseTo(300, 0) rather than exact float equality on a browser-computed offset; and the dead section parameter is gone.

The 1:1 case is restored where it is true. Re-framing the file's only page-relative assertion would have left nothing tying a cluster to raw document scroll, while the file header still teaches that symptom 4 survived every earlier test "because they all measured a cluster against its own field rather than against the page". So a new journey covers cluster 6 in newsletter — no TRACKS entry, no sticky stage, and unlike cta it has room below for the scroll. It carries the original assertion verbatim; measured 1:1 error is 0.58px.

Verification

Rebased onto main at 7863210a.

Control, on unpatched main: 47 passed, 1 failed — and the single failure is this journey. The lane is still red on current main, so this is still the fix it needs.

runresult
control (no patch)47 passed, 1 failed ← this journey
full suite, patched, run 149 passed, 0 failed
full suite, patched, run 249 passed, 0 failed
landing-drag-field.spec.ts ×37/7, 7/7, 7/7

Mutation test — the assertion is load-bearing, not vacuous:

product stateoffset driftresult
weld intact0.45pxpasses
faq dropped from TRACKS96.69pxfails

The new newsletter journey measures 1:1 error of 0.58px. tsc --noEmit and eslint clean.

On the gradebook question raised against the previous revision:gradebook.spec.ts:35 failed 2 of 3 full-suite runs then, always on a tree carrying an in-flight #553, and the signature (the Exams category, which exists only on the fall-2025 enrollment) matched "resolved the wrong enrollment" as readily as "rendered slowly". #553 has since merged; across the three full suites above it passed every time. It has not reproduced on current main, so there is nothing to file.

Note the e2e (browser lane) workflow is main-only, so this PR's own checks cannot exercise the lane it fixes — hence the local gate above.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Expanded drag-and-drop coverage for FAQ and untracked sections.
    • Added checks confirming dropped elements maintain their position relative to FAQ content during scrolling.
    • Added validation that dropped elements in untracked sections move consistently with page scrolling.

@coderabbitai

coderabbitaiBot commented Aug 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 116adc67-32b7-4996-a550-5269781748f7

📥 Commits

Reviewing files that changed from the base of the PR and between 7863210 and 9da3e4c.

📒 Files selected for processing (1)
  • frontend/e2e/landing-drag-field.spec.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The landing drag-field E2E tests now compare dropped nodes with FAQ copies during scrolling and verify that nodes in untracked sections move with the document.

Changes

Landing drag-field scroll validation

Layer / File(s)Summary
Scroll probe and section definitions
frontend/e2e/landing-drag-field.spec.ts
The test defines FAQ and untracked-section identifiers. probeAgainstCopy measures the dropped node, its FAQ copy, and the scroll offset in one page evaluation.
Dropped-node scroll assertions
frontend/e2e/landing-drag-field.spec.ts
The FAQ test checks scroll and pre-pinning conditions and preserves the node-to-copy offset. A new test checks 1:1 movement for nodes in untracked sections.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk:⚪ Minimal · up to 9da3e

This change corrects the drag-field E2E assertions for welded FAQ content and restores direct scroll coverage for newsletter content; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers:darkest-teddy

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe changes address issue #566 by correcting the sticky FAQ assertion and preserving direct scroll coverage for an untracked newsletter cluster.
Out of Scope Changes check✅ PassedThe changes are limited to the landing drag-field E2E tests and directly support the linked issue objectives.
Title check✅ PassedThe title clearly describes the main change: updating the E2E test to measure a dropped node against its welded copy.
Description check✅ PassedThe description explains the failure, root cause, changes, related issue, testing, and reviewer context in sufficient detail.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/566-drag-field-weld-frame

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.

@supabase

supabaseBot commented Aug 22, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging9da3e4cCommit Preview URL

Branch Preview URL
Aug 22 2026, 07:32 PM

AndresL230and others added 2 commits August 22, 2026 15:17
)
The scroll-follow half of "a dropped node stays where it was put, and
scrolls with the page" has been red on main since the landing-v5 port
(#524) merged, taking the whole e2e browser lane with it. It is not
flaky: three CI retries and every local run land within 0.2px of each
other, ~98px short of where the assertion expects the node.
The product is right and the journey was wrong. `faq` is the one section
in DragField's `TRACKS`, and `engine/sim.ts::syncClusters()` positions a
tracked cluster at `field.top + a.top + dy`, where
`dy = (track.top - field.top) - trackTop`. That collapses to
`cluster_y = track.top + const`: the cluster is welded to the FAQ
question column, which is `sticky; top:110`. Instrumented over the
journey's own 300px scroll:
scrollY +300.00
#faq top -300.00
track top -202.81 <- pinned at top:110 partway through
cluster top -202.80
probe node y -202.60
error 97.40 (CI: 98.2-98.4)
The column had exactly 202.81px of travel left before its pin, so ~97px
of the scroll happened with the copy held still and the cluster
correctly held still with it. Welding those clusters to their copy is
deliberate — 8bb3486 added it because they slid 374px out from under
the words they belong to.
So the assertion was measuring the one coupling this section does not
have. The other two scroll journeys in this file already measure
relatively — a ring against its own cluster, a cluster against its act's
stage — and both pass; this one reached for raw scrollY, on the single
cluster where that is untrue. It picked cluster 4 for elbow room, not
knowing `faq` is also the only tracked field.
Re-frames it against the weld instead, read from the field's own
`data-drag-track` so the test follows the product rather than
duplicating it: drop the weld and `weldTop()` falls back to the field,
which is the plain 1:1 page-scroll reference. The intent is unchanged —
placed, not detached — and it keeps its teeth: a guard that the weld
target really moved (>100px) plus the exact 300px scroll, so a node
pinned to the screen over a page that never scrolled still fails.
Pointing the same assertion at the untracked section reproduces the
97.40px failure, so it is load-bearing, not vacuous.
Verified on the full local stack: this spec 6/6 twice, full suite 47/48
with one unrelated gradebook flake that passed 3/3 on re-run (retries
are CI-only).
Closes#566
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…566)
Review of the first commit found the fix had bought its elegance with the
thing the test is for.
`weldTop()` read the reference out of `data-drag-track` — the product's
own declaration — and fell back to the field when it was absent. So
deleting the `faq` entry from DragField's `TRACKS` would regress the
product (clusters slide 374px out from under the words again, the bug
8bb3486 fixed) while the test re-framed itself to match and stayed
green. A test that derives its expectation from the code under test
cannot fail when that code is wrong. Proven, not argued: emptying
`TRACKS` against the first version left it passing; against this one the
offset drifts 96.69px and it fails, while the unmutated page sits at
0.45px.
The oracle now belongs to the test — `FAQ_COPY` names the column — and
the reference is resolved section-scoped, the way `engine/sim.ts:296`
resolves it (`(field.closest('section') ?? document)`), so the journey
measures against the element the sim actually bound to instead of
whatever matches first document-wide.
Also from the review:
- The invariant is expressed as an offset that must not change, reusing
the idiom the act-tutor journey already uses, rather than a signed
delta that only worked out because the page scrolls downward.
- The "did the reference move" guard is signed (`< -100`), not
`Math.abs(...) > 100`: scrolling down must carry content up, and an
absolute value would bless a copy that translated the wrong way.
- Preconditions run before the guard, so a page that failed to scroll
says so instead of blaming the weld.
- Node and reference are sampled in ONE `page.evaluate`; two round trips
compared two different frames of a still-integrating sim.
- `toBeCloseTo(300, 0)` rather than exact float equality on a
browser-computed scroll offset.
- The dead `section` parameter is gone. It was never passed, and
`weldTop(page, CLUSTER)` — plausible, given the adjacent constant —
built `#4 .drag-field` and threw a raw SyntaxError.
The deeper finding was coverage: re-framing the only page-relative
assertion in the file left nothing tying a cluster to raw document
scroll, while the file header still teaches that symptom 4 survived
every earlier test because they measured against a field instead of the
page. So the plain case is restored where it is actually true — a new
journey on cluster 6 in `newsletter`, which has no `TRACKS` entry, no
sticky stage, and unlike `cta` has room below it for the scroll. It
carries the original assertion verbatim; measured 1:1 error is 0.58px.
Verified on the full local stack, rebased onto main at 7863210.
Control first, on UNPATCHED main: 47 passed, 1 failed — and the one
failure is this journey, so the lane is still red on current main and
this is still the fix it needs. With the patch applied, two consecutive
full suites: 49 passed, 0 failed. The spec alone, three consecutive
runs: 7/7 each time.
That also closes out the gradebook.spec.ts:35 question raised against
the previous revision. It failed 2 of 3 full-suite runs then, always on
a tree carrying an in-flight #553, and I could not tell a suite-context
flake from a real intermittent in enrollment resolution. #553 has since
merged; across the three full suites above it passed every time. It has
not reproduced on current main, so there is nothing to file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the fix/566-drag-field-weld-frame branch from 5774111 to 9da3e4cCompareAugust 22, 2026 19:30
@AndresL230
AndresL230 merged commit 752a82c into mainAug 23, 2026
7 checks passed
AndresL230 added a commit that referenced this pull request Aug 23, 2026
* Revert "test(e2e): measure a dropped node against the copy it is welded to (#566) (#568)"
This reverts commit 752a82c.
* Revert "Merge pull request #524 from SaplingLearn/feat/landing-v5-port"
This reverts commit 4f5a7f9, reversing
changes made to 0effc9e.
Sign up for freeto 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.

e2e lane red on main since #524: a dropped landing node doesn't scroll with the page

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' test(e2e): measure a dropped node against the copy it is welded to (#566) by AndresL230 · Pull Request #568 · SaplingLearn/Sapling · GitHub
Skip to content

test(e2e): measure a dropped node against the copy it is welded to (#566) - #568

Merged
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame
Aug 23, 2026
Merged

test(e2e): measure a dropped node against the copy it is welded to (#566)#568
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame

Conversation

@AndresL230

@AndresL230AndresL230 commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Fixes the e2e (browser lane) red on main, failing since the landing-v5 port (#524) merged on 2026-08-16.

Closes#566.

The failure

frontend/e2e/landing-drag-field.spec.ts"a dropped node stays where it was put, and scrolls with the page" — fails its scroll-follow assertion:

Expected: < 12 Received: 98.40079543249837

Not flaky: three CI retries land within 0.2px of each other, and it reproduces identically across two CI runs six days apart and every local run.

Root cause

The product is right; the journey was wrong.

faq is the only section in DragField.tsx's TRACKS, and engine/sim.ts::syncClusters() positions a tracked cluster at field.top + a.top + dy where dy = (track.top - field.top) - trackTop. That collapses to cluster_y = track.top + const — the cluster is welded to the FAQ question column, which is sticky; top:110.

Instrumented over the journey's own 300px scroll:

measurementΔ
scrollY+300.00
#faq top−300.00
track top−202.81 ← hit its top:110 pin partway
cluster top−202.80
probe node y−202.60
error97.40 (CI: 98.2–98.4)

The column had exactly 202.81px of travel left before pinning, so ~97px of the scroll happened with the copy held still — and the cluster correctly held still with it. That weld is deliberate: 8bb34869 added it because the clusters were sliding 374px out from under the words they annotate.

The journey asserted raw scrollY on the single cluster where 1:1 page coupling is deliberately untrue. It picked cluster 4 for elbow room, not knowing faq is also the only tracked field.

The change

Two commits — the second is the response to review, and it matters more than the first.

The oracle belongs to the test. The first attempt read the reference out of the field's own data-drag-track and fell back to the field when absent. That was self-defeating: deleting faq from TRACKS would regress the product and re-frame the test to match, keeping it green on exactly the bug it exists to catch. Verified by mutation, not argument — emptying TRACKS leaves the first version passing; against the landed version the offset drifts 96.69px and it fails, while the unmutated page sits at 0.45px. The column is now named in the spec (FAQ_COPY) and resolved section-scoped, the way sim.ts:296 resolves it.

The invariant is an offset that must not change, reusing the idiom the act-tutor journey already uses, instead of a signed delta that only worked because the page scrolls downward. Also from review: the "did the reference move" guard is signed (< -100) rather than Math.abs(...) > 100; preconditions run before the guard so a page that failed to scroll says so; node and reference are sampled in one page.evaluate rather than two frames; toBeCloseTo(300, 0) rather than exact float equality on a browser-computed offset; and the dead section parameter is gone.

The 1:1 case is restored where it is true. Re-framing the file's only page-relative assertion would have left nothing tying a cluster to raw document scroll, while the file header still teaches that symptom 4 survived every earlier test "because they all measured a cluster against its own field rather than against the page". So a new journey covers cluster 6 in newsletter — no TRACKS entry, no sticky stage, and unlike cta it has room below for the scroll. It carries the original assertion verbatim; measured 1:1 error is 0.58px.

Verification

Rebased onto main at 7863210a.

Control, on unpatched main: 47 passed, 1 failed — and the single failure is this journey. The lane is still red on current main, so this is still the fix it needs.

runresult
control (no patch)47 passed, 1 failed ← this journey
full suite, patched, run 149 passed, 0 failed
full suite, patched, run 249 passed, 0 failed
landing-drag-field.spec.ts ×37/7, 7/7, 7/7

Mutation test — the assertion is load-bearing, not vacuous:

product stateoffset driftresult
weld intact0.45pxpasses
faq dropped from TRACKS96.69pxfails

The new newsletter journey measures 1:1 error of 0.58px. tsc --noEmit and eslint clean.

On the gradebook question raised against the previous revision:gradebook.spec.ts:35 failed 2 of 3 full-suite runs then, always on a tree carrying an in-flight #553, and the signature (the Exams category, which exists only on the fall-2025 enrollment) matched "resolved the wrong enrollment" as readily as "rendered slowly". #553 has since merged; across the three full suites above it passed every time. It has not reproduced on current main, so there is nothing to file.

Note the e2e (browser lane) workflow is main-only, so this PR's own checks cannot exercise the lane it fixes — hence the local gate above.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Expanded drag-and-drop coverage for FAQ and untracked sections.
    • Added checks confirming dropped elements maintain their position relative to FAQ content during scrolling.
    • Added validation that dropped elements in untracked sections move consistently with page scrolling.

@coderabbitai

coderabbitaiBot commented Aug 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 116adc67-32b7-4996-a550-5269781748f7

📥 Commits

Reviewing files that changed from the base of the PR and between 7863210 and 9da3e4c.

📒 Files selected for processing (1)
  • frontend/e2e/landing-drag-field.spec.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The landing drag-field E2E tests now compare dropped nodes with FAQ copies during scrolling and verify that nodes in untracked sections move with the document.

Changes

Landing drag-field scroll validation

Layer / File(s)Summary
Scroll probe and section definitions
frontend/e2e/landing-drag-field.spec.ts
The test defines FAQ and untracked-section identifiers. probeAgainstCopy measures the dropped node, its FAQ copy, and the scroll offset in one page evaluation.
Dropped-node scroll assertions
frontend/e2e/landing-drag-field.spec.ts
The FAQ test checks scroll and pre-pinning conditions and preserves the node-to-copy offset. A new test checks 1:1 movement for nodes in untracked sections.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk:⚪ Minimal · up to 9da3e

This change corrects the drag-field E2E assertions for welded FAQ content and restores direct scroll coverage for newsletter content; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers:darkest-teddy

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe changes address issue #566 by correcting the sticky FAQ assertion and preserving direct scroll coverage for an untracked newsletter cluster.
Out of Scope Changes check✅ PassedThe changes are limited to the landing drag-field E2E tests and directly support the linked issue objectives.
Title check✅ PassedThe title clearly describes the main change: updating the E2E test to measure a dropped node against its welded copy.
Description check✅ PassedThe description explains the failure, root cause, changes, related issue, testing, and reviewer context in sufficient detail.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/566-drag-field-weld-frame

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.

@supabase

supabaseBot commented Aug 22, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging9da3e4cCommit Preview URL

Branch Preview URL
Aug 22 2026, 07:32 PM

AndresL230and others added 2 commits August 22, 2026 15:17
)
The scroll-follow half of "a dropped node stays where it was put, and
scrolls with the page" has been red on main since the landing-v5 port
(#524) merged, taking the whole e2e browser lane with it. It is not
flaky: three CI retries and every local run land within 0.2px of each
other, ~98px short of where the assertion expects the node.
The product is right and the journey was wrong. `faq` is the one section
in DragField's `TRACKS`, and `engine/sim.ts::syncClusters()` positions a
tracked cluster at `field.top + a.top + dy`, where
`dy = (track.top - field.top) - trackTop`. That collapses to
`cluster_y = track.top + const`: the cluster is welded to the FAQ
question column, which is `sticky; top:110`. Instrumented over the
journey's own 300px scroll:
scrollY +300.00
#faq top -300.00
track top -202.81 <- pinned at top:110 partway through
cluster top -202.80
probe node y -202.60
error 97.40 (CI: 98.2-98.4)
The column had exactly 202.81px of travel left before its pin, so ~97px
of the scroll happened with the copy held still and the cluster
correctly held still with it. Welding those clusters to their copy is
deliberate — 8bb3486 added it because they slid 374px out from under
the words they belong to.
So the assertion was measuring the one coupling this section does not
have. The other two scroll journeys in this file already measure
relatively — a ring against its own cluster, a cluster against its act's
stage — and both pass; this one reached for raw scrollY, on the single
cluster where that is untrue. It picked cluster 4 for elbow room, not
knowing `faq` is also the only tracked field.
Re-frames it against the weld instead, read from the field's own
`data-drag-track` so the test follows the product rather than
duplicating it: drop the weld and `weldTop()` falls back to the field,
which is the plain 1:1 page-scroll reference. The intent is unchanged —
placed, not detached — and it keeps its teeth: a guard that the weld
target really moved (>100px) plus the exact 300px scroll, so a node
pinned to the screen over a page that never scrolled still fails.
Pointing the same assertion at the untracked section reproduces the
97.40px failure, so it is load-bearing, not vacuous.
Verified on the full local stack: this spec 6/6 twice, full suite 47/48
with one unrelated gradebook flake that passed 3/3 on re-run (retries
are CI-only).
Closes#566
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…566)
Review of the first commit found the fix had bought its elegance with the
thing the test is for.
`weldTop()` read the reference out of `data-drag-track` — the product's
own declaration — and fell back to the field when it was absent. So
deleting the `faq` entry from DragField's `TRACKS` would regress the
product (clusters slide 374px out from under the words again, the bug
8bb3486 fixed) while the test re-framed itself to match and stayed
green. A test that derives its expectation from the code under test
cannot fail when that code is wrong. Proven, not argued: emptying
`TRACKS` against the first version left it passing; against this one the
offset drifts 96.69px and it fails, while the unmutated page sits at
0.45px.
The oracle now belongs to the test — `FAQ_COPY` names the column — and
the reference is resolved section-scoped, the way `engine/sim.ts:296`
resolves it (`(field.closest('section') ?? document)`), so the journey
measures against the element the sim actually bound to instead of
whatever matches first document-wide.
Also from the review:
- The invariant is expressed as an offset that must not change, reusing
the idiom the act-tutor journey already uses, rather than a signed
delta that only worked out because the page scrolls downward.
- The "did the reference move" guard is signed (`< -100`), not
`Math.abs(...) > 100`: scrolling down must carry content up, and an
absolute value would bless a copy that translated the wrong way.
- Preconditions run before the guard, so a page that failed to scroll
says so instead of blaming the weld.
- Node and reference are sampled in ONE `page.evaluate`; two round trips
compared two different frames of a still-integrating sim.
- `toBeCloseTo(300, 0)` rather than exact float equality on a
browser-computed scroll offset.
- The dead `section` parameter is gone. It was never passed, and
`weldTop(page, CLUSTER)` — plausible, given the adjacent constant —
built `#4 .drag-field` and threw a raw SyntaxError.
The deeper finding was coverage: re-framing the only page-relative
assertion in the file left nothing tying a cluster to raw document
scroll, while the file header still teaches that symptom 4 survived
every earlier test because they measured against a field instead of the
page. So the plain case is restored where it is actually true — a new
journey on cluster 6 in `newsletter`, which has no `TRACKS` entry, no
sticky stage, and unlike `cta` has room below it for the scroll. It
carries the original assertion verbatim; measured 1:1 error is 0.58px.
Verified on the full local stack, rebased onto main at 7863210.
Control first, on UNPATCHED main: 47 passed, 1 failed — and the one
failure is this journey, so the lane is still red on current main and
this is still the fix it needs. With the patch applied, two consecutive
full suites: 49 passed, 0 failed. The spec alone, three consecutive
runs: 7/7 each time.
That also closes out the gradebook.spec.ts:35 question raised against
the previous revision. It failed 2 of 3 full-suite runs then, always on
a tree carrying an in-flight #553, and I could not tell a suite-context
flake from a real intermittent in enrollment resolution. #553 has since
merged; across the three full suites above it passed every time. It has
not reproduced on current main, so there is nothing to file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the fix/566-drag-field-weld-frame branch from 5774111 to 9da3e4cCompareAugust 22, 2026 19:30
@AndresL230
AndresL230 merged commit 752a82c into mainAug 23, 2026
7 checks passed
AndresL230 added a commit that referenced this pull request Aug 23, 2026
* Revert "test(e2e): measure a dropped node against the copy it is welded to (#566) (#568)"
This reverts commit 752a82c.
* Revert "Merge pull request #524 from SaplingLearn/feat/landing-v5-port"
This reverts commit 4f5a7f9, reversing
changes made to 0effc9e.
Sign up for freeto 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.

e2e lane red on main since #524: a dropped landing node doesn't scroll with the page

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' test(e2e): measure a dropped node against the copy it is welded to (#566) by AndresL230 · Pull Request #568 · SaplingLearn/Sapling · GitHub
Skip to content

test(e2e): measure a dropped node against the copy it is welded to (#566) - #568

Merged
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame
Aug 23, 2026
Merged

test(e2e): measure a dropped node against the copy it is welded to (#566)#568
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame

Conversation

@AndresL230

@AndresL230AndresL230 commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Fixes the e2e (browser lane) red on main, failing since the landing-v5 port (#524) merged on 2026-08-16.

Closes#566.

The failure

frontend/e2e/landing-drag-field.spec.ts"a dropped node stays where it was put, and scrolls with the page" — fails its scroll-follow assertion:

Expected: < 12 Received: 98.40079543249837

Not flaky: three CI retries land within 0.2px of each other, and it reproduces identically across two CI runs six days apart and every local run.

Root cause

The product is right; the journey was wrong.

faq is the only section in DragField.tsx's TRACKS, and engine/sim.ts::syncClusters() positions a tracked cluster at field.top + a.top + dy where dy = (track.top - field.top) - trackTop. That collapses to cluster_y = track.top + const — the cluster is welded to the FAQ question column, which is sticky; top:110.

Instrumented over the journey's own 300px scroll:

measurementΔ
scrollY+300.00
#faq top−300.00
track top−202.81 ← hit its top:110 pin partway
cluster top−202.80
probe node y−202.60
error97.40 (CI: 98.2–98.4)

The column had exactly 202.81px of travel left before pinning, so ~97px of the scroll happened with the copy held still — and the cluster correctly held still with it. That weld is deliberate: 8bb34869 added it because the clusters were sliding 374px out from under the words they annotate.

The journey asserted raw scrollY on the single cluster where 1:1 page coupling is deliberately untrue. It picked cluster 4 for elbow room, not knowing faq is also the only tracked field.

The change

Two commits — the second is the response to review, and it matters more than the first.

The oracle belongs to the test. The first attempt read the reference out of the field's own data-drag-track and fell back to the field when absent. That was self-defeating: deleting faq from TRACKS would regress the product and re-frame the test to match, keeping it green on exactly the bug it exists to catch. Verified by mutation, not argument — emptying TRACKS leaves the first version passing; against the landed version the offset drifts 96.69px and it fails, while the unmutated page sits at 0.45px. The column is now named in the spec (FAQ_COPY) and resolved section-scoped, the way sim.ts:296 resolves it.

The invariant is an offset that must not change, reusing the idiom the act-tutor journey already uses, instead of a signed delta that only worked because the page scrolls downward. Also from review: the "did the reference move" guard is signed (< -100) rather than Math.abs(...) > 100; preconditions run before the guard so a page that failed to scroll says so; node and reference are sampled in one page.evaluate rather than two frames; toBeCloseTo(300, 0) rather than exact float equality on a browser-computed offset; and the dead section parameter is gone.

The 1:1 case is restored where it is true. Re-framing the file's only page-relative assertion would have left nothing tying a cluster to raw document scroll, while the file header still teaches that symptom 4 survived every earlier test "because they all measured a cluster against its own field rather than against the page". So a new journey covers cluster 6 in newsletter — no TRACKS entry, no sticky stage, and unlike cta it has room below for the scroll. It carries the original assertion verbatim; measured 1:1 error is 0.58px.

Verification

Rebased onto main at 7863210a.

Control, on unpatched main: 47 passed, 1 failed — and the single failure is this journey. The lane is still red on current main, so this is still the fix it needs.

runresult
control (no patch)47 passed, 1 failed ← this journey
full suite, patched, run 149 passed, 0 failed
full suite, patched, run 249 passed, 0 failed
landing-drag-field.spec.ts ×37/7, 7/7, 7/7

Mutation test — the assertion is load-bearing, not vacuous:

product stateoffset driftresult
weld intact0.45pxpasses
faq dropped from TRACKS96.69pxfails

The new newsletter journey measures 1:1 error of 0.58px. tsc --noEmit and eslint clean.

On the gradebook question raised against the previous revision:gradebook.spec.ts:35 failed 2 of 3 full-suite runs then, always on a tree carrying an in-flight #553, and the signature (the Exams category, which exists only on the fall-2025 enrollment) matched "resolved the wrong enrollment" as readily as "rendered slowly". #553 has since merged; across the three full suites above it passed every time. It has not reproduced on current main, so there is nothing to file.

Note the e2e (browser lane) workflow is main-only, so this PR's own checks cannot exercise the lane it fixes — hence the local gate above.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Expanded drag-and-drop coverage for FAQ and untracked sections.
    • Added checks confirming dropped elements maintain their position relative to FAQ content during scrolling.
    • Added validation that dropped elements in untracked sections move consistently with page scrolling.

@coderabbitai

coderabbitaiBot commented Aug 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 116adc67-32b7-4996-a550-5269781748f7

📥 Commits

Reviewing files that changed from the base of the PR and between 7863210 and 9da3e4c.

📒 Files selected for processing (1)
  • frontend/e2e/landing-drag-field.spec.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The landing drag-field E2E tests now compare dropped nodes with FAQ copies during scrolling and verify that nodes in untracked sections move with the document.

Changes

Landing drag-field scroll validation

Layer / File(s)Summary
Scroll probe and section definitions
frontend/e2e/landing-drag-field.spec.ts
The test defines FAQ and untracked-section identifiers. probeAgainstCopy measures the dropped node, its FAQ copy, and the scroll offset in one page evaluation.
Dropped-node scroll assertions
frontend/e2e/landing-drag-field.spec.ts
The FAQ test checks scroll and pre-pinning conditions and preserves the node-to-copy offset. A new test checks 1:1 movement for nodes in untracked sections.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk:⚪ Minimal · up to 9da3e

This change corrects the drag-field E2E assertions for welded FAQ content and restores direct scroll coverage for newsletter content; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers:darkest-teddy

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe changes address issue #566 by correcting the sticky FAQ assertion and preserving direct scroll coverage for an untracked newsletter cluster.
Out of Scope Changes check✅ PassedThe changes are limited to the landing drag-field E2E tests and directly support the linked issue objectives.
Title check✅ PassedThe title clearly describes the main change: updating the E2E test to measure a dropped node against its welded copy.
Description check✅ PassedThe description explains the failure, root cause, changes, related issue, testing, and reviewer context in sufficient detail.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/566-drag-field-weld-frame

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.

@supabase

supabaseBot commented Aug 22, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging9da3e4cCommit Preview URL

Branch Preview URL
Aug 22 2026, 07:32 PM

AndresL230and others added 2 commits August 22, 2026 15:17
)
The scroll-follow half of "a dropped node stays where it was put, and
scrolls with the page" has been red on main since the landing-v5 port
(#524) merged, taking the whole e2e browser lane with it. It is not
flaky: three CI retries and every local run land within 0.2px of each
other, ~98px short of where the assertion expects the node.
The product is right and the journey was wrong. `faq` is the one section
in DragField's `TRACKS`, and `engine/sim.ts::syncClusters()` positions a
tracked cluster at `field.top + a.top + dy`, where
`dy = (track.top - field.top) - trackTop`. That collapses to
`cluster_y = track.top + const`: the cluster is welded to the FAQ
question column, which is `sticky; top:110`. Instrumented over the
journey's own 300px scroll:
scrollY +300.00
#faq top -300.00
track top -202.81 <- pinned at top:110 partway through
cluster top -202.80
probe node y -202.60
error 97.40 (CI: 98.2-98.4)
The column had exactly 202.81px of travel left before its pin, so ~97px
of the scroll happened with the copy held still and the cluster
correctly held still with it. Welding those clusters to their copy is
deliberate — 8bb3486 added it because they slid 374px out from under
the words they belong to.
So the assertion was measuring the one coupling this section does not
have. The other two scroll journeys in this file already measure
relatively — a ring against its own cluster, a cluster against its act's
stage — and both pass; this one reached for raw scrollY, on the single
cluster where that is untrue. It picked cluster 4 for elbow room, not
knowing `faq` is also the only tracked field.
Re-frames it against the weld instead, read from the field's own
`data-drag-track` so the test follows the product rather than
duplicating it: drop the weld and `weldTop()` falls back to the field,
which is the plain 1:1 page-scroll reference. The intent is unchanged —
placed, not detached — and it keeps its teeth: a guard that the weld
target really moved (>100px) plus the exact 300px scroll, so a node
pinned to the screen over a page that never scrolled still fails.
Pointing the same assertion at the untracked section reproduces the
97.40px failure, so it is load-bearing, not vacuous.
Verified on the full local stack: this spec 6/6 twice, full suite 47/48
with one unrelated gradebook flake that passed 3/3 on re-run (retries
are CI-only).
Closes#566
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…566)
Review of the first commit found the fix had bought its elegance with the
thing the test is for.
`weldTop()` read the reference out of `data-drag-track` — the product's
own declaration — and fell back to the field when it was absent. So
deleting the `faq` entry from DragField's `TRACKS` would regress the
product (clusters slide 374px out from under the words again, the bug
8bb3486 fixed) while the test re-framed itself to match and stayed
green. A test that derives its expectation from the code under test
cannot fail when that code is wrong. Proven, not argued: emptying
`TRACKS` against the first version left it passing; against this one the
offset drifts 96.69px and it fails, while the unmutated page sits at
0.45px.
The oracle now belongs to the test — `FAQ_COPY` names the column — and
the reference is resolved section-scoped, the way `engine/sim.ts:296`
resolves it (`(field.closest('section') ?? document)`), so the journey
measures against the element the sim actually bound to instead of
whatever matches first document-wide.
Also from the review:
- The invariant is expressed as an offset that must not change, reusing
the idiom the act-tutor journey already uses, rather than a signed
delta that only worked out because the page scrolls downward.
- The "did the reference move" guard is signed (`< -100`), not
`Math.abs(...) > 100`: scrolling down must carry content up, and an
absolute value would bless a copy that translated the wrong way.
- Preconditions run before the guard, so a page that failed to scroll
says so instead of blaming the weld.
- Node and reference are sampled in ONE `page.evaluate`; two round trips
compared two different frames of a still-integrating sim.
- `toBeCloseTo(300, 0)` rather than exact float equality on a
browser-computed scroll offset.
- The dead `section` parameter is gone. It was never passed, and
`weldTop(page, CLUSTER)` — plausible, given the adjacent constant —
built `#4 .drag-field` and threw a raw SyntaxError.
The deeper finding was coverage: re-framing the only page-relative
assertion in the file left nothing tying a cluster to raw document
scroll, while the file header still teaches that symptom 4 survived
every earlier test because they measured against a field instead of the
page. So the plain case is restored where it is actually true — a new
journey on cluster 6 in `newsletter`, which has no `TRACKS` entry, no
sticky stage, and unlike `cta` has room below it for the scroll. It
carries the original assertion verbatim; measured 1:1 error is 0.58px.
Verified on the full local stack, rebased onto main at 7863210.
Control first, on UNPATCHED main: 47 passed, 1 failed — and the one
failure is this journey, so the lane is still red on current main and
this is still the fix it needs. With the patch applied, two consecutive
full suites: 49 passed, 0 failed. The spec alone, three consecutive
runs: 7/7 each time.
That also closes out the gradebook.spec.ts:35 question raised against
the previous revision. It failed 2 of 3 full-suite runs then, always on
a tree carrying an in-flight #553, and I could not tell a suite-context
flake from a real intermittent in enrollment resolution. #553 has since
merged; across the three full suites above it passed every time. It has
not reproduced on current main, so there is nothing to file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the fix/566-drag-field-weld-frame branch from 5774111 to 9da3e4cCompareAugust 22, 2026 19:30
@AndresL230
AndresL230 merged commit 752a82c into mainAug 23, 2026
7 checks passed
AndresL230 added a commit that referenced this pull request Aug 23, 2026
* Revert "test(e2e): measure a dropped node against the copy it is welded to (#566) (#568)"
This reverts commit 752a82c.
* Revert "Merge pull request #524 from SaplingLearn/feat/landing-v5-port"
This reverts commit 4f5a7f9, reversing
changes made to 0effc9e.
Sign up for freeto 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.

e2e lane red on main since #524: a dropped landing node doesn't scroll with the page

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' test(e2e): measure a dropped node against the copy it is welded to (#566) by AndresL230 · Pull Request #568 · SaplingLearn/Sapling · GitHub
Skip to content

test(e2e): measure a dropped node against the copy it is welded to (#566) - #568

Merged
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame
Aug 23, 2026
Merged

test(e2e): measure a dropped node against the copy it is welded to (#566)#568
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame

Conversation

@AndresL230

@AndresL230AndresL230 commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Fixes the e2e (browser lane) red on main, failing since the landing-v5 port (#524) merged on 2026-08-16.

Closes#566.

The failure

frontend/e2e/landing-drag-field.spec.ts"a dropped node stays where it was put, and scrolls with the page" — fails its scroll-follow assertion:

Expected: < 12 Received: 98.40079543249837

Not flaky: three CI retries land within 0.2px of each other, and it reproduces identically across two CI runs six days apart and every local run.

Root cause

The product is right; the journey was wrong.

faq is the only section in DragField.tsx's TRACKS, and engine/sim.ts::syncClusters() positions a tracked cluster at field.top + a.top + dy where dy = (track.top - field.top) - trackTop. That collapses to cluster_y = track.top + const — the cluster is welded to the FAQ question column, which is sticky; top:110.

Instrumented over the journey's own 300px scroll:

measurementΔ
scrollY+300.00
#faq top−300.00
track top−202.81 ← hit its top:110 pin partway
cluster top−202.80
probe node y−202.60
error97.40 (CI: 98.2–98.4)

The column had exactly 202.81px of travel left before pinning, so ~97px of the scroll happened with the copy held still — and the cluster correctly held still with it. That weld is deliberate: 8bb34869 added it because the clusters were sliding 374px out from under the words they annotate.

The journey asserted raw scrollY on the single cluster where 1:1 page coupling is deliberately untrue. It picked cluster 4 for elbow room, not knowing faq is also the only tracked field.

The change

Two commits — the second is the response to review, and it matters more than the first.

The oracle belongs to the test. The first attempt read the reference out of the field's own data-drag-track and fell back to the field when absent. That was self-defeating: deleting faq from TRACKS would regress the product and re-frame the test to match, keeping it green on exactly the bug it exists to catch. Verified by mutation, not argument — emptying TRACKS leaves the first version passing; against the landed version the offset drifts 96.69px and it fails, while the unmutated page sits at 0.45px. The column is now named in the spec (FAQ_COPY) and resolved section-scoped, the way sim.ts:296 resolves it.

The invariant is an offset that must not change, reusing the idiom the act-tutor journey already uses, instead of a signed delta that only worked because the page scrolls downward. Also from review: the "did the reference move" guard is signed (< -100) rather than Math.abs(...) > 100; preconditions run before the guard so a page that failed to scroll says so; node and reference are sampled in one page.evaluate rather than two frames; toBeCloseTo(300, 0) rather than exact float equality on a browser-computed offset; and the dead section parameter is gone.

The 1:1 case is restored where it is true. Re-framing the file's only page-relative assertion would have left nothing tying a cluster to raw document scroll, while the file header still teaches that symptom 4 survived every earlier test "because they all measured a cluster against its own field rather than against the page". So a new journey covers cluster 6 in newsletter — no TRACKS entry, no sticky stage, and unlike cta it has room below for the scroll. It carries the original assertion verbatim; measured 1:1 error is 0.58px.

Verification

Rebased onto main at 7863210a.

Control, on unpatched main: 47 passed, 1 failed — and the single failure is this journey. The lane is still red on current main, so this is still the fix it needs.

runresult
control (no patch)47 passed, 1 failed ← this journey
full suite, patched, run 149 passed, 0 failed
full suite, patched, run 249 passed, 0 failed
landing-drag-field.spec.ts ×37/7, 7/7, 7/7

Mutation test — the assertion is load-bearing, not vacuous:

product stateoffset driftresult
weld intact0.45pxpasses
faq dropped from TRACKS96.69pxfails

The new newsletter journey measures 1:1 error of 0.58px. tsc --noEmit and eslint clean.

On the gradebook question raised against the previous revision:gradebook.spec.ts:35 failed 2 of 3 full-suite runs then, always on a tree carrying an in-flight #553, and the signature (the Exams category, which exists only on the fall-2025 enrollment) matched "resolved the wrong enrollment" as readily as "rendered slowly". #553 has since merged; across the three full suites above it passed every time. It has not reproduced on current main, so there is nothing to file.

Note the e2e (browser lane) workflow is main-only, so this PR's own checks cannot exercise the lane it fixes — hence the local gate above.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Expanded drag-and-drop coverage for FAQ and untracked sections.
    • Added checks confirming dropped elements maintain their position relative to FAQ content during scrolling.
    • Added validation that dropped elements in untracked sections move consistently with page scrolling.

@coderabbitai

coderabbitaiBot commented Aug 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 116adc67-32b7-4996-a550-5269781748f7

📥 Commits

Reviewing files that changed from the base of the PR and between 7863210 and 9da3e4c.

📒 Files selected for processing (1)
  • frontend/e2e/landing-drag-field.spec.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The landing drag-field E2E tests now compare dropped nodes with FAQ copies during scrolling and verify that nodes in untracked sections move with the document.

Changes

Landing drag-field scroll validation

Layer / File(s)Summary
Scroll probe and section definitions
frontend/e2e/landing-drag-field.spec.ts
The test defines FAQ and untracked-section identifiers. probeAgainstCopy measures the dropped node, its FAQ copy, and the scroll offset in one page evaluation.
Dropped-node scroll assertions
frontend/e2e/landing-drag-field.spec.ts
The FAQ test checks scroll and pre-pinning conditions and preserves the node-to-copy offset. A new test checks 1:1 movement for nodes in untracked sections.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk:⚪ Minimal · up to 9da3e

This change corrects the drag-field E2E assertions for welded FAQ content and restores direct scroll coverage for newsletter content; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers:darkest-teddy

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe changes address issue #566 by correcting the sticky FAQ assertion and preserving direct scroll coverage for an untracked newsletter cluster.
Out of Scope Changes check✅ PassedThe changes are limited to the landing drag-field E2E tests and directly support the linked issue objectives.
Title check✅ PassedThe title clearly describes the main change: updating the E2E test to measure a dropped node against its welded copy.
Description check✅ PassedThe description explains the failure, root cause, changes, related issue, testing, and reviewer context in sufficient detail.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/566-drag-field-weld-frame

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.

@supabase

supabaseBot commented Aug 22, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging9da3e4cCommit Preview URL

Branch Preview URL
Aug 22 2026, 07:32 PM

AndresL230and others added 2 commits August 22, 2026 15:17
)
The scroll-follow half of "a dropped node stays where it was put, and
scrolls with the page" has been red on main since the landing-v5 port
(#524) merged, taking the whole e2e browser lane with it. It is not
flaky: three CI retries and every local run land within 0.2px of each
other, ~98px short of where the assertion expects the node.
The product is right and the journey was wrong. `faq` is the one section
in DragField's `TRACKS`, and `engine/sim.ts::syncClusters()` positions a
tracked cluster at `field.top + a.top + dy`, where
`dy = (track.top - field.top) - trackTop`. That collapses to
`cluster_y = track.top + const`: the cluster is welded to the FAQ
question column, which is `sticky; top:110`. Instrumented over the
journey's own 300px scroll:
scrollY +300.00
#faq top -300.00
track top -202.81 <- pinned at top:110 partway through
cluster top -202.80
probe node y -202.60
error 97.40 (CI: 98.2-98.4)
The column had exactly 202.81px of travel left before its pin, so ~97px
of the scroll happened with the copy held still and the cluster
correctly held still with it. Welding those clusters to their copy is
deliberate — 8bb3486 added it because they slid 374px out from under
the words they belong to.
So the assertion was measuring the one coupling this section does not
have. The other two scroll journeys in this file already measure
relatively — a ring against its own cluster, a cluster against its act's
stage — and both pass; this one reached for raw scrollY, on the single
cluster where that is untrue. It picked cluster 4 for elbow room, not
knowing `faq` is also the only tracked field.
Re-frames it against the weld instead, read from the field's own
`data-drag-track` so the test follows the product rather than
duplicating it: drop the weld and `weldTop()` falls back to the field,
which is the plain 1:1 page-scroll reference. The intent is unchanged —
placed, not detached — and it keeps its teeth: a guard that the weld
target really moved (>100px) plus the exact 300px scroll, so a node
pinned to the screen over a page that never scrolled still fails.
Pointing the same assertion at the untracked section reproduces the
97.40px failure, so it is load-bearing, not vacuous.
Verified on the full local stack: this spec 6/6 twice, full suite 47/48
with one unrelated gradebook flake that passed 3/3 on re-run (retries
are CI-only).
Closes#566
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…566)
Review of the first commit found the fix had bought its elegance with the
thing the test is for.
`weldTop()` read the reference out of `data-drag-track` — the product's
own declaration — and fell back to the field when it was absent. So
deleting the `faq` entry from DragField's `TRACKS` would regress the
product (clusters slide 374px out from under the words again, the bug
8bb3486 fixed) while the test re-framed itself to match and stayed
green. A test that derives its expectation from the code under test
cannot fail when that code is wrong. Proven, not argued: emptying
`TRACKS` against the first version left it passing; against this one the
offset drifts 96.69px and it fails, while the unmutated page sits at
0.45px.
The oracle now belongs to the test — `FAQ_COPY` names the column — and
the reference is resolved section-scoped, the way `engine/sim.ts:296`
resolves it (`(field.closest('section') ?? document)`), so the journey
measures against the element the sim actually bound to instead of
whatever matches first document-wide.
Also from the review:
- The invariant is expressed as an offset that must not change, reusing
the idiom the act-tutor journey already uses, rather than a signed
delta that only worked out because the page scrolls downward.
- The "did the reference move" guard is signed (`< -100`), not
`Math.abs(...) > 100`: scrolling down must carry content up, and an
absolute value would bless a copy that translated the wrong way.
- Preconditions run before the guard, so a page that failed to scroll
says so instead of blaming the weld.
- Node and reference are sampled in ONE `page.evaluate`; two round trips
compared two different frames of a still-integrating sim.
- `toBeCloseTo(300, 0)` rather than exact float equality on a
browser-computed scroll offset.
- The dead `section` parameter is gone. It was never passed, and
`weldTop(page, CLUSTER)` — plausible, given the adjacent constant —
built `#4 .drag-field` and threw a raw SyntaxError.
The deeper finding was coverage: re-framing the only page-relative
assertion in the file left nothing tying a cluster to raw document
scroll, while the file header still teaches that symptom 4 survived
every earlier test because they measured against a field instead of the
page. So the plain case is restored where it is actually true — a new
journey on cluster 6 in `newsletter`, which has no `TRACKS` entry, no
sticky stage, and unlike `cta` has room below it for the scroll. It
carries the original assertion verbatim; measured 1:1 error is 0.58px.
Verified on the full local stack, rebased onto main at 7863210.
Control first, on UNPATCHED main: 47 passed, 1 failed — and the one
failure is this journey, so the lane is still red on current main and
this is still the fix it needs. With the patch applied, two consecutive
full suites: 49 passed, 0 failed. The spec alone, three consecutive
runs: 7/7 each time.
That also closes out the gradebook.spec.ts:35 question raised against
the previous revision. It failed 2 of 3 full-suite runs then, always on
a tree carrying an in-flight #553, and I could not tell a suite-context
flake from a real intermittent in enrollment resolution. #553 has since
merged; across the three full suites above it passed every time. It has
not reproduced on current main, so there is nothing to file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the fix/566-drag-field-weld-frame branch from 5774111 to 9da3e4cCompareAugust 22, 2026 19:30
@AndresL230
AndresL230 merged commit 752a82c into mainAug 23, 2026
7 checks passed
AndresL230 added a commit that referenced this pull request Aug 23, 2026
* Revert "test(e2e): measure a dropped node against the copy it is welded to (#566) (#568)"
This reverts commit 752a82c.
* Revert "Merge pull request #524 from SaplingLearn/feat/landing-v5-port"
This reverts commit 4f5a7f9, reversing
changes made to 0effc9e.
Sign up for freeto 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.

e2e lane red on main since #524: a dropped landing node doesn't scroll with the page

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' test(e2e): measure a dropped node against the copy it is welded to (#566) by AndresL230 · Pull Request #568 · SaplingLearn/Sapling · GitHub
Skip to content

test(e2e): measure a dropped node against the copy it is welded to (#566) - #568

Merged
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame
Aug 23, 2026
Merged

test(e2e): measure a dropped node against the copy it is welded to (#566)#568
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame

Conversation

@AndresL230

@AndresL230AndresL230 commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Fixes the e2e (browser lane) red on main, failing since the landing-v5 port (#524) merged on 2026-08-16.

Closes#566.

The failure

frontend/e2e/landing-drag-field.spec.ts"a dropped node stays where it was put, and scrolls with the page" — fails its scroll-follow assertion:

Expected: < 12 Received: 98.40079543249837

Not flaky: three CI retries land within 0.2px of each other, and it reproduces identically across two CI runs six days apart and every local run.

Root cause

The product is right; the journey was wrong.

faq is the only section in DragField.tsx's TRACKS, and engine/sim.ts::syncClusters() positions a tracked cluster at field.top + a.top + dy where dy = (track.top - field.top) - trackTop. That collapses to cluster_y = track.top + const — the cluster is welded to the FAQ question column, which is sticky; top:110.

Instrumented over the journey's own 300px scroll:

measurementΔ
scrollY+300.00
#faq top−300.00
track top−202.81 ← hit its top:110 pin partway
cluster top−202.80
probe node y−202.60
error97.40 (CI: 98.2–98.4)

The column had exactly 202.81px of travel left before pinning, so ~97px of the scroll happened with the copy held still — and the cluster correctly held still with it. That weld is deliberate: 8bb34869 added it because the clusters were sliding 374px out from under the words they annotate.

The journey asserted raw scrollY on the single cluster where 1:1 page coupling is deliberately untrue. It picked cluster 4 for elbow room, not knowing faq is also the only tracked field.

The change

Two commits — the second is the response to review, and it matters more than the first.

The oracle belongs to the test. The first attempt read the reference out of the field's own data-drag-track and fell back to the field when absent. That was self-defeating: deleting faq from TRACKS would regress the product and re-frame the test to match, keeping it green on exactly the bug it exists to catch. Verified by mutation, not argument — emptying TRACKS leaves the first version passing; against the landed version the offset drifts 96.69px and it fails, while the unmutated page sits at 0.45px. The column is now named in the spec (FAQ_COPY) and resolved section-scoped, the way sim.ts:296 resolves it.

The invariant is an offset that must not change, reusing the idiom the act-tutor journey already uses, instead of a signed delta that only worked because the page scrolls downward. Also from review: the "did the reference move" guard is signed (< -100) rather than Math.abs(...) > 100; preconditions run before the guard so a page that failed to scroll says so; node and reference are sampled in one page.evaluate rather than two frames; toBeCloseTo(300, 0) rather than exact float equality on a browser-computed offset; and the dead section parameter is gone.

The 1:1 case is restored where it is true. Re-framing the file's only page-relative assertion would have left nothing tying a cluster to raw document scroll, while the file header still teaches that symptom 4 survived every earlier test "because they all measured a cluster against its own field rather than against the page". So a new journey covers cluster 6 in newsletter — no TRACKS entry, no sticky stage, and unlike cta it has room below for the scroll. It carries the original assertion verbatim; measured 1:1 error is 0.58px.

Verification

Rebased onto main at 7863210a.

Control, on unpatched main: 47 passed, 1 failed — and the single failure is this journey. The lane is still red on current main, so this is still the fix it needs.

runresult
control (no patch)47 passed, 1 failed ← this journey
full suite, patched, run 149 passed, 0 failed
full suite, patched, run 249 passed, 0 failed
landing-drag-field.spec.ts ×37/7, 7/7, 7/7

Mutation test — the assertion is load-bearing, not vacuous:

product stateoffset driftresult
weld intact0.45pxpasses
faq dropped from TRACKS96.69pxfails

The new newsletter journey measures 1:1 error of 0.58px. tsc --noEmit and eslint clean.

On the gradebook question raised against the previous revision:gradebook.spec.ts:35 failed 2 of 3 full-suite runs then, always on a tree carrying an in-flight #553, and the signature (the Exams category, which exists only on the fall-2025 enrollment) matched "resolved the wrong enrollment" as readily as "rendered slowly". #553 has since merged; across the three full suites above it passed every time. It has not reproduced on current main, so there is nothing to file.

Note the e2e (browser lane) workflow is main-only, so this PR's own checks cannot exercise the lane it fixes — hence the local gate above.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Expanded drag-and-drop coverage for FAQ and untracked sections.
    • Added checks confirming dropped elements maintain their position relative to FAQ content during scrolling.
    • Added validation that dropped elements in untracked sections move consistently with page scrolling.

@coderabbitai

coderabbitaiBot commented Aug 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 116adc67-32b7-4996-a550-5269781748f7

📥 Commits

Reviewing files that changed from the base of the PR and between 7863210 and 9da3e4c.

📒 Files selected for processing (1)
  • frontend/e2e/landing-drag-field.spec.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The landing drag-field E2E tests now compare dropped nodes with FAQ copies during scrolling and verify that nodes in untracked sections move with the document.

Changes

Landing drag-field scroll validation

Layer / File(s)Summary
Scroll probe and section definitions
frontend/e2e/landing-drag-field.spec.ts
The test defines FAQ and untracked-section identifiers. probeAgainstCopy measures the dropped node, its FAQ copy, and the scroll offset in one page evaluation.
Dropped-node scroll assertions
frontend/e2e/landing-drag-field.spec.ts
The FAQ test checks scroll and pre-pinning conditions and preserves the node-to-copy offset. A new test checks 1:1 movement for nodes in untracked sections.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk:⚪ Minimal · up to 9da3e

This change corrects the drag-field E2E assertions for welded FAQ content and restores direct scroll coverage for newsletter content; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers:darkest-teddy

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe changes address issue #566 by correcting the sticky FAQ assertion and preserving direct scroll coverage for an untracked newsletter cluster.
Out of Scope Changes check✅ PassedThe changes are limited to the landing drag-field E2E tests and directly support the linked issue objectives.
Title check✅ PassedThe title clearly describes the main change: updating the E2E test to measure a dropped node against its welded copy.
Description check✅ PassedThe description explains the failure, root cause, changes, related issue, testing, and reviewer context in sufficient detail.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/566-drag-field-weld-frame

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.

@supabase

supabaseBot commented Aug 22, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging9da3e4cCommit Preview URL

Branch Preview URL
Aug 22 2026, 07:32 PM

AndresL230and others added 2 commits August 22, 2026 15:17
)
The scroll-follow half of "a dropped node stays where it was put, and
scrolls with the page" has been red on main since the landing-v5 port
(#524) merged, taking the whole e2e browser lane with it. It is not
flaky: three CI retries and every local run land within 0.2px of each
other, ~98px short of where the assertion expects the node.
The product is right and the journey was wrong. `faq` is the one section
in DragField's `TRACKS`, and `engine/sim.ts::syncClusters()` positions a
tracked cluster at `field.top + a.top + dy`, where
`dy = (track.top - field.top) - trackTop`. That collapses to
`cluster_y = track.top + const`: the cluster is welded to the FAQ
question column, which is `sticky; top:110`. Instrumented over the
journey's own 300px scroll:
scrollY +300.00
#faq top -300.00
track top -202.81 <- pinned at top:110 partway through
cluster top -202.80
probe node y -202.60
error 97.40 (CI: 98.2-98.4)
The column had exactly 202.81px of travel left before its pin, so ~97px
of the scroll happened with the copy held still and the cluster
correctly held still with it. Welding those clusters to their copy is
deliberate — 8bb3486 added it because they slid 374px out from under
the words they belong to.
So the assertion was measuring the one coupling this section does not
have. The other two scroll journeys in this file already measure
relatively — a ring against its own cluster, a cluster against its act's
stage — and both pass; this one reached for raw scrollY, on the single
cluster where that is untrue. It picked cluster 4 for elbow room, not
knowing `faq` is also the only tracked field.
Re-frames it against the weld instead, read from the field's own
`data-drag-track` so the test follows the product rather than
duplicating it: drop the weld and `weldTop()` falls back to the field,
which is the plain 1:1 page-scroll reference. The intent is unchanged —
placed, not detached — and it keeps its teeth: a guard that the weld
target really moved (>100px) plus the exact 300px scroll, so a node
pinned to the screen over a page that never scrolled still fails.
Pointing the same assertion at the untracked section reproduces the
97.40px failure, so it is load-bearing, not vacuous.
Verified on the full local stack: this spec 6/6 twice, full suite 47/48
with one unrelated gradebook flake that passed 3/3 on re-run (retries
are CI-only).
Closes#566
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…566)
Review of the first commit found the fix had bought its elegance with the
thing the test is for.
`weldTop()` read the reference out of `data-drag-track` — the product's
own declaration — and fell back to the field when it was absent. So
deleting the `faq` entry from DragField's `TRACKS` would regress the
product (clusters slide 374px out from under the words again, the bug
8bb3486 fixed) while the test re-framed itself to match and stayed
green. A test that derives its expectation from the code under test
cannot fail when that code is wrong. Proven, not argued: emptying
`TRACKS` against the first version left it passing; against this one the
offset drifts 96.69px and it fails, while the unmutated page sits at
0.45px.
The oracle now belongs to the test — `FAQ_COPY` names the column — and
the reference is resolved section-scoped, the way `engine/sim.ts:296`
resolves it (`(field.closest('section') ?? document)`), so the journey
measures against the element the sim actually bound to instead of
whatever matches first document-wide.
Also from the review:
- The invariant is expressed as an offset that must not change, reusing
the idiom the act-tutor journey already uses, rather than a signed
delta that only worked out because the page scrolls downward.
- The "did the reference move" guard is signed (`< -100`), not
`Math.abs(...) > 100`: scrolling down must carry content up, and an
absolute value would bless a copy that translated the wrong way.
- Preconditions run before the guard, so a page that failed to scroll
says so instead of blaming the weld.
- Node and reference are sampled in ONE `page.evaluate`; two round trips
compared two different frames of a still-integrating sim.
- `toBeCloseTo(300, 0)` rather than exact float equality on a
browser-computed scroll offset.
- The dead `section` parameter is gone. It was never passed, and
`weldTop(page, CLUSTER)` — plausible, given the adjacent constant —
built `#4 .drag-field` and threw a raw SyntaxError.
The deeper finding was coverage: re-framing the only page-relative
assertion in the file left nothing tying a cluster to raw document
scroll, while the file header still teaches that symptom 4 survived
every earlier test because they measured against a field instead of the
page. So the plain case is restored where it is actually true — a new
journey on cluster 6 in `newsletter`, which has no `TRACKS` entry, no
sticky stage, and unlike `cta` has room below it for the scroll. It
carries the original assertion verbatim; measured 1:1 error is 0.58px.
Verified on the full local stack, rebased onto main at 7863210.
Control first, on UNPATCHED main: 47 passed, 1 failed — and the one
failure is this journey, so the lane is still red on current main and
this is still the fix it needs. With the patch applied, two consecutive
full suites: 49 passed, 0 failed. The spec alone, three consecutive
runs: 7/7 each time.
That also closes out the gradebook.spec.ts:35 question raised against
the previous revision. It failed 2 of 3 full-suite runs then, always on
a tree carrying an in-flight #553, and I could not tell a suite-context
flake from a real intermittent in enrollment resolution. #553 has since
merged; across the three full suites above it passed every time. It has
not reproduced on current main, so there is nothing to file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the fix/566-drag-field-weld-frame branch from 5774111 to 9da3e4cCompareAugust 22, 2026 19:30
@AndresL230
AndresL230 merged commit 752a82c into mainAug 23, 2026
7 checks passed
AndresL230 added a commit that referenced this pull request Aug 23, 2026
* Revert "test(e2e): measure a dropped node against the copy it is welded to (#566) (#568)"
This reverts commit 752a82c.
* Revert "Merge pull request #524 from SaplingLearn/feat/landing-v5-port"
This reverts commit 4f5a7f9, reversing
changes made to 0effc9e.
Sign up for freeto 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.

e2e lane red on main since #524: a dropped landing node doesn't scroll with the page

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' test(e2e): measure a dropped node against the copy it is welded to (#566) by AndresL230 · Pull Request #568 · SaplingLearn/Sapling · GitHub
Skip to content

test(e2e): measure a dropped node against the copy it is welded to (#566) - #568

Merged
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame
Aug 23, 2026
Merged

test(e2e): measure a dropped node against the copy it is welded to (#566)#568
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame

Conversation

@AndresL230

@AndresL230AndresL230 commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Fixes the e2e (browser lane) red on main, failing since the landing-v5 port (#524) merged on 2026-08-16.

Closes#566.

The failure

frontend/e2e/landing-drag-field.spec.ts"a dropped node stays where it was put, and scrolls with the page" — fails its scroll-follow assertion:

Expected: < 12 Received: 98.40079543249837

Not flaky: three CI retries land within 0.2px of each other, and it reproduces identically across two CI runs six days apart and every local run.

Root cause

The product is right; the journey was wrong.

faq is the only section in DragField.tsx's TRACKS, and engine/sim.ts::syncClusters() positions a tracked cluster at field.top + a.top + dy where dy = (track.top - field.top) - trackTop. That collapses to cluster_y = track.top + const — the cluster is welded to the FAQ question column, which is sticky; top:110.

Instrumented over the journey's own 300px scroll:

measurementΔ
scrollY+300.00
#faq top−300.00
track top−202.81 ← hit its top:110 pin partway
cluster top−202.80
probe node y−202.60
error97.40 (CI: 98.2–98.4)

The column had exactly 202.81px of travel left before pinning, so ~97px of the scroll happened with the copy held still — and the cluster correctly held still with it. That weld is deliberate: 8bb34869 added it because the clusters were sliding 374px out from under the words they annotate.

The journey asserted raw scrollY on the single cluster where 1:1 page coupling is deliberately untrue. It picked cluster 4 for elbow room, not knowing faq is also the only tracked field.

The change

Two commits — the second is the response to review, and it matters more than the first.

The oracle belongs to the test. The first attempt read the reference out of the field's own data-drag-track and fell back to the field when absent. That was self-defeating: deleting faq from TRACKS would regress the product and re-frame the test to match, keeping it green on exactly the bug it exists to catch. Verified by mutation, not argument — emptying TRACKS leaves the first version passing; against the landed version the offset drifts 96.69px and it fails, while the unmutated page sits at 0.45px. The column is now named in the spec (FAQ_COPY) and resolved section-scoped, the way sim.ts:296 resolves it.

The invariant is an offset that must not change, reusing the idiom the act-tutor journey already uses, instead of a signed delta that only worked because the page scrolls downward. Also from review: the "did the reference move" guard is signed (< -100) rather than Math.abs(...) > 100; preconditions run before the guard so a page that failed to scroll says so; node and reference are sampled in one page.evaluate rather than two frames; toBeCloseTo(300, 0) rather than exact float equality on a browser-computed offset; and the dead section parameter is gone.

The 1:1 case is restored where it is true. Re-framing the file's only page-relative assertion would have left nothing tying a cluster to raw document scroll, while the file header still teaches that symptom 4 survived every earlier test "because they all measured a cluster against its own field rather than against the page". So a new journey covers cluster 6 in newsletter — no TRACKS entry, no sticky stage, and unlike cta it has room below for the scroll. It carries the original assertion verbatim; measured 1:1 error is 0.58px.

Verification

Rebased onto main at 7863210a.

Control, on unpatched main: 47 passed, 1 failed — and the single failure is this journey. The lane is still red on current main, so this is still the fix it needs.

runresult
control (no patch)47 passed, 1 failed ← this journey
full suite, patched, run 149 passed, 0 failed
full suite, patched, run 249 passed, 0 failed
landing-drag-field.spec.ts ×37/7, 7/7, 7/7

Mutation test — the assertion is load-bearing, not vacuous:

product stateoffset driftresult
weld intact0.45pxpasses
faq dropped from TRACKS96.69pxfails

The new newsletter journey measures 1:1 error of 0.58px. tsc --noEmit and eslint clean.

On the gradebook question raised against the previous revision:gradebook.spec.ts:35 failed 2 of 3 full-suite runs then, always on a tree carrying an in-flight #553, and the signature (the Exams category, which exists only on the fall-2025 enrollment) matched "resolved the wrong enrollment" as readily as "rendered slowly". #553 has since merged; across the three full suites above it passed every time. It has not reproduced on current main, so there is nothing to file.

Note the e2e (browser lane) workflow is main-only, so this PR's own checks cannot exercise the lane it fixes — hence the local gate above.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Expanded drag-and-drop coverage for FAQ and untracked sections.
    • Added checks confirming dropped elements maintain their position relative to FAQ content during scrolling.
    • Added validation that dropped elements in untracked sections move consistently with page scrolling.

@coderabbitai

coderabbitaiBot commented Aug 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 116adc67-32b7-4996-a550-5269781748f7

📥 Commits

Reviewing files that changed from the base of the PR and between 7863210 and 9da3e4c.

📒 Files selected for processing (1)
  • frontend/e2e/landing-drag-field.spec.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The landing drag-field E2E tests now compare dropped nodes with FAQ copies during scrolling and verify that nodes in untracked sections move with the document.

Changes

Landing drag-field scroll validation

Layer / File(s)Summary
Scroll probe and section definitions
frontend/e2e/landing-drag-field.spec.ts
The test defines FAQ and untracked-section identifiers. probeAgainstCopy measures the dropped node, its FAQ copy, and the scroll offset in one page evaluation.
Dropped-node scroll assertions
frontend/e2e/landing-drag-field.spec.ts
The FAQ test checks scroll and pre-pinning conditions and preserves the node-to-copy offset. A new test checks 1:1 movement for nodes in untracked sections.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk:⚪ Minimal · up to 9da3e

This change corrects the drag-field E2E assertions for welded FAQ content and restores direct scroll coverage for newsletter content; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers:darkest-teddy

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe changes address issue #566 by correcting the sticky FAQ assertion and preserving direct scroll coverage for an untracked newsletter cluster.
Out of Scope Changes check✅ PassedThe changes are limited to the landing drag-field E2E tests and directly support the linked issue objectives.
Title check✅ PassedThe title clearly describes the main change: updating the E2E test to measure a dropped node against its welded copy.
Description check✅ PassedThe description explains the failure, root cause, changes, related issue, testing, and reviewer context in sufficient detail.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/566-drag-field-weld-frame

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.

@supabase

supabaseBot commented Aug 22, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging9da3e4cCommit Preview URL

Branch Preview URL
Aug 22 2026, 07:32 PM

AndresL230and others added 2 commits August 22, 2026 15:17
)
The scroll-follow half of "a dropped node stays where it was put, and
scrolls with the page" has been red on main since the landing-v5 port
(#524) merged, taking the whole e2e browser lane with it. It is not
flaky: three CI retries and every local run land within 0.2px of each
other, ~98px short of where the assertion expects the node.
The product is right and the journey was wrong. `faq` is the one section
in DragField's `TRACKS`, and `engine/sim.ts::syncClusters()` positions a
tracked cluster at `field.top + a.top + dy`, where
`dy = (track.top - field.top) - trackTop`. That collapses to
`cluster_y = track.top + const`: the cluster is welded to the FAQ
question column, which is `sticky; top:110`. Instrumented over the
journey's own 300px scroll:
scrollY +300.00
#faq top -300.00
track top -202.81 <- pinned at top:110 partway through
cluster top -202.80
probe node y -202.60
error 97.40 (CI: 98.2-98.4)
The column had exactly 202.81px of travel left before its pin, so ~97px
of the scroll happened with the copy held still and the cluster
correctly held still with it. Welding those clusters to their copy is
deliberate — 8bb3486 added it because they slid 374px out from under
the words they belong to.
So the assertion was measuring the one coupling this section does not
have. The other two scroll journeys in this file already measure
relatively — a ring against its own cluster, a cluster against its act's
stage — and both pass; this one reached for raw scrollY, on the single
cluster where that is untrue. It picked cluster 4 for elbow room, not
knowing `faq` is also the only tracked field.
Re-frames it against the weld instead, read from the field's own
`data-drag-track` so the test follows the product rather than
duplicating it: drop the weld and `weldTop()` falls back to the field,
which is the plain 1:1 page-scroll reference. The intent is unchanged —
placed, not detached — and it keeps its teeth: a guard that the weld
target really moved (>100px) plus the exact 300px scroll, so a node
pinned to the screen over a page that never scrolled still fails.
Pointing the same assertion at the untracked section reproduces the
97.40px failure, so it is load-bearing, not vacuous.
Verified on the full local stack: this spec 6/6 twice, full suite 47/48
with one unrelated gradebook flake that passed 3/3 on re-run (retries
are CI-only).
Closes#566
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…566)
Review of the first commit found the fix had bought its elegance with the
thing the test is for.
`weldTop()` read the reference out of `data-drag-track` — the product's
own declaration — and fell back to the field when it was absent. So
deleting the `faq` entry from DragField's `TRACKS` would regress the
product (clusters slide 374px out from under the words again, the bug
8bb3486 fixed) while the test re-framed itself to match and stayed
green. A test that derives its expectation from the code under test
cannot fail when that code is wrong. Proven, not argued: emptying
`TRACKS` against the first version left it passing; against this one the
offset drifts 96.69px and it fails, while the unmutated page sits at
0.45px.
The oracle now belongs to the test — `FAQ_COPY` names the column — and
the reference is resolved section-scoped, the way `engine/sim.ts:296`
resolves it (`(field.closest('section') ?? document)`), so the journey
measures against the element the sim actually bound to instead of
whatever matches first document-wide.
Also from the review:
- The invariant is expressed as an offset that must not change, reusing
the idiom the act-tutor journey already uses, rather than a signed
delta that only worked out because the page scrolls downward.
- The "did the reference move" guard is signed (`< -100`), not
`Math.abs(...) > 100`: scrolling down must carry content up, and an
absolute value would bless a copy that translated the wrong way.
- Preconditions run before the guard, so a page that failed to scroll
says so instead of blaming the weld.
- Node and reference are sampled in ONE `page.evaluate`; two round trips
compared two different frames of a still-integrating sim.
- `toBeCloseTo(300, 0)` rather than exact float equality on a
browser-computed scroll offset.
- The dead `section` parameter is gone. It was never passed, and
`weldTop(page, CLUSTER)` — plausible, given the adjacent constant —
built `#4 .drag-field` and threw a raw SyntaxError.
The deeper finding was coverage: re-framing the only page-relative
assertion in the file left nothing tying a cluster to raw document
scroll, while the file header still teaches that symptom 4 survived
every earlier test because they measured against a field instead of the
page. So the plain case is restored where it is actually true — a new
journey on cluster 6 in `newsletter`, which has no `TRACKS` entry, no
sticky stage, and unlike `cta` has room below it for the scroll. It
carries the original assertion verbatim; measured 1:1 error is 0.58px.
Verified on the full local stack, rebased onto main at 7863210.
Control first, on UNPATCHED main: 47 passed, 1 failed — and the one
failure is this journey, so the lane is still red on current main and
this is still the fix it needs. With the patch applied, two consecutive
full suites: 49 passed, 0 failed. The spec alone, three consecutive
runs: 7/7 each time.
That also closes out the gradebook.spec.ts:35 question raised against
the previous revision. It failed 2 of 3 full-suite runs then, always on
a tree carrying an in-flight #553, and I could not tell a suite-context
flake from a real intermittent in enrollment resolution. #553 has since
merged; across the three full suites above it passed every time. It has
not reproduced on current main, so there is nothing to file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the fix/566-drag-field-weld-frame branch from 5774111 to 9da3e4cCompareAugust 22, 2026 19:30
@AndresL230
AndresL230 merged commit 752a82c into mainAug 23, 2026
7 checks passed
AndresL230 added a commit that referenced this pull request Aug 23, 2026
* Revert "test(e2e): measure a dropped node against the copy it is welded to (#566) (#568)"
This reverts commit 752a82c.
* Revert "Merge pull request #524 from SaplingLearn/feat/landing-v5-port"
This reverts commit 4f5a7f9, reversing
changes made to 0effc9e.
Sign up for freeto 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.

e2e lane red on main since #524: a dropped landing node doesn't scroll with the page

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); test(e2e): measure a dropped node against the copy it is welded to (#566) by AndresL230 · Pull Request #568 · SaplingLearn/Sapling · GitHub
Skip to content

test(e2e): measure a dropped node against the copy it is welded to (#566) - #568

Merged
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame
Aug 23, 2026
Merged

test(e2e): measure a dropped node against the copy it is welded to (#566)#568
AndresL230 merged 2 commits into
mainfrom
fix/566-drag-field-weld-frame

Conversation

@AndresL230

@AndresL230AndresL230 commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Fixes the e2e (browser lane) red on main, failing since the landing-v5 port (#524) merged on 2026-08-16.

Closes#566.

The failure

frontend/e2e/landing-drag-field.spec.ts"a dropped node stays where it was put, and scrolls with the page" — fails its scroll-follow assertion:

Expected: < 12 Received: 98.40079543249837

Not flaky: three CI retries land within 0.2px of each other, and it reproduces identically across two CI runs six days apart and every local run.

Root cause

The product is right; the journey was wrong.

faq is the only section in DragField.tsx's TRACKS, and engine/sim.ts::syncClusters() positions a tracked cluster at field.top + a.top + dy where dy = (track.top - field.top) - trackTop. That collapses to cluster_y = track.top + const — the cluster is welded to the FAQ question column, which is sticky; top:110.

Instrumented over the journey's own 300px scroll:

measurementΔ
scrollY+300.00
#faq top−300.00
track top−202.81 ← hit its top:110 pin partway
cluster top−202.80
probe node y−202.60
error97.40 (CI: 98.2–98.4)

The column had exactly 202.81px of travel left before pinning, so ~97px of the scroll happened with the copy held still — and the cluster correctly held still with it. That weld is deliberate: 8bb34869 added it because the clusters were sliding 374px out from under the words they annotate.

The journey asserted raw scrollY on the single cluster where 1:1 page coupling is deliberately untrue. It picked cluster 4 for elbow room, not knowing faq is also the only tracked field.

The change

Two commits — the second is the response to review, and it matters more than the first.

The oracle belongs to the test. The first attempt read the reference out of the field's own data-drag-track and fell back to the field when absent. That was self-defeating: deleting faq from TRACKS would regress the product and re-frame the test to match, keeping it green on exactly the bug it exists to catch. Verified by mutation, not argument — emptying TRACKS leaves the first version passing; against the landed version the offset drifts 96.69px and it fails, while the unmutated page sits at 0.45px. The column is now named in the spec (FAQ_COPY) and resolved section-scoped, the way sim.ts:296 resolves it.

The invariant is an offset that must not change, reusing the idiom the act-tutor journey already uses, instead of a signed delta that only worked because the page scrolls downward. Also from review: the "did the reference move" guard is signed (< -100) rather than Math.abs(...) > 100; preconditions run before the guard so a page that failed to scroll says so; node and reference are sampled in one page.evaluate rather than two frames; toBeCloseTo(300, 0) rather than exact float equality on a browser-computed offset; and the dead section parameter is gone.

The 1:1 case is restored where it is true. Re-framing the file's only page-relative assertion would have left nothing tying a cluster to raw document scroll, while the file header still teaches that symptom 4 survived every earlier test "because they all measured a cluster against its own field rather than against the page". So a new journey covers cluster 6 in newsletter — no TRACKS entry, no sticky stage, and unlike cta it has room below for the scroll. It carries the original assertion verbatim; measured 1:1 error is 0.58px.

Verification

Rebased onto main at 7863210a.

Control, on unpatched main: 47 passed, 1 failed — and the single failure is this journey. The lane is still red on current main, so this is still the fix it needs.

runresult
control (no patch)47 passed, 1 failed ← this journey
full suite, patched, run 149 passed, 0 failed
full suite, patched, run 249 passed, 0 failed
landing-drag-field.spec.ts ×37/7, 7/7, 7/7

Mutation test — the assertion is load-bearing, not vacuous:

product stateoffset driftresult
weld intact0.45pxpasses
faq dropped from TRACKS96.69pxfails

The new newsletter journey measures 1:1 error of 0.58px. tsc --noEmit and eslint clean.

On the gradebook question raised against the previous revision:gradebook.spec.ts:35 failed 2 of 3 full-suite runs then, always on a tree carrying an in-flight #553, and the signature (the Exams category, which exists only on the fall-2025 enrollment) matched "resolved the wrong enrollment" as readily as "rendered slowly". #553 has since merged; across the three full suites above it passed every time. It has not reproduced on current main, so there is nothing to file.

Note the e2e (browser lane) workflow is main-only, so this PR's own checks cannot exercise the lane it fixes — hence the local gate above.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Expanded drag-and-drop coverage for FAQ and untracked sections.
    • Added checks confirming dropped elements maintain their position relative to FAQ content during scrolling.
    • Added validation that dropped elements in untracked sections move consistently with page scrolling.

@coderabbitai

coderabbitaiBot commented Aug 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 116adc67-32b7-4996-a550-5269781748f7

📥 Commits

Reviewing files that changed from the base of the PR and between 7863210 and 9da3e4c.

📒 Files selected for processing (1)
  • frontend/e2e/landing-drag-field.spec.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The landing drag-field E2E tests now compare dropped nodes with FAQ copies during scrolling and verify that nodes in untracked sections move with the document.

Changes

Landing drag-field scroll validation

Layer / File(s)Summary
Scroll probe and section definitions
frontend/e2e/landing-drag-field.spec.ts
The test defines FAQ and untracked-section identifiers. probeAgainstCopy measures the dropped node, its FAQ copy, and the scroll offset in one page evaluation.
Dropped-node scroll assertions
frontend/e2e/landing-drag-field.spec.ts
The FAQ test checks scroll and pre-pinning conditions and preserves the node-to-copy offset. A new test checks 1:1 movement for nodes in untracked sections.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk:⚪ Minimal · up to 9da3e

This change corrects the drag-field E2E assertions for welded FAQ content and restores direct scroll coverage for newsletter content; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers:darkest-teddy

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedThe changes address issue #566 by correcting the sticky FAQ assertion and preserving direct scroll coverage for an untracked newsletter cluster.
Out of Scope Changes check✅ PassedThe changes are limited to the landing drag-field E2E tests and directly support the linked issue objectives.
Title check✅ PassedThe title clearly describes the main change: updating the E2E test to measure a dropped node against its welded copy.
Description check✅ PassedThe description explains the failure, root cause, changes, related issue, testing, and reviewer context in sufficient detail.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/566-drag-field-weld-frame

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.

@supabase

supabaseBot commented Aug 22, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project ybgqdonkoqftwrmweuyv because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Aug 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging9da3e4cCommit Preview URL

Branch Preview URL
Aug 22 2026, 07:32 PM

AndresL230and others added 2 commits August 22, 2026 15:17
)
The scroll-follow half of "a dropped node stays where it was put, and
scrolls with the page" has been red on main since the landing-v5 port
(#524) merged, taking the whole e2e browser lane with it. It is not
flaky: three CI retries and every local run land within 0.2px of each
other, ~98px short of where the assertion expects the node.
The product is right and the journey was wrong. `faq` is the one section
in DragField's `TRACKS`, and `engine/sim.ts::syncClusters()` positions a
tracked cluster at `field.top + a.top + dy`, where
`dy = (track.top - field.top) - trackTop`. That collapses to
`cluster_y = track.top + const`: the cluster is welded to the FAQ
question column, which is `sticky; top:110`. Instrumented over the
journey's own 300px scroll:
scrollY +300.00
#faq top -300.00
track top -202.81 <- pinned at top:110 partway through
cluster top -202.80
probe node y -202.60
error 97.40 (CI: 98.2-98.4)
The column had exactly 202.81px of travel left before its pin, so ~97px
of the scroll happened with the copy held still and the cluster
correctly held still with it. Welding those clusters to their copy is
deliberate — 8bb3486 added it because they slid 374px out from under
the words they belong to.
So the assertion was measuring the one coupling this section does not
have. The other two scroll journeys in this file already measure
relatively — a ring against its own cluster, a cluster against its act's
stage — and both pass; this one reached for raw scrollY, on the single
cluster where that is untrue. It picked cluster 4 for elbow room, not
knowing `faq` is also the only tracked field.
Re-frames it against the weld instead, read from the field's own
`data-drag-track` so the test follows the product rather than
duplicating it: drop the weld and `weldTop()` falls back to the field,
which is the plain 1:1 page-scroll reference. The intent is unchanged —
placed, not detached — and it keeps its teeth: a guard that the weld
target really moved (>100px) plus the exact 300px scroll, so a node
pinned to the screen over a page that never scrolled still fails.
Pointing the same assertion at the untracked section reproduces the
97.40px failure, so it is load-bearing, not vacuous.
Verified on the full local stack: this spec 6/6 twice, full suite 47/48
with one unrelated gradebook flake that passed 3/3 on re-run (retries
are CI-only).
Closes#566
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…566)
Review of the first commit found the fix had bought its elegance with the
thing the test is for.
`weldTop()` read the reference out of `data-drag-track` — the product's
own declaration — and fell back to the field when it was absent. So
deleting the `faq` entry from DragField's `TRACKS` would regress the
product (clusters slide 374px out from under the words again, the bug
8bb3486 fixed) while the test re-framed itself to match and stayed
green. A test that derives its expectation from the code under test
cannot fail when that code is wrong. Proven, not argued: emptying
`TRACKS` against the first version left it passing; against this one the
offset drifts 96.69px and it fails, while the unmutated page sits at
0.45px.
The oracle now belongs to the test — `FAQ_COPY` names the column — and
the reference is resolved section-scoped, the way `engine/sim.ts:296`
resolves it (`(field.closest('section') ?? document)`), so the journey
measures against the element the sim actually bound to instead of
whatever matches first document-wide.
Also from the review:
- The invariant is expressed as an offset that must not change, reusing
the idiom the act-tutor journey already uses, rather than a signed
delta that only worked out because the page scrolls downward.
- The "did the reference move" guard is signed (`< -100`), not
`Math.abs(...) > 100`: scrolling down must carry content up, and an
absolute value would bless a copy that translated the wrong way.
- Preconditions run before the guard, so a page that failed to scroll
says so instead of blaming the weld.
- Node and reference are sampled in ONE `page.evaluate`; two round trips
compared two different frames of a still-integrating sim.
- `toBeCloseTo(300, 0)` rather than exact float equality on a
browser-computed scroll offset.
- The dead `section` parameter is gone. It was never passed, and
`weldTop(page, CLUSTER)` — plausible, given the adjacent constant —
built `#4 .drag-field` and threw a raw SyntaxError.
The deeper finding was coverage: re-framing the only page-relative
assertion in the file left nothing tying a cluster to raw document
scroll, while the file header still teaches that symptom 4 survived
every earlier test because they measured against a field instead of the
page. So the plain case is restored where it is actually true — a new
journey on cluster 6 in `newsletter`, which has no `TRACKS` entry, no
sticky stage, and unlike `cta` has room below it for the scroll. It
carries the original assertion verbatim; measured 1:1 error is 0.58px.
Verified on the full local stack, rebased onto main at 7863210.
Control first, on UNPATCHED main: 47 passed, 1 failed — and the one
failure is this journey, so the lane is still red on current main and
this is still the fix it needs. With the patch applied, two consecutive
full suites: 49 passed, 0 failed. The spec alone, three consecutive
runs: 7/7 each time.
That also closes out the gradebook.spec.ts:35 question raised against
the previous revision. It failed 2 of 3 full-suite runs then, always on
a tree carrying an in-flight #553, and I could not tell a suite-context
flake from a real intermittent in enrollment resolution. #553 has since
merged; across the three full suites above it passed every time. It has
not reproduced on current main, so there is nothing to file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the fix/566-drag-field-weld-frame branch from 5774111 to 9da3e4cCompareAugust 22, 2026 19:30
@AndresL230
AndresL230 merged commit 752a82c into mainAug 23, 2026
7 checks passed
AndresL230 added a commit that referenced this pull request Aug 23, 2026
* Revert "test(e2e): measure a dropped node against the copy it is welded to (#566) (#568)"
This reverts commit 752a82c.
* Revert "Merge pull request #524 from SaplingLearn/feat/landing-v5-port"
This reverts commit 4f5a7f9, reversing
changes made to 0effc9e.
Sign up for freeto 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.

e2e lane red on main since #524: a dropped landing node doesn't scroll with the page

1 participant

@AndresL230