# Files changed · PR \#22564 · pingdotgg/t3code

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

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

Visibility: public

[test\(server\): ban real-clock waits in tests](https://git.cafe/pingdotgg/t3code/pull/22564.md)

Version: 20

Head commit: a9550e8cf64dfa10a839f19573a6384017669bbe

Base commit: 595c60457c6bdfc4569d7212072c70f8fe9e4fe9

Comparison base: 595c60457c6bdfc4569d7212072c70f8fe9e4fe9

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## apps/desktop/src/preview/CdpRelay.test.ts

Status: modified; +1; −0

```diff
@@ -15,6 +15,7 @@
 });
 
 // Each relay reply resolves after a few microtasks; one macrotask lets them all land.
+// oxlint-disable-next-line t3code/no-real-time-wait-in-tests -- a zero-delay macrotask boundary, not a wait for time
 const settle = () => new Promise<void>((resolve) => setImmediate(resolve));
 
 describe("CDP relay", () => {
```

## apps/desktop/src/preview/DesktopBrowserHost.test.ts

Status: modified; +0; −1

```diff
@@ -82,7 +82,6 @@
       yield* host.handleCommandLine(encodeJson({ type: "release", ...key }));
       yield* command(2, "DOM.enable");
       debuggee.release();
-      yield* Effect.promise(() => new Promise((resolve) => setImmediate(resolve)));
       const [, reply] = yield* Fiber.join(reader);
 
       expect(reply).toMatchObject({ type: "cdp" });
```

## apps/desktop/src/preview/Passkeys.test.ts

Status: modified; +6; −2

```diff
@@ -257,9 +257,13 @@
 
       page.state.focused = true;
       let finish: (value: unknown) => void = () => {};
-      webauthn.getCredential.mockReturnValueOnce(new Promise((resolve) => (finish = resolve)));
+      const ceremonyStarted = Promise.withResolvers<void>();
+      webauthn.getCredential.mockImplementationOnce(() => {
+        ceremonyStarted.resolve();
+        return new Promise((resolve) => (finish = resolve));
+      });
       const first = yield* Effect.forkChild(page.call(PASSKEY_GET_CHANNEL, publicKey));
-      yield* Effect.promise(() => new Promise((resolve) => setImmediate(resolve)));
+      yield* Effect.promise(() => ceremonyStarted.promise);
       assert.deepStrictEqual(yield* page.call(PASSKEY_GET_CHANNEL, publicKey), notAllowed);
 
       page.mainFrame.origin = "https://other.example";
```

## apps/mobile/src/features/agent-awareness/remoteRegistration.test.ts

Status: modified; +14; −5

```diff
@@ -405,15 +405,24 @@
       yield* Effect.promise(() => oldRequestArrived);
 
       tokenListener?.({ pushToken: "rotated-activity-token" });
+      const deviceIdLookups = vi.mocked(loadOrCreateAgentAwarenessDeviceId).mock.calls.length;
+      const rotated = backgroundRuntime.pending.shift()!;
+      // Starting immediately runs the listener's registration until it suspends: on the
+      // activity lock, or, without one, at its device-id lookup.
+      const rotatedRegistration = yield* Effect.forkChild(
+        Effect.exit(
+          rotated.operation as Effect.Effect<unknown, unknown, ManagedRelay.ManagedRelayClient>,
+        ),
+        { startImmediately: true },
+      );
+      expect(vi.mocked(loadOrCreateAgentAwarenessDeviceId).mock.calls).toHaveLength(
+        deviceIdLookups,
+      );
-      const listenerRegistration = yield* Effect.forkChild(runBackgroundOperations());
-      for (let pass = 0; pass < 5; pass++) {
-        yield* Effect.promise(() => new Promise<void>((resolve) => setImmediate(resolve)));
-      }
       expect(arrivals).toEqual(["activity-token"]);
 
       openOldRequest?.();
       yield* Fiber.join(lookup);
-      yield* Fiber.join(listenerRegistration);
+      rotated.resolve(yield* Fiber.join(rotatedRegistration));
       expect(arrivals).toEqual(["activity-token", "rotated-activity-token"]);
     }).pipe(
       Effect.provideService(FetchHttpClient.Fetch, globalThis.fetch),
```

## apps/server/src/orchestration-v2/legacy/LegacyV1Cutover.integration.test.ts

Status: modified; +22; −11

```diff
@@ -559,18 +559,29 @@
 
 const waitForIdle = Effect.fn("LegacyV1Cutover.waitForIdle")(function* (threadId: ThreadId) {
   const orchestrator = yield* Orchestrator.OrchestratorV2;
+  const eventSink = yield* EventSink.EventSinkV2;
+  const isIdle = (projection: OrchestrationV2ThreadProjection) =>
+    projection.runs.every(
+      (run) => !["queued", "starting", "running", "waiting"].includes(run.status),
+    );
+  const afterSequence = yield* eventSink.latestSequence();
+  const current = yield* orchestrator.getThreadProjection(threadId);
+  if (isIdle(current)) {
+    return current;
+  }
+  // Projections commit with their events, so each later event is the only time to re-read.
+  const [idle] = yield* eventSink.stream({ afterSequence }).pipe(
+    Stream.mapEffect(() => orchestrator.getThreadProjection(threadId)),
+    Stream.filter(isIdle),
+    Stream.take(1),
+    Stream.runCollect,
+  );
-  for (let attempt = 0; attempt < 1_000; attempt += 1) {
-    const projection = yield* orchestrator.getThreadProjection(threadId);
-    if (
-      projection.runs.every(
-        (run) => !["queued", "starting", "running", "waiting"].includes(run.status),
-      )
-    ) {
-      return projection;
-    }
-    yield* Effect.sleep("5 millis");
+  if (idle === undefined) {
+    return yield* Effect.die(
+      new Error("Cutover test event stream ended before the thread went idle"),
+    );
   }
-  return yield* Effect.die(new Error("Cutover test timed out waiting for idle"));
+  return idle;
 });
 
 const layerBoot = (input: {
```

## apps/server/src/orchestration-v2/testkit/ProviderSwitch.integration.test.ts

Status: modified; +23; −11

```diff
@@ -12,6 +12,7 @@
   type OrchestrationV2ProviderSession,
   type OrchestrationV2ProviderThread,
   type OrchestrationV2ProviderTurnTokenUsage,
+  type OrchestrationV2ThreadProjection,
   ProjectId,
   ProviderInstanceId,
   ProviderSessionId,
@@ -379,18 +380,29 @@
   targetThreadId: ThreadId,
 ) {
   const orchestrator = yield* Orchestrator.OrchestratorV2;
+  const eventSink = yield* EventSink.EventSinkV2;
+  const isIdle = (projection: OrchestrationV2ThreadProjection) =>
+    projection.runs.every(
+      (run) => !["queued", "starting", "running", "waiting"].includes(run.status),
-  for (let attempt = 0; attempt < 1_000; attempt += 1) {
-    const projection = yield* orchestrator.getThreadProjection(targetThreadId);
+    );
+  const afterSequence = yield* eventSink.latestSequence();
-    if (
-      projection.runs.every(
-        (run) => !["queued", "starting", "running", "waiting"].includes(run.status),
-      )
-    ) {
-      return projection;
-    }
-    yield* Effect.sleep("5 millis");
+  const current = yield* orchestrator.getThreadProjection(targetThreadId);
+  if (isIdle(current)) {
+    return current;
   }
+  // Projections commit with their events, so each later event is the only time to re-read.
+  const [idle] = yield* eventSink.stream({ afterSequence }).pipe(
+    Stream.mapEffect(() => orchestrator.getThreadProjection(targetThreadId)),
+    Stream.filter(isIdle),
+    Stream.take(1),
+    Stream.runCollect,
-  return yield* Effect.die(new Error("Provider switch test timed out waiting for idle"));
+  );
+  if (idle === undefined) {
+    return yield* Effect.die(
+      new Error("Provider switch test event stream ended before the thread went idle"),
+    );
+  }
+  return idle;
 });
 
 describe("orchestration v2 provider switching", () => {
```

## apps/server/src/preview/ServerBrowser.test.ts

Status: modified; +9; −2

```diff
@@ -390,13 +390,20 @@
       });
       const navigation = yield* viewer.input(message).pipe(Effect.forkScoped);
       yield* Effect.promise(() => started.promise);
+      const cdp = contexts[0]!.sessions.at(-1)!;
+      const send = cdp.send.getMockImplementation()!;
+      const typed = Promise.withResolvers<void>();
+      cdp.send.mockImplementation(async (operation, input) => {
+        if (operation === "Input.insertText") typed.resolve();
+        return send(operation, input);
+      });
       const correction = yield* Effect.gen(function* () {
         yield* viewer.input({ type: "navigate", url: "http://localhost:5173/fixed" });
         yield* viewer.input({ type: "text", text: "hello" });
       }).pipe(Effect.forkScoped);
       yield* Effect.addFinalizer(() => Effect.sync(() => committed.resolve()));
+      // The text reaches the page while the first navigation is still uncommitted.
+      yield* Effect.promise(() => typed.promise);
-      // Check after runnable promises drain, without waiting for navigation or a timeout.
-      yield* Effect.promise(() => new Promise<void>((resolve) => setImmediate(resolve)));
       expect(page.goto).toHaveBeenLastCalledWith(
         "http://localhost:5173/fixed",
         expect.objectContaining({ waitUntil: "commit" }),
```

## apps/server/src/preview/ServerBrowserPage.test.ts

Status: modified; +4; −2

```diff
@@ -273,8 +273,10 @@
       );
       void settled.then(() => (finished = true));
       // Two macrotask turns: Node reports a rejection nobody observed after the first.
-      await new Promise((resolve) => setImmediate(resolve));
+      for (let turn = 0; turn < 2; turn++) {
-      await new Promise((resolve) => setImmediate(resolve));
+        // oxlint-disable-next-line t3code/no-real-time-wait-in-tests -- Node emits unhandledRejection only between macrotasks; no promise can mark that point.
+        await new Promise((resolve) => setImmediate(resolve));
+      }
       expect(finished).toBe(false);
       targetBox.resolve(box);
 
```

## apps/server/src/pullRequest/PullRequestReviewGuideService.test.ts

Status: modified; +36; −19

```diff
@@ -7,7 +7,8 @@
 import * as FileSystem from "effect/FileSystem";
 import * as Layer from "effect/Layer";
 import * as Ref from "effect/Ref";
-import * as Schedule from "effect/Schedule";
+import * as Queue from "effect/Queue";
+import * as Tracer from "effect/Tracer";
 import {
   ProjectId,
   GitCommandError,
@@ -223,18 +224,33 @@
 
 type Repositories = Effect.Success<typeof makeRepositories>;
 
+// A run forks into the service's scope, so a test learns it settled when the run's `write` span
+// ends; the run records its outcome synchronously right after.
+const watchRuns = Effect.gen(function* () {
+  const ended = yield* Queue.unbounded<void>();
+  const tracer = Tracer.make({
+    span: (options) => {
+      const span = new Tracer.NativeSpan(options);
+      if (options.name !== "PullRequestReviewGuideService.write") return span;
+      const end = span.end.bind(span);
+      span.end = (endTime, exit) => {
+        end(endTime, exit);
+        Queue.offerUnsafe(ended, undefined);
+      };
+      return span;
+    },
+  });
+  return {
+    generate: (
+      service: PullRequestReviewGuideService.PullRequestReviewGuideService["Service"],
+      input: PullRequestRef,
+    ) => service.generate(input).pipe(Effect.withTracer(tracer)),
+    settled: (
+      service: PullRequestReviewGuideService.PullRequestReviewGuideService["Service"],
+      input: PullRequestRef,
+    ) => Queue.take(ended).pipe(Effect.andThen(service.get(input))),
-const awaitRunFailure = (
-  service: PullRequestReviewGuideService.PullRequestReviewGuideService["Service"],
-  input: PullRequestRef,
-) =>
-  service.get(input).pipe(
-    Effect.flatMap((result) =>
-      result.run?._tag === "Failed"
-        ? Effect.succeed(result.run.detail)
-        : Effect.fail("still running" as const),
+  };
+});
-    ),
-    Effect.retry(Schedule.spaced("20 millis")),
-  );
 
 const startSingleBranchRun = (input: {
   readonly repositoryName: string;
@@ -330,7 +346,8 @@
       PullRequestReviewGuideService.PullRequestReviewGuideService,
     );
     const guideReference = { ...reference, repository: `acme/${input.repositoryName}` };
-    yield* service.generate(guideReference);
+    const runs = yield* watchRuns;
+    yield* runs.generate(service, guideReference);
     return {
       repositories,
       reportedHead,
@@ -339,10 +356,10 @@
       stored,
       fetchedRefs,
       observed,
+      settled: runs.settled(service, guideReference),
     };
   });
 
-// Runs drive real git, which the test clock does not advance, so waits use real time.
 it.layer(layerBase, { excludeTestServices: true })("PullRequestReviewGuideService", (it) => {
   it.effect.each([
     {
@@ -393,10 +410,10 @@
               Effect.asVoid,
             ),
         });
+        const result = yield* run.settled;
+        assert.isFalse(yield* Deferred.isDone(run.stored), "a guide was written");
-        const detail = yield* Effect.raceFirst(
-          awaitRunFailure(run.service, run.reference),
-          Deferred.await(run.stored).pipe(Effect.andThen(Effect.die("a guide was written"))),
+        assert.strictEqual(result.run?._tag, "Failed");
+        const detail = result.run?._tag === "Failed" ? result.run.detail : "";
-        );
         for (const part of expected) assert.include(detail, part);
         assert.deepStrictEqual(run.observed, []);
         assert.isFalse(run.fetchedRefs.some((ref) => ref.startsWith("-")));
```

## apps/server/src/terminal/Manager.test.ts

Status: modified; +9; −32

```diff
@@ -12,20 +12,16 @@
   TerminalProviderInstanceNotFoundError,
 } from "@t3tools/contracts";
 import * as HostProcess from "@t3tools/shared/HostProcess";
-import * as Data from "effect/Data";
 import * as Deferred from "effect/Deferred";
-import * as Duration from "effect/Duration";
 import * as Effect from "effect/Effect";
 import * as Base64Url from "effect/encoding/Base64Url";
 import * as Exit from "effect/Exit";
 import * as Fiber from "effect/Fiber";
 import * as FileSystem from "effect/FileSystem";
 import * as Layer from "effect/Layer";
-import * as Option from "effect/Option";
 import * as PlatformError from "effect/PlatformError";
 import * as Path from "effect/Path";
 import * as Ref from "effect/Ref";
-import * as Schedule from "effect/Schedule";
 import * as Schema from "effect/Schema";
 import * as Scope from "effect/Scope";
 import * as Stream from "effect/Stream";
@@ -50,10 +46,6 @@
 
 const encodeUnknownJson = Schema.encodeUnknownSync(Schema.fromJsonString(Schema.Unknown));
 
-class WaitForConditionError extends Data.TaggedError("WaitForConditionError")<{
-  readonly message: string;
-}> {}
-
 class FakePtyProcess implements PtyAdapter.PtyProcess {
   readonly writes: string[] = [];
   readonly resizeCalls: Array<{ cols: number; rows: number }> = [];
@@ -205,26 +197,6 @@
   }
 }
 
-const waitFor = <E, R>(
-  predicate: Effect.Effect<boolean, E, R>,
-  timeout: Duration.Input = 800,
-): Effect.Effect<void, WaitForConditionError | E, R> =>
-  predicate.pipe(
-    Effect.filterOrFail(
-      (done) => done,
-      () => new WaitForConditionError({ message: "Condition not met" }),
-    ),
-    Effect.retry(Schedule.spaced("15 millis")),
-    Effect.timeoutOption(timeout),
-    Effect.flatMap((result) =>
-      Option.match(result, {
-        onNone: () =>
-          Effect.fail(new WaitForConditionError({ message: "Timed out waiting for condition" })),
-        onSome: () => Effect.void,
-      }),
-    ),
-  );
-
 function openInput(overrides: Partial<TerminalOpenInput> = {}): TerminalOpenInput {
   return {
     threadId: "thread-1",
@@ -773,7 +745,7 @@
     Effect.gen(function* () {
       const ptyAdapter = new FakePtyAdapter();
       let duringCheck: (pid: number) => Effect.Effect<void> = () => Effect.void;
+      const { manager } = yield* createManager(5, {
-      const { manager, getEvents } = yield* createManager(5, {
         ptyAdapter,
         subprocessPollIntervalMs: 60_000,
         subprocessInspector: (pid) =>
@@ -781,6 +753,13 @@
             Effect.as({ hasRunningSubprocess: false, childCommand: null, processIds: [] }),
           ),
       });
+      const outputSeen = yield* Deferred.make<void>();
+      const unsubscribeOutput = yield* manager.subscribe((event) =>
+        event.type === "output"
+          ? Deferred.succeed(outputSeen, undefined).pipe(Effect.asVoid)
+          : Effect.void,
+      );
+      yield* Effect.addFinalizer(() => Effect.sync(unsubscribeOutput));
       yield* manager.open(openInput({ terminalId: "typed" }));
       yield* manager.open(openInput({ terminalId: "echoed" }));
       const [typed, echoed] = ptyAdapter.processes;
@@ -791,9 +770,7 @@
               .pipe(Effect.orDie)
           : Effect.gen(function* () {
               echoed!.emitData("make build\r\n");
+              yield* Deferred.await(outputSeen);
-              yield* waitFor(
-                Effect.map(getEvents, (events) => events.some((event) => event.type === "output")),
-              );
             }).pipe(Effect.orDie);
 
       yield* manager.closeIdle({ threadId: "thread-1" });
```

## oxlint-plugin-t3code/index.ts

Status: modified; +2; −0

```diff
@@ -16,6 +16,7 @@
 import noOutsetStateIndicators from "./rules/no-outset-state-indicators.ts";
 import noRawMcpRegistration from "./rules/no-raw-mcp-registration.ts";
 import noRawResizeObserver from "./rules/no-raw-resize-observer.ts";
+import noRealTimeWaitInTests from "./rules/no-real-time-wait-in-tests.ts";
 import noTestInLoop from "./rules/no-test-in-loop.ts";
 import noTestTimeoutOrUnreasonedSkip from "./rules/no-test-timeout-or-unreasoned-skip.ts";
 import noRpcPermissionBypass from "./rules/no-rpc-permission-bypass.ts";
@@ -48,6 +49,7 @@
     "no-outset-state-indicators": noOutsetStateIndicators,
     "no-raw-mcp-registration": noRawMcpRegistration,
     "no-raw-resize-observer": noRawResizeObserver,
+    "no-real-time-wait-in-tests": noRealTimeWaitInTests,
     "no-test-in-loop": noTestInLoop,
     "no-test-timeout-or-unreasoned-skip": noTestTimeoutOrUnreasonedSkip,
     "no-rpc-permission-bypass": noRpcPermissionBypass,
```

## oxlint-plugin-t3code/rules/no-real-time-wait-in-tests.test.ts

Status: added; +268; −0

```diff
@@ -1,0 +1,268 @@
+import { assert, describe } from "@effect/vitest";
+
+import { createOxlintRuleHarness } from "../test/utils.ts";
+
+const rule = createOxlintRuleHarness("t3code/no-real-time-wait-in-tests", {
+  filename: "fixture.test.ts",
+});
+
+const REAL_CLOCK = /Real-clock wait in a test/;
+const UNDRIVEN = /never fire without `TestClock.adjust`/;
+const MIXED = /they never meet/;
+
+describe("t3code/no-real-time-wait-in-tests", () => {
+  rule.valid(
+    "allows TestClock-driven sleeps and timeout safety nets",
+    `
+      import { it } from "@effect/vitest";
+      import * as Effect from "effect/Effect";
+      import * as TestClock from "effect/testing/TestClock";
+
+      it.effect("fires after a minute", () =>
+        Effect.gen(function* () {
+          const fiber = yield* Effect.sleep("1 minute").pipe(Effect.forkChild);
+          yield* TestClock.adjust("1 minute");
+          yield* fiber.await;
+        }),
+      );
+
+      it.live("spawns a fixture", () => Effect.void.pipe(Effect.timeout("5 seconds")));
+    `,
+  );
+
+  rule.valid(
+    "ignores sleeps inside strings and child-process script templates",
+    `
+      import { it } from "@effect/vitest";
+      import * as Effect from "effect/Effect";
+
+      it.live("runs a script", () =>
+        Effect.sync(() => [
+          "setTimeout(() => process.exit(0), 50)",
+          \`await new Promise((resolve) => setTimeout(resolve, 10)); Effect.sleep(1)\`,
+        ]),
+      );
+    `,
+  );
+
+  rule.valid(
+    "ignores shared helpers in files that drive the TestClock",
+    `
+      import { it } from "@effect/vitest";
+      import * as Effect from "effect/Effect";
+      import * as TestClock from "effect/testing/TestClock";
+
+      const slowStep = (delay: number) =>
+        Effect.gen(function* () {
+          if (delay > 0) yield* Effect.sleep(delay);
+        });
+
+      it.effect("advances", () => slowStep(5).pipe(Effect.andThen(TestClock.adjust(5))));
+    `,
+  );
+
+  rule.valid(
+    "ignores bare setTimeout callbacks outside promise executors",
+    `
+      import { it } from "vitest";
+
+      it("schedules work", () => {
+        const handle = setTimeout(() => {}, 1000);
+        clearTimeout(handle);
+      });
+    `,
+  );
+
+  rule.invalid(
+    "reports Effect.sleep under it.live",
+    `
+      import { it } from "@effect/vitest";
+      import * as Effect from "effect/Effect";
+
+      it.live("waits", () => Effect.sleep("50 millis"));
+    `,
+    (output) => {
+      assert.match(output, REAL_CLOCK);
+    },
+  );
+
+  rule.invalid(
+    "reports retry schedules under curried testers",
+    `
+      import { it } from "@effect/vitest";
+      import * as Effect from "effect/Effect";
+      import * as Schedule from "effect/Schedule";
+
+      it.live.skipIf(false)("polls", () =>
+        Effect.void.pipe(Effect.retry(Schedule.spaced("10 millis"))),
+      );
+    `,
+    (output) => {
+      assert.match(output, REAL_CLOCK);
+    },
+  );
+
+  rule.invalid(
+    "reports Effect.delay under plain it",
+    `
+      import { it } from "vitest";
+      import * as Effect from "effect/Effect";
+
+      it("delays", async () => {
+        await Effect.runPromise(Effect.void.pipe(Effect.delay("10 millis")));
+      });
+    `,
+    (output) => {
+      assert.match(output, REAL_CLOCK);
+    },
+  );
+
+  rule.invalid(
+    "reports it.effect inside an excludeTestServices layer as a real-clock wait",
+    `
+      import { it } from "@effect/vitest";
+      import * as Effect from "effect/Effect";
+      import * as Layer from "effect/Layer";
+      import * as TestClock from "effect/testing/TestClock";
+
+      it.layer(Layer.empty, { excludeTestServices: true })("live services", (it) => {
+        it.effect("waits", () => Effect.sleep("10 millis"));
+      });
+
+      it.effect("advances", () => TestClock.adjust(1));
+    `,
+    (output) => {
+      assert.match(output, REAL_CLOCK);
+    },
+  );
+
+  rule.invalid(
+    "reports it.effect sleeps in files that never adjust the TestClock",
+    `
+      import { it } from "@effect/vitest";
+      import * as Effect from "effect/Effect";
+
+      it.effect("hangs", () => Effect.sleep("200 millis"));
+    `,
+    (output) => {
+      assert.match(output, UNDRIVEN);
+    },
+  );
+
+  rule.invalid(
+    "reports polling helpers even when the file drives the TestClock",
+    `
+      import { it } from "@effect/vitest";
+      import * as Effect from "effect/Effect";
+      import * as TestClock from "effect/testing/TestClock";
+
+      let ready = false;
+      const waitUntilReady = Effect.gen(function* () {
+        while (!ready) {
+          yield* Effect.sleep("10 millis");
+        }
+      });
+
+      it.effect("advances", () => TestClock.adjust(1));
+      it.live("waits", () => waitUntilReady);
+    `,
+    (output) => {
+      assert.match(output, REAL_CLOCK);
+    },
+  );
+
+  rule.invalid(
+    "reports promise-wrapped setTimeout in vitest tests",
+    `
+      import { it } from "vitest";
+
+      it.each([1])("waits %s", async () => {
+        await new Promise((resolve) => setTimeout(resolve, 10));
+      });
+    `,
+    (output) => {
+      assert.match(output, REAL_CLOCK);
+      assert.notMatch(output, MIXED);
+    },
+  );
+
+  rule.invalid(
+    "reports promise-wrapped setTimeout under it.effect as a mixed clock",
+    `
+      import { it } from "@effect/vitest";
+      import * as Effect from "effect/Effect";
+
+      it.effect("waits", () =>
+        Effect.promise(() => new Promise<void>((resolve) => setTimeout(resolve, 10))),
+      );
+    `,
+    (output) => {
+      assert.match(output, REAL_CLOCK);
+      assert.match(output, MIXED);
+    },
+  );
+
+  rule.invalid(
+    "reports TestClock sleeps when TestClock.adjust appears only in a comment",
+    `
+      import { it } from "@effect/vitest";
+      import * as Effect from "effect/Effect";
+
+      // Callers would normally TestClock.adjust("1 minute") here.
+      const note = "TestClock.setTime";
+      it.effect("hangs", () => Effect.sleep("1 minute"));
+    `,
+    (output) => {
+      assert.match(output, UNDRIVEN);
+    },
+  );
+
+  rule.invalid(
+    "reports timers hidden in module-level helpers",
+    `
+      import { setTimeout as delay } from "node:timers/promises";
+      import { it } from "vitest";
+
+      const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+      const pause = () => delay(10);
+
+      it("waits", async () => {
+        await sleep(10);
+        await pause();
+      });
+    `,
+    (output) => {
+      assert.equal(output.match(/Real-clock wait in a test/g)?.length, 2);
+      assert.notMatch(output, MIXED);
+    },
+  );
+
+  rule.invalid(
+    "reports awaited timers/promises sleeps",
+    `
+      import * as NodeTimers from "node:timers/promises";
+      import { setTimeout as sleep } from "node:timers/promises";
+      import { it } from "vitest";
+
+      it("waits", async () => {
+        await NodeTimers.setTimeout(10);
+        await sleep(10);
+      });
+    `,
+    (output) => {
+      assert.equal(output.match(/Real-clock wait in a test/g)?.length, 2);
+    },
+  );
+});
+
+const productionRule = createOxlintRuleHarness("t3code/no-real-time-wait-in-tests");
+
+productionRule.valid(
+  "ignores non-test files",
+  `
+    import * as Effect from "effect/Effect";
+
+    const it = { live: (_name: string, run: () => unknown) => run() };
+    it.live("waits", () => Effect.sleep("1 second"));
+  `,
+);
```

## oxlint-plugin-t3code/rules/no-real-time-wait-in-tests.ts

Status: added; +277; −0

```diff
@@ -1,0 +1,277 @@
+import { defineRule, type ESTree } from "@oxlint/plugins";
+import * as Option from "effect/Option";
+
+import { getPropertyName, isIdentifier, unwrapExpression } from "../utils.ts";
+
+const TEST_FILE_PATTERN = /\.(?:test|spec)\.[cm]?[jt]sx?$/u;
+const TEST_CLOCK_DRIVERS = new Set(["adjust", "setTime"]);
+const TIMER_PROMISES_MODULES = new Set(["node:timers/promises", "timers/promises"]);
+const TIMER_FUNCTIONS = new Set(["setTimeout", "setInterval", "setImmediate"]);
+const SCHEDULE_WAITS = new Set(["spaced", "fixed", "exponential"]);
+const TESTER_ROOTS = new Set(["it", "test"]);
+const FUNCTIONS = new Set(["ArrowFunctionExpression", "FunctionExpression", "FunctionDeclaration"]);
+
+const REAL_CLOCK_MESSAGE =
+  "Real-clock wait in a test. Await the `Deferred`/`Queue`/event that marks the milestone, or run under `it.effect` and `TestClock.adjust`. `it.live` is for subprocess/network fixtures, not for sleeping.";
+const UNDRIVEN_TEST_CLOCK_MESSAGE =
+  "Sleeps on the TestClock never fire without `TestClock.adjust`; this test can only time out.";
+const MIXED_CLOCK_MESSAGE =
+  "`setTimeout` is wall-clock while `Effect.sleep` here is TestClock; they never meet.";
+
+type Tester = "effect" | "live" | "plain";
+
+interface CalleeChain {
+  readonly root: string;
+  readonly members: ReadonlyArray<string>;
+  readonly calls: ReadonlyArray<ESTree.CallExpression>;
+}
+
+// Flattens `it.live.skipIf(cond)` or `it.layer(L, opts)` into the root name, the member
+// names, and the curried calls whose arguments carry options like `excludeTestServices`.
+const calleeChain = (callee: unknown): Option.Option<CalleeChain> => {
+  const members: Array<string> = [];
+  const calls: Array<ESTree.CallExpression> = [];
+  let current = unwrapExpression(callee);
+  while (Option.isSome(current)) {
+    const node = current.value;
+    if (node.type === "Identifier") {
+      return Option.some({ root: node.name, members: members.toReversed(), calls });
+    }
+    if (node.type === "CallExpression") {
+      calls.push(node);
+      current = unwrapExpression(node.callee);
+      continue;
+    }
+    if (node.type === "MemberExpression") {
+      const name = getPropertyName(node.property);
+      if (Option.isNone(name)) return Option.none();
+      members.push(name.value);
+      current = unwrapExpression(node.object);
+      continue;
+    }
+    return Option.none();
+  }
+  return Option.none();
+};
+
+const isLayerBlock = (chain: CalleeChain) =>
+  chain.root === "layer" || (TESTER_ROOTS.has(chain.root) && chain.members.includes("layer"));
+
+const testerKind = (chain: CalleeChain): Option.Option<Tester> => {
+  if (!TESTER_ROOTS.has(chain.root) || chain.members.includes("layer")) return Option.none();
+  if (chain.members.includes("live") || chain.members.includes("scopedLive")) {
+    return Option.some("live");
+  }
+  if (chain.members.includes("effect") || chain.members.includes("scoped")) {
+    return Option.some("effect");
+  }
+  return Option.some("plain");
+};
+
+const excludesTestServices = (chain: CalleeChain) =>
+  chain.calls.some((call) =>
+    call.arguments.some((argument) => {
+      const expression = unwrapExpression(argument);
+      if (Option.isNone(expression) || expression.value.type !== "ObjectExpression") return false;
+      return expression.value.properties.some((property) => {
+        if (property.type !== "Property") return false;
+        const key = getPropertyName(property.key);
+        const value = unwrapExpression(property.value);
+        return (
+          Option.isSome(key) &&
+          key.value === "excludeTestServices" &&
+          Option.isSome(value) &&
+          value.value.type === "Literal" &&
+          value.value.value === true
+        );
+      });
+    }),
+  );
+
+// The clock a call runs on: the nearest enclosing test call decides, and an enclosing
+// `it.layer(_, { excludeTestServices: true })` block strips the TestClock from `it.effect`.
+const enclosingTester = (node: ESTree.Node): Option.Option<Tester> => {
+  let child: ESTree.Node = node;
+  let current = node.parent;
+  let tester: Option.Option<Tester> = Option.none();
+  while (current) {
+    if (
+      current.type === "CallExpression" &&
+      current.arguments.some((argument) => argument === child)
+    ) {
+      const chain = calleeChain(current.callee);
+      if (Option.isSome(chain)) {
+        if (Option.isNone(tester)) {
+          tester = testerKind(chain.value);
+          if (Option.isSome(tester) && tester.value !== "effect") return tester;
+        } else if (isLayerBlock(chain.value) && excludesTestServices(chain.value)) {
+          return Option.some("live");
+        }
+      }
+    }
+    child = current;
+    current = current.parent;
+  }
+  return tester;
+};
+
+const memberCallName = (callee: unknown): Option.Option<readonly [string, string]> => {
+  const expression = unwrapExpression(callee);
+  if (Option.isNone(expression) || expression.value.type !== "MemberExpression") {
+    return Option.none();
+  }
+  const object = unwrapExpression(expression.value.object);
+  const property = getPropertyName(expression.value.property);
+  if (Option.isNone(property) || Option.isNone(object) || object.value.type !== "Identifier") {
+    return Option.none();
+  }
+  return Option.some([object.value.name, property.value] as const);
+};
+
+const LOOPS = new Set([
+  "DoWhileStatement",
+  "ForInStatement",
+  "ForOfStatement",
+  "ForStatement",
+  "WhileStatement",
+]);
+
+// `while (...) { yield* Effect.sleep(...) }`: a statement of the loop body itself, not a
+// conditional delay inside it.
+const isPollingLoopStep = (node: ESTree.Node): boolean => {
+  let statement: ESTree.Node | null = node.parent;
+  while (statement && statement.type !== "ExpressionStatement") {
+    if (FUNCTIONS.has(statement.type)) return false;
+    statement = statement.parent;
+  }
+  const block = statement?.parent;
+  if (!block) return false;
+  if (LOOPS.has(block.type)) return true;
+  return block.type === "BlockStatement" && block.parent !== null && LOOPS.has(block.parent.type);
+};
+
+const isPromiseExecutor = (fn: ESTree.Node) => {
+  const parent = fn.parent;
+  return (
+    parent?.type === "NewExpression" &&
+    parent.arguments[0] === fn &&
+    isIdentifier(unwrapExpression(parent.callee), "Promise")
+  );
+};
+
+const isInsidePromiseExecutor = (node: ESTree.Node): boolean => {
+  let current = node.parent;
+  while (current) {
+    if (FUNCTIONS.has(current.type)) return isPromiseExecutor(current);
+    current = current.parent;
+  }
+  return false;
+};
+
+export default defineRule({
+  meta: {
+    type: "problem",
+    docs: {
+      description:
+        "Disallow real-clock waits in tests; await the milestone or drive the TestClock instead.",
+    },
+  },
+  create(context) {
+    if (!TEST_FILE_PATTERN.test(context.filename)) return {};
+    let drivesTestClock = false;
+    // Whether the file drives the TestClock is only known after the whole file is walked.
+    const untilDrivenReports: Array<{ node: ESTree.Node; message: string }> = [];
+    const timerPromiseLocals = new Set<string>();
+    const timerPromiseNamespaces = new Set<string>();
+
+    const isTimerPromiseCall = (callee: unknown) => {
+      const expression = unwrapExpression(callee);
+      if (Option.isNone(expression)) return false;
+      if (expression.value.type === "Identifier") {
+        return timerPromiseLocals.has(expression.value.name);
+      }
+      return Option.match(memberCallName(callee), {
+        onNone: () => false,
+        onSome: ([object, property]) =>
+          timerPromiseNamespaces.has(object) && TIMER_FUNCTIONS.has(property),
+      });
+    };
+
+    const isCallbackTimer = (callee: unknown) => {
+      const expression = unwrapExpression(callee);
+      return (
+        Option.isSome(expression) &&
+        expression.value.type === "Identifier" &&
+        TIMER_FUNCTIONS.has(expression.value.name) &&
+        !timerPromiseLocals.has(expression.value.name)
+      );
+    };
+
+    return {
+      ImportDeclaration(node) {
+        if (!TIMER_PROMISES_MODULES.has(node.source.value)) return;
+        for (const specifier of node.specifiers) {
+          if (specifier.type === "ImportNamespaceSpecifier") {
+            timerPromiseNamespaces.add(specifier.local.name);
+          } else if (specifier.type === "ImportSpecifier") {
+            const imported = getPropertyName(specifier.imported);
+            if (Option.isSome(imported) && TIMER_FUNCTIONS.has(imported.value)) {
+              timerPromiseLocals.add(specifier.local.name);
+            }
+          }
+        }
+      },
+      CallExpression(node) {
+        const reportFor = (message: string) => context.report({ node: node.callee, message });
+
+        if (
+          isTimerPromiseCall(node.callee) ||
+          (isCallbackTimer(node.callee) && isInsidePromiseExecutor(node))
+        ) {
+          const tester = enclosingTester(node);
+          reportFor(
+            Option.isSome(tester) && tester.value === "effect"
+              ? `${REAL_CLOCK_MESSAGE} ${MIXED_CLOCK_MESSAGE}`
+              : REAL_CLOCK_MESSAGE,
+          );
+          return;
+        }
+
+        const name = memberCallName(node.callee);
+        if (Option.isNone(name)) return;
+        const [object, property] = name.value;
+        if (object === "TestClock" && TEST_CLOCK_DRIVERS.has(property)) {
+          drivesTestClock = true;
+          return;
+        }
+        const isSleep = object === "Effect" && property === "sleep";
+        const isWait =
+          isSleep ||
+          (object === "Effect" && property === "delay") ||
+          (object === "Schedule" && SCHEDULE_WAITS.has(property));
+        if (!isWait) return;
+
+        const tester = enclosingTester(node);
+        if (Option.isNone(tester)) {
+          // A shared helper can serve TestClock tests, so only report it when nothing in
+          // the file drives the TestClock or it polls in a loop, which only a real clock ends.
+          if (isPollingLoopStep(node)) {
+            reportFor(REAL_CLOCK_MESSAGE);
+          } else {
+            untilDrivenReports.push({ node: node.callee, message: REAL_CLOCK_MESSAGE });
+          }
+          return;
+        }
+        if (tester.value !== "effect") {
+          reportFor(REAL_CLOCK_MESSAGE);
+        } else if (isSleep) {
+          untilDrivenReports.push({ node: node.callee, message: UNDRIVEN_TEST_CLOCK_MESSAGE });
+        }
+      },
+      "Program:exit"() {
+        if (drivesTestClock) return;
+        for (const report of untilDrivenReports) context.report(report);
+      },
+    };
+  },
+});
```

## packages/provider-opencode/src/server/status.test.ts

Status: modified; +1; −8

```diff
@@ -4,7 +4,6 @@
 import * as NodeServices from "@effect/platform-node/NodeServices";
 import { it } from "@effect/vitest";
 import * as Deferred from "effect/Deferred";
-import * as Duration from "effect/Duration";
 import * as Effect from "effect/Effect";
 import * as Fiber from "effect/Fiber";
 import * as FileSystem from "effect/FileSystem";
@@ -288,7 +287,6 @@
   state: {
     runVersionError: null as Error | null,
     runVersionPending: false,
-    runVersionDelay: null as Duration.Input | null,
     versionStdout: DEFAULT_VERSION_STDOUT,
     inventoryError: null as Error | null,
     connectionError: null as Error | null,
@@ -309,7 +307,6 @@
   reset() {
     this.state.runVersionError = null;
     this.state.runVersionPending = false;
-    this.state.runVersionDelay = null;
     this.state.versionStdout = DEFAULT_VERSION_STDOUT;
     this.state.inventoryError = null;
     this.state.connectionError = null;
@@ -384,11 +381,7 @@
               cause: runtimeMock.state.runVersionError,
             }),
           )
-        : runtimeMock.state.runVersionDelay
+        : Effect.succeed({ stdout: runtimeMock.state.versionStdout, stderr: "", code: 0 }),
-          ? Effect.succeed({ stdout: runtimeMock.state.versionStdout, stderr: "", code: 0 }).pipe(
-              Effect.delay(runtimeMock.state.runVersionDelay),
-            )
-          : Effect.succeed({ stdout: runtimeMock.state.versionStdout, stderr: "", code: 0 }),
   createOpenCodeSdkClient: (input) => {
     runtimeMock.state.sdkClientInputs.push(input);
     return {} as unknown as ReturnType<
```

## packages/provider-pi/src/server/status.test.ts

Status: modified; +1; −13

```diff
@@ -1,8 +1,6 @@
 import * as NodeServices from "@effect/platform-node/NodeServices";
 import { assert, describe, it } from "@effect/vitest";
 import * as Cause from "effect/Cause";
-import * as Deferred from "effect/Deferred";
-import * as Duration from "effect/Duration";
 import * as Effect from "effect/Effect";
 import * as Queue from "effect/Queue";
 import * as Schema from "effect/Schema";
@@ -54,13 +52,7 @@
   });
 }
 
-const piDiscoverySpawner = (
-  inventory: unknown,
-  slowStartup?: {
-    readonly delay: Duration.Input;
-    readonly getStateReceived: Deferred.Deferred<void>;
-  },
-) =>
+const piDiscoverySpawner = (inventory: unknown) =>
   Effect.gen(function* () {
     const stdout = yield* Queue.unbounded<Uint8Array, Cause.Done>();
     let stdinBuffer = "";
@@ -74,10 +66,6 @@
           stdinBuffer = stdinBuffer.slice(newline + 1);
           if (line.length === 0) continue;
           const request = decodeRpcRequest(line);
-          if (request.type === "get_state" && slowStartup !== undefined) {
-            yield* Deferred.succeed(slowStartup.getStateReceived, undefined);
-            yield* Effect.sleep(slowStartup.delay);
-          }
           const data =
             request.type === "get_available_models"
               ? inventory
```

## packages/shared/src/atomicWrite.test.ts

Status: modified; +1; −0

```diff
@@ -138,6 +138,7 @@
   Effect.gen(function* () {
     while (fiber.pollUnsafe() === undefined) {
       yield* TestClock.adjust("100 millis");
+      // oxlint-disable-next-line t3code/no-real-time-wait-in-tests -- a macrotask boundary so the real stat I/O can finish; time moves only via TestClock
       yield* Effect.promise(() => new Promise((resolve) => setImmediate(resolve)));
     }
     return yield* Fiber.join(fiber);
```

## packages/shared/src/nodeSqliteClient.test.ts

Status: modified; +1; −0

```diff
@@ -327,6 +327,7 @@
   }).pipe(Effect.provide(SqliteClient.layer({ filename: ":memory:" }))),
 );
 
+// oxlint-disable-next-line t3code/no-real-time-wait-in-tests -- a zero-delay macrotask boundary that lets the worker's queued messages land, not a wait for time
 const eventLoopTurn = Effect.promise(() => new Promise<void>((resolve) => setImmediate(resolve)));
 
 it.effect.each([
```

## vite.config.ts

Status: modified; +5; −0

```diff
@@ -188,6 +188,7 @@
       "t3code/no-manual-effect-runtime-in-tests": "error",
       "t3code/no-native-title-tooltip": "error",
       "t3code/no-raw-mcp-registration": "error",
+      "t3code/no-real-time-wait-in-tests": "error",
       "t3code/no-test-in-loop": "error",
       "t3code/no-test-timeout-or-unreasoned-skip": "error",
       "t3code/no-rpc-permission-bypass": ["error", { allowRawClientAccess: true }],
@@ -217,6 +218,10 @@
         rules: { "t3code/no-frame-waits-in-browser-tests": "error" },
       },
       {
+        files: ["**/*.browser.test.*"],
+        rules: { "t3code/no-real-time-wait-in-tests": "off" },
+      },
+      {
         files: ["apps/**/src/**", "packages/**/src/**"],
         excludeFiles: DOM_TEST_ENVIRONMENT_ALLOWLIST,
         rules: { "t3code/no-dom-test-environment": "error" },
```
