Skip to content

[draft] fix(agent_subrun): launch a wasm engine through moonrun - #1292

Open
bobzhang wants to merge 1 commit into
mainfrom
fix/subrun-wasm-launcher
Open

[draft] fix(agent_subrun): launch a wasm engine through moonrun#1292
bobzhang wants to merge 1 commit into
mainfrom
fix/subrun-wasm-launcher

Conversation

@bobzhang

@bobzhang bobzhang commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to #1290. With openseek 0.3.1 on mooncakes, moonx bobzhang/openseek/cmd/openseek run '...' works — but only for flat runs. Anything that spawns a child engine died at spawn:

explore produced no report (Failed("subrun spawn failed:
  OSError(\"@process.spawn(): Permission denied\")"))

Cause

self_executable() (cmd/openseek/subrun.mbt) resolves the child from argv[0]. On a wasm build that is the .wasm module itself, which no OS will exec.

Affected: the model-callable explore, review, and subtask tools, and the --review-gate audit — every path that reaches @agent_subrun.run_subrun. The parent turn itself worked fine, which is why it was invisible to CI: the wasm row never spawned a child.

Not affected: --concurrency best-of-N (its attempts are in-process tasks on the same task group, not child engines) and the openseek review subcommand (in-process, no spawn).

Fix

The module needs its runtime in front of it — moonrun <module> -- <args...>, the same shape moonx used to start the parent. The rewrite goes in run_subrun, once, right after the argv is fully assembled (after the --session append, so the fence lands in the right place).

Doing it at that choke point fixes all four callers without touching a signature. self_exe stays a String, agent_explore's public definition is unchanged, and there is no .mbti drift — so openseek_tui and any other consumer of the just-published 0.3.1 are unaffected.

On naming the runtime rather than discovering it

Worth being explicit, since sniffing a file extension is not usually the right instinct.

A host normally hands a program a way back to itself — process.execPath (Node), sys.executable (Python), RbConfig.ruby (Ruby) — and self-respawn is supposed to go through that API. wasm has no equivalent: a module cannot ask who is hosting it. There is also nothing to choose between here, because moonrun is the only host that supplies the FFI this binary needs (process spawn, sockets, filesystem) — a bare WASI runtime could not have run the parent either, so it can never be the answer.

The suffix test reads the child path, not the parent's own backend. That keeps a native parent pointed at a wasm engine working, and leaves the scripted test children (sh, absolute paths, bare names) untouched.

Verification

Before, on the wasm build — three failed delegations. After:

{"event":"subrun_started","id":"sr-1","kind":"explore",...}
{"event":"subrun_finished","id":"sr-1","status":"captured","steps":16,...}
{"event":"tool_result","tool_name":"explore","is_error":false,...2 citations...}
  • 5 new whitebox tests over launch_argv cover the rewrite, the empty-argv fence, native passthrough, scripted/PATH children, and .wasm as suffix-only (/build/wasm/openseek and openseek.wasm.bak must not trigger).
  • moon test -p bobzhang/openseek/agent_subrun — 33/33 on both native and wasm.
  • moon check, moon check --target wasm, moon fmt --check all clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Xp3xk2xvGLmknC4eCFW3yX

Every sub-run under `moonx` died at spawn:

    explore produced no report (Failed("subrun spawn failed:
      OSError(\"@process.spawn(): Permission denied\")"))

`self_executable()` resolves the child from argv[0], which on a wasm build is
the `.wasm` module itself. No OS will `exec` that, so the `explore`, `review`,
and `subtask` tools and the `--review-gate` audit all failed while the parent
turn worked fine — the failure only shows up on a backend CI never spawned
children on. (`--concurrency` best-of-N is unaffected: its attempts are
in-process tasks, not child engines.)

The module needs its runtime in front of it, `moonrun <module> -- <args...>`,
which is the shape `moonx` used to start the parent. Doing that rewrite in
`run_subrun`, once, just after the argv is fully assembled, fixes every caller
without touching a signature: `self_exe` stays a `String` and `agent_explore`'s
public `definition` is unchanged, so nothing downstream (`openseek_tui`) breaks.
There is no .mbti drift.

The runtime is named rather than discovered. Hosts normally hand a program a
way back to themselves — `process.execPath`, `sys.executable`, `RbConfig.ruby`
— and self-respawn goes through that; wasm has no equivalent, and `moonrun` is
the only host supplying the FFI this binary needs, so there is nothing to
choose between. The suffix test reads the child path rather than the parent's
own backend, which keeps a native parent pointed at a wasm engine working and
leaves the scripted test children alone.

Verified on the wasm build: explore now reports
`subrun_finished status="captured"` with citations, where it previously failed
three times.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xp3xk2xvGLmknC4eCFW3yX
@bobzhang
bobzhang force-pushed the fix/subrun-wasm-launcher branch from 49f5ab5 to dde5e82 Compare September 5, 2026 08:22
@bobzhang bobzhang changed the title fix(agent_subrun): launch a wasm engine through moonrun [draft] fix(agent_subrun): launch a wasm engine through moonrun Sep 5, 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.

1 participant