diff --git a/evidence/dm-initial-publication-20261008/README.md b/evidence/dm-initial-publication-20261008/README.md index f5429be52..aebdc483b 100644 --- a/evidence/dm-initial-publication-20261008/README.md +++ b/evidence/dm-initial-publication-20261008/README.md @@ -980,3 +980,41 @@ not a performance pass. Current wheel is published for new launches through the reviewed original publication owner; existing windows retain loaded code. Exact checks/publication live under visible-retirement-delivery-20261008 and retirement-installed.log/retirement-sidebar-drag.log in existing scratch. + +## Tail demand uses reverse native traversal + +Toad580 ac60b1cf: source_tail stops the existing body-root traversal at the +first reverse-order native boundary instead of acquiring and reversing every +body root. Default forward traversal remains unchanged. requires_body first +rejects unregistered bodies and admits actual visible bodies before acquiring +interaction ancestry. No order catalog or protection cache was added. + +Native source and exact installed Apps passed tail/reverse order after actual +native reorder and removal.705 source/test modules parsed without omissions. +No broad performance measurement or frame-time improvement is claimed. +The builder finished wheels then refused a relative output URI before prefix +creation; original negative retained. Installation/verification continued with +the original completed wheel and absolute path, without rebuilding. The same +builder now resolves its output path before acquisition.954 assets/full69 and +native package verification passed. Reviewed cohort published for new launches; +backend and existing loaded windows preserved. Raw tail-source.log, +tail-installed.log and tail-build.log remain in existing scratch. + +## Current installed pacing remains unqualified + +Current document-tail cohort160-wheel/20incoming private native-driver burst +completed ordered/no App error/provider0. Wheel median84.4/p95345.7ms; +frame median13.0/p95114.5/max291.6ms. Single run under actual host conditions; +no causal regression/improvement claim. UI-thread CPU spans: full reflow25 +calls/425.2ms/worst50.6; visible62/231.2/worst32.1; paint160/266.5/worst6.1. +WorkerStatic preparation122/7.6ms total and row render69/2.2ms total do not +explain this layout cost. Direct thread-time records retained in tail-wheel; +cProfile is not used for these measurements. Native compositional geometry +migration remains open, including held publication loans and explicit reader +demand. Matched installed application comparison follows its final checkpoint. + +Fresh privileged283-process census without gaps or borrowers and executable +symlink checks permitted removal of two superseded runtime directories, +110389417bytes. Current document-tail, visible-retirement rollback and backend +remain. Source/wheels/proofs/raw are preserved; original removal evidence in +Parent-tail-superseded-runtime-retired-20261008.json. diff --git a/src/toad/application_lifetime.py b/src/toad/application_lifetime.py index 348ec6ca0..72683d122 100644 --- a/src/toad/application_lifetime.py +++ b/src/toad/application_lifetime.py @@ -3,6 +3,7 @@ from datetime import datetime, timezone from functools import partial +from importlib import import_module import platform from time import monotonic from typing import TYPE_CHECKING @@ -27,7 +28,7 @@ async def send(self, event_name: str, properties: dict[str, object]) -> None: async def deliver(self, event_name: str, properties: dict[str, object]) -> None: app = self.app - import httpx + httpx = await app.preparation.run_thread(import_module, "httpx") from toad import get_version width, height = app.size @@ -52,6 +53,16 @@ async def deliver(self, event_name: str, properties: dict[str, object]) -> None: class ApplicationLifetime: """A confirmed quit and explicit quit share the same save-after-blur path.""" + @staticmethod + def set_process_title(title: str) -> None: + """Name the terminal application or its CLI server process.""" + try: + import setproctitle + + setproctitle.setproctitle(title) + except Exception: + pass + def __init__(self, app: ToadApp, initial_mode: str | None) -> None: self.app = app self.initial_mode = initial_mode @@ -70,12 +81,11 @@ async def start(self) -> None: await app.session_navigation.new(app.session_navigation.default_source) app.terminal_attention.attach() app.set_timer(1, self.check_version) - from toad.cli import set_process_title - app.run_worker(partial(set_process_title, "toad"), thread=True, exit_on_error=False) + app.run_worker(partial(self.set_process_title, "toad"), thread=True, exit_on_error=False) app.update_show_sessions() def check_version(self) -> None: - self.app.run_worker(self.version.check, exit_on_error=False) + self.app.run_worker(partial(self.version.check, self.app.preparation.run_thread), exit_on_error=False) def confirm_quit(self) -> None: now = monotonic() diff --git a/src/toad/cli.py b/src/toad/cli.py index 6b341f16f..c1ff930d6 100644 --- a/src/toad/cli.py +++ b/src/toad/cli.py @@ -3,6 +3,7 @@ import click from toad.app import ToadApp +from toad.application_lifetime import ApplicationLifetime from toad.agent_schema import AgentDefinition from toad.render_backend import Renderer from toad.render_choices import RendererChoice @@ -47,20 +48,6 @@ def renderer_from_cli(backend: str | None) -> Renderer | None: raise click.ClickException(str(error)) from error -def set_process_title(title: str) -> None: - """Set the process title. - - Args: - title: Desired title. - """ - try: - import setproctitle - - setproctitle.setproctitle(title) - except Exception: - pass - - def check_directory(path: str) -> None: """Check a path is directory, or exit the app. @@ -212,7 +199,7 @@ def run( title=serve_command, public_url=public_url, ) - set_process_title("toad --serve") + ApplicationLifetime.set_process_title("toad --serve") server.serve() else: app = ToadApp( @@ -316,7 +303,7 @@ def acp( port=port, title=serve_command, ) - set_process_title("toad acp --serve") + ApplicationLifetime.set_process_title("toad acp --serve") server.serve() else: @@ -381,7 +368,7 @@ def serve(port: int, host: str, public_url: str | None = None) -> None: server = ToadWebServer( sys.argv[0], host=host, port=port, title="Toad", public_url=public_url ) - set_process_title("toad serve") + ApplicationLifetime.set_process_title("toad serve") server.serve() diff --git a/src/toad/version.py b/src/toad/version.py index f96d235e8..4ee96f9d4 100644 --- a/src/toad/version.py +++ b/src/toad/version.py @@ -1,8 +1,14 @@ +from __future__ import annotations + import asyncio -from typing import NamedTuple +from collections.abc import Awaitable, Callable +from typing import Any, NamedTuple, TYPE_CHECKING from abc import ABC, abstractmethod from dataclasses import dataclass +if TYPE_CHECKING: + from packaging.version import Version + VERSION_TOML_URL = "https://www.batrachian.ai/toad.toml" @@ -46,67 +52,80 @@ class VersionMonitor: def __init__(self) -> None: self.status: VersionStatus = CurrentVersion() - async def check(self) -> None: + async def check( + self, run_thread: Callable[..., Awaitable[Any]] = asyncio.to_thread, + ) -> None: try: - self.status = await check_version() + self.status = await self.check_version(run_thread) except VersionCheckFailed: return + @staticmethod + def prepare_version() -> Version: + """Acquire cold dependencies and installed metadata outside UI work.""" + import httpx # noqa: F401 - acquired before the UI consumes the module + import packaging.version + import tomllib # noqa: F401 - acquired before the UI decodes the response -async def check_version() -> VersionStatus: - """Check for a new version of Toad. - - The outcome owns whether an upgrade notice exists. - """ - import httpx - import packaging.version - import tomllib - - from toad import get_version - - try: - current_version = packaging.version.parse(get_version()) - except packaging.version.InvalidVersion as error: - raise VersionCheckFailed(f"Invalid version;{error}") + from toad import get_version - try: - client = await asyncio.to_thread(httpx.AsyncClient) - async with client: - response = await client.get(VERSION_TOML_URL) - version_toml_bytes = await response.aread() - except Exception as error: - raise VersionCheckFailed(f"Failed to retrieve version;{error}") - - try: - version_toml = version_toml_bytes.decode("utf-8", "replace") - version_meta = tomllib.loads(version_toml) - except Exception as error: - raise VersionCheckFailed(f"Failed to decode version TOML;{error}") + try: + return packaging.version.parse(get_version()) + except packaging.version.InvalidVersion as error: + raise VersionCheckFailed(f"Invalid version;{error}") + + @classmethod + async def check_version( + cls, run_thread: Callable[..., Awaitable[Any]] = asyncio.to_thread, + ) -> VersionStatus: + """Check for a new version of Toad. + + The outcome owns whether an upgrade notice exists. + """ + current_version = await run_thread(cls.prepare_version) + # These modules were acquired by the preparation worker above. + import httpx + import packaging.version + import tomllib - if not isinstance(version_meta, dict): - raise VersionCheckFailed("Response isn't TOML") + try: + client = await run_thread(httpx.AsyncClient) + async with client: + response = await client.get(VERSION_TOML_URL) + version_toml_bytes = await response.aread() + except Exception as error: + raise VersionCheckFailed(f"Failed to retrieve version;{error}") - toad_version = str(version_meta.get("version", "0")) - version_message = str(version_meta.get("upgrade_message", "")) - version_message = version_message.replace("$VERSION", toad_version) - verison_meta = VersionMeta( - version=toad_version, - upgrade_message=version_message, - visit_url=str(version_meta.get("visit_url", "")), - ) + try: + version_toml = version_toml_bytes.decode("utf-8", "replace") + version_meta = tomllib.loads(version_toml) + except Exception as error: + raise VersionCheckFailed(f"Failed to decode version TOML;{error}") + + if not isinstance(version_meta, dict): + raise VersionCheckFailed("Response isn't TOML") + + toad_version = str(version_meta.get("version", "0")) + version_message = str(version_meta.get("upgrade_message", "")) + version_message = version_message.replace("$VERSION", toad_version) + verison_meta = VersionMeta( + version=toad_version, + upgrade_message=version_message, + visit_url=str(version_meta.get("visit_url", "")), + ) - try: - new_version = packaging.version.parse(verison_meta.version) - except packaging.version.InvalidVersion as error: - raise VersionCheckFailed(f"Invalid remote version;{error}") + try: + new_version = packaging.version.parse(verison_meta.version) + except packaging.version.InvalidVersion as error: + raise VersionCheckFailed(f"Invalid remote version;{error}") - return AvailableVersion(verison_meta) if new_version > current_version else CurrentVersion() + return AvailableVersion(verison_meta) if new_version > current_version else CurrentVersion() if __name__ == "__main__": async def run() -> None: - result = await check_version() + result = await VersionMonitor.check_version() from rich import print print(result)