feat: add step-by-step visual replay and harden process lifecycle

- UI: Implement dual-mode playback in FloatingVideoPlayer supporting video and step-by-step frame replay with scrub controls and auto-fallback
- Runtime: Prevent asyncio event loop hangs on Darwin and Linux by avoiding psutil.wait_procs on direct child processes
- Task Queue: Add worker exit watchdog and automatic recording recovery for completed or interrupted sessions
- Recording: Add ffmpeg ultrafast transcode fallback, shield recording finalization against cancellation, and handle on-the-fly MP4 conversion
- Docs: Polish README_CN styling and streamline flash runner agent prompt
This commit is contained in:
somew1nd committed 2026-08-26 00:53:24 -07:00
1 parent 06643e955b
commit a36a66ce6d
26 files changed
+1152 -117

No files matched your search

@@ -553,3 +553,54 @@ async def test_immediate_cancel_ignores_stale_ipc_and_runs_next_task():
await task
except (asyncio.CancelledError, Exception):
pass
@pytest.mark.asyncio
async def test_wait_for_worker_process_watchdog_handles_reaped_process():
"""Verify that _wait_for_worker_process never hangs even if the process was already reaped."""
mock_proc = MagicMock()
mock_proc.pid = 999999
mock_proc.returncode = None
async def hanging_wait():
await asyncio.sleep(10)
return 0
mock_proc.wait = AsyncMock(side_effect=hanging_wait)
with patch("psutil.Process") as mock_psutil_proc:
mock_p = MagicMock()
mock_p.is_running.return_value = False
mock_psutil_proc.return_value = mock_p
rc = await TaskQueueService._wait_for_worker_process(mock_proc)
assert rc == -15
def test_darwin_terminate_process_tree_preserves_direct_child_for_asyncio():
"""Verify darwin terminate_process_tree does not pass direct children to psutil.wait_procs."""
import os
from artemis.platform.darwin import DarwinPlatformProcess
process = DarwinPlatformProcess()
current_pid = os.getpid()
with (
patch("psutil.Process") as mock_psutil_proc,
patch("psutil.wait_procs") as mock_wait_procs,
):
parent = MagicMock()
parent.pid = 12345
parent.ppid.return_value = current_pid
parent.children.return_value = []
parent.is_running.return_value = False
mock_psutil_proc.return_value = parent
mock_wait_procs.return_value = ([], [])
success = process.terminate_process_tree(12345, timeout_seconds=0.1)
assert success is True
parent.send_signal.assert_called_once()
for call_args in mock_wait_procs.call_args_list:
procs_waited = call_args[0][0]
assert parent not in procs_waited