From 565437fec566c88ce78a9ca37724edc2556dbb39 Mon Sep 17 00:00:00 2001 From: Saturday-boyi <2174084306@qq.com> Date: Mon, 7 Sep 2026 13:25:34 +0800 Subject: [PATCH] Fix negative durations from clock-skewed transcripts --- agentrace/parse.py | 3 ++- tests/test_cli.py | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/agentrace/parse.py b/agentrace/parse.py index 582f399..162e1ab 100644 --- a/agentrace/parse.py +++ b/agentrace/parse.py @@ -39,7 +39,8 @@ class AgentRun: @property def duration_s(self) -> float | None: if self.started_at and self.ended_at: - return (self.ended_at - self.started_at).total_seconds() + duration = (self.ended_at - self.started_at).total_seconds() + return duration if duration >= 0 else None return None @property diff --git a/tests/test_cli.py b/tests/test_cli.py index 1489d5c..ba17ee4 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -56,6 +56,27 @@ def test_stats_median(durations, expected, monkeypatch, capsys): assert " ".join(median_rows[0].split()) == f"median run {expected}" +def test_stats_excludes_clock_skewed_duration(monkeypatch, capsys): + start = datetime(2026, 1, 1, tzinfo=UTC) + runs = [ + AgentRun( + tool_use_id=str(i), + description="test", + prompt="prompt", + result="result", + started_at=start, + ended_at=start + timedelta(seconds=duration), + ) + for i, duration in enumerate([-5, 10, 20]) + ] + monkeypatch.setattr(cli, "_load", lambda args: runs) + assert cli.cmd_stats(Namespace(json=True)) == 0 + output = capsys.readouterr().out + assert '"total_seconds": 30.0' in output + assert "median run 15 s" in " ".join(output.split()) + assert runs[0].duration_s is None + + def test_file_and_dir_are_mutually_exclusive(tmp_path): transcript = tmp_path / "session.jsonl" transcript.write_text("")