test: wait for events instead of a 5 s wall-clock deadline - #22
Merged
Merged
Conversation
anotherAgentsRunBeforeTheAckNeverHijacksTheIntent and preAckEventsOfTheAckedRunAreReplayed failed on main (CI run 36644330107, xcode-27 runner) with "Caught error: CancellationError()" after ~8 s; the same tree passed on PR #20. waitForSend polled the requester every 5 ms and threw once a 5 s ContinuousClock deadline passed. With ~3,150 Swift Testing tests saturating the cooperative pool, the host.send task (and the poller itself) did not run for more than 5 s, so the deadline expired although nothing in GatewayOpenClawIntentHost.send is slow. Blocking every cooperative thread for 6 s reproduces the failure locally. - OpenClawAppIntentsRunMatchingTests: HeldChatSendRequester yields each chat.send's params to an AsyncStream the moment the request arrives, and waitForSend awaits that stream. Assertions are unchanged. - GatewayNetworkConnectionTransportTests: the loopback NWListener start waits on stateUpdateHandler (ready/failed/cancelled) instead of polling listener.state against the same 5 s deadline, which also had no final re-check after a late wake-up. - WatchNodeClientTests: eventually() drops its 5 s deadline. Its conditions read production client state that has no change hook, so it still polls, but slowness no longer fails it. Each suite gains .timeLimit(.minutes(1)) for hang protection. Every wait ends on cancellation, so a time-limit overrun reports "Time limit was exceeded" instead of hanging. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MarcoDotIO
enabled auto-merge (squash)
September 30, 2026 14:19
keepalivePingIsBoundedWhenNoPongArrives and handshakeTimeoutOptionBoundsTheWholeHandshake failed on PR #22's macOS job (Xcode 27) with `ContinuousClock.now - start < .seconds(5)` at ~5.7 s. A trivial test in the same window took 5.3 s, so the runner stalled; the timeouts under test were 50 ms and 100 ms. - keepalive ping: the socket never pongs, so the thrown URLError already proves the ping deadline ended the wait; a time limit catches a hang. - handshake option: the fallback budget is raised to an hour through _test_setConnectTimeoutSeconds, so a channel that ignored the 100 ms option would trip the time limit instead of finishing at the 30 s default. Checked with a scratch test that drops the option: it fails with "Time limit was exceeded" and does not hang. Both tests take .timeLimit(.minutes(1)) and pass while every cooperative thread is blocked for several seconds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MarcoDotIO
added a commit
that referenced
this pull request
Sep 30, 2026
* test: wait for events instead of a 5 s wall-clock deadline anotherAgentsRunBeforeTheAckNeverHijacksTheIntent and preAckEventsOfTheAckedRunAreReplayed failed on main (CI run 36644330107, xcode-27 runner) with "Caught error: CancellationError()" after ~8 s; the same tree passed on PR #20. waitForSend polled the requester every 5 ms and threw once a 5 s ContinuousClock deadline passed. With ~3,150 Swift Testing tests saturating the cooperative pool, the host.send task (and the poller itself) did not run for more than 5 s, so the deadline expired although nothing in GatewayOpenClawIntentHost.send is slow. Blocking every cooperative thread for 6 s reproduces the failure locally. - OpenClawAppIntentsRunMatchingTests: HeldChatSendRequester yields each chat.send's params to an AsyncStream the moment the request arrives, and waitForSend awaits that stream. Assertions are unchanged. - GatewayNetworkConnectionTransportTests: the loopback NWListener start waits on stateUpdateHandler (ready/failed/cancelled) instead of polling listener.state against the same 5 s deadline, which also had no final re-check after a late wake-up. - WatchNodeClientTests: eventually() drops its 5 s deadline. Its conditions read production client state that has no change hook, so it still polls, but slowness no longer fails it. Each suite gains .timeLimit(.minutes(1)) for hang protection. Every wait ends on cancellation, so a time-limit overrun reports "Time limit was exceeded" instead of hanging. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: bound lifecycle timeouts with a time limit, not a 5 s wall clock keepalivePingIsBoundedWhenNoPongArrives and handshakeTimeoutOptionBoundsTheWholeHandshake failed on PR #22's macOS job (Xcode 27) with `ContinuousClock.now - start < .seconds(5)` at ~5.7 s. A trivial test in the same window took 5.3 s, so the runner stalled; the timeouts under test were 50 ms and 100 ms. - keepalive ping: the socket never pongs, so the thrown URLError already proves the ping deadline ended the wait; a time limit catches a hang. - handshake option: the fallback budget is raised to an hour through _test_setConnectTimeoutSeconds, so a channel that ignored the 100 ms option would trip the time limit instead of finishing at the 30 s default. Checked with a scratch test that drops the option: it fails with "Time limit was exceeded" and does not hang. Both tests take .timeLimit(.minutes(1)) and pass while every cooperative thread is blocked for several seconds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: show the device-auth write leaves the actor free by ordering, not elapsed time issuedTokenPersistenceNeverBlocksTheChannelActor asserted the actor answered within 3 s while a token write waited on another connection's SQLite lock. A saturated test pool can stall the run for longer than that, and a time limit alone would miss the regression it guards (a write on the actor holds it for SQLite's 30 s busy timeout, less than the one-minute limit). The test now relies on ordering. It releases the lock only after the actor answers, so the token can reach disk only if the actor answered while the write was pending. A write on the actor fails with SQLITE_BUSY first, the token never lands, and the final wait trips the new suite time limit. The 200 ms sleep that let hello-ok reach the write is replaced by a DEBUG hook, _test_setDeviceTokenPersistenceStartedHandler, that fires on the actor just before the persistence hop. Once the hop starts, shutdown cannot stop it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: bound the FIFO, AsyncTimeout and link-preview tests with a time limit The three tests asserted wall-clock bounds (2 s, 5 s, 1 s) that a saturated test pool can overrun. Each now has a one-minute time limit instead, and each makes sure a regression ends on the limit's cancellation instead of hanging the run: - file fetch refuses a FIFO: a blocking open(2) never returns. The cancellation handler opens the FIFO's write end once to release it. - operationThatIgnoresCancellationStillTimesOut: the parked operation is a gate the test opens on cancellation (and afterwards), not a never-resumed continuation that a loser-joining race would wait on forever. - total deadline can fire before the session starts: URLSession's own timeouts move past the limit, so only the fetcher's zero-second deadline can end the fetch. The fetch is awaited through a no-deadline AsyncTimeout race, because a deadline lost before start would leave it unresumed even on cancellation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: drop wall-clock deadlines from the gateway and session-action waits gatewayCoreWaitUntil gave up after 10 s (15 s at two call sites) and the ChatViewModelSessionActionTests helpers after 15 s. A pool stall adds to those waits, so they could time out even though the condition was about to hold. Both now wait until the condition holds or the test is cancelled. Every suite that uses them has a one-minute time limit. gatewayCoreWaitUntil records its GatewayCoreWaitTimeout as an issue before throwing, because Swift Testing drops errors thrown after a time-limit cancellation and the label says which wait hung. waitForForkStart awaits the gate's stream directly instead of racing it against a sleep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: drop the wall-clock deadline from waitUntil waitUntil gave up after 15 s (7, 10 or 30 s at five call sites). The macOS CI job runs about 3,150 tests in parallel and the cooperative pool stalls for 5 to 8 s at a time, so a wait could time out even though the condition was about to hold. waitUntil now polls until the condition holds or the test is cancelled, and the timeoutSeconds and now: parameters are gone (nothing injected a clock, and with no deadline there is nothing to measure). Every suite whose tests reach waitUntil, directly or through a file-local helper, has a one-minute time limit; ChatViewModelSessionActionTests already had one. On cancellation the helper records AsyncWaitTimeoutError as an issue before throwing it, because Swift Testing drops errors thrown after a time-limit cancellation and the label says which wait hung. No call site relied on the timeout: nothing expects AsyncWaitTimeoutError, no wait runs in a child task that the test cancels, and the one try? wait is cleanup in a catch block that rethrows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(e2e): wait without a wall-clock deadline in the channel and reconnect tests ChannelAdaptersE2ETests.waitFor gave up after 15 s and only recorded an unlabelled expectation failure. reconnectFailureSchedulesAnotherAttempt polled against a 10 s deadline. A pool stall can outlast either one. waitFor now takes a label, polls until the condition holds or the test is cancelled, and records a WaitTimeout issue before throwing it. The reconnect loop polls until cancelled. The suite and the reconnect test each have a one-minute time limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: drop the 1 s deadline from the chat UI view-model waits OpenClawChatUITests had its own waitUntil that gave up after 1 s and returned false. On the macOS CI job for this PR, a pool stall of about 10 s ran past it: both view-model tests failed after about 12 s, and the bootstrap expectations that followed failed with them. The tests now call the shared deadline-free waitUntil with a label for each wait, and the suite has a one-minute time limit. The private helper is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2 of 3 tasks
MarcoDotIO
added a commit
that referenced
this pull request
Oct 1, 2026
* test: wait for events instead of a 5 s wall-clock deadline anotherAgentsRunBeforeTheAckNeverHijacksTheIntent and preAckEventsOfTheAckedRunAreReplayed failed on main (CI run 36644330107, xcode-27 runner) with "Caught error: CancellationError()" after ~8 s; the same tree passed on PR #20. waitForSend polled the requester every 5 ms and threw once a 5 s ContinuousClock deadline passed. With ~3,150 Swift Testing tests saturating the cooperative pool, the host.send task (and the poller itself) did not run for more than 5 s, so the deadline expired although nothing in GatewayOpenClawIntentHost.send is slow. Blocking every cooperative thread for 6 s reproduces the failure locally. - OpenClawAppIntentsRunMatchingTests: HeldChatSendRequester yields each chat.send's params to an AsyncStream the moment the request arrives, and waitForSend awaits that stream. Assertions are unchanged. - GatewayNetworkConnectionTransportTests: the loopback NWListener start waits on stateUpdateHandler (ready/failed/cancelled) instead of polling listener.state against the same 5 s deadline, which also had no final re-check after a late wake-up. - WatchNodeClientTests: eventually() drops its 5 s deadline. Its conditions read production client state that has no change hook, so it still polls, but slowness no longer fails it. Each suite gains .timeLimit(.minutes(1)) for hang protection. Every wait ends on cancellation, so a time-limit overrun reports "Time limit was exceeded" instead of hanging. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: bound lifecycle timeouts with a time limit, not a 5 s wall clock keepalivePingIsBoundedWhenNoPongArrives and handshakeTimeoutOptionBoundsTheWholeHandshake failed on PR #22's macOS job (Xcode 27) with `ContinuousClock.now - start < .seconds(5)` at ~5.7 s. A trivial test in the same window took 5.3 s, so the runner stalled; the timeouts under test were 50 ms and 100 ms. - keepalive ping: the socket never pongs, so the thrown URLError already proves the ping deadline ended the wait; a time limit catches a hang. - handshake option: the fallback budget is raised to an hour through _test_setConnectTimeoutSeconds, so a channel that ignored the 100 ms option would trip the time limit instead of finishing at the 30 s default. Checked with a scratch test that drops the option: it fails with "Time limit was exceeded" and does not hang. Both tests take .timeLimit(.minutes(1)) and pass while every cooperative thread is blocked for several seconds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: show the device-auth write leaves the actor free by ordering, not elapsed time issuedTokenPersistenceNeverBlocksTheChannelActor asserted the actor answered within 3 s while a token write waited on another connection's SQLite lock. A saturated test pool can stall the run for longer than that, and a time limit alone would miss the regression it guards (a write on the actor holds it for SQLite's 30 s busy timeout, less than the one-minute limit). The test now relies on ordering. It releases the lock only after the actor answers, so the token can reach disk only if the actor answered while the write was pending. A write on the actor fails with SQLITE_BUSY first, the token never lands, and the final wait trips the new suite time limit. The 200 ms sleep that let hello-ok reach the write is replaced by a DEBUG hook, _test_setDeviceTokenPersistenceStartedHandler, that fires on the actor just before the persistence hop. Once the hop starts, shutdown cannot stop it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: bound the FIFO, AsyncTimeout and link-preview tests with a time limit The three tests asserted wall-clock bounds (2 s, 5 s, 1 s) that a saturated test pool can overrun. Each now has a one-minute time limit instead, and each makes sure a regression ends on the limit's cancellation instead of hanging the run: - file fetch refuses a FIFO: a blocking open(2) never returns. The cancellation handler opens the FIFO's write end once to release it. - operationThatIgnoresCancellationStillTimesOut: the parked operation is a gate the test opens on cancellation (and afterwards), not a never-resumed continuation that a loser-joining race would wait on forever. - total deadline can fire before the session starts: URLSession's own timeouts move past the limit, so only the fetcher's zero-second deadline can end the fetch. The fetch is awaited through a no-deadline AsyncTimeout race, because a deadline lost before start would leave it unresumed even on cancellation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: drop wall-clock deadlines from the gateway and session-action waits gatewayCoreWaitUntil gave up after 10 s (15 s at two call sites) and the ChatViewModelSessionActionTests helpers after 15 s. A pool stall adds to those waits, so they could time out even though the condition was about to hold. Both now wait until the condition holds or the test is cancelled. Every suite that uses them has a one-minute time limit. gatewayCoreWaitUntil records its GatewayCoreWaitTimeout as an issue before throwing, because Swift Testing drops errors thrown after a time-limit cancellation and the label says which wait hung. waitForForkStart awaits the gate's stream directly instead of racing it against a sleep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: drop the wall-clock deadline from waitUntil waitUntil gave up after 15 s (7, 10 or 30 s at five call sites). The macOS CI job runs about 3,150 tests in parallel and the cooperative pool stalls for 5 to 8 s at a time, so a wait could time out even though the condition was about to hold. waitUntil now polls until the condition holds or the test is cancelled, and the timeoutSeconds and now: parameters are gone (nothing injected a clock, and with no deadline there is nothing to measure). Every suite whose tests reach waitUntil, directly or through a file-local helper, has a one-minute time limit; ChatViewModelSessionActionTests already had one. On cancellation the helper records AsyncWaitTimeoutError as an issue before throwing it, because Swift Testing drops errors thrown after a time-limit cancellation and the label says which wait hung. No call site relied on the timeout: nothing expects AsyncWaitTimeoutError, no wait runs in a child task that the test cancels, and the one try? wait is cleanup in a catch block that rethrows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(e2e): wait without a wall-clock deadline in the channel and reconnect tests ChannelAdaptersE2ETests.waitFor gave up after 15 s and only recorded an unlabelled expectation failure. reconnectFailureSchedulesAnotherAttempt polled against a 10 s deadline. A pool stall can outlast either one. waitFor now takes a label, polls until the condition holds or the test is cancelled, and records a WaitTimeout issue before throwing it. The reconnect loop polls until cancelled. The suite and the reconnect test each have a one-minute time limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: drop the 1 s deadline from the chat UI view-model waits OpenClawChatUITests had its own waitUntil that gave up after 1 s and returned false. On the macOS CI job for this PR, a pool stall of about 10 s ran past it: both view-model tests failed after about 12 s, and the bootstrap expectations that followed failed with them. The tests now call the shared deadline-free waitUntil with a label for each wait, and the suite has a one-minute time limit. The private helper is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: drop the wall-clock waits left in the provider and realtime relay tests - ProviderStreamingCancellationTests polls with the shared deadline-free waitUntil under a one-minute suite time limit. Both request timeouts are now an hour, so a cancellation that never reaches the request fails as a hang instead of passing once the 60 s request timeout fires. - ModelRouterStreamingFallbackTests waits for the stream termination (and the two stream/generate starts) with waitUntil, under a suite time limit. - RealtimeTalkRelaySession._test_waitForStartupCancelled() drops its timeoutSeconds parameter. A closed session answers before any timer is armed; a zero timeout makes one that is not closed fail at once rather than after a 1 s wall-clock wait. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(linux): wait without a wall-clock deadline in the runtime tests - A shared TestAsyncHelpers.swift replaces AgentLoopHardeningTests' waitUntil (500 x 10 ms, recorded an unlabelled issue and returned). It polls until the test is cancelled, then records and throws AsyncWaitTimeoutError(label:). - GatewayServerTestHarness.collect (5 s) and Recorder.waitFor (5 s) take a label and wait until the time limit. The one deliberate negative wait now uses frames(_:arrivingWithinMs:), which returns what arrived; the old helper returned [] whenever its timer won, so that assertion could never fail. - ChannelAdaptersLinuxSmokeTests.poll (15 s), the SIWC loopback-page wait (10 s), the automation run waits (10 s) and the MCP list_changed waits (3 s + 2 s) use waitUntil. - addingAJobWakesTheSleepingLoop pushes the scheduler's idle cap to an hour through the new DEBUG hook CronScheduler._test_setMaximumSleepSeconds, so a missed wake hangs instead of racing the 60 s cap against the time limit. - The MCP list_changed test uses an hour request timeout and drops its "< 1.5 s" elapsed assertion: a stall behind another request now hangs. - .timeLimit(.minutes(1)) is on the nine suites that reach these waits and had no limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: drop the bounded polls, elapsed bounds and positive-path timeouts #26 left three kinds of wall-clock bounds in the test waits. A stalled cooperative pool (5-8 s on the macOS CI job) could fail any of them. - Iteration-count sleep polls now use waitUntil, under a one-minute time limit. The Task.yield loops before negative checks, the sampler repetitions and the media teardown grace are kept. - Elapsed "< N s" asserts are replaced by what they stood for: error payloads that name the short deadline, kill checks, and a stalled server or run that outlives the time limit. Lower bounds and the synchronous cron search are kept. The throttle drop and delay checks use an hour-long window, so a stall between the two requests cannot let the window pass. - Positive-path product timeouts move past the time limit where the wait ends on cancellation (SIWC signIn, runtime.run). Waits that park on a continuation cancellation never resumes (runtime.wait, agent.wait, the brokers, A2ATaskStore, imsg) go through the new awaitCancellable(_:) with no timeout. - The stale-timer test retries until its first run beats its 200 ms timer. The run-id collision test holds its run on a gate instead of a 3 s sleep. The coalescing reporter test uses a frozen clock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: wait for agent runs without a positive-path timeout #29 left runtime.wait and agent.wait calls with 2-5 s timeouts in the agent gateway, session branch, loop, event stream and wire shape tests, and in the OpenClawKitTests stack, gateway server and registry tests. A stalled cooperative pool (5-8 s on the macOS CI job) can let that timer beat the run and turn a passing wait into a "timeout". Both waits park on a continuation that cancellation never resumes, so they now go through awaitCancellable(_:) with no timeout, under a one-minute time limit. OpenClawKitTests gets its own copy of the helper. The client-side request timeout on the SDK gateway test's agent.wait moves past the limit too. The intended short timeouts (timeoutMs 1 and 10) are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): 2026.3.2 Date the release 2026-10-01 and move the ConfigBoxed and config-decode stack entries under it. Add a Tests section for the test-wait hardening (#22, #25, #26, #29, #30), the per-test synthesizer (#23), the reply clock (#27) and the stack-depth tests (#28), with the release test counts and CI gates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
On
mainat cd49cd2, CI run 36644330107 ("macOS Build and Tests (Xcode 27)", attempt 1) failed two tests after ~8 s withCaught error: CancellationError():anotherAgentsRunBeforeTheAckNeverHijacksTheIntent()(line 50)preAckEventsOfTheAckedRunAreReplayed()(line 81)The same tree (39c1e22) passed on #20.
The file-private
waitForSend(_:)polledHeldChatSendRequester.idempotencyKeyevery 5 ms and threwCancellationError()once a 5 sContinuousClockdeadline had passed. About 3,150 Swift Testing tests run in parallel and saturate the cooperative pool. Under that load, thehost.sendtask, and the poller itself, did not get a thread for more than 5 s, so the deadline expired. Nothing inGatewayOpenClawIntentHost.sendis slow.What
Waits now finish when an event arrives, not when a wall-clock deadline passes. Hang protection comes from
.timeLimit(.minutes(1)).OpenClawAppIntentsRunMatchingTests:HeldChatSendRequesteryields eachchat.send's params to anAsyncStreamas soon as the request arrives, andwaitForSendawaits that stream. Test assertions are unchanged.GatewayNetworkConnectionTransportTests: this is the only otheradvanced(by: .seconds(5))hit. Starting the loopbackNWListenernow waits on itsstateUpdateHandler(.ready/.failed/.cancelled) instead of pollinglistener.stateagainst a 5 s deadline. The old loop had no final check after the deadline, so a late wake-up threwURLError(.timedOut)even when the listener was already ready.WatchNodeClientTests:eventually()used the same 5 s poll-with-deadline, written asContinuousClock.now + timeout, so the grep missed it. Its conditions read production client state (isConnected,state,voiceAccess()) that has no change hook, so it still polls, but without a deadline. A slow run now takes longer instead of failing.All three suites get
.timeLimit(.minutes(1)). Every wait stops when its task is cancelled, so going over the limit reportsTime limit was exceededinstead of hanging the job.GatewayChannelLifecycleTests(added after this PR's first CI run):keepalivePingIsBoundedWhenNoPongArrivesandhandshakeTimeoutOptionBoundsTheWholeHandshakefailed withContinuousClock.now - start < .seconds(5)at about 5.7 s. A trivial test in the same window took 5.3 s. Both now use@Test(.timeLimit(.minutes(1)))instead of the elapsed-time bound.URLErroralready proves the 50 ms deadline fired._test_setConnectTimeoutSeconds. A channel that ignored the 100 ms option would then trip the time limit instead of quietly finishing at the 30 s default.Not changed:
gatewayCoreWaitUntil(10 s, with a final check) and theChatViewModelSessionActionTestswait helpers (15 s). Both deadlines are well above the ~8 s stall seen in this run.GatewayDeviceAuthOffActorTests(< 3 s),FileTransferNodeCommandsTests(< 2 s),AsyncTimeoutRaceTests(< 5 s) andChatLinkPreviewTests(< 1 s). They are left for a follow-up because each needs its own discriminator.Verification (macOS, Xcode 27)
anotherAgentsRunBeforeTheAckNeverHijacksTheIntentandpreAckEventsOfTheAckedRunAreReplayedfail withCaught error: CancellationError()at lines 50 and 81, the same as CI.chat.sendnever arrives fails withTime limit was exceeded: 60.000 secondsinstead of hanging.swift test --filter OpenClawAppIntentsRunMatchingTestsswift test --filter "GatewayNetworkConnectionTransportTests|WatchNodeClientTests"handshakeTimeoutMsfails withTime limit was exceededand does not hang, so the test still catches the regression it guards.swift testruns 582 + 3,150 + 20 tests.Scripts/lint-swift.shreports 0 violations.🤖 Generated with Claude Code