Repository navigation
Description
Finding
PR #673 (fixing #672) reworked bin/muse-proxy.mjs's failure handling so failure is judged by the authoritative terminal state, and — when stderr is empty — the error detail names that terminal state instead of the misleading muse exec exited with code 0. The fallback expression it left behind (current main, bin/muse-proxy.mjs:404-409) has two remaining gaps in the same spot:
const failed = terminalState ? terminalState !== 'completed' : code !== 0;
if (failed) {
const stderrLine = stderrTail.trim().split('\n').pop();
const fallback = terminalState && terminalState !== 'completed'
? `muse run terminated with state '${terminalState}'`
: `muse exec exited with code ${code}`;1. Signal-killed runs report muse exec exited with code null
The close handler is child.on('close', (code, signal) => …) (line 367), but signal is never used. When the child dies by signal (OOM SIGKILL, SIGTERM, …), Node reports code === null, the run correctly counts as failed (code !== 0), but with empty stderr the detail becomes the literal string muse exec exited with code null — the same class of misleading detail #673 set out to eliminate for the terminal-state case.
Reproduced on current main (4a91edc3, merge of #673) with a stub muse that kills itself and prints nothing:
#!/bin/sh
kill -9 $$- Non-stream:
500 {"error":{"message":"Muse execution failed: muse exec exited with code null"}} - Stream: SSE error event with the same
code nullmessage, thendata: [DONE]
2. The empty-stderr terminal-state fallback added by #673 has no test coverage
Both failure stubs added by #671/#673 emit a stderr line (stub muse: run failed midway, stub muse: crashed midway), and the #672 test only asserts /Muse execution failed: stub muse:/ — so the stderrLine is always what satisfies the assertion and the new fallback string is never exercised.
Verified by ablation in a scratch copy: reverting only the fallback-detail lines to the pre-#673 form (stderrTail… || \muse exec exited with code ${code}`), while keeping #673's failure predicate, still passes the full suite 4/4 (node --test bin/muse-proxy.test.mjs). The behaviour itself works today — a stub emitting run.terminal.failedwith **no** stderr returnsMuse execution failed: muse run terminated with state 'failed'in both modes — but nothing pins it, so a future refactor can silently regress it back toexited with code 0`.
Evidence
- PR fix(muse-proxy): surface runs that fail after partial output as errors (#672) #673: fix(muse-proxy): surface runs that fail after partial output as errors (#672) #673 (commit
df1dd2502d98b2a7bf435e7a2789f49965354dbe, merged in4a91edc359063734da7d0b8a8dedbd2d49591216), diff hunk changing the failure predicate and detail fallback inbin/muse-proxy.mjs. - Current main:
bin/muse-proxy.mjs:367(close handler signature(code, signal)),:404-409(predicate + fallback quoted above). - Tests:
bin/muse-proxy.test.mjs—FAILPARTIAL/CRASHPARTIALstubs (lines 32-43) both write to stderr; the muse-proxy: a run that fails after producing partial output is reported as a successful 'stop' — failure is masked whenever any content exists #672 test's error assertions (lines 175, 188) match only the stderr text. - Live repro of both the
code nullmessage and the working-but-untested terminal-state message, run against current main as described above.
Why it matters
This proxy is the OpenAI-compatible surface other tools drive muse exec through; when a run dies, the error detail is often the only diagnostic a client (or a human reading its logs) gets. exited with code null sends debugging in the wrong direction (a signal kill — e.g. OOM — looks like a corrupt message), and the one fallback #673 deliberately improved can regress unnoticed because no test reaches it.
Acceptance criteria
- A run terminated by a signal with empty stderr and no terminal event produces an error detail that names the signal (e.g.
muse exec terminated by signal SIGKILL) instead ofexited with code null, in both stream (SSE error frame +[DONE]) and non-stream (500) modes. - A stub mode that emits a non-
completedterminal event with empty stderr is added tobin/muse-proxy.test.mjs, asserting the detail names the terminal state (e.g.terminated with state 'failed') in both modes. - A stub mode whose child dies by signal (e.g.
kill -9 $$) with empty stderr is added, asserting the signal-naming detail in both modes. -
node --test bin/muse-proxy.test.mjspasses, and ablating either fallback branch makes exactly the new assertions fail.
Activity