stop asking what was playing, and say what the socket is doing
Two things. The music now-playing restore is disabled on the web. The music sidecar is not running on every machine that serves this app, so every page load fired /music/now-playing and logged a 503 in the console of a browser that was not there for music. Restoring a paused track is a nicety; a permanent error on every load of every screen is not. The player is untouched — it simply no longer asks what WAS playing. And the chat socket now logs its own lifecycle: create, open, close with code and whether it was stale or tearing down, every message received, and every message sent or queued with the socket readyState. window.__officerWs = false turns it off. This is instrumentation I should have added two rounds ago. A pane connects and then sits silent, and I have now reasoned from this hook source three times without explaining it — the browser says a socket closed and never says who closed it or whether the message left. The handover doc says instrument before theorising and I did not follow my own note. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -43,10 +43,19 @@ export const useChatWebSocket = ({ url, onMessage, onOpen }: UseChatWebSocketPar
|
||||
const socket = new WebSocket(url);
|
||||
socketRef.current = socket;
|
||||
|
||||
// Temporary, and deliberately loud. A pane connected and then sat silent, and reasoning from this
|
||||
// hook's source three times running did not explain it — the console says a socket closed, never who
|
||||
// closed it or whether the message went out. `window.__officerWs = false` turns it off.
|
||||
const host = new URL(url).host;
|
||||
const log = (what: string, extra?: unknown) =>
|
||||
(window as any).__officerWs !== false && console.log(`[ws ${host}] ${what}`, extra ?? '');
|
||||
log('creating');
|
||||
|
||||
socket.addEventListener('open', () => {
|
||||
if (socketRef.current !== socket) return;
|
||||
setIsConnected(true);
|
||||
retryRef.current = 0;
|
||||
log('OPEN');
|
||||
// BEFORE onOpen, deliberately: onOpen sends the resume/attach handshake, and anything the user
|
||||
// typed while connecting belongs after that, not in front of it.
|
||||
const queued = pendingRef.current;
|
||||
@@ -57,6 +66,7 @@ export const useChatWebSocket = ({ url, onMessage, onOpen }: UseChatWebSocketPar
|
||||
|
||||
socket.addEventListener('message', (ev) => {
|
||||
try {
|
||||
log('recv', String(ev.data).slice(0, 120));
|
||||
const data = JSON.parse(typeof ev.data === 'string' ? ev.data : new TextDecoder().decode(ev.data));
|
||||
onMessageRef.current(data);
|
||||
} catch {
|
||||
@@ -64,7 +74,13 @@ export const useChatWebSocket = ({ url, onMessage, onOpen }: UseChatWebSocketPar
|
||||
}
|
||||
});
|
||||
|
||||
socket.addEventListener('close', () => {
|
||||
socket.addEventListener('close', (ev) => {
|
||||
log('close', {
|
||||
code: ev.code,
|
||||
reason: ev.reason,
|
||||
stale: socketRef.current !== socket,
|
||||
tearingDown: isCleaningUpRef.current,
|
||||
});
|
||||
if (isCleaningUpRef.current) return;
|
||||
if (socketRef.current !== socket) return;
|
||||
setIsConnected(false);
|
||||
@@ -123,9 +139,12 @@ export const useChatWebSocket = ({ url, onMessage, onOpen }: UseChatWebSocketPar
|
||||
const socket = socketRef.current;
|
||||
const message = JSON.stringify(data);
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
if ((window as any).__officerWs !== false) console.log(`[ws ${new URL(url).host}] send`, message.slice(0, 120));
|
||||
socket.send(message);
|
||||
return;
|
||||
}
|
||||
if ((window as any).__officerWs !== false)
|
||||
console.log(`[ws ${new URL(url).host}] QUEUED (socket ${socket?.readyState ?? 'none'})`, message.slice(0, 80));
|
||||
// Not open yet, or reconnecting. Hold it rather than dropping it — see `pendingRef`. Bounded so a
|
||||
// socket that never comes back cannot grow this without limit; the oldest go first, because the
|
||||
// newest message is the one the user is still waiting on.
|
||||
|
||||
@@ -119,7 +119,16 @@ export const MusicPlayerHost = () => {
|
||||
|
||||
// Restore the saved "currently playing" on first load — paused, at its position — so a reload/return
|
||||
// lands back on the track. Skipped when a queue already exists (an in-app nav kept player state).
|
||||
//
|
||||
// DISABLED on the web (Andre, 2026-08-10). The music sidecar is not running on every machine that
|
||||
// serves this app, so every page load fired `/music/now-playing` and logged a 503 in the console of a
|
||||
// browser that was not there for music at all. Restoring a paused track is a nicety; a permanent error
|
||||
// on every load of every screen is not worth it. The player itself is untouched — play something and
|
||||
// it works; it simply no longer asks what WAS playing.
|
||||
const RESTORE_NOW_PLAYING = false;
|
||||
|
||||
useEffect(() => {
|
||||
if (!RESTORE_NOW_PLAYING) return;
|
||||
if (restoredRef.current) return;
|
||||
restoredRef.current = true;
|
||||
if (queue.length) return;
|
||||
|
||||
Reference in New Issue
Block a user