In arcp-client/src/main/java/dev/arcp/client/WebSocketTransport.java the connect static factory around line 50 creates the WebSocket listener and starts the buildAsync call, then waits for the buildAsync future to complete, then constructs the WebSocketTransport wrapper, and only afterward calls futureSocket.set(transport) around line 108. The listener body inside buildAsync references futureSocket.get() for every onText, onClose, and onError event — and the listener may begin firing immediately after onOpen runs and requests a frame. If the server sends an envelope before the assembly completes and the AtomicReference is published, futureSocket.get() returns null, the onText handler silently drops the frame, and the envelope is lost. The same null check makes onClose and onError silently no-op during the same window. With a fast-handshaking server (especially the in-process MemoryTransport or a local Jetty), the first session.welcome envelope can be the one that gets dropped, leaving the client hanging on connect().get(...) forever.\n\nFix prompt: Refactor arcp-client/src/main/java/dev/arcp/client/WebSocketTransport.java connect so that the WebSocketTransport instance is constructed before builder.buildAsync is invoked, and the listener captures that instance directly via final reference instead of through an AtomicReference holder. Because the WebSocketTransport currently requires the WebSocket itself in its constructor, change the field private final WebSocket socket to non-final and add a package-private attachSocket(WebSocket) setter that runs once after buildAsync completes; meanwhile buffer inbound frames into the SubmissionPublisher whether or not the socket has been attached (the listener never needs the WebSocket reference for read, only for write). For send safety, gate send(Envelope) on the socket being attached (throw IllegalStateException with a clear message if called pre-attach). Add a regression test using a stub WebSocketListener that fires onOpen + onText immediately and asserts the inbound publisher receives the frame even when delivery beats the buildAsync future resolution.
In arcp-client/src/main/java/dev/arcp/client/WebSocketTransport.java the connect static factory around line 50 creates the WebSocket listener and starts the buildAsync call, then waits for the buildAsync future to complete, then constructs the WebSocketTransport wrapper, and only afterward calls
futureSocket.set(transport)around line 108. The listener body inside buildAsync referencesfutureSocket.get()for every onText, onClose, and onError event — and the listener may begin firing immediately after onOpen runs and requests a frame. If the server sends an envelope before the assembly completes and the AtomicReference is published,futureSocket.get()returns null, the onText handler silently drops the frame, and the envelope is lost. The same null check makes onClose and onError silently no-op during the same window. With a fast-handshaking server (especially the in-process MemoryTransport or a local Jetty), the first session.welcome envelope can be the one that gets dropped, leaving the client hanging on connect().get(...) forever.\n\nFix prompt: Refactor arcp-client/src/main/java/dev/arcp/client/WebSocketTransport.java connect so that the WebSocketTransport instance is constructed beforebuilder.buildAsyncis invoked, and the listener captures that instance directly via final reference instead of through an AtomicReference holder. Because the WebSocketTransport currently requires the WebSocket itself in its constructor, change the fieldprivate final WebSocket socketto non-final and add a package-privateattachSocket(WebSocket)setter that runs once after buildAsync completes; meanwhile buffer inbound frames into the SubmissionPublisher whether or not the socket has been attached (the listener never needs the WebSocket reference for read, only for write). For send safety, gatesend(Envelope)on the socket being attached (throw IllegalStateException with a clear message if called pre-attach). Add a regression test using a stub WebSocketListener that fires onOpen + onText immediately and asserts the inbound publisher receives the frame even when delivery beats the buildAsync future resolution.