From db0d4e6ac62ce9bb06ca039431c916da1b4baa57 Mon Sep 17 00:00:00 2001 From: SevenX77 Date: Wed, 22 Apr 2026 16:49:08 +0000 Subject: [PATCH] fix(keeper): env-override for config-check ping timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keeper calls `CcbdClient(...).ping('ccbd')` with a hardcoded 0.2s timeout during every reconcile tick. When ccbd is busy (paste+verify, poll loop), the ping races → keeper marks lifecycle.failed:config_check_failed:timed out → all `ccb ask` return socket_unreachable until manual restart. - add `CCB_KEEPER_PING_TIMEOUT_S` (default 2.0s, invalid/neg/empty → default) - allowlist the env in runtime_env/control_plane.py Mirrors the earlier CCB_CCBD_CLIENT_TIMEOUT_S fix (CLI→ccbd path). --- lib/ccbd/keeper_runtime/loop.py | 20 +++++++++++++++++++- lib/runtime_env/control_plane.py | 1 + 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/ccbd/keeper_runtime/loop.py b/lib/ccbd/keeper_runtime/loop.py index d69dd8e96..61a1ede57 100644 --- a/lib/ccbd/keeper_runtime/loop.py +++ b/lib/ccbd/keeper_runtime/loop.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os from pathlib import Path from agents.config_identity import project_config_identity_payload @@ -14,6 +15,23 @@ from .support import reap_child_processes, try_acquire_keeper_lock +def _keeper_ping_timeout_s() -> float: + """Keeper→ccbd config-check ping timeout. + + Upstream hardcodes 0.2s which spuriously trips when ccbd is busy with a + paste/poll cycle → lifecycle.failed → all ccb ask rejected. Env-override + lets ops raise it without patching upstream again. + """ + raw = os.environ.get('CCB_KEEPER_PING_TIMEOUT_S', '').strip() + if not raw: + return 2.0 + try: + value = float(raw) + except (TypeError, ValueError): + return 2.0 + return value if value > 0 else 2.0 + + def run_forever(app, *, poll_interval: float = 0.5, start_timeout_s: float = 5.0) -> int: lock_path = app.paths.ccbd_dir / 'keeper.lock' lock_handle = try_acquire_keeper_lock(lock_path) @@ -176,7 +194,7 @@ def stale_restart_state(app, *, state: KeeperState, inspection, occurred_at: str def daemon_matches_project_config(app) -> bool: expected = project_config_identity_payload(load_project_config(app.project_root).config) - payload = CcbdClient(app.paths.ccbd_socket_path, timeout_s=0.2).ping('ccbd') + payload = CcbdClient(app.paths.ccbd_socket_path, timeout_s=_keeper_ping_timeout_s()).ping('ccbd') actual_signature = str(payload.get('config_signature') or '').strip() if actual_signature: return actual_signature == expected['config_signature'] diff --git a/lib/runtime_env/control_plane.py b/lib/runtime_env/control_plane.py index 35b52d5f6..7dd39d400 100644 --- a/lib/runtime_env/control_plane.py +++ b/lib/runtime_env/control_plane.py @@ -11,6 +11,7 @@ 'CCB_CCBD_MIN_POLL_INTERVAL_S', 'CCB_DEBUG', 'CCB_KEEPER_PID', + 'CCB_KEEPER_PING_TIMEOUT_S', 'CCB_LANG', 'CCB_NO_ATTACH', 'CCB_REPLY_LANG',