diff --git a/docs/MCP_RUNTIME_STATUS_ALIGNMENT_2026-03-14.md b/docs/MCP_RUNTIME_STATUS_ALIGNMENT_2026-03-14.md new file mode 100644 index 0000000..b7522b5 --- /dev/null +++ b/docs/MCP_RUNTIME_STATUS_ALIGNMENT_2026-03-14.md @@ -0,0 +1,38 @@ +# MCP Runtime Status Alignment — 2026-03-14 + +## Scope + +Close `K-DA-020` by replaying `bash tools/test_python.sh --suite stable` and isolating the first real failure around the MCP runtime stack. + +## Finding + +The first red was not a runtime regression in `tools/mcp_runtime_status.py`. + +It was a test harness mismatch: + +- `tools/mcp_runtime_status.run_check(...)` is now async and uses `asyncio.create_subprocess_exec(...)` +- `test/test_mcp_runtime_status.py` was still mocking the legacy synchronous surface `subprocess.run(...)` + +Failure observed before the fix: + +- `AttributeError: module 'tools.mcp_runtime_status' has no attribute 'subprocess'` + +## Fix + +`test/test_mcp_runtime_status.py` now matches the real async contract: + +- patch `tools.mcp_runtime_status.asyncio.create_subprocess_exec` +- use an async fake process with `communicate()` +- call `asyncio.run(run_check(spec))` + +## Validation + +Stable suite replay: + +- `bash tools/test_python.sh --suite stable` + +Result: + +- green + +No runtime code was changed in this lot. The closure is a test-alignment closure, not a behavior change in the MCP runtime itself. diff --git a/test/test_mcp_runtime_status.py b/test/test_mcp_runtime_status.py index ad21498..04444ac 100644 --- a/test/test_mcp_runtime_status.py +++ b/test/test_mcp_runtime_status.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 from __future__ import annotations +import asyncio import json -import subprocess import sys import unittest from pathlib import Path @@ -14,6 +14,15 @@ from tools.mcp_runtime_status import classify_overall, derive_blockers, run_chec class McpRuntimeStatusTests(unittest.TestCase): + class _FakeProc: + def __init__(self, stdout: str, stderr: str = "", returncode: int = 0): + self._stdout = stdout.encode("utf-8") + self._stderr = stderr.encode("utf-8") + self.returncode = returncode + + async def communicate(self): + return self._stdout, self._stderr + def test_classify_ready(self): results = [ {"status": "ready", "accept_degraded": False}, @@ -97,14 +106,13 @@ class McpRuntimeStatusTests(unittest.TestCase): "error": "quota exceeded", } ) - completed = subprocess.CompletedProcess( - args=spec["cmd"], - returncode=0, - stdout=stdout, - stderr="", - ) - with mock.patch("tools.mcp_runtime_status.subprocess.run", return_value=completed): - payload = run_check(spec) + fake_proc = self._FakeProc(stdout=stdout, stderr="", returncode=0) + + async def fake_spawn(*args, **kwargs): + return fake_proc + + with mock.patch("tools.mcp_runtime_status.asyncio.create_subprocess_exec", side_effect=fake_spawn): + payload = asyncio.run(run_check(spec)) self.assertTrue(payload["optional_degraded"]) self.assertEqual(payload["task"], "K-014")