Recapture the gallery stills from the inputs main actually ships - #134
Conversation
… after the chrome change (#138) ## Problem [Run 33327310444](https://github.com/max-sixty/leaf/actions/runs/33327310444) on d2dd825 failed with 34 tests red. Most of them are owned: #121 has the `scrollbar-gutter` set, #132 the generic CONTROL contract, #134 the catalog stills, #136 the sidebar width, #137 the versions menu, and #133 tracks the digest. What was left unowned is five tests that all fail on one surface — what the panel's opening does to the page around it — and each is a reading [335bf9a](335bf9a) "Delegate page chrome to browser standards" moved without a second reading moving with it. All five pass at 335bf9a's parent; each was reproduced at `-n0` before it was touched. ## Solution Four readings, each corrected where it is taken. No assertion is loosened and no test is changed. **The margin's 900px floor asks the window again.** That number is half of a pair: `@container lf-shell (min-width: 900px)` grants the rail and the marker's strip, and `@media screen and (max-width: 899px)` stops drawing the living margin at all. 335bf9a converted the grant to a container query and left the hide a media query, so the two came apart the moment the panel narrowed body: at 1200px with the panel open the shell is 765px, the grant lapsed, and the markers stayed on screen with no strip reserved for them. Measured on `SUGGESTION_PAGE`, `--strip-r` resolved to `max(0px, 0px, 0px, 0px)` and every suggestion's control row docked beside an empty rail. The floors below it stay container queries: 1152 and 1416 ask how much room is left beside the panel, which is the question the runtime's old `data-lf-cramped` veto asked, and their answer is supposed to move with the panel. **Body's inline size is back under the layout observer.** 335bf9a dropped `layoutSizes.observe(document.body)`, and with it the only thing that re-ran `syncLayout` while the panel's eased margin was still narrowing the page. `setPanel` runs one synchronous pass at the press, reads the wide box, and returns; nothing reads the narrow one. So a composer placed in a wide window's margin kept a place the narrowed page no longer had — an absolute child past body's client box, which is sideways-scrollable overflow, with the box standing on the panel that displaced it. Restored with the content-box width filter the removed code carried, so ordinary document-height growth still does not feed back into a writer that reserves flow content. **The panel layer is shown before anything is rendered into it.** The panel is a `<dialog>` now, and a dialog nobody has shown is `display: none` — but `setPanel` called `renderPanel()` first and `syncPanelLayer()` (which calls `show()`) second. `renderPanel` is where the anchor pass runs for the threads it draws, and a mark hangs on the boxes its element shows through, so a widget an agent sent in a reply resolved to an element measuring 0×0, took no mark, and never got one afterwards: the thread stayed open in the panel pointing at nothing on either side. Swapping the two lines is the whole fix; the focus target is `threadsBox`, which the render fills rather than replaces. **The margin preview shows without naming the marker as its source.** `showPopover({source})` puts the card in the sequential focus order directly after the control that invoked it — but this card is not invoked. It opens on the marker merely taking focus, so a reader walking the margin was made to Tab through a close button and two preview actions they had not asked for, three stops between one suggestion's controls and the next, with the card's contents changing under them as they went. Its position comes from the `anchor-name` the code already writes on the button, so the placement asks nothing of `source`. ## Testing - Everyday suite green on this branch: `uv run pytest tests` — 723 passed, 6 skipped. - The five: `test_suggestion_controls_stay_out_of_the_column`, `test_suggestions_sharing_a_block_keep_source_and_keyboard_order`, `test_a_settlement_in_a_reply_leaves_its_own_anchor_on_the_page`, `test_a_mark_in_the_layer_promises_no_press_the_layer_will_not_take`, `test_a_float_the_panel_displaces_hands_the_page_no_sideways_scroll` — all pass with `--run-nightly`. - Whole files: `test_render_margin.py`, `test_render_conversations.py`, `test_render_widgets.py`, `test_render_outbox.py`, `test_render_navigation.py` — the only failures left in them are ones that also fail on `origin/main` unchanged, at identical values. - `prettier --check` and `node --check` pass on both JavaScript files; CSS is prettier-excluded here by `.pre-commit-config.yaml`. <details><summary>How each was measured</summary> The margin floor, on `SUGGESTION_PAGE` at 1200×900, before and after pressing Threads: ``` BEFORE bodyRight 1185 main 768 --strip-r "max(0px, 250px, 0px, 0px)" --claim-rail 250px AFTER bodyRight 765 main 765 --strip-r "max(0px, 0px, 0px, 0px)" --claim-rail "" ``` The same probe at 335bf9a's parent reports `--claim-rail: 250px` in both postures, with `data-lf-cramped` standing — d226ee2 had already taken that veto off the rail claim, so the 900 floor was the window's question and nothing else. The float, on `LONG_PAGE` at 1600×900 with the composer in the margin and the panel opened: ``` body.scrollWidth 1487 body.clientWidth 1165 composer.left 1166.9 composer.right 1486.9 ``` The mark, on `REPLY_TRAVEL_PAGE` with an `lf-options` frozen into a reply: `#tv-decision` is present and `checkVisibility()` is true once the panel is open, its box 355×32, and `.lf-mark-el` is on nothing — at +6s as much as at +0. The same probe at 335bf9a's parent has the mark the moment the panel opens. The tab walk from a suggestion's Accept, before: accept → reject → marker → **preview close → preview action → preview action** → next row's accept. </details> ## What this does not fix Two of the four groups #127 recorded are still open, and both are a product decision rather than a stale reading. - **A covering panel is a modal dialog now**, so the document behind it is inert: `test_selection_hints_do_not_name_page_content_behind_a_covering_panel` presses `s` at 700×900 and gets no hints because the page cannot hear the key. Whether a covering panel should take the page out of reach entirely, or cover it and leave it working, is the call — `test_coarse_pointer_chrome_gives_its_compact_controls_humane_aims`, `test_workspaces_replace_each_other_instead_of_stacking[500]` and `test_coarse_pointer_resize_reach_stays_reachable_without_trapping_scroll` fail as `<dialog … class="lf-ui lf-panel open"> intercepts pointer events` on the same fact. - **Escape belongs to whichever layer the browser is holding** — the four `test_render_anchors.py` failures and `test_esc_hands_the_page_back_after_it_has_closed_the_last_panel`. #137 is open on this. Separately, [0d7e0d0](0d7e0d0) "Open threads beside their source" landed while this was being verified and brought two of its own new tests red on `main` — `test_the_shipped_long_thread_opens_beside_its_source_in_the_right_margin` and `test_the_full_thread_posture_follows_the_page_container_and_left_claims`. They fail identically with and without this branch's changes; noted here so the next run does not re-derive them. --- Automated fix for [failed run](https://github.com/max-sixty/leaf/actions/runs/33327310444) --------- Co-authored-by: leaf-agent <318509791+leaf-agent@users.noreply.github.com>
#141) ## Problem [`ci` run 33327519405](https://github.com/max-sixty/leaf/actions/runs/33327519405) on e7299eb failed with 33 tests red. One of them is `test_render_controls.py::test_esc_hands_the_page_back_after_it_has_closed_the_last_panel`, which #133 lists among the unowned remainder ("`esc` handback"). It still reproduces deterministically on `main` at 1ca4f40, in about 7 seconds. The test's first assertion after a pointer open is the one that fails: the panel is visible and the toggle is no longer focused. ``` FAILED tests/test_render_controls.py::test_esc_hands_the_page_back_after_it_has_closed_the_last_panel E AssertionError: Locator expected to be focused E Actual value: inactive E - waiting for locator(".lf-threads-toggle") E 14 × locator resolved to <button aria-expanded="true" class="lf-btn lf-threads-toggle" ...> tests/test_render_controls.py:2089 ``` `syncPanelLayer` raises the panel with `panel.show()` beside the page and `panel.showModal()` over it. Both run the dialog focusing steps, so the browser moves focus into the dialog whichever one is called — the panel takes the focus off whatever raised it. The function already knew to put focus back, but only for a focus it found *inside* the panel: `active` is `null` whenever the reader was standing outside it, and the restore arm reads `active`. So the pointer route lost the toggle, and what the test's docstring bills for lands: the button holds `aria-expanded` with no ring on it, and the reader's next Space is that button rather than the page's scroll — the panel they dismissed comes back and nothing says why. ## Solution Carry the element that was standing outside the layer alongside the one found inside it, and restore that too when nothing else claims the focus. Beside the page a panel is a column that arrived, so the reader stays where they were; a covering panel is a mode and still takes them in, which is the existing `focusEnteredLayer` arm and is unchanged. Body is not somewhere to put anyone back, so it is not carried. This is `syncPanelLayer`'s own restore, extended to the case it did not name — not a second focus policy beside it, and not a call at the press site, which would have to be repeated at every door the panel has. ## Scope Not the other 32. Measured against the current tip, they are owned or out of reach here: | | | | --- | --- | | #121 | 8 of them (`scrollbar-gutter: stable` on the root scroller) | | #132 | `test_example_renders[command-hub]`, `test_example_renders[corpus]` | | #137 | the four version-menu cases in `test_render_anchors.py` | | #134 / #133 | the catalog preview digest | | this PR | `esc` handback | I reached the version-menu focus break independently and had the same one-line fix (`versionBtn.focus()` before `showPopover`) before #137 appeared; #137 covers it and the two contracts around it, so nothing of it is here. <details><summary>How the split was measured</summary> Each row is a local `-n0` or `-n3` run on the named tree, not an inference from the failure text. - The 33 node ids from the run were re-run on `main` (26dbf42): 31 still failed. Applying #121's `chrome-style.js` hunk alone took that to 23; adding #132's branch took it to 20. - #138 also edits `chrome-layout.js`, so its hunk was applied to `main` on its own and the `esc` test re-run against it — still red. The two changes are in different functions (`setPanel` and the `ResizeObserver` there, `syncPanelLayer` here) and do not overlap. - Four failures that appeared only under `-n3` — `test_render_navigation.py::test_a_label_press_keeps_the_controls_keyboard_standing`, `test_render_outbox.py::test_a_withdrawal_restores_what_still_stands_not_what_stood_then`, `test_render_startup.py::test_a_fast_first_replay_does_not_flash_the_waiting_surface` and `test_render_controls.py::test_covering_panel_takes_the_page_scroll_with_it` — each pass at `-n0` on both trees, or fail on both. None of them is this change. </details> ## Testing `test_esc_hands_the_page_back_after_it_has_closed_the_last_panel` is the regression: it asserts both halves the docstring names — the ring the pointer reader never asked for, and the Space that reopens what they just dismissed — and it goes from red to green here. No new test, because that one already owns the behaviour. <details><summary>Runs on this branch, against the same files on clean 1ca4f40</summary> ``` uv run pytest tests -q -n3 723 passed, 6 skipped uv run pytest tests/test_render_controls.py \ tests/test_render_conversations.py 10 failed, 101 passed uv run pytest tests/test_render_outbox.py \ tests/test_render_margin.py \ tests/test_render_drafts.py 3 failed, 108 passed ``` Every remaining failure above reproduces on clean `main`; `test_esc_hands_the_page_back_after_it_has_closed_the_last_panel` is the only test whose result this change moves. Earlier on 26dbf42 the wider render set (`anchors`, `gate`, `navigation`, `widgets`, `projection`, `startup`, `drafts`, `outbox`, `options`, `pages`, `aim`, `reactions`, `margin`, `semantic_selection`, `commands`, `export`, `site`) ran the same way with the same result. </details> --- Automated fix for [failed run](https://github.com/max-sixty/leaf/actions/runs/33327519405) Co-authored-by: leaf-agent <318509791+leaf-agent@users.noreply.github.com>
) ## Problem `test_render_gate.py::test_example_renders[corpus]` has been red on every `ci` run since [26dbf42](26dbf42) "Add external link treatment", in both colour schemes, and it still reproduces deterministically on `1ca4f40` in 11 seconds. #132 diagnosed it as "a different finding, from 26dbf42's external-link treatment rather than from 335bf9a" and left it; no PR or issue owns it. ``` assert ['[light] <lf-tab id=corpus-pr-walkthrough> puts "opens in a new tab" under .lf-ui, where no comment can reach it', '[dark] ...same...'] == [] ``` The subject is not the example. `render_version` is the pass `version check --render` runs on an agent-authored page, so any page with a cross-origin link inside a widget now fails handover on a word the reader is never shown. [`unreachableWords`](https://github.com/max-sixty/leaf/blob/03b4a0b2776102041a2d48580046fbf03b5e3446/skills/leaf/scripts/leaf/render-checks/reachability.js#L208) walks every text node under a widget's `.lf-ui` and reports the ones no declaration claims. Its excuse list held `.lf-quiet` and nothing else. 26dbf42's `renderExternalLinks` writes a note beside each external link — `class="lf-ui lf-external-note"`, `hidden`, textContent "opens in a new tab" — as the `aria-describedby` target for the link, and the walk found it: not `data-lf-said`, not inside the link's own `CONTROL` box (a describedby target is referenced by id, so it sits beside the link rather than in it), not `.lf-quiet`. ## Solution Excuse `[hidden]` beside `.lf-quiet`, one line and its comment. The check's own opening sentence is "Words the page shows that no user can select", and a `[hidden]` subtree shows none. The note cannot simply become a `.lf-quiet` word instead — which would have been the tidier answer, since `quietWord` is the repo's one idiom for "a word for a reader listening, silent on screen". `renderExternalLinks` is enrolled on declared shadow roots as well as the document, and the note is placed inside whatever root its link stands in, while `.lf-quiet`'s clip is a rule in the document stylesheet that no shadow tree adopts (only `MARK_RULES` is adopted, in `shadow.js`). A `.lf-quiet` note in an `x-shadow` widget would render as visible text next to the link. `hidden` is the silence available in every root. `[hidden]` is safe for this reading to trust rather than a hole for hidden-but-revealable words: the browser removes the attribute when it reveals `hidden="until-found"` content, so a word the reader finds is a word this check sees again, at the moment it is on screen. The two sibling gate readings that ask what a box shows already spell the same pair — [`widgets.js`](https://github.com/max-sixty/leaf/blob/03b4a0b2776102041a2d48580046fbf03b5e3446/skills/leaf/scripts/leaf/render-checks/widgets.js#L146) and [`standalone.js`](https://github.com/max-sixty/leaf/blob/03b4a0b2776102041a2d48580046fbf03b5e3446/skills/leaf/scripts/leaf/render-checks/standalone.js#L34). No assertion is loosened and no runtime file is touched. `skills/leaf/CLAUDE.md`'s row for this gate already reads "**visible** page words remain in reachable flow". ## Testing - `test_example_renders[corpus]` and `test_example_renders[pr-walkthrough]` — pass with `--run-nightly`; the first fails on `origin/main` unchanged at the two findings above. - `test_render_reports_words_a_widget_puts_out_of_reach` gains the pair: the same `.lf-ui` note inside `lf-option` reports nothing when `hidden` and is still reported when shown. Bug-back: with the `[hidden]` excuse reverted, the hidden half fails with exactly the corpus's finding. - `uv run pytest tests` — 723 passed, 6 skipped, on this branch rebased onto `c7b3706`. - `uv run pytest tests/test_render_commands.py tests/test_render_gate.py --run-nightly` — 77 passed, 4 failed, and all four are in the failed run's own list and owned elsewhere: `test_the_render_gate_measures_sideways_room_at_the_root_scrollport`, `test_the_reader_draws_an_edge_to_the_width_they_want[comments]` and `test_a_window_with_no_room_for_a_chosen_width_does_not_un_choose_it[comments]` are #121's `-15`, and `test_a_reader_arrives_at_what_they_left_rather_than_watching_it_arrive` is the intermittent one #121 documents. - `node --check`, pinned `prettier --check`, `ruff check` and `ruff format --check` pass on the two touched files. `pre-commit` is not on this sandbox's PATH, so its remaining hooks went unrun here. The preview digest is unmoved: `capture_input_files()` hashes `examples/`, `skills/leaf/assets`, the selected packages and three site scripts, and `skills/leaf/scripts/` is in none of them. ## What this does not fix The failed run had 32 red. Everything else in it sits with an open PR or issue — #121, #132, #134, #136, #137, #138, #140, #141, and issues #133 and #139 — with two exceptions I did not reach: - `test_render_controls.py::test_every_ring_the_layer_draws_is_shown_whole_somewhere_in_the_corpus` reports one unlit ring, `version-diff (.lf-version-diff:is(:focus-visible, .lf-focus-visible))`. That control is inside the versions menu #137 is rewriting, so it is likely downstream of that branch rather than its own cause; I have not verified it against #137's tree. - `test_render_navigation.py::test_a_widget_that_renames_its_role_keeps_the_press_offer_gave_it` is the generic-press question #132 raised and deliberately left as a product decision. --- Automated fix for [failed run](https://github.com/max-sixty/leaf/actions/runs/33330752582) Co-authored-by: leaf-agent <318509791+leaf-agent@users.noreply.github.com>
…p's own press (#140) ## Problem Two leftovers from [335bf9a](335bf9a) "Delegate page chrome to browser standards", which moved control activation off a page-wide `CONTROL` scope and into rows declared where each control is. Both are on `ci`'s red list for [the run this was dispatched for](https://github.com/max-sixty/leaf/actions/runs/33328596524) or reachable from the same change, and both still reproduce at `1ca4f40`. **`lf-worktree`'s head names fewer keys than work it.** The commit gave the head a row binding `PRESS` — Enter and Space — over a button wearing `aria-expanded`, which is ARIA's disclosure pattern. `skills/leaf/CLAUDE.md` states what that costs: "every row over a disclosure reads [`DISCLOSE`] — this scope's, and a widget's own row re-wording the same press. Two rows naming different sets is not two promises but one: `lineRows` prints the nearer row and drops the other whole, so a widget naming one key fewer takes the rest off the line". The widget row is nearer, so the arrow that opens the evidence went on working and stopped being named — on the key line and in `aria-keyshortcuts`, which is written once when the scope is declared and so froze at `Enter Space`. Nothing tested it; the identical bug in `lf-options` is what [#132](#132) is for, and that PR does not reach this package. **`test_a_widget_that_renames_its_role_keeps_the_press_offer_gave_it` still asks for the deleted scope's word.** It asserts the key line reads `press it` over a focused tab. `press it` was `CONTROL`'s `line`, and 335bf9a removed the scope in the same commit that gave `lf-tabs` its own `tab.activate` row reading `open the tab`. The commit updated several tests in that file and dropped `test_the_reference_names_the_space_that_works_a_control` outright; this assertion was missed, so the test fails before reaching the two facts it exists to hold. ## Solution `lf-worktree`'s head takes its keys from `DISCLOSE(head)`, the way `lf-diff` and `lf-draft` already do, and keeps its own `run`. `DISCLOSE` hands over only the arrow that changes the state, so the press stays a direction rather than a second toggle; it also answers for where the head stands, and the runtime's disclosure scope stops at the chrome. The head is a span from `selectableOffer`, so a copy frozen into thread markup has no platform pair underneath it the way a `details > summary` does — the row's own press is the only thing there, which is the shape [#132](#132) keeps for the sibling widget. Its `line` reads the way the tree is standing, and `aria-expanded` is written at birth so the head is never briefly a control the runtime cannot place — without that write `DISCLOSE` hands back both arrows, and that is the value `aria-keyshortcuts` is written with when the scope is declared. The repaint that keeps the line and `aria-keyshortcuts` together is the document's disclosure watch, which [#132](#132) moved from `paintHere()` to `paintKeys()` while this branch was open; the widget owes none of its own, which is what `skills/leaf/CLAUDE.md` now says in as many words. The tabs test asserts `open the tab` — the strip's own word for the same press — and its docstring now describes where that press is declared rather than the scope that used to supply it. Everything after the assertion is unchanged: Enter still has to re-select the focused tab, and Space still has to be consumed rather than scrolling the page out from under it. New coverage in `test_render_projection.py` for the worktree head, since nothing exercised it. It holds the row rather than the repaint: on the page, both surfaces naming the row's keys — the line and `aria-keyshortcuts` — plus a second `→` on an open tree, so a scope answering nothing at all could not pass it. In a message: the same head in the threads panel, where `aria-keyshortcuts` reads `Enter Space`, the arrow moves nothing, and Enter and Space fold it. The repaint behind both surfaces is held by `test_a_widgets_native_control_names_the_press_the_platform_makes`, which #132 added. ## Testing `uv run pytest tests` at `c292244` — 723 passed, 6 skipped. After merging `main` (which brought #132 and #143) and taking the repaint out at `890e7ff`, `test_render_projection.py`, `test_render_navigation.py`, and `test_render_anchors.py` — 193 passed, 4 failed, all four the version-menu group [#137](#137) owns and all four failing identically on `origin/main` in a clean worktree. With `--run-nightly`, `test_render_projection.py` and `test_render_navigation.py` — 137 passed, 1 failed, that one being `test_the_arrows_say_which_way_the_section_under_the_reader_goes`, which is #132's settled ask and untouched here; it fails the same way at the merge base `1ca4f40`. Each half of the new test was run against the code it holds and fails there: the page half at `assert 'Enter Space' == 'Enter Space ArrowRight'` on `main`'s `lf-worktree.js`, and the thread-markup half at `aria-expanded` staying `"false"` under Enter with the row's `run` removed. <details><summary>What this does not fix in that run</summary> The run reported 34 failures. Most of the rest already have a PR open: the `-15` root scrollbar-gutter family is [#121](#121) (`assert -15 == 0`, the drawn edge landing a bar's width off the hand, the covering panel's blank strip), the version-menu group is [#137](#137), the settled ask's disclosure keys were [#132](#132), now merged, the margin and float placements are [#138](#138), the gallery stills are [#134](#134), and the sidebar's stated window is [#136](#136). No guard stops the third widget from declaring a fixed key pair over a disclosure. `DISCLOSE` exists so the two surfaces cannot come apart, but a widget has to remember to call it, and two of the four widgets over a disclosure did not. A corpus-wide check — every focusable disclosure names the keys `DISCLOSE` gives it — would catch the next one, and is left out of this PR because it belongs beside the corpus checks rather than in a fix for two widgets. </details> --- Automated fix for [failed run](https://github.com/max-sixty/leaf/actions/runs/33328596524) --------- Co-authored-by: leaf-agent <318509791+leaf-agent@users.noreply.github.com>
The catalog's checked-in JPEGs and their manifest are pinned to a digest over every file that can change a captured standalone page — the examples, the shipped layer, the site scripts. Main has moved through that set since the stills were last taken, so `test_the_public_catalog_is_a_visual_index_of_full_page_routes` reports the manifest's inputs digest against the tree's. Rerun `scripts/example-previews.py` at 4b5b4b8.
b824d6d to
cd998bf
Compare
|
Recut onto Nothing about the analysis above changes: this is the second recapture on the same branch, which is the treadmill the body already names. Dispatched by the nightly run 33342915540 on |
Problem
The nightly
test_the_public_catalog_is_a_visual_index_of_full_page_routesasserts thatdocs/example-previews.json'sinputs_sha256still equals the digest of every checked-in file that can change a captured standalone page — 142 of them, the examples plus the whole shipped layer plus the three site scripts. On the run this was dispatched for it readassert '00101a03…' == '8e94db55…': "preview inputs changed — rerun scripts/example-previews.py". The stills in the public gallery are pictures of a page the repo no longer builds.The manifest was last written by fff16e6 "Diversify examples and add full-page gallery". Five merges after it edited captured inputs without rerunning the owning script —
ac5ba54,d2dd825(#127),84aef1d(#122),2a1ca10(#123), and2937e3b(#129), the commit the failed run stands on. #129 is the sixth commit to carry the staleness rather than the one that caused it; this was simply the first nightly run to reach the test afterfff16e6.It was still red at the tip when this branch was cut, and #130 shows why that is not just a missed step. #130 did rerun the script — nine refreshed JPEGs and a new manifest are in its diff. Its branch was cut at
ac5ba54, and by the time it squash-landed asaf4eb72eighteen captured input files had moved under it (leaf.js, seven runtime modules, both themes, the marked bundle, two widgets, andexamples/CLAUDE.md). So the digestaf4eb72records is the digest of9aa4f61's tree, and a regeneration taken correctly still landed stale.Measured here
The manifest matches the tree it was written against and no other.
git diff --stat 9aa4f61 af4eb72over the captured set is 18 files, 337 insertions.Solution
Reran
scripts/example-previews.py, which is whatscripts/CLAUDE.mdasks for — "Run the owning script after changing its source dependency or registry input" — and what the assertion's own message asks for. Nine stills recaptured over HTTP from the built site, one manifest rewritten. No test touched.The stills were captured on this Linux runner through the host's Chrome, where the previous set was captured on macOS. Two differences show up in the image diff and neither is a product change: the body text carries this platform's font rasterisation, and the page is a classic scrollbar's width narrower because Linux draws a bar in the gutter that macOS's overlay bars leave empty. Nothing in the test reads appearance — it checks the input digest, the nine filenames, their hashes, and 896×560 — so a mac-captured set and this one are equally green.
Testing
uv run pytest tests/test_site.py --run-nightly— 20 passed, on this branch recut onto4b5b4b8(Ask the code gate which role came back unread, not what ratio it read at #149). The digest assertion is its own bug-back: onaf4eb72unchanged it reads5cade914… != 7236821…, and reverting justdocs/example-previews.jsonhere reproduces the same shape.pre-commitis not on this sandbox's PATH, so its hooks went unrun here. Only a JSON file and nine JPEGs changed: the JSON is written by the script withindent=2and a trailing newline and is matched bycheck-jsonalone, and prettier is scoped to[javascript, html].The staleness is structural, and this PR is inside it
This branch has now been recaptured twice for that reason. It was first cut at
26dbf42"Add external link treatment"; nine merges later — through #150, #145, #153, #137, #148 and #149 — that capture was stale and the branch no longer merged at all, because two of its JPEGs conflicted with main's. It was recut at4b5b4b8and the script rerun, so what is here is fresh against4b5b4b8— and it will be stale again at whatevermainthis merges onto if that commit is not4b5b4b8.Two properties combine into that. The digest is taken on the branch's tree while the squash lands on a moved
main, so a PR cannot make the assertion true of the commit it becomes; and the assertion is nightly-only, so neither PR CI norwt merge's everyday-suite gate ever asks the question at the point where the tree is final. At this repo's merge rate a green tip is the exception, and rerunning the script is a treadmill rather than a fix.Two ways out, both decisions about what a checked-in gallery is worth rather than something the code can settle, so neither is done here:
sha256— so asserting it in the everyday suite puts it in front ofwt mergeon the rebased tree, where the answer is still actionable. The cost is a Chrome-driven rebuild demanded of every asset-touching PR, and nine new JPEGs in each of them.publish-site.yamlruns on the pushes that matter and CI has a browser, so capturing there removes the window entirely.example-previews.py's docstring is deliberate that the site build needs no browser; a capture step in the workflow beside it does not change that.What this does not fix
Nothing else outstanding. Every failure from run 33342915540 was rerun against
4b5b4b8: this digest,test_a_coined_class_cannot_reach_the_chromes_rules(#152) and the two seat tests (#151) are all that remain red onmain.Automated fix for failed run