Repository navigation
fix(renderer): deliver ResizeObserver updates through bounded rendering turns - #1593
lanyue-llk wants to merge 1 commit into
Conversation
Spider Bench A/B✅ All benchmark browser service runs completed; results are informational. Public HEAD: 184 / 240 rows; 38 / 48 sites produced rows; 10 unexpected empty sites. ✅ Deterministic fixture contract is clean. Common ancestor Public 48-site run · informational
Public-site content, timing, and memory are noisy. This single A/B run reports evidence only; site outcome counts explain missing rows without treating them as a deterministic regression. CPU and memory timelinesThese are bounded 40-point views of the same complete process-tree samples used by the HTML report. CPU uses 100% per occupied logical core; memory lines are RSS first and PSS second when complete PSS samples are available. Basexychart-beta
title "Base CPU"
x-axis "Elapsed seconds" [1.536, 2.537, 4.04, 5.54, 6.541, 8.041, 9.541, 11.043, 12.045, 13.547, 15.049, 16.051, 16.551, 17.551, 19.052, 20.054, 21.556, 23.057, 24.557, 25.558, 26.059, 27.061, 28.562, 29.564, 31.067, 32.569, 33.571, 35.072, 36.574, 37.575, 39.078, 40.581, 42.082, 43.083, 44.585, 45.587, 46.088, 47.089]
y-axis "CPU percent" 0 --> 200
line [125.83, 113.87, 133.75, 123.82, 103.84, 105.99, 106.02, 125.97, 31.93, 129.72, 13.98, 3.99, 0, 0, 5.99, 2, 21.96, 65.9, 94.01, 127.78, 99.82, 101.78, 113.99, 73.86, 105.79, 90.01, 75.84, 2, 119.8, 121.83, 9.98, 99.89, 101.88, 99.88, 123.83, 155.84, 41.91, 129.97]
xychart-beta
title "Base memory: RSS then PSS"
x-axis "Elapsed seconds" [0.035, 1.536, 2.537, 4.04, 5.54, 6.541, 8.041, 9.541, 11.043, 12.045, 13.547, 15.049, 16.051, 16.551, 17.551, 19.052, 20.054, 21.556, 23.057, 24.557, 25.558, 26.059, 27.061, 28.562, 29.564, 31.067, 32.569, 33.571, 35.072, 36.574, 37.575, 39.078, 40.581, 42.082, 43.083, 44.585, 45.587, 46.088, 47.089, 48.252]
y-axis "Memory MiB" 0 --> 500
line [13.14, 98.36, 101.32, 135.82, 140.85, 140.59, 141.31, 153.6, 148.01, 120.26, 161.07, 116, 120.04, 119.8, 118.7, 118.7, 118.61, 125.37, 147.8, 144.04, 183.09, 251.78, 185.32, 211.06, 144.09, 149.27, 132.64, 149.58, 140.64, 198.91, 154.58, 128.32, 173.19, 177.63, 177.73, 163.33, 144.52, 141.13, 157.56, 0]
line [9.6, 94.67, 97.6, 132.15, 136.87, 136.9, 137.57, 149.95, 144.3, 116.51, 157.23, 111.91, 116.2, 115.96, 114.86, 114.86, 114.77, 121.53, 143.96, 140.2, 179.31, 248.21, 181.51, 207.15, 140.24, 145.57, 128.86, 145.81, 136.93, 195.26, 150.89, 124.69, 169.55, 173.99, 174.09, 159.69, 140.85, 137.55, 153.36, 0]
HEADxychart-beta
title "HEAD CPU"
x-axis "Elapsed seconds" [0.536, 1.036, 2.037, 3.039, 4.04, 5.042, 5.542, 6.543, 7.543, 8.543, 9.544, 10.545, 11.548, 12.549, 13.551, 15.054, 16.056, 17.058, 18.059, 19.061, 20.062, 21.063, 22.063, 23.065, 24.566, 25.566, 26.568, 27.57, 28.571, 29.572, 30.572, 31.574, 32.574, 34.077, 35.077, 36.078, 37.08, 37.58]
y-axis "CPU percent" 0 --> 200
line [6, 27.98, 103.86, 109.84, 145.69, 139.89, 133.96, 107.81, 128.02, 106.02, 109.89, 127.75, 77.85, 83.86, 85.83, 45.91, 107.82, 59.87, 113.74, 127.76, 114.02, 101.89, 113.96, 57.86, 108.1, 97.93, 103.8, 103.88, 21.98, 147.73, 89.97, 9.98, 8, 107.93, 103.95, 109.94, 133.67, 157.79]
xychart-beta
title "HEAD memory: RSS then PSS"
x-axis "Elapsed seconds" [0.036, 0.536, 1.036, 2.037, 3.039, 4.04, 5.042, 5.542, 6.543, 7.543, 8.543, 9.544, 10.545, 11.548, 12.549, 13.551, 15.054, 16.056, 17.058, 18.059, 19.061, 20.062, 21.063, 22.063, 23.065, 24.566, 25.566, 26.568, 27.57, 28.571, 29.572, 30.572, 31.574, 32.574, 34.077, 35.077, 36.078, 37.08, 37.58, 37.737]
y-axis "Memory MiB" 0 --> 500
line [13.17, 54.75, 83.42, 103.18, 146.64, 136.3, 155.45, 121.73, 129.57, 133.61, 158.87, 150.69, 152.3, 132.56, 127.24, 156.42, 119.84, 133.16, 152.77, 139.24, 159, 278.95, 184.59, 210.19, 142.36, 146.93, 131.12, 138.23, 134.91, 139.35, 143.16, 148.77, 141.11, 126.55, 175.39, 156.54, 143.96, 167.12, 168, 0]
line [9.76, 51.18, 79.83, 99.55, 143.01, 133.24, 152.24, 118.07, 126.33, 130.16, 155.22, 147.07, 148.63, 128.86, 123.33, 152.72, 116.29, 129.5, 149, 137.25, 155.32, 275.39, 180.89, 206.56, 138.79, 143.27, 127.52, 134.69, 131.21, 135.64, 133.23, 145.13, 137.46, 122.89, 172.23, 151.64, 140.45, 163.42, 164.57, 0]
Deterministic fixture · required contractThe fixture exercises 15 routes: 8 are expected to emit rows and 7 intentionally exercise empty, loading, or timeout behavior.
Full HTML, JSON, CSV, logs, and page snapshots: workflow run and full The public-web A/B is informational. The exact HEAD fixture contract runs as a separate required CI check; timing and public-site content deltas remain non-blocking. |
Sequential Navigation Soak A/B✅ HEAD completed the 200-navigation resilience and memory observation. One browser process, one target, and one CDP session navigate CSDN → SegmentFault → Huaban → example.com repeatedly. This run issued 200 Common ancestor Session resilience
A public-page failure is reported separately from an unrecoverable session. The soak requires all navigation attempts, zero failed recovery, zero lifecycle/network ordering violations, and complete resource evidence. Process-tree memory
HEAD memory by 50-navigation quarter
Raw reports and per-navigation boundary samples: workflow run and full Public-site timing and memory are observational. Use the A/B deltas and trend shape as evidence, not as a deterministic performance threshold. |
CI Regression ReportSource CI run · source state at render:
Release regression — ✅ HEAD/base failures 0/0; raw binary +0.009316%Package and image size
Startup latency and PSS
Concurrency matrix
Frontend differential — ✅ 1,020/1,020 cases matched; 0 issues
Agent episodes · Moli vs Chromium — ✅ 8/8 Moli episodes passed; 0 failures
Runtime and CDP session contracts — ✅ 26 contract cases; 0 failures
CDP smoke — ✅ 53/53 groups passed; 625 scenariosWorkers: 4 · cumulative group time: 144.31 s · failed groups: 0
WebMainBench · 545 pages — ✅ 545/545 completed; 0 unexpected failures
Frozen HTML · Linux · Diagnostics: All artifact fields are parsed by the trusted default-branch renderer; missing or invalid inputs remain visible as unavailable. |
lanyue-llk
left a comment
There was a problem hiding this comment.
复核提交 bd49a5afe436,发现一项后续通知丢失问题,建议修正后再合入,具体位置和最小场景见行内评论。
把通知交给 rendering 队列、使用 flattened-tree depth,以及给深层有限更新保留 continuation depth,方向都有依据。当前退出逻辑只为 32 轮预算耗尽安排 continuation;同深度变化被跳过、正常结束当前 broadcast 时,还需要下一次 rendering opportunity。
已追踪 observe/DOM mutation 入队、广播抑制、layout 发布与任务结束路径,并实测 Chrome 对照:首次通知 50、回调改为 60 后,得到一次 loop error,随后仍收到 60。此处 Moli 的结论来自提交源码的调度路径;本机缺少原生开发工具,未运行该提交的 V8 测试。
lanyue-llk
left a comment
There was a problem hiding this comment.
复审 ceb46d382471:上次的 skipped observations 后续通知问题已修正。现在预算耗尽继续携带原 depth,broadcast 已结束但仍有 skipped 目标时从 depth=0 开始下一轮;新增测试也检查了第二轮交付及 disconnect 后收敛。
还发现一个本轮通知目标集合未固定的问题,具体场景见行内评论。当前实现把 gather 与逐个 callback 的执行混在一起,前一个 callback 能把新目标塞进后一个 observer 的当前批次。建议先修正这处批次语义再合入。
已用隔离 Chrome 执行最小对照场景,结果为 first → second:[b] → loop error → second:[c]。Moli 的差异结论来自最新提交的目标列表与广播调用链;本机未运行原生 V8 测试。
b3df66a to
edd161c
Compare
|
Current-main integration exposed two pre-existing OnDemand ResizeObserver controls in Nextest. This was a real semantic conflict rather than a flaky retry:
The focused local Rust run could not link because this macOS host has no Apple Command Line Tools. |
48e375b to
a42084f
Compare
lanyue-llk
left a comment
There was a problem hiding this comment.
This PR moves real-layout ResizeObserver delivery onto the document rendering-update source and implements the spec's gather -> broadcast -> relayout -> gather loop with flat-tree depth, a loop-error ErrorEvent, a per-callback microtask checkpoint, and a 32-round budget that yields and resumes at the current depth. The core loop is a clear improvement and matches the spec ordering: everything is gathered before any callback runs, the shallowest delivered depth is computed across all observers, and skipped observations produce the error and a depth-0 retry. I am requesting changes for three reasons. The per-observer scheduled flag can now get stuck, which makes an observer go permanently silent. The new checkpoint call also runs end-of-checkpoint work inside the legacy microtask flush. And a sustained feedback loop now re-queues itself with no frame pacing.
Findings not anchored to a diff line:
- [major] CI: Nextest is red.
moli-core::wpt_compatfontface_basicandfontface_idlharnessfail all 3 tries withfetch readiness timed out after 5000 ms while waiting for response headers. I found neither failure in recent main runs or in the neighbouring PRs I checked (1591, 1592, 1594, 1595). This PR changes rendering-update work, and its fresh layout pass now runsreconcile_document_web_fonts_for_layoutfrom rendering turns, so please triage this before merging. Do not assume it is a flake. - [major] OnDemand is no longer on demand, and resource use went up. In OnDemand mode, every published layout now runs
queue_resize_observer_checks(layout.rs). That queues a ResizeObserver rendering update, and the update runs at least one more fullwith_fresh_layout_passbefore it measures anything, even when no observation is active. So any page with an observer pays roughly double the layout cost per publish, plus up to 32 fresh layouts per turn. The Spider A/B in this PR shows HEAD at +23.7% duration, +17.5% CPU and +14.9% peak PSS, with an identical row count. That run is informational and noisy, but it is consistent with this change. Consider skipping the fresh layout when the published snapshot is current (layout not dirty), or gathering first and laying out only when an observation is active. - [minor] The tests do not cover the regressions above. No test covers re-observing after an observer drained while it was unregistered (the scheduled-flag bug), the non-real-layout path together with IndexedDB or end-of-checkpoint ordering, or a new shallow change arriving while a resumed depth-N continuation is pending.
resize_observer_deep_growth_yields_without_masking_real_feedbackasserts onlycreated > 1 && created < 120, which implicitly depends on the magic 32 and does not pin the resume depth. The PR description says the tests are the causal proof for the MUI grid case. The fetch_dump tests do approximate that case, but the description's Browser Eval reproduction is external and unverifiable here. - [nit] Both callers ignore the
skippedelement returned bydeliver_resize_observer. In the gathered pathminimum_depthisNone, so the depth filter inbuild_resize_observer_entries_at_depthonly ever runs withSome(0), wheredepth <= 0cannot be true for an Element. That branch is dead, so the API can drop the depth parameter and the(entries, depth, skipped)triple from the legacy path.
Verdict: changes requested (posted as a comment because GitHub does not allow requesting changes on your own PR).
Automated review by Claude Code.
7d1ff5f to
2ae7f4e
Compare
lanyue-llk
left a comment
There was a problem hiding this comment.
Re-review of the updated version (2ae7f4e12)
This PR delivers real-layout ResizeObserver notifications through the document rendering-update source. It implements the gather -> broadcast -> relayout -> gather loop with flat-tree depth, the loop-error ErrorEvent, and a named 32-round budget per turn. The new commit 2ae7f4e12 fixes most of round 1: the stuck scheduled flag, the misplaced checkpoint, unpaced retries, payload-blind depth coalescing, the magic constant and the guard field. Every one of those fixes except the IndexedDB one has a regression test that would fail without it.
Verdict: request changes. The OnDemand layout-cost finding from round 1 is still unaddressed. The new IndexedDB regression test never reaches the legacy path it is named for.
Previous findings
| # | Finding | Status | Evidence |
|---|---|---|---|
| 1 | [major] Per-observer scheduled flag stuck, observer goes silent |
FIXED | queue_resize_observer_delivery (resize_observer_runtime.rs:784-794) now returns on the real-layout path before it reads or sets the flag. resize_observer_reobserve_after_a_cancelled_real_layout_delivery would fail on the old code, because the second turn would return false. |
| 2 | [major] Per-callback checkpoint on the legacy microtask path deactivates IDB transactions | FIXED in code, test does not cover it | The checkpoint moved into broadcast_resize_observer_rendering_update (line 918), and deliver_resize_observer no longer has one. The regression test runs under the harness default LayoutPolicy::OnDemand, so it never takes the legacy flush (see inline). |
| 3 | [major] Sustained same-depth feedback spins the rendering source | FIXED | An undelivered result now arms a 16 ms Window timer instead of an immediate re-queue (rendering_update.rs:117-145). Both feedback tests now assert that no turn is ready before the timer fires, and both would fail on the old code. New minor finding about how the timer is implemented: see inline. |
| 4 | [minor] Depth-blind coalescing causes false loop errors | FIXED | queue_resize_observer_rendering_update_for_target merges min(pending, new) (rendering_updates.rs:276-289), and the generic coalescer compares payloads again. resize_observer_fresh_shallow_work_resets_a_pending_deep_continuation would fail on the old code: the root is skipped at depth N and fires an error. |
| 5 | [nit] Magic 32 |
FIXED | RESIZE_OBSERVER_ROUNDS_PER_RENDERING_TURN is documented as a fairness bound, and the deep-growth test now asserts the exact budget + 1 boundary. |
| 6 | [nit] Opaque guard condition and pub(crate) field |
FIXED, pushback accepted | is_broadcasting_resize_observers_for is shared by both call sites, and the field is private behind begin/restore. The author pushed back on RAII: the fallible loop is captured into a Result before restore_resize_observer_broadcast, so a ? cannot skip the restore. |
| 7 | [major] CI Nextest red (fontface_basic, fontface_idlharness) |
RESOLVED | Nextest passes on 2ae7f4e12 (run 39731395791). The new base contains 41e608f91 fix(renderer): load and retire web fonts by child Document owner. The failures do not reproduce, and I can no longer attribute them to this PR. Some A/B jobs (Build release · common ancestor, Spider fixture, Frontend differential, Agent episodes, Runtime/CDP contracts) were still pending at review time. |
| 8 | [major] OnDemand is no longer on demand: extra full layouts per publish and per turn | NOT FIXED, no response | The code is unchanged, see inline. No Spider A/B has been posted for the new head. The latest one is for 7d1ff5f9f and still shows +23.7% duration, +17.5% CPU and +14.9% peak PSS. |
| 9 | [minor] Tests miss the regressions | MOSTLY FIXED | Re-observe, depth reset, exact budget and paced retries are now covered. The legacy-path IDB case is still uncovered (#2). |
| 10 | [nit] Dead minimum_depth filter and ignored skipped in the legacy path |
NOT FIXED | resize_observer_runtime.rs:310 still passes Some(0) only on the legacy path, where depth <= 0 never holds for an Element. Line 915 still discards skipped. |
New findings
- [minor] The IndexedDB regression test does not exercise the legacy path (indexed_db.rs:4447).
- [minor] The pacing retry is a page-visible, page-cancellable
setTimeout-class timer, and it is not deduplicated (rendering_updates.rs:313). - [nit] The dead depth parameter is still present (resize_observer_runtime.rs:310).
Not anchorable:
- [minor] Docs and description are now stale.
LayoutPolicy::OnDemand's doc comment (moli-page-types/src/layout.rs:9, "Only screenshot, screencast, and PDF output build layout") is false now that ResizeObserver rendering turns build and publish layouts. The author's PR comment of 2026-10-20 claims "the rendering task no longer creates an unsolicited layout pass ... zero-extra-layout contract". The following commitba4247b6breverted that by addingwith_fresh_layout_passbefore every gather. The rewritten description's "Problem" section lists four bugs that this PR's own earlier revision introduced (stuck flag, legacy checkpoint, spin, depth merge) as if they were the problem being solved. The real motivation (main never delivered real-layout observations through rendering turns, which lost the MUI grid rows) is buried under "Browser Eval integration". Please update the doc comment and rewrite the Problem section againstmain. - [nit] Stack hygiene.
b3976852e("preserve on-demand layout during resize delivery": removes the layout pass) andba4247b6b("measure resize updates after layout": restores it) cancel each other out.162bab8b1,c735e7c45and2ae7f4e12are review fixups to the same files. Please squash before merge so that bisect does not land on the intermediate states. - Merge check:
git merge-tree --write-tree origin/main refs/review/r2-1593is clean.
Verdict: changes requested (posted as a comment because GitHub does not allow requesting changes on your own PR).
Automated review by Claude Code.
c1b2fcb to
9e83f5c
Compare
|
Handled the non-inline review items in
I did not add a layout-generation cache. Once a document has active observation targets, a fresh layout is the input needed to detect size changes. Reusing the previous layout without a complete style/layout invalidation contract could silently miss the exact virtual-content update this PR fixes. The current boundary is smaller and testable: no targets means no layout; active targets use a bounded 32-round observation loop and deduplicated delayed retries. |
lanyue-llk
left a comment
There was a problem hiding this comment.
Final re-review (P0/P1 only) (9e83f5cb3)
Verdict: approve. No P0/P1 issues remain. 9e83f5cb3 squashes the stack into one commit and addresses all three round-2 items.
Previous P0/P1-level findings
| # | Finding | Status | Evidence at 9e83f5cb3 |
|---|---|---|---|
| 1 | Stuck per-observer scheduled flag |
FIXED (r2) | queue_resize_observer_delivery returns on the real-layout path before it reads or sets the flag (resize_observer_runtime.rs:763-768). |
| 2 | Legacy-path checkpoint deactivates IDB transactions; the regression test was vacuous | FIXED | The checkpoint runs only in broadcast_resize_observer_rendering_update (resize_observer_runtime.rs:948). The test now calls set_layout_policy(Mock) (indexed_db.rs:4451), so observe() queues resize_observer_flush_callback as a host microtask ahead of the Promise.then. With the old deliver_resize_observer checkpoint, run_end_of_microtask_checkpoint_tasks would deactivate tx and the test would see throw:TransactionInactiveError. A real-layout twin (indexed_db.rs:4494) covers a transaction created inside the RO callback. |
| 3 | Same-depth feedback spins the rendering source | FIXED (r2) | Paced 16 ms retry; tests assert that no turn is ready before the deadline. |
| 4 | Pacing retry is a page-cancellable Function timer and is not deduplicated |
FIXED | It now uses queue_internal_task_after (timers.rs:286), which builds an InternalFunction task that cancel_window_timer refuses to cancel (timers.rs:895-898). resize_observer_retry_targets coalesces repeat arms and is cleared in the callback (rendering_updates.rs:12-26, 355-373). resize_observer_same_depth_feedback_still_reports_a_loop_error now clears every page timer id before advancing and still requires the retry turn. |
| 5 | Depth-blind coalescing causes false loop errors | FIXED (r2) | min(pending, new) merge (rendering_updates.rs:293-308). |
| 6 | OnDemand cost: unconditional full layout per RO turn | PARTIAL, not blocking | A consumed RO turn now checks resize_observer_target_has_observations and returns before any layout when the document has no observed targets (script_vm/rendering_update.rs:80-88). queue_resize_observer_checks only queues for observers with targets. The reobserve test asserts zero added layout passes on the empty turn. With live targets, each turn still runs one fresh full layout plus one per delivering round. HTML "update the rendering" requires up-to-date layout before each gather, and the author declined a generation cache, which I accept. I traced the publish → RO turn → publish path: the broadcast guard (layout.rs:262-266) stops the RO layout from re-queueing itself, and RO is the only rendering-update kind that calls with_fresh_layout_pass, so it cannot ping-pong with IntersectionObserver turns. No Spider A/B exists for this head yet (Build release jobs are pending). The only A/B, +23.7% duration, is for 7d1ff5f9f, which predates both the per-turn layout and this early exit, so I cannot attribute a severe regression to the current code. |
| 7 | Nextest red (fontface) | RESOLVED (r2) | Not attributable to this PR. |
New findings
No P0/P1 issues found in the squashed commit. Hang/starvation check: rounds are bounded at 32 per turn. A budget continuation re-queues immediately, so unbounded deep growth keeps yielding between Page tasks instead of hanging. Same-depth feedback retries only through the deduplicated 16 ms internal timer. The no-target early exit cannot drop notifications, because a document without observed targets has nothing to deliver.
CI and merge
CDP smoke (Chromium)failed oninspector-routing(raw_cdp_navigation_replacement_during_active_javascript: TimeoutError). That job drives Playwright's Chrome for Testing as a control, not the Moli renderer, so it is unrelated to this ResizeObserver change.- Clippy, Format, Node, Python and Windows jobs pass.
- Nextest, Build release (HEAD and ancestor) and the A/B jobs are still pending. This approval assumes Nextest goes green. Please post the Spider A/B for this head before merging.
git merge-treeagainstorigin/mainis clean, and GitHub reports MERGEABLE.
Verdict: approve (posted as a comment; self-approval is not allowed).
Automated review by Claude Code.
|
Current-head Browser Eval badcase is green.
Reproduction uses Browser Eval's provider adapter rather than a hand-written fixture: uv run --frozen python scripts/run_certified_benchmark.py \
--dataset <frozen-430-dataset.json> \
--output <output-dir> \
--work-root <work-dir> \
--providers moli-candidate \
--case-ids dynamic-104-mui-data-grid \
--workers 1 \
--max-failure-retries 0 \
--provider-config configs/providers.headless.json \
--retrieval-mode native \
--diagnosticThe current Chromium-reference smoke failure is unrelated to this code path: it timed out in |
Problem
Moli did not run layout-backed ResizeObserver delivery as part of document rendering updates. Pages that materialize content from measured size changes could therefore return an incomplete DOM; in the Browser Eval MUI grid case, the four required rows were absent.
Adding real-layout delivery also requires browser-observable scheduling rules: gather every observer before invoking callbacks, preserve microtask and IndexedDB transaction ordering, bound same-depth feedback, and carry finite deeper work into a later rendering turn.
Change
ResizeObserver delivery now runs through the existing exact-document rendering-update queue. A task exits before layout when its document has no observation targets. When targets exist, each observation round uses a fresh layout, freezes all active records before callbacks, publishes the resulting geometry, and runs the callback microtask checkpoint at the rendering-update boundary.
Same-depth feedback reports the required loop error and retries after a deduplicated 16 ms internal browser task. Deeper finite work retains its depth across a named 32-round fairness boundary. Fresh shallower work resets the pending minimum depth. Legacy delivery keeps the surrounding checkpoint in control.
The implementation stays inside the existing layout, exact-document task, and timer owners. It adds no polling, site rule, benchmark case branch, or speculative layout cache.
Reproduce and verify
The focused tests cover:
observe()anddisconnect()changes.cargo nextest run --locked -p moli-renderer-v8 --lib \ -E 'test(resize_observer_) | test(indexed_db_transaction_stays_active_past_legacy_resize_observer_microtasks) | test(indexed_db_transaction_created_by_real_resize_observer_survives_callback_microtasks)' \ --no-fail-fastBrowser Eval reproduction
dynamic-104-mui-data-gridis the frozen Browser Eval case that exposed the missing virtualized rows. Build this PR, bind that executable and SHA-256 asmoli-candidate, then run the case through the certified native-fetch entry point:Moli 1.1.15 returned none of the four required grid records. Repository fixtures isolate the ResizeObserver behavior; the Browser Eval command checks the complete native-fetch and scoring path against the frozen task.
Dependency
Independent PR targeting
main.