fix(engines): stop a signal-killed child reporting success#37
Merged
ralyodio merged 1 commit intoJul 26, 2026
Merged
Conversation
runCmd resolves { ok: true, code: null, signal } when a child dies on a
signal, but the three status checks that consume it treated a null code as
a clean exit:
r.ok && (r.code == null || r.code === 0)
So an installer killed by the OOM killer, a timeout wrapper's SIGTERM, or
Ctrl-C was reported as success. `moshcode upgrade` printed "up to date"
and counted it in "upgraded N/N"; `skill install` printed "installed";
`mcp add` printed "added". All three then exited 0, so a wrapper script
could not tell the difference either.
Adds ranOk() and exitReason() next to runCmd and uses them at the three
sites, so success now means "exited 0" and nothing else. The results also
carry `signal`, and the failure lines name it, so a killed child says
"failed (SIGKILL)" instead of an unexplained "failed".
Clean exits and non-zero exits behave exactly as before.
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.
The bug
runCmdresolves{ ok: true, code: null, signal }when a child dies on a signal — Node puts the signal name insignaland leavescodenull. Three places that consume it treat a null code as a clean exit:src/upgrade.mjs:128—const ok = r.ok && (r.code == null || r.code === 0)src/skills.mjs:69— same expression, status"installed"src/mcp.mjs:115— same expression, status"added"So a child killed by the OOM killer, a timeout wrapper's SIGTERM, or Ctrl-C reads as success.
moshcode upgradeprints✓ up to dateand counts it in✓ upgraded N/N;skill installprintsinstalled;mcp addprintsadded. All three then exit 0, so a wrapper script cannot tell the difference either.Reproduced on
mainbefore touching anything:The failure lines are unhelpful too: with
code == nulland noerror,upgradeprints a bare✗ … upgrade failedandintegrations.mjsprints a barefailed, with nothing saying what happened.The fix
Adds two small helpers next to
runCmdinsrc/engines.mjs:ranOk(r)—r.ok && r.code === 0. Success now means "exited 0" and nothing else.exitReason(r)—"code 128","SIGKILL", or the spawn error message.Used at the three status sites. Results now also carry
signal, and the failure lines name it, so a killed child readsfailed (SIGKILL)instead of an unexplainedfailed.Clean exits and non-zero exits behave exactly as before. Display sites that already handled signals (
bin/moshcode.mjs,src/tui.mjs,src/cli.mjs,src/commands.mjs) are untouched.Tests
3 added: 2 in
test/engines.test.mjs(one spawns a realbash -c 'kill -9 $$'and asserts the whole{ok, code, signal}shape, one covers clean exit / bad code / ENOENT), 1 each intest/skills.test.mjsandtest/mcp.test.mjs.Verified fail-before / pass-after by stashing only
src/:src: 129 pass / 3 failmain)