Skip to content

fix(moshscript): keep code in the result under --dry-run - #139

Merged
ralyodio merged 1 commit into
moshcoder:mainfrom
clawedassistant26:fix/dryrun-result-missing-code
Jul 31, 2026
Merged

fix(moshscript): keep code in the result under --dry-run#139
ralyodio merged 1 commit into
moshcoder:mainfrom
clawedassistant26:fix/dryrun-result-missing-code

Conversation

@clawedassistant26

Copy link
Copy Markdown
Contributor

The bug

runMoshcode() in src/cli.mjs is documented:

Returns { ok, code }always. A non-zero exit returns { ok: false, code } so scripts can branch on outcomes ... This is the R8 convention from PRD 0004.

and the shell() verb in src/commands.mjs carries its own version of the same promise:

Returns { ok, code } so scripts can branch on the exit status: const r = shell("npm test"); if (!r.ok) say("tests failed");

Under --dry-run neither actually returned code. Both returned { ok: true, dryRun: true }, so a script written to the documented convention read undefined and took the failure branch on a run where nothing was spawned and nothing failed.

PRD 0004 R8 [P0] requires "a documented convention ... MUST be specified so scripts can branch on outcomes", and R7 [P0] requires --dry-run to narrate instead of acting. Narrating is correct; changing the result shape while narrating is what broke the branch.

Reproducing it

On unmodified main (v0.13.2, 1d2f1e1), end-to-end through the real CLI:

// repro.mosh
const r = agents("claude");
notify("result keys: " + JSON.stringify(Object.keys(r)));
if (r.code !== 0) {
  notify("BRANCH TAKEN: treated as FAILURE (r.code = " + r.code + ")");
} else {
  notify("BRANCH TAKEN: treated as success");
}
$ node bin/moshcode.mjs run repro.mosh --dry-run
  ▶ agents(claude) → would run: moshcode agents claude
  🔔 notify()  → result keys: ["ok","dryRun"]
  🔔 notify()  → BRANCH TAKEN: treated as FAILURE (r.code = undefined)

After the fix, same command:

  🔔 notify()  → result keys: ["ok","code","dryRun"]
  🔔 notify()  → BRANCH TAKEN: treated as success

The asymmetry against real runs, which is the whole defect:

call result
real, success {"ok":true,"code":0}
real, failure {"ok":false,"code":1,"signal":null}
dry-run (before) {"ok":true,"dryRun":true}

The fix

code: 0 added to both dry-run returns. 5 insertions / 2 deletions of real change, the rest comment. Real runs are untouched and still report the true exit status. The dryRun: true flag stays, so anything keying off it keeps working.

Tests

New test/dryrun-result-code.test.mjs, 11 tests.

5 are the bug: runMoshcode carries a numeric code: 0 in dry-run; the documented .code !== 0 branch no longer misfires; shell() the same; a sweep over all 13 CLI verbs from the vocabulary; and one end-to-end through runScript asserting a dry run reports SUCCESS and never prints FAILURE.

6 are controls that pass both before and after, and deliberately assert the opposite direction so the fix cannot buy a passing .code by pretending everything succeeded or by quietly executing for real: a real success still reports code: 0; a real failure still reports a true non-zero code; shell("exit 3") in a real run still returns code: 3; dry-run still narrates and never spawns (both verbs); and dryRun: true is still set.

Fail-before verified by reverting src/cli.mjs and src/commands.mjs only: 5 fail / 6 pass unpatched, 11/11 patched.

Full suite 489 → 500, 0 failing. Baseline measured by moving the new test file out, not assumed.

The existing test/cli.test.mjs assertion "all CLI verbs return { ok: true, dryRun: true } in dry-run mode" still passes unchanged — it only ever checked ok and dryRun, never code, which is why this slipped through.

Scoped out

notify() returns bare { dryRun: true } under dry-run (no ok, no code), but its documented contract is { id, url } rather than the { ok, code } branch convention, so it is a different question and I left it alone. Say the word if you want it aligned too.

runMoshcode() is documented "Returns { ok, code } — always" and shell() as
"Returns { ok, code } so scripts can branch on the exit status", but both
dropped `code` on the --dry-run path. A script written to the documented
convention read `undefined` and took the failure branch on a dry run where
nothing was spawned and nothing failed.

Add `code: 0` to both dry-run returns. Real runs are untouched and still
report the true exit status.

Adds test/dryrun-result-code.test.mjs (11 tests).
@ralyodio
ralyodio merged commit 54402e8 into moshcoder:main Jul 31, 2026
3 checks passed
@ralyodio ralyodio mentioned this pull request Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants