process(action='wait') hitting its window returned status='timeout' with a terse note — models read it as an error and re-issued identical waits (process is the #1 exact-duplicate tool call in production: 511 dupes in a 400k-msg window; wait is 57% of all process actions). The timeout result now carries: - process_running: true — machine-readable 'this is a status, not a failure' - an explicit note: 'Wait window of Ns elapsed — the process is still running. This is not an error. Uptime: Ms.' plus the right next step: when notify_on_complete is set, 'you will be notified on exit — do more work instead of waiting again'; otherwise a pointer to notify_on_complete for next time. - the clamp note (requested > max) now composes with the status note instead of replacing it. Exited/interrupted results are unchanged.
65 lines
2.3 KiB
Python
65 lines
2.3 KiB
Python
"""Tests for process wait timeout-result clarity (not-an-error semantics)."""
|
|
|
|
import pytest
|
|
|
|
from tools.process_registry import ProcessRegistry
|
|
|
|
|
|
@pytest.fixture
|
|
def registry(tmp_path, monkeypatch):
|
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes"))
|
|
return ProcessRegistry()
|
|
|
|
|
|
def _spawn_sleeper(registry, notify=False):
|
|
session = registry.spawn_local("sleep 30", cwd="/tmp", task_id="t-waitclar")
|
|
session.notify_on_complete = notify
|
|
return session.id
|
|
|
|
|
|
class TestWaitTimeoutClarity:
|
|
def test_wait_timeout_marks_process_running(self, registry):
|
|
sid = _spawn_sleeper(registry)
|
|
try:
|
|
r = registry.wait(sid, timeout=1)
|
|
assert r["status"] == "timeout"
|
|
assert r["process_running"] is True
|
|
assert "not an error" in r["timeout_note"]
|
|
assert "Uptime" in r["timeout_note"]
|
|
finally:
|
|
registry.kill_process(sid)
|
|
|
|
def test_wait_timeout_suggests_notify_when_unset(self, registry):
|
|
sid = _spawn_sleeper(registry, notify=False)
|
|
try:
|
|
r = registry.wait(sid, timeout=1)
|
|
assert "notify_on_complete=true" in r["timeout_note"]
|
|
finally:
|
|
registry.kill_process(sid)
|
|
|
|
def test_wait_timeout_defers_to_notify_when_set(self, registry):
|
|
sid = _spawn_sleeper(registry, notify=True)
|
|
try:
|
|
r = registry.wait(sid, timeout=1)
|
|
assert "you will be notified on exit" in r["timeout_note"]
|
|
finally:
|
|
registry.kill_process(sid)
|
|
|
|
def test_clamped_wait_keeps_clamp_note_and_running_semantics(self, registry, monkeypatch):
|
|
monkeypatch.setenv("TERMINAL_TIMEOUT", "1")
|
|
sid = _spawn_sleeper(registry)
|
|
try:
|
|
r = registry.wait(sid, timeout=600)
|
|
assert r["status"] == "timeout"
|
|
assert "clamped" in r["timeout_note"]
|
|
assert "not an error" in r["timeout_note"]
|
|
assert r["process_running"] is True
|
|
finally:
|
|
registry.kill_process(sid)
|
|
|
|
def test_exited_process_unaffected(self, registry):
|
|
session = registry.spawn_local("true", cwd="/tmp", task_id="t-waitclar")
|
|
r = registry.wait(session.id, timeout=10)
|
|
assert r["status"] == "exited"
|
|
assert "process_running" not in r
|