From b0506d936c281c69bcc63ed36cd69435e1ad021e Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Thu, 8 Oct 2026 10:03:48 -0400 Subject: [PATCH 1/2] Record delivered scroll intent and title publication checks --- .../dm-initial-publication-20261008/README.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/evidence/dm-initial-publication-20261008/README.md b/evidence/dm-initial-publication-20261008/README.md index dfdc9364f..5e6a395b0 100644 --- a/evidence/dm-initial-publication-20261008/README.md +++ b/evidence/dm-initial-publication-20261008/README.md @@ -935,3 +935,26 @@ without gaps/references and checking default executable symlinks:282680401bytes. Current, immediate rollback and backend runtimes remain; source, wheels, proof, publication and raw results are retained. Cleanup original evidence: disk-cleanup-owner-20261002/Parent-caption-superseded-runtimes-retired-20261008.json. + +## Independent scroll intent and unchanged notification title + +Native125,154f41637, preserves Widget's original scroll request when layout is +also pending. Both intents reach Screen, whose existing held/actionable-source +decision owns full versus visible reflow. Reviewed exact source; affected +native held/committed/released geometry control passed. No new flag or policy. + +Toad578,d44f4868, compares its fresh derived notification title against the +original native title using Content.is_same before publishing. Detail-only +result/error changes still update expanded details and busy/error styling. +Changed summary/count/priority continue publishing. Explicit native mutable +title/span publication remains unchanged.705 modules parse without omissions. +Actual native disclosure and installed title layout/detail checks passed; +read-only source review found no semantic blocker. + +Combined installed native-driver workload passed160 wheel packets/3 reversals +and20 incoming messages, all input ordered, no provider/public input. Frame +p95~98.7ms/max270.6ms remains unacceptable. Full reflow24calls/382.4CPUms, +worst39.9ms; visible53/170.1/worst6.71ms. No broad latency improvement claim. +Exact raw checks: title-scroll-installed.log, title-scroll-wheel.log and +title-scroll-wheel/ under existing dm-native-caption scratch. Published the +reviewed combined cohort for new launches; backend and existing windows remain. From fd5dfde0129c3336fdce281c7c02925f191aec5e Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Thu, 8 Oct 2026 10:21:19 -0400 Subject: [PATCH 2/2] Revoke native body retirement when viewport demand changes --- src/toad/widgets/viewport_body.py | 8 ++-- tests/viewport_body_lifetime_pilot.py | 53 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/toad/widgets/viewport_body.py b/src/toad/widgets/viewport_body.py index 049657c95..83020c407 100644 --- a/src/toad/widgets/viewport_body.py +++ b/src/toad/widgets/viewport_body.py @@ -758,7 +758,7 @@ def retire_native_body(self, current): return BodyMeasurement.retire(current, self) if self.lock.is_locked or self._body_viewport is None: return BodyMeasurement.retire(current, self) - if self in self._body_viewport.protected(): + if self._body_viewport.requires_body(self): return BodyMeasurement.retire(current, self) children = self.reconstructible_children() if not children: @@ -824,9 +824,10 @@ async def measured(): return measured() def retirement_current(self, current): + """Borrow current viewport demand again after asynchronous capture.""" return (self._body_measurement is current and self.is_attached and not self._closing and self._body_viewport is not None - and self not in self._body_viewport.protected()) + and not self._body_viewport.requires_body(self)) async def release_native_body(self, current): if not self.body_ready or not self.retirement_current(current): @@ -835,8 +836,7 @@ async def release_native_body(self, current): if not children: return False async with self.retirement_custody() as can_commit: - if (not can_commit or not self.retirement_current(current) - or self._body_viewport.requires_body(self)): + if not can_commit or not self.retirement_current(current): return False self._update_body_measurement(MeasuredBody(current.width, current.rows, current.widgets)) self.retire_body_resources() diff --git a/tests/viewport_body_lifetime_pilot.py b/tests/viewport_body_lifetime_pilot.py index b191fbde3..fa23dfff2 100644 --- a/tests/viewport_body_lifetime_pilot.py +++ b/tests/viewport_body_lifetime_pilot.py @@ -147,6 +147,57 @@ def observe(frame, event, argument): print(json.dumps(receipt), flush=True) +async def retirement_visibility(output: Path): + """A captured offscreen body keeps its native tree after real exposure.""" + output.mkdir(parents=True, exist_ok=True) + with TemporaryDirectory(prefix="retirement-visible-", dir=output) as directory: + root = Path(directory) + os.environ.update(AGENT_COMMS_ROOT=str(root / "wire"), + XDG_CONFIG_HOME=str(root / "config"), + XDG_STATE_HOME=str(root / "state"), + XDG_DATA_HOME=str(root / "data")) + app = ToadApp(project_dir=str(root)) + async with app.run_test(size=(100, 35)) as pilot: + await app.selected_session.wait_content_ready() + view = app.selected_session.conversation + window, manager = view.window, view.window.document_viewport + first = AgentResponse("Original body\n\nActual retained paragraph.") + await view.contents.mount(first) + async with asyncio.timeout(15): + while not first.body_ready or not first.prepared_paint_is_current(first.prepared_paint_sources()): + await pilot.pause(.02) + # Suspend original housekeeping so this check owns one retirement. + await manager.suspend_source() + second = AgentResponse("\n\n".join("Later original paragraph " * 8 for _ in range(30))) + await view.contents.mount(second) + window.scroll_end(animate=False, immediate=True) + await pilot.pause() + assert not manager.requires_body(first) + children = first.reconstructible_children() + assert children and first.prepared_paint_is_current(first.prepared_paint_sources()) + operation = first.retire_body() + # Actual scrolling exposes the body after synchronous acquisition, + # before the returned operation measures/commits its captured rows. + window.release_anchor() + window.scroll_to_widget(first, animate=False, immediate=True, top=True) + await pilot.pause() + assert manager.requires_body(first) + assert await operation is False + assert first.reconstructible_children() == children and not first.body_dormant + assert all(child.is_attached for child in children) + assert first.body_ready + assert await first.retire_body() is False + window.scroll_end(animate=False, immediate=True) + await pilot.pause() + assert not manager.requires_body(first) + assert await first.retire_body() is True + assert first.body_dormant and not any(child.is_attached for child in children) + manager.resume_source() + assert app._exception is None + await asyncio.get_running_loop().shutdown_default_executor() + print("PASS: real exposure revokes captured retirement; native body retained; offscreen retirement still completes") + + async def main(): with TemporaryDirectory(prefix="toad-viewport-body-", dir=os.environ.get("VIEWPORT_EVIDENCE")) as directory: root = Path(directory) @@ -328,6 +379,8 @@ async def switch_during_transaction(): if __name__ == "__main__": if len(sys.argv) == 3 and sys.argv[1] == "--worker-custody-only": asyncio.run(worker_custody(Path(sys.argv[2]).resolve())) + elif len(sys.argv) == 3 and sys.argv[1] == "--retirement-visibility-only": + asyncio.run(retirement_visibility(Path(sys.argv[2]).resolve())) elif len(sys.argv) == 1: asyncio.run(main()) else: