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);
|
const socket = new WebSocket(url);
|
||||||
socketRef.current = socket;
|
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', () => {
|
socket.addEventListener('open', () => {
|
||||||
if (socketRef.current !== socket) return;
|
if (socketRef.current !== socket) return;
|
||||||
setIsConnected(true);
|
setIsConnected(true);
|
||||||
retryRef.current = 0;
|
retryRef.current = 0;
|
||||||
|
log('OPEN');
|
||||||
// BEFORE onOpen, deliberately: onOpen sends the resume/attach handshake, and anything the user
|
// BEFORE onOpen, deliberately: onOpen sends the resume/attach handshake, and anything the user
|
||||||
// typed while connecting belongs after that, not in front of it.
|
// typed while connecting belongs after that, not in front of it.
|
||||||
const queued = pendingRef.current;
|
const queued = pendingRef.current;
|
||||||
@@ -57,6 +66,7 @@ export const useChatWebSocket = ({ url, onMessage, onOpen }: UseChatWebSocketPar
|
|||||||
|
|
||||||
socket.addEventListener('message', (ev) => {
|
socket.addEventListener('message', (ev) => {
|
||||||
try {
|
try {
|
||||||
|
log('recv', String(ev.data).slice(0, 120));
|
||||||
const data = JSON.parse(typeof ev.data === 'string' ? ev.data : new TextDecoder().decode(ev.data));
|
const data = JSON.parse(typeof ev.data === 'string' ? ev.data : new TextDecoder().decode(ev.data));
|
||||||
onMessageRef.current(data);
|
onMessageRef.current(data);
|
||||||
} catch {
|
} 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 (isCleaningUpRef.current) return;
|
||||||
if (socketRef.current !== socket) return;
|
if (socketRef.current !== socket) return;
|
||||||
setIsConnected(false);
|
setIsConnected(false);
|
||||||
@@ -123,9 +139,12 @@ export const useChatWebSocket = ({ url, onMessage, onOpen }: UseChatWebSocketPar
|
|||||||
const socket = socketRef.current;
|
const socket = socketRef.current;
|
||||||
const message = JSON.stringify(data);
|
const message = JSON.stringify(data);
|
||||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
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);
|
socket.send(message);
|
||||||
return;
|
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
|
// 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
|
// 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.
|
// 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
|
// 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).
|
// 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(() => {
|
useEffect(() => {
|
||||||
|
if (!RESTORE_NOW_PLAYING) return;
|
||||||
if (restoredRef.current) return;
|
if (restoredRef.current) return;
|
||||||
restoredRef.current = true;
|
restoredRef.current = true;
|
||||||
if (queue.length) return;
|
if (queue.length) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user