Skip to content

Press the mark where the reader can see it - #37

Closed
leaf-agent wants to merge 3 commits into
mainfrom
fix/ci-32749573557
Closed

Press the mark where the reader can see it#37
leaf-agent wants to merge 3 commits into
mainfrom
fix/ci-32749573557

Conversation

@leaf-agent

@leaf-agent leaf-agent commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Problem

ci is red on main at 91515b5 with two failures. This PR is the new one: test_an_open_composer_does_not_eat_the_next_click times out in panel_settled — a mark it pressed did not open its thread. It was green on the run before, and 91515b5 is the commit that reaches it. (The other failure, test_a_note_sets_the_page_axis_with_its_whole_strip, is the fifth occurrence of the 7.5px scroll gutter and belongs to #36, which is open; nothing about it is in this diff.)

The test opens its composer on #q, and reaching #q scrolls the commented paragraph 141px above the top of the window. It then presses the mark up there anyway. A painted range keeps its client rects while it is scrolled away — they simply go negative — so mark_point handed back (413, -142) on a 1200×900 window and page.mouse pressed it. Nothing above the fold is anyone's: elementFromPoint answers null outside the window, so the press arrived at <html> and the panel stayed shut.

It used to be heard because the old hit test only vetoed inUi(over), which is false for null, and then matched the point against the rects' arithmetic — which happily contains a point off the screen. 91515b5 replaced that veto with pageWords, whose first clause is Boolean(node), so markAt now declines a point that is over none of the page's words.

Solution

The fix is in the test. The runtime is right — a point outside the window is over none of the page's words, and a reader cannot press there; elementFromPoint returns an element for every point inside it, so no real press is affected. What was wrong is the gesture: the suite's own rule is to drive the browser a reader gets, and a press above the top edge is an impossible event history.

So the passage is scrolled back into view before the press. That is what the fixture puts #q that far down the page for — with the scroll, the mark (y=138) and the composer (y=750) are on screen together, which is the state the test's name describes. The composer stays up across a scroll (only a mousedown outside it stands it down), so it is still an open composer that the press has to get past.

mark_point then asserts its point is one a press can reach, which is the durable half. It is the shared helper for every real press on a highlight, and an unreachable point out of it is undetectable at the call site: the failure surfaces 30 seconds later in whatever wait came next, as a thread that would not open. The question it asks is the runtime's own — elementFromPoint at the point — so it refuses null (outside the window) and anything inside .lf-ui (the banner holding the top of every page, the panel, a composer floating over the words it quotes), naming which of the two it was. That is markAt's veto rather than a copy of --lf-banner-h, so it stays right on a page whose banner is shorter or absent.

test_a_click_on_a_mark_decides_once built the same point inline, character for character, and ended in the same panel_settled wait; it calls the helper now.

Testing

  • uv run pytest tests/test_render.py --run-nightly at this head, on a Linux runner: 553 passed, 1 failed — the failure being test_a_note_sets_the_page_axis_with_its_whole_strip, which is Read the note-set axis against the page's box, not the window #36's and unreachable from this diff. Both tests touched here pass.
  • Bug-back on the test's own subject: with the composer's teardown patched to rewrite the marked paragraph's nodes at mousedown — the node swap between press and release the test exists for — it fails again at the same panel_settled wait. The scroll did not make it vacuous.
  • Bug-backs on the new refusal, both branches: with the passage scrolled under the bar, the lf-mark mark at index 0 is painted at (413, 30), under the layer's lf-banner — no press there reaches the page, so bring the passage into view before pressing it; with the scroll removed, the same sentence ending outside the window. Both at the call, instead of the timeout.
  • pre-commit run --files tests/test_render.py clean.

Automated fix for failed run

`test_an_open_composer_does_not_eat_the_next_click` opened its composer on `#q`,
which scrolled the commented paragraph 141px above the top of the window, and
then pressed the mark there anyway. A painted range keeps its client rects while
it is scrolled away — they simply go negative — so `mark_point` handed back a
point outside the window and `page.mouse` pressed it. The press was heard
because the old hit test asked only the rects' arithmetic; `markAt` now asks
`pageWords` about the element under the point first, and `elementFromPoint`
answers null outside the window, so the click reached `<html>` and the panel
never opened.

The runtime is right: a point over none of the page's words is not a mark, and
no reader can press above the window. So the gesture becomes one a reader makes
— the passage is scrolled back into view before the press, which is what the
fixture puts `#q` that far down the page for, and the composer stays up across a
scroll, so it is still an open composer the press has to get past.

`mark_point` asserts the point is on screen, so the next caller to press off the
window is told which mark and where, rather than 30 seconds later in
`panel_settled` as a thread that would not open.

Bug-back: with the composer's teardown made to rewrite the marked paragraph's
nodes at mousedown — the swap the test exists for — it fails again at the same
wait.

@leaf-agent leaf-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The diagnosis holds up: pageWords's Boolean(node) clause is what turned the off-window press into a null hit, elementFromPoint answers for every point inside the window, and a press above the top edge is an event history no reader produces — so fixing the gesture rather than the hit test is the right level. Two things about the guard itself, neither blocking.

The same point is computed inline a few tests down. test_a_click_on_a_mark_decides_once builds mark_point's expression by hand — const r = [...CSS.highlights.get('lf-mark')][0].getClientRects()[0]; returning {x: r.left + r.width / 2, y: r.top + r.height / 2} — and presses it with page.mouse.click(spot["x"], spot["y"]), after its own scroll_into_view_if_needed. It is the same gesture on the same highlight, so it inherits none of the new assertion, and if that press ever goes off-window it fails as the same 30-second panel_settled timeout this PR set out to make legible. page.mouse.click(*mark_point(page, "lf-mark")) is behaviour-identical there. I haven't run the browser suite from this job, so that swap wants a run before it lands.

The floor lets the banner through — see the inline note. Worth weighing because the helper's second call in this test asserts an absence, so a press the banner eats reads as green.

Comment thread tests/test_render.py Outdated
@leaf-agent

Copy link
Copy Markdown
Collaborator Author

The same failure came back on the next commit — run 32750967520 at 9b7a7ee, byte-identical: test_an_open_composer_does_not_eat_the_next_click timing out in panel_settled, alongside #36's 7.5px. 9b7a7ee touches only two reference docs and test_interact.py, so this branch still applies to current maingit merge-tree against 9b7a7ee is clean, with tests/test_render.py the only file either side touches. No second PR opened; this one is the fix.

Reached the same diagnosis independently from that run before finding this PR, and it lands in the same two places: the press was at (413, −142) on a 1200×900 window because reaching #q scrolled the marked paragraph off the top, elementFromPoint answered null there, and pageWords's Boolean(node) clause is what stopped markAt from matching the rect arithmetic anyway. So both halves of the fix — scroll the passage back, and refuse an off-window point in mark_point — are independently arrived at rather than one reading of the evidence.

The one thing not yet on this thread: #36 and this PR are jointly sufficient and individually not. This branch's own test run leaves exactly one failure, 1 failed / 1061 passed, and it is test_a_note_sets_the_page_axis_with_its_whole_strip. Merging either alone leaves main red.

Local verification of current main plus this change

On 9b7a7ee with the two edits applied, Linux, full suite split across two runs:

uv run pytest tests/test_render.py -q -n4 --run-nightly
553 passed, 1 failed in 593.34s   # the failure is #36's note-axis assertion

uv run pytest tests/test_interact.py tests/test_product_page.py tests/test_site.py -q -n4 --run-nightly
507 passed, 6 skipped in 103.86s

That is 1060 passed against the failing run's own 1060, with the composer test moved from failed to passed and nothing else moving.

Bisected rather than inferred: reverting assets/ and bundled/ to 2bdb38c on an otherwise-current tree makes the composer test pass in 4s, which puts the change at 91515b5 and at nothing before it. (Reverting leaf.js alone does not run — the widgets import measure, which only exists after that commit.)

An extra bug-back for the assertion this PR adds, on the other side from the one in the description: with markAt forced to return null and the scroll left in place, the test fails at the same panel_settled wait it failed at before. So the scroll did not turn the press into something that would pass whatever the hit test does.

Review on #37: the floor the guard put under a press was `0`, and the top of
every page belongs to the banner — `.lf-ui`, fixed, over the words — so a mark
scrolled under the bar passed the guard and lost its press exactly as the
off-window one did. It is the worse of the two here, because the second press in
`test_an_open_composer_does_not_eat_the_next_click` asserts that nothing opens.

The floor is now read off the page rather than written as a number: the helper
asks `elementFromPoint` what is under the point and refuses `null` — outside the
window — or anything inside `.lf-ui`, which is `markAt`'s own veto. A constant
would have been a second copy of `--lf-banner-h`, and wrong on any page whose
banner is shorter or absent.

`test_a_click_on_a_mark_decides_once` built the same point inline, character for
character, and so inherited none of this while ending in the same
`panel_settled` wait. It calls the helper now.

Bug-backs: with the passage scrolled under the bar, "painted at (413, 30), under
the layer's lf-banner"; with the scroll removed, the same sentence ending
"outside the window".
@leaf-agent

Copy link
Copy Markdown
Collaborator Author

Both taken; pushed as a second commit.

The inline copy in test_a_click_on_a_mark_decides_once is now page.mouse.click(*mark_point(page, "lf-mark")). It was the same expression character for character, so the swap is behaviour-identical and the test inherits the guard — which matters there for the same reason it matters here, since that press is followed by a panel_settled too.

The floor is read off the page instead of written as 48 — see the inline reply. The helper asks elementFromPoint what is under the point and refuses null or anything inside .lf-ui, which is markAt's own veto rather than a copy of the banner's height.

Verified on a Linux runner, not by inspection: uv run pytest tests/test_render.py --run-nightly at this head, and both branches of the new refusal bug-backed. Numbers in the updated description.

On the recurrence above: the branch still applies to current maingit merge-tree --write-tree origin/main fix/ci-32749573557 returns a tree cleanly at ac03b7f, three commits past the one this was cut from.

@leaf-agent leaf-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Both points from the last pass are settled — the inline copy in test_a_click_on_a_mark_decides_once calls the helper, and the floor is read off the page instead of copying --lf-banner-h. One thing about the replacement itself, inline with a suggestion: the veto it asks is a near-copy of pageWords's rather than the same question, and [data-lf-said] is where the two come apart. Non-blocking — nothing in the corpus reaches it today, and the failure it would produce is a named assertion at the call site rather than the silent timeout this PR set out to remove.

Comment thread tests/test_render.py
The helper's veto was `closest('.lf-ui')`, which parts from `inUi` on a declared label: the nearest element wins there, so a label a widget declared as the page's own words is nearer than the control it sits in and a mark painted on it is pressable. The helper refused that press and named the control. It also named the wrong class whenever chrome came from `offer()`, whose `${cls} lf-ui` puts `lf-ui` last.
@leaf-agent

Copy link
Copy Markdown
Collaborator Author

main is now red on both open test fixes in the same run — run 32754431626 at ac03b7f fails this PR's test_an_open_composer_does_not_eat_the_next_click and #36's test_a_note_sets_the_page_axis_with_its_whole_strip together. That much is another occurrence, and the note above already covers it.

The new part is that the pair has now been verified together, on the current head — which neither PR can claim alone: each was verified against its own base, neither against the other, and ac03b7f itself added 130 lines to tests/test_render.py. Merged onto main at ac03b7f, both branches apply cleanly — to main and to each other — and the whole of tests/test_render.py is green: 555 passed, including the two that fail on main. Your merge-tree check above holds for #36 alongside it, and the two touch tests/test_render.py in different places.

The green is the fixes rather than the runner: the same machine reproduces both failures byte-identically at plain ac03b7f, the 30s panel_settled timeout here and assert 7.5 <= 1 with the column at 600.5px there. So merging the two is enough to green test on main; nothing further is needed from a third change, and this session opened no PR.

What was run

Merge base ac03b7f (current main), git merge of refs/pull/36/head (5bfa3ef) then refs/pull/37/head, both auto-merging tests/test_render.py with no conflict.

# both failures reproduced at plain ac03b7f, before any fix:
2 failed, 553 deselected
  test_an_open_composer_does_not_eat_the_next_click - TimeoutError: 30000ms
  test_a_note_sets_the_page_axis_with_its_whole_strip - assert 7.5 <= 1 (600.5px)

# with both branches merged onto ac03b7f:
uv run pytest tests/test_render.py --run-nightly   ->  555 passed

Run in two halves (271 + 284) because this runner has 4 cores and the suite does not fit one window; the split is by collected node id, so every test ran.

Timing note: the 555-test run was against 00cb3b4, this branch's head when it started. f63cf5c landed while it ran, so the nine tests touching the two changed helpers — the three mark_point call sites, including the one 00cb3b4 converted in test_a_click_on_a_mark_decides_once, and the six ROOM_GEOMETRY readers — were re-run at f63cf5c and are green, and the branches still merge cleanly. The 555 figure is pinned to 00cb3b4; the delta since is confined to mark_point.

@leaf-agent

Copy link
Copy Markdown
Collaborator Author

Check state at f63cf5c, which is where this branch stands: run 32758251932 is 1 failed, 1064 passed, 6 skipped, and the one failure is test_a_note_sets_the_page_axis_with_its_whole_strip#36's 7.5px, unreachable from this diff. lint and review are green.

So the test this PR is for passes on the branch, and main needs both this and #36 to come back green. I ran the same module locally at each of the three commits, with the same result each time.

@leaf-agent

Copy link
Copy Markdown
Collaborator Author

Seventh occurrence, at 20f387crun 32763448588, same two failures as the sixth: this PR's test_an_open_composer_does_not_eat_the_next_click timing out in panel_settled, and #36's test_a_note_sets_the_page_axis_with_its_whole_strip. That much is already on the record above and needs nothing new.

What is new is that the caveat on the previous verification is closed. That run was the whole module against #37's head at the time (00cb3b4), with only nine targeted tests re-run once it advanced to f63cf5c. This one is the whole of tests/test_render.py at both PRs' current heads5bfa3ef and f63cf5c — merged onto the current main, 20f387c. Both still auto-merge, to main and to each other, and the module is green: 555 passed of 556.

The one that is not green is test_the_page_marks_the_comment_the_reader_is_standing_in, and it is neither PR's: it fails identically on plain 20f387c with no branch merged, and it is not among run 32763448588's two failures. This sandbox has only chromium_headless_shell-1234 installed, while the CI job that passed it launched full Chromium (executable_path=…/chromium-1234/chrome-linux64/chrome in its own traceback), so I read it as the browser binary rather than the commit and did not chase it. Nothing about it touches ROOM_GEOMETRY or mark_point.

So the conclusion from the sixth occurrence stands two commits further out: merging this and #36 is enough to green test on main, and no third change is needed. This session opened no PR.

What was run

Merge base 20f387c (current main), git merge of refs/pull/36/head (5bfa3ef) then refs/pull/37/head (f63cf5c), both auto-merging tests/test_render.py with no conflict.

# both failures reproduced at plain 20f387c, byte-identical to CI:
2 failed, 553 deselected
  test_an_open_composer_does_not_eat_the_next_click - TimeoutError: 30000ms
  test_a_note_sets_the_page_axis_with_its_whole_strip - assert 7.5 <= 1 (600.5px)

# with both branches merged onto 20f387c, whole module:
176 passed in 410.32s
189 passed, 1 failed in 205.20s   <- test_the_page_marks_the_comment_the_reader_is_standing_in
190 passed in 269.85s

Run in three chunks at -n2 because this runner has 4 cores; the split is by collected node id, so every one of the 556 ran. The lone failure was then re-run alone at -n0 on both trees — failing at 34s each time, on the merged tree and on plain main alike — so it is not the contention shape running-tend warns about, and not something either branch introduces.

@max-sixty

Copy link
Copy Markdown
Owner

Closing as superseded: the required reader-visible mark behavior is already on main in a simpler form. This branch is now conflicting and red; any remaining helper hardening can return as a narrow follow-up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants