mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
Root-cause fixes from the flake audit (session-DB mining + repo sweep): Event-based sync instead of sleep-sync: - title_generator: mock sets threading.Event, wait(10) replaces sleep(0.3) hoping the daemon thread got scheduled - docker zombie_reaping / profile_gateway: poll-for-state helpers replace fixed 1-3s sleeps (s6 transitions + SIGCHLD reaping are async) - process_registry tree test: select()-bounded readline replaces an unbounded blocking read (parent wedge now fails THIS test with a clear message instead of an opaque rc=124 file kill); SIGTERM grace 1s->2s (the 1s partition window mid-interpreter-startup is how a child PID escaped the live-system guard in CI) Timeout raises (loaded 8-way-sliced runners see ~5s scheduling floors; all of these complete in ms-to-1s when healthy so the raises cost nothing on green runs): - subprocess/thread waits <= 2s raised to 10-15s across mcp_tool, mcp_circuit_breaker, mcp_reconnect_retry_reset, mcp_parked_self_probe, mcp_cancelled_error_propagation, registry, clarify_gateway, interrupt, voice_cli_integration, docker_environment, session_store_lock_io, planned_stop_watcher, cli_interrupt_subagent, thread_scoped_output (joins now also assert not is_alive() so stragglers fail loudly) - wall-clock discrimination ceilings loosened where the guarded hang is 10x larger: local_background_child_hang 4s->10s, interrupt_cleanup setup 5s->20s + pgid-exit 30s->60s, mcp_stability grandchild spinup 5s->15s, protocol/gil-starvation fast-handler 0.5s->2s, iso_certify_seam 1.5s->5s, wait_for_mcp_discovery 0.1s->1s - narrow assertion windows widened: honcho first-turn wait 0.4..0.65 -> 0.25..2.0 (property is bounded-not-hung, not an exact wall-clock); compression fork-lock TTL 1s->3s (12 refresh chances per lease); compression-lock expiry margins symmetric (ttl 0.05->0.5, sleep 1.0) - telegram hung-DNS bound 1.0->1.4 (fake hang is 1.5s — must stay under)
46 lines
No EOL
1.8 KiB
Python
46 lines
No EOL
1.8 KiB
Python
"""Harness: PID 1 must reap orphaned zombie processes.
|
|
|
|
tini (current PID 1) reaps zombies via its built-in subreaper behavior.
|
|
s6-overlay's ``/init`` (Phase 2 PID 1) does the same. This invariant is
|
|
required for long-running containers spawning subprocesses (subagents,
|
|
dashboard, dynamic gateways) — otherwise the process table fills with
|
|
defunct entries and eventually exhausts the kernel PID space.
|
|
|
|
Every ``docker exec`` here runs as the unprivileged ``hermes`` user
|
|
(via :func:`docker_exec_sh` in conftest); see the conftest module
|
|
docstring.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
|
|
from tests.docker.conftest import docker_exec, docker_exec_sh, start_container, start_container
|
|
|
|
|
|
def test_orphan_zombies_reaped(
|
|
built_image: str, container_name: str,
|
|
) -> None:
|
|
"""Spawn an orphan child that exits immediately. PID 1 must reap it."""
|
|
start_container(built_image, container_name, cmd="sleep 60")
|
|
|
|
# `( ( sleep 0.1 & ) & ); sleep 1` creates a grandchild detached from
|
|
# the original docker exec session — it becomes an orphan reparented
|
|
# to PID 1 in the container. When it exits, PID 1 must reap it.
|
|
docker_exec_sh(
|
|
container_name, "( ( sleep 0.1 & ) & ); sleep 1", timeout=10,
|
|
)
|
|
|
|
# Poll for zombies-absent instead of a fixed sleep: reaping is
|
|
# asynchronous (SIGCHLD) and can lag on a loaded host.
|
|
deadline = time.monotonic() + 10
|
|
zombies = ["(never checked)"]
|
|
while time.monotonic() < deadline:
|
|
r = docker_exec(container_name, "ps", "axo", "stat,pid,comm")
|
|
zombies = [
|
|
line for line in r.stdout.split("\n")
|
|
if line.strip().startswith("Z")
|
|
]
|
|
if not zombies:
|
|
break
|
|
time.sleep(0.5)
|
|
assert not zombies, f"Zombies not reaped by PID 1: {zombies}" |