From 49be05fb6740a1e193ff1f08e5ab2b3436e990c6 Mon Sep 17 00:00:00 2001 From: skulitom Date: Sat, 10 Oct 2026 05:14:44 +0100 Subject: [PATCH] Quick checks: wait for conditions instead of fixed times (about 5 s faster) - MCP ownership: once the stale stand-in's pipe is listed, its backend connects with a 50 ms wait, so the two calls after the quit fail fast instead of each waiting out the 1 s default. - MCP lease helper: poll the lease until the waiter is in line instead of sleeping 1.5 s. - agent deadline replies: the read-only and command-start cases use 300 ms deadlines (their time text isn't asserted); the queued 800 ms and the held 1.5 s cases keep their asserted messages. - pipe forwarded time left: queue the request 0.6 s instead of 1.5 s, with the same 100 ms and 3 s margins. - desktop ignored set_value: drop `window.Value == "Free"`, which only restates the stand-in's reset (its "ignore" mode never writes the value). No product code changes; every other assertion is unchanged. Reviewer question: does any changed or removed assertion lose the only coverage of a behaviour, or make a check timing-sensitive on a slow CI runner in a way the old values weren't? Co-Authored-By: Claude Opus 5.5 --- src/Anode/Cli/AgentChecks.cs | 15 +++++++++++---- src/Anode/Cli/DesktopChecks.cs | 2 +- src/Anode/Cli/TransportChecks.cs | 6 +++--- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/Anode/Cli/AgentChecks.cs b/src/Anode/Cli/AgentChecks.cs index d0e1a93..1aea0ef 100644 --- a/src/Anode/Cli/AgentChecks.cs +++ b/src/Anode/Cli/AgentChecks.cs @@ -382,11 +382,11 @@ async Task Dispatch(JsonObject request, CancellationToken cancel) Require(stopped.Bool("ok") == false && stopped.Str("errorCode") == "timed_out" && stopped.Bool("started") == true && stopped.Str("error") is { } partway && partway.Contains("1.5 s") && partway.Contains("may have run"), "an action stopped partway did not say so: " + stopped.ToJsonString()); - var look = new JsonObject { ["op"] = "desktop.wait", ["agentId"] = "A", ["leaseToken"] = token, ["windowId"] = "w_1", ["textContains"] = "never", ["timeoutMs"] = 1000 }; + var look = new JsonObject { ["op"] = "desktop.wait", ["agentId"] = "A", ["leaseToken"] = token, ["windowId"] = "w_1", ["textContains"] = "never", ["timeoutMs"] = 300 }; var read = await lease.HandleAsync(look, Dispatch).WaitAsync(TimeSpan.FromSeconds(5)); Require(read.Str("errorCode") == "timed_out" && read.Bool("started") == true && read.Str("error") is { } safe && safe.Contains("nothing changed") && !safe.Contains("may have run"), "a read-only action stopped partway was said to have acted: " + read.ToJsonString()); - var start = new JsonObject { ["op"] = "exec.start", ["agentId"] = "A", ["leaseToken"] = token, ["path"] = "unused-test-command", ["timeoutMs"] = 1000 }; + var start = new JsonObject { ["op"] = "exec.start", ["agentId"] = "A", ["leaseToken"] = token, ["path"] = "unused-test-command", ["timeoutMs"] = 300 }; var started = await lease.HandleAsync(start, Dispatch).WaitAsync(TimeSpan.FromSeconds(5)); Require(started.Str("errorCode") == "timed_out" && started.Bool("started") == true && started.Str("error") is { } job && job.Contains("seat_job action=list"), "a command start cut short didn't point at the job list: " + started.ToJsonString()); @@ -540,7 +540,10 @@ public static async Task McpLeaseHelper() // Waiting in line: the owner hears someone is waiting, and the waiter gets the desktop on release. var waiting = Call(second, "seat_lease", new JsonObject { ["action"] = "acquire", ["waitSeconds"] = 10 }); - await Task.Delay(1500); + var asked = Stopwatch.StartNew(); // Its first ask puts it in line. + while ((await lease.HandleAsync(Lease("watcher", "status"), (_, _) => Task.FromResult(JsonLine.Ok()))).Obj("result")?["waiting"] is not JsonArray { Count: 1 } + && asked.ElapsedMilliseconds < 5000) + await Task.Delay(10); Require(McpChecks.Text(await Call(first, "seat_windows")).Contains("Another agent is waiting"), "the owner was not told another agent is waiting"); await Call(first, "seat_lease", new JsonObject { ["action"] = "release" }); var granted = await waiting.WaitAsync(TimeSpan.FromSeconds(5)); @@ -599,9 +602,13 @@ public static async Task McpOwnership() var staleLease = new DesktopLease(); using var staleDaemon = new JsonPipeServer(stalePipe, request => staleLease.HandleAsync(request, (_, _) => Task.FromResult(JsonLine.Ok()))); staleDaemon.Start(); + // A short connect wait makes each call after the quit fail fast, once the stand-in's pipe is there to connect to. + var listening = Stopwatch.StartNew(); + while (!Core.Launch.SeatSlot.Pipes().Contains(stalePipe, StringComparer.OrdinalIgnoreCase) && listening.ElapsedMilliseconds < 5000) + await Task.Delay(10); using var stale = new McpServer(stalePipe, () => { staleLaunches++; throw new InvalidOperationException("a cached lease token launched a daemon"); }, - () => { staleReadiness++; return null; }, "C"); + () => { staleReadiness++; return null; }, "C") { ConnectTimeoutMs = 50 }; Require((await Call(stale, "seat_lease", acquire)).Bool("isError") != true && stale.HoldsLease, "stale-token fixture could not acquire"); staleDaemon.Dispose(); // The first call discovers the closed connection and reports the quit; it is never replayed. diff --git a/src/Anode/Cli/DesktopChecks.cs b/src/Anode/Cli/DesktopChecks.cs index d89ef78..bb67d62 100644 --- a/src/Anode/Cli/DesktopChecks.cs +++ b/src/Anode/Cli/DesktopChecks.cs @@ -273,7 +273,7 @@ JsonObject Set(string mode, JsonObject? control = null) Require(!error.Message.Contains("Free", StringComparison.Ordinal) && error.Message.Contains("expand the list", StringComparison.Ordinal), "the failure quoted the control's text or left out the drop-down advice: " + error.Message); } - Require(window.Value == "Free" && watch.ElapsedMilliseconds >= AccessibilityReader.SetValueSettleMs - 100, + Require(watch.ElapsedMilliseconds >= AccessibilityReader.SetValueSettleMs - 100, "an ignored value was refused before the control had time to apply it"); // The value went out before the control went away, so that must not read as "nothing happened". var unread = Set("vanish"); diff --git a/src/Anode/Cli/TransportChecks.cs b/src/Anode/Cli/TransportChecks.cs index 012a2f0..ddd5948 100644 --- a/src/Anode/Cli/TransportChecks.cs +++ b/src/Anode/Cli/TransportChecks.cs @@ -88,13 +88,13 @@ public static async Task ForwardTimeLeft() { await entered.Task.WaitAsync(TimeSpan.FromSeconds(3)); queued = Daemon.AnodeDaemon.SendOnAsync(seat, "queued", new JsonObject { ["timeoutMs"] = 8000 }, 8000); - await Task.Delay(1500); + await Task.Delay(600); } finally { release.TrySetResult(); } Require((await first.WaitAsync(TimeSpan.FromSeconds(3))).Bool("ok") == true && (await queued.WaitAsync(TimeSpan.FromSeconds(3))).Bool("ok") == true, "a request lost its reply"); - Require(given["queued"] is int left && left <= 8000 - 1400 && left > 8000 - 4500, - $"a request queued about 1.5 s behind another was forwarded with {given["queued"]} ms of 8000, not the time it had left"); + Require(given["queued"] is int left && left <= 8000 - 500 && left > 8000 - 3600, + $"a request queued about 0.6 s behind another was forwarded with {given["queued"]} ms of 8000, not the time it had left"); // A request left with almost no time isn't sent: its reply would come too late and cost the connection. entered = Signal();