Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions evidence/dm-initial-publication-20261008/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
18 changes: 14 additions & 4 deletions src/toad/application_lifetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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()
Expand Down
21 changes: 4 additions & 17 deletions src/toad/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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()


Expand Down
113 changes: 66 additions & 47 deletions src/toad/version.py
Original file line number Diff line number Diff line change
@@ -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"

Expand Down Expand Up @@ -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)
Expand Down
Loading