Context
PR #17822 delivered Neo.component.Splitter#liveResize by moving pointer-frame geometry to the main thread: Neo.main.draggable.Resize mutates the registered target's outer layout node per frame, and the App Worker receives one frozen {axis, size, targetId} tuple at release for a single durable wrapperStyle commit.
The operator has since stated the driver for that design, and it is not an ownership boundary:
it was not really about ownership boundaries, but performance. if we want a live-resize, worker roundtrips in the shape app -> vdom -> main -> app can cost us lags and missing frames.
#17819 makes a specific promise about readiness: "the real Grid/TreeGrid witness decides whether the mode is ready beyond opt-in use." Under an ownership premise that is the right gate — correctness under heavy widgets is the question. Under the actual performance premise it is the wrong gate, because the witness measures geometry and never measures time.
Live latest-open sweep: checked the latest 20 open issues at 2026-08-28T07:28:12Z; no equivalent found. A2A claim sweep over the latest 30 messages found no overlapping [lane-claim]/[lane-intent]. Nearest neighbours are #9494 (main-thread scroll sync for Grid — same problem class, different surface) and #9486 (zero-jitter locked columns); neither covers splitter live-resize cadence.
The Problem
The eliminated round trip is the mechanism. Held frames are the outcome. Only the mechanism has a witness.
Verified at e5ebac2679, the state PR #17822 shipped:
| Property |
Measured |
Where |
| Zero App-Worker move frames per pointer frame |
yes, positive-controlled |
test/playwright/unit/main/addon/DragDrop.spec.mjs — asserts width === '220px' before sent.toEqual([]), so the claim cannot pass with the code doing nothing |
| No geometry split-brain across buffered Grid + TreeGrid |
yes |
test/playwright/component/component/SplitterHeavyContent.spec.mjs — virtualized header/cell alignment, ≤3px return-to-boundary across four alternating real drags |
| Transient frames never become durable worker state |
yes |
test/playwright/e2e/core/SplitterLiveResizeNL.spec.mjs:114 |
| Frame cadence, dropped frames, gesture latency |
no |
nothing |
Every toBeLessThan* in both added specs is a pixel tolerance. There is no performance.now(), no requestAnimationFrame counting, no elapsed-time, duration, or budget assertion anywhere in the added coverage.
This is not a claim that live mode is slow. Removing an app → vdom → main → app hop per pointer frame is the single largest win available on this path, so the prior is strongly favourable. The defect is that the property the architecture was chosen for is the one property nobody can regress-detect, while three of its side effects are locked down.
The consequence is deferred, not present: liveResize defaults to false, so nothing ships on an unmeasured claim today. It becomes live the moment anyone proposes the default flip — or, sooner, when #17820 gives the dock a live preview whose entire value proposition is per-frame feedback on a surface where pane proportions are the workspace.
The Architectural Reality
- The suppression point is
src/main/addon/DragDrop.mjs#onDragMove: if ((!me.dragProxyElement && !resizeLocally) || me.alwaysFireDragMove) gates the DomEvents.sendMessageToApp forward. When a live resize is registered, resizeLocally is true and no move frame crosses the boundary.
- Deferred mode reaches the same terminal math through
Resize#apply with preview: false, so it computes size without touching the DOM and keeps its proxy presentation. Both modes are therefore driveable in one test run — this is the load-bearing fact for the fix below.
- The fixture already exists.
SplitterHeavyContent.spec.mjs drives four alternating real pointer drags across a buffered Grid and TreeGrid and already toggles between modes. No new harness, app, or example is required.
Neo.manager.VDomUpdate coalescing is what makes the worker path survivable at all; it is not what makes it fast enough for a per-frame gesture. That distinction is exactly what is unmeasured.
The Fix
- Extend
test/playwright/component/component/SplitterHeavyContent.spec.mjs to sample frame timing across an existing heavy drag — not a new spec, not a new fixture app.
- Drive both
liveResize: true and liveResize: false in the same run over the same gesture path, and assert the relative cadence relationship rather than an absolute millisecond budget.
- Establish the threshold by measurement first, then commit it as an assertion. Record the measured numbers in the PR body so the bar has provenance.
- Add the resulting gate to #17819's readiness language: the default flip requires the cadence assertion, not the geometry assertion.
Why relative, not absolute. An absolute wall-clock budget on shared CI runners is clock-brittle and will produce exactly the flake class that costs more triage than the coverage is worth. A same-run comparison self-normalizes against runner speed, and it simultaneously supplies the positive control: if the harness cannot distinguish live cadence from deferred cadence on the same machine, it cannot detect a regression in either, and a green result means nothing. The contrast is both the calibration and the falsifier.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
test/playwright/component/component/SplitterHeavyContent.spec.mjs (exists, added by PR #17822) |
This leaf under parent #13158 |
Gains frame-timing capture across an existing heavy alternating drag, for both modes in one run |
Timing capture unavailable in the runner → the test fails loud rather than skipping silently |
Spec-level comment naming why the comparison is relative |
The measured live-vs-deferred figures, recorded in the implementing PR |
| The cadence threshold |
Measurement taken in step 3, not authored in advance |
One committed assertion on the live-vs-deferred relationship |
A run where the two modes are indistinguishable fails the positive control and blocks the gate |
Spec comment carrying the measured baseline |
Same-run contrast, reproducible locally and in CI |
| #17819's readiness statement |
#17819 body, "the real Grid/TreeGrid witness decides whether the mode is ready beyond opt-in use" |
Amended so the readiness gate names the cadence assertion |
If #17819 has closed by then, the amendment lands on parent #13158 instead |
#17819 / #13158 body |
The assertion existing and being green |
Decision Record impact
none — additive test coverage inside existing owners; no new module, no public surface, no ADR authority touched. It does inform D#17818's edge-resize contract, where the same frame-budget rationale currently appears as an authority ordering with correctness-shaped falsifiers only; that contract is Euclid's and is tracked there, not here.
Acceptance Criteria
Out of Scope
- Changing
liveResize to default true. This leaf builds the gate; it does not walk through it.
Neo.dashboard.DockSplitter live preview — that is #17820, and it should consume this gate rather than re-derive one.
- The historical sibling min/max request
#4723, still deliberately deferred per #17819.
- A general-purpose performance harness, a benchmark dashboard, or any reporting artifact. The deliverable is one assertion with a threshold; a report nobody can regress against is the anti-pattern here.
- Re-measuring the worker-roundtrip path as a design question. #17822 already settled the architecture; this measures the outcome it was chosen for.
Avoided Traps
- A report-only benchmark. A number in a PR body decays the moment the next commit lands. Only a committed assertion detects a regression, which is why the deliverable is an assertion and not a measurement.
- A cadence assertion with no contrast. Asserting that live mode holds some frame rate, alone, is unfalsifiable in practice — it passes on a fast runner regardless of whether the code is any good. The deferred-mode arm is not extra coverage; it is what makes the live arm mean something.
- Inventing the threshold before measuring it. An authored number gets tuned until green. Measure, then commit the number, then let it fail.
- An absolute wall-clock budget in CI. Shared runners make this flake, the flake gets muted, and a muted gate is worse than no gate because it reads as coverage.
- Filing this as a diagnostic ticket. The temptation is a "measure and report" leaf. That produces no durable gate and would be correctly rejected as proof-only work.
Related
Parent: #13158
Related: #17819
Related: #17820
Related: #9494
Related: #9486
Origin Session ID: 460b7b3d-aef1-4ae5-a9d0-10a889cf06dd
Retrieval Hint: query_raw_memories("live resize frame cadence unmeasured mechanism versus outcome Splitter heavy witness")
Authored by Vega (Opus 5, Claude Code) 🌿
Context
PR #17822 delivered
Neo.component.Splitter#liveResizeby moving pointer-frame geometry to the main thread:Neo.main.draggable.Resizemutates the registered target's outer layout node per frame, and the App Worker receives one frozen{axis, size, targetId}tuple at release for a single durablewrapperStylecommit.The operator has since stated the driver for that design, and it is not an ownership boundary:
#17819 makes a specific promise about readiness: "the real Grid/TreeGrid witness decides whether the mode is ready beyond opt-in use." Under an ownership premise that is the right gate — correctness under heavy widgets is the question. Under the actual performance premise it is the wrong gate, because the witness measures geometry and never measures time.
Live latest-open sweep: checked the latest 20 open issues at 2026-08-28T07:28:12Z; no equivalent found. A2A claim sweep over the latest 30 messages found no overlapping
[lane-claim]/[lane-intent]. Nearest neighbours are #9494 (main-thread scroll sync for Grid — same problem class, different surface) and #9486 (zero-jitter locked columns); neither covers splitter live-resize cadence.The Problem
The eliminated round trip is the mechanism. Held frames are the outcome. Only the mechanism has a witness.
Verified at
e5ebac2679, the state PR #17822 shipped:test/playwright/unit/main/addon/DragDrop.spec.mjs— assertswidth === '220px'beforesent.toEqual([]), so the claim cannot pass with the code doing nothingtest/playwright/component/component/SplitterHeavyContent.spec.mjs— virtualized header/cell alignment, ≤3px return-to-boundary across four alternating real dragstest/playwright/e2e/core/SplitterLiveResizeNL.spec.mjs:114Every
toBeLessThan*in both added specs is a pixel tolerance. There is noperformance.now(), norequestAnimationFramecounting, no elapsed-time, duration, or budget assertion anywhere in the added coverage.This is not a claim that live mode is slow. Removing an
app → vdom → main → apphop per pointer frame is the single largest win available on this path, so the prior is strongly favourable. The defect is that the property the architecture was chosen for is the one property nobody can regress-detect, while three of its side effects are locked down.The consequence is deferred, not present:
liveResizedefaults tofalse, so nothing ships on an unmeasured claim today. It becomes live the moment anyone proposes the default flip — or, sooner, when #17820 gives the dock a live preview whose entire value proposition is per-frame feedback on a surface where pane proportions are the workspace.The Architectural Reality
src/main/addon/DragDrop.mjs#onDragMove:if ((!me.dragProxyElement && !resizeLocally) || me.alwaysFireDragMove)gates theDomEvents.sendMessageToAppforward. When a live resize is registered,resizeLocallyistrueand no move frame crosses the boundary.Resize#applywithpreview: false, so it computes size without touching the DOM and keeps its proxy presentation. Both modes are therefore driveable in one test run — this is the load-bearing fact for the fix below.SplitterHeavyContent.spec.mjsdrives four alternating real pointer drags across a bufferedGridandTreeGridand already toggles between modes. No new harness, app, or example is required.Neo.manager.VDomUpdatecoalescing is what makes the worker path survivable at all; it is not what makes it fast enough for a per-frame gesture. That distinction is exactly what is unmeasured.The Fix
test/playwright/component/component/SplitterHeavyContent.spec.mjsto sample frame timing across an existing heavy drag — not a new spec, not a new fixture app.liveResize: trueandliveResize: falsein the same run over the same gesture path, and assert the relative cadence relationship rather than an absolute millisecond budget.Why relative, not absolute. An absolute wall-clock budget on shared CI runners is clock-brittle and will produce exactly the flake class that costs more triage than the coverage is worth. A same-run comparison self-normalizes against runner speed, and it simultaneously supplies the positive control: if the harness cannot distinguish live cadence from deferred cadence on the same machine, it cannot detect a regression in either, and a green result means nothing. The contrast is both the calibration and the falsifier.
Contract Ledger
test/playwright/component/component/SplitterHeavyContent.spec.mjs(exists, added by PR #17822)Decision Record impact
none— additive test coverage inside existing owners; no new module, no public surface, no ADR authority touched. It does inform D#17818's edge-resize contract, where the same frame-budget rationale currently appears as an authority ordering with correctness-shaped falsifiers only; that contract is Euclid's and is tracked there, not here.Acceptance Criteria
SplitterHeavyContent.spec.mjssamples per-frame timing across at least one existing heavy alternating drag, with no new fixture app or spec file added.liveResize: trueandliveResize: falseover the same gesture path.Out of Scope
liveResizeto defaulttrue. This leaf builds the gate; it does not walk through it.Neo.dashboard.DockSplitterlive preview — that is #17820, and it should consume this gate rather than re-derive one.#4723, still deliberately deferred per #17819.Avoided Traps
Related
Parent: #13158 Related: #17819 Related: #17820 Related: #9494 Related: #9486
Origin Session ID: 460b7b3d-aef1-4ae5-a9d0-10a889cf06dd
Retrieval Hint:
query_raw_memories("live resize frame cadence unmeasured mechanism versus outcome Splitter heavy witness")Authored by Vega (Opus 5, Claude Code) 🌿