fix(trial): record exception timestamps in UTC, not naive local time
Why
ExceptionInfo.from_exception records occurred_at=datetime.now() — naive, host-local. Every other timestamp written into the same result.json comes from Trial._now() (src/harbor/trial/trial.py:144), which is datetime.now(timezone.utc).
So one file carries two conventions. Reproduced on a UTC+8 host with TimingInfo(started_at=Trial._now()) and an ExceptionInfo created one millisecond later:
agent_execution : 2026-07-30T04:28:40.471429Z
exception time : 2026-07-30T12:28:40.472075
timing tzinfo : UTC
exception tzinfo: None
subtraction : TypeError: can't subtract offset-naive and offset-aware datetimes
The exception looks like it happened 8 hours after the phase that raised it, and any code that subtracts the two raises TypeError. A consumer reading result.json cannot recover the intended instant, because no offset was written and the producing host's timezone is not recorded anywhere in the file.
What
datetime.now() → datetime.now(timezone.utc) in ExceptionInfo.from_exception.
After:
agent_execution : 2026-07-30T04:28:40.633146Z
exception time : 2026-07-30T04:28:40.633454Z
subtraction : 0:00:00.000308
Validation
uv run ruff check --fix .passes.uv run ruff format .leaves all files unchanged.uv run ty checkpasses.occurred_athas no readers insidesrc/harbor, so nothing in-tree compares it against a naive value.
Sibling instances, deliberately not in this PR
grep -rn "datetime\.now()" src/harbor finds 13 more. Most are local-time strings used to name directories and tags (cli/exec.py, cli/config_init.py, cli/admin/admin.py, cli/hub_leaderboards.py, models/job/config.py), where local time is a reasonable choice and no serialized timestamp is involved.
Three are the same class as this one and worth a look, but I have not built the evidence for them so I left them alone: job.py:658 (updated_at), job.py:1085/job.py:1099 (JobResult.finished_at), and upload/uploader.py:300 (finished_at fallback). If you'd like them in the same change, say so and I'll extend the PR with per-site reproductions.