# test: warn on polling loops in tests and make adapter test harnesses event-driven · PR \#22565 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/22565)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: tests/no-polling-loop

Target branch: tests/no-real-time-wait

Head commit: c6593a8410f4cd26803a8b87f1266dc49f86df2e

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/22565)

[Commits](https://git.cafe/pingdotgg/t3code/pull/22565/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/22565/diffs?format=markdown)

## Description

---

## Problem
Adapter test harnesses waited with `awaitUntil`: a loop of up to 5,000 `Effect.yieldNow` spins. That is a scheduler budget, not a signal, and it is why the Claude steer test timed out at 120 s on 20 main runs last week.

## Fix
- New rule `t3code/no-polling-loop-in-tests` as **warn**: a loop whose body spins on `yieldNow`/`sleep`/a timer and exits on a predicate. Fixed-N flushes are exempt. Flip to error once the remaining 31 warnings are gone (down from 86 after #22557 removed most pollers; the list is in the last review reply).
- Claude and Codex harnesses publish to a `PubSub` and expose `awaitTerminal(n)`, `awaitContinuation(n)`, `awaitSubagentStatus(id, status)`; 234 call sites converted. OpenCode2 loops became Deferreds; the desktop preview Manager test waits on a change signal instead of `settle`.
- Claude adapter file passed 342/342 three times in a row.

Part of the anti-flake stack (#22563). Context: main was red in ~600 of ~2,900 runs since September; the genuinely flaky tests all waited on real time, scheduler budgets, or renderer frames. These rules make those tests fail lint instead of CI.

Made with Claude Opus 5.5 and Claude Fable 5.1 in Claude Code (T3 Code).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
