fix(api-server): count "stopping" runs as active in readiness work counts

_readiness_work_counts()'s active_api_runs set is {"queued", "running",
"waiting_for_approval"} — it excludes "stopping", the status
_handle_stop_run() sets while a run is being interrupted. Since the stop
is fully cooperative (the run stays "stopping" — doing real
executor-thread work — until the agent actually notices the interrupt and
the task settles to "cancelled", an unbounded window, not a fixed
timeout), /health/detailed's background_queues.active_api_runs
undercounts real active work for that whole duration.

Fix: add "stopping" to the active-status set. background_queues.status
itself is hardcoded "ok" (gateway/readiness.py), so this doesn't change
overall readiness — it only corrects the count value external monitoring
tooling reads from this endpoint.
This commit is contained in:
pierrenode 2026-07-15 12:36:11 +03:00 committed by Teknium
parent eff293115d
commit 0d5982d910
2 changed files with 27 additions and 1 deletions

View File

@ -1071,7 +1071,13 @@ class APIServerAdapter(BasePlatformAdapter):
active_api_runs = sum(
1
for status in self._run_statuses.values()
if status.get("status") in {"queued", "running", "waiting_for_approval"}
# "stopping" (set by _handle_stop_run) is not terminal: the run
# stays in this state, doing real executor-thread work, until the
# agent actually notices the interrupt and the task settles to
# "cancelled" — an unbounded window, not the old ~5s hard-timeout
# wait. Excluding it here undercounts active_api_runs for the
# whole duration of a cooperative stop.
if status.get("status") in {"queued", "running", "waiting_for_approval", "stopping"}
)
process_depth = 0
active_delegations = 0

View File

@ -926,6 +926,26 @@ class TestHealthDetailedEndpoint:
patch("tools.async_delegation.active_count", return_value=2):
assert adapter._readiness_work_counts() == (3, 4, 2)
def test_readiness_work_counts_include_stopping_runs(self, adapter):
"""Regression: _handle_stop_run() sets status="stopping" and holds it
there cooperatively, with no hard timeout until the agent notices
the interrupt and the task actually exits. A run in that window is
still doing real executor-thread work and must count as active,
the same as "running"; excluding it undercounts active_api_runs for
the whole (now-unbounded) cooperative-stop duration."""
adapter._run_statuses = {
"queued": {"status": "queued"},
"running": {"status": "running"},
"approval": {"status": "waiting_for_approval"},
"stopping": {"status": "stopping"},
"done": {"status": "completed"},
"cancelled": {"status": "cancelled"},
}
with patch("tools.process_registry.process_registry.completion_queue.qsize", return_value=0), \
patch("tools.async_delegation.active_count", return_value=0):
assert adapter._readiness_work_counts() == (4, 0, 0)
# ---------------------------------------------------------------------------
# /v1/models endpoint