f89d275650
Layered polish on top of 593d142f (Tier 2 deterministic dispatchers)
plus the systemd-supervised launcher that realises the SystemExit(2)
restart contract in production.
Code:
- Promote `_opencode_worker.list_sessions(server_url)` to public so the
coexistence guard no longer reaches into private `_request`.
- `assert_no_legacy_supervisor` truncates with `(showing first N)` and
takes `Iterable[str]` for supervisor_tags.
- Collapse `_sanitize_release_detail` to a single `str.replace()`
(substitute has no backticks; second pass was always a no-op).
- Telemetry `_api_cycles` routes through a single `_DISPATCH_TABLES`
dict so the rows query and breakdown query can never drift onto
different tables.
- `dispatch_one` docstring now lists `labels-fetch-failed` alongside
the other terminal states.
- `_loader.load_sibling` raises `ImportError` up front for a missing
file (was bubbling `FileNotFoundError` from `exec_module`).
Operations:
- `scripts/dispatchers-launcher.sh` supervises both dispatchers in one
process, restarts on non-zero exit with backoff, enforces a
per-child crash-loop budget, and forwards SIGTERM cleanly.
- `contrib/systemd/cleveragents-dispatchers.service` wires that into a
systemd unit with hardening defaults and journalctl visibility.
Tests (350 pass, 1 skipped):
- New `tests/auto_agents/test_loader.py` (4 cases).
- New `tests/auto_agents/test_telemetry_server.py` (5 synthetic-row
cases covering both tables, composite breakdown, unknown driver,
table isolation).
- Extended `test_dispatch_runtime.py` with truncation-suffix and
`list_sessions` direct tests; refit existing supervisor-guard tests
to monkeypatch the public helper.
Docs:
- AGENTS.md cross-links the launcher / systemd unit.
- CHANGELOG.md entry under [Unreleased] dated 2026-05-07.
Co-authored-by: Cursor <cursoragent@cursor.com>
55 lines
2.1 KiB
Python
55 lines
2.1 KiB
Python
"""Shared sibling-module loader for the auto-agents Python tooling.
|
|
|
|
Every driver and runtime helper in :mod:`tools` needs to load its
|
|
peer modules without forcing callers to install the package. The
|
|
historical pattern was to redeclare ``_load_sibling`` in each entry
|
|
point; that left three independent copies in
|
|
``_dispatch_runtime.py``, ``dispatch_review.py``, and
|
|
``dispatch_implementer.py`` (plus near-identical copies elsewhere).
|
|
|
|
This module collapses those copies into one helper. Behavior is
|
|
identical: load by file path relative to ``tools/``, cache the result
|
|
in :data:`sys.modules` under the requested name, and return the
|
|
cached instance on subsequent calls.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import sys
|
|
from pathlib import Path
|
|
from types import ModuleType
|
|
|
|
|
|
TOOLS_DIR = Path(__file__).resolve().parent
|
|
|
|
|
|
def load_sibling(module_name: str, filename: str) -> ModuleType:
|
|
"""Load ``filename`` from ``tools/`` as ``module_name``.
|
|
|
|
Returns the cached module from :data:`sys.modules` if already
|
|
loaded; otherwise compiles and registers a fresh instance. Used by
|
|
drivers that need to import other ``tools/`` files without making
|
|
``tools`` an installable package.
|
|
"""
|
|
cached = sys.modules.get(module_name)
|
|
if cached is not None:
|
|
return cached
|
|
path = TOOLS_DIR / filename
|
|
if not path.exists():
|
|
# ``spec_from_file_location`` happily builds a spec for a
|
|
# non-existent path and only raises ``FileNotFoundError`` from
|
|
# ``exec_module``, which is confusing to operators reading a
|
|
# traceback. Surface a clean ``ImportError`` up front.
|
|
raise ImportError(
|
|
f"cannot load sibling {module_name!r}: file does not exist at {path}"
|
|
)
|
|
spec = importlib.util.spec_from_file_location(module_name, path)
|
|
if spec is None or spec.loader is None:
|
|
raise ImportError(
|
|
f"cannot build module spec for {module_name} at {path}"
|
|
)
|
|
mod = importlib.util.module_from_spec(spec)
|
|
sys.modules[module_name] = mod
|
|
spec.loader.exec_module(mod)
|
|
return mod
|