From 864d5521ad716614fa1c4cd4a6f1da0652e91351 Mon Sep 17 00:00:00 2001 From: Bartok Date: Fri, 26 Jun 2026 08:18:54 -0400 Subject: [PATCH] test(curator): join straggler curator-review thread on fixture teardown The curator_env fixture left async review threads (synchronous=False spawns a daemon 'curator-review' thread that calls save_state() on completion) running past test teardown. save_state() resolves the state path from HERMES_HOME at write time, so a straggler could write into the next test's tmp home, corrupting test_state_file_survives_corrupt_read (and others) under CI load. Join the thread on teardown while HERMES_HOME is still pinned to this test's home. --- tests/agent/test_curator.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/agent/test_curator.py b/tests/agent/test_curator.py index 7fab220b9637..151faf138f40 100644 --- a/tests/agent/test_curator.py +++ b/tests/agent/test_curator.py @@ -7,6 +7,7 @@ tests run fully offline and the curator module doesn't need real credentials. from __future__ import annotations import importlib +import threading from datetime import datetime, timedelta, timezone from pathlib import Path @@ -37,7 +38,21 @@ def curator_env(tmp_path, monkeypatch): # directly). Tests opt in with _enable_prune_builtins(...). monkeypatch.setattr(usage, "_prune_builtins_enabled", lambda: False) - return {"home": home, "curator": curator, "usage": usage} + yield {"home": home, "curator": curator, "usage": usage} + + # Teardown: a curator review launched with synchronous=False spawns a + # daemon "curator-review" thread that calls save_state() when it finishes. + # save_state() resolves the state path from HERMES_HOME at write time, so a + # straggler thread that outlives this test would write into whatever home + # the *next* test has configured (or the default ~/.hermes once monkeypatch + # restores the env) — corrupting an unrelated test's state file. This race + # is invisible on a fast machine but flakes under CI load. Join any such + # thread here, while HERMES_HOME is still pinned to this test's tmp home + # (curator_env depends on monkeypatch, so this teardown runs before the + # monkeypatch env is restored). See the salvage of #14261 CI flake. + for t in threading.enumerate(): + if t.name == "curator-review" and t.is_alive(): + t.join(timeout=10.0) def _write_skill(skills_dir: Path, name: str):