Files
turnstone/docs
Patrick Buckley 6560f1ec4f fix(judge): daemon-thread call deadlines; raise local-model timeouts
The judges and the regex ReDoS probe ran a blocking call on a
ThreadPoolExecutor and abandoned the worker with shutdown(wait=False) on
timeout or cancel. concurrent.futures joins every executor worker from an
atexit hook regardless of wait=False, so a wedged call could pin
interpreter exit — and hang the test suite at shutdown.

Add turnstone/core/deadline.py::run_with_deadline: run a blocking callable
on a daemon thread bounded by a wall-clock timeout and an optional cancel
event. A daemon worker is never joined at exit, so abandoning one is safe.

Migrate three sites onto it:
- OutputGuardJudge.evaluate()
- IntentJudge._evaluate_single / _run_judge — this also removes
  _ExecutorPoisonedError and the executor-restart dance: per-call daemon
  threads can't poison a shared single-slot pool, so a timeout now returns
  None and the caller delivers one fallback verdict.
- console/server.py _validate_regex_pattern (regex ReDoS probe)

Also:
- Double the default judge LLM timeouts for slower local models:
  judge.timeout 60->120s and judge.output_guard_llm_timeout 30->60s
  (settings registry, JudgeConfig dataclass, --judge-timeout CLI default,
  class docstring, docs). Correct a stale doc that described the per-turn
  timeout as a total budget across turns.
- Raise the regex probe bound 0.5->3.0s so a legitimately complex pattern
  isn't false-flagged as catastrophic backtracking.
- CI: run pytest with -v instead of -q so a hang names the offending test
  instead of riding the job timeout.
- Tests: cover deadline.py and the regex validator; move test_judge.py off
  fixed sleeps onto the existing _wait_for helper.
2026-06-17 01:23:51 -07:00
..