WASM host qualification (internal)

The internal, experimental mck wasm-host commands that run the crafted WASM host cases against a host’s worker mode

WASM host qualification

Product morphir mck ... forwards unchanged to the separately installed morphir-mck. Neither product executable embeds a compatibility runner. The WASM host adapter is retained as development source awaiting adoption by a dedicated tool, alongside the generic-kit work in finos/morphir#1154. The historical runner commands below need that migration before they can run again. The worker remains in the product as morphir extension worker. Reports contain observations only; qualification remains blocked on parent normative task 1.4. The adapter reference is WASM host runner adapter.

What runs where

PartLocation
The cases (generated Gherkin)conformance/suites/wasm-host/, from tests/Fixtures/MepWasm/wasm-host.cases.json
The worker adapter, comparer, judge, report and checksrc/Morphir.Conformance/WasmHost/ (Worker.fs, Comparer.fs, Judge.fs, HostRunner.fs, HostReport.fs, Commands.fs)
The commandssrc/Morphir.Tooling/Features/Mck/
The tests, with a scripted fake workertests/Morphir.Conformance.Tests/, tests/Morphir.Conformance.FakeWorker/
The generic guest kit’s model tier through a worker (no command)src/Morphir.Conformance/GuestKit/WorkerSessions.fs, tests/Morphir.E2E.Tests/Features/ExecutableGuestKitModelTierTests.cs

The .NET worker

morphir extension worker is the worker mode of this repository’s host, in both morphir and dotnet-morphir. It is internal and experimental, and it exists for one purpose: so that a runner can drive the host’s WASM runtime (src/Morphir.Extensions.Wasm) with crafted modules over stdio, the way it drives the MoonBit worker. Product commands do not use it: a command that runs a WASM extension opens a HostSession on the same WasmChannel in process. The reference is morphir extension worker.

PartLocation
Arguments, help and defaultssrc/Morphir.Tooling/Features/Extensions/WorkerInvocation.cs
The relay, the start-up order and the exit statusessrc/Morphir.Tooling/Features/Extensions/ExtensionWorker.cs, WorkerMessage.cs
The stderr eventssrc/Morphir.Tooling/Features/Extensions/WorkerReport.cs
The build identity, and the engine identity it coversWorkerBuildIdentity.cs, WasmRuntimeIdentity.cs
Stdio, Ctrl+C and the private directoryExtensionWorkerProcess.cs, called from MorphirApplication.Run, where every entry point starts, before the CLI host is built
Service teststests/Morphir.Tooling.Tests/Features/Extensions/ExtensionWorker*Tests.cs
Process tests, both executablestests/Morphir.E2E.Tests/Features/ExecutableExtensionWorker.*

The worker relays messages; it is not a second MEP host (design WRK-2). It does not negotiate, and it looks inside a message only for its id and method. Its events and exit statuses follow the pinned contract’s worker section. When the contract and the MoonBit worker’s README differ, the contract wins.

To run the end-to-end tests against each executable, build src/Morphir, src/Morphir.Managed and src/Morphir.Tool in Release and run the E2E assembly once for each: with no variable for the Native AOT morphir in artifacts/executables/<rid>/, with MORPHIR_EXECUTABLE_PATH set to src/Morphir/bin/Release/net10.0/morphir or src/Morphir.Managed/bin/Release/net10.0/morphir (.exe on Windows), and with MORPHIR_EXECUTABLE_TYPE=tool. MORPHIR_WORKER_CORPUS=all drives every case of the pinned corpus through the worker in ExecutableExtensionWorkerTests, not only the sample. On Windows the Ctrl+C and Ctrl+Break tests send the console control event through a hidden Windows PowerShell helper that attaches to the worker’s console; no window opens.

Running it

The commands in this section record the former embedded adapter invocation. A product build no longer supplies the runner. The dedicated development entry point must replace the runner path in these examples; it may drive any host executable independently.

dotnet build src/Morphir/Morphir.csproj -c Release

Against a .NET executable, here the Native AOT morphir published for win-arm64:

src/Morphir/bin/Release/net10.0/morphir mck wasm-host run --kit conformance/suites/wasm-host \
  --host artifacts/executables/win-arm64/morphir.exe \
  --offering native-aot --platform win-arm64 --engine "wasmtime 50.0.1" \
  --report .dev/wasm-host-native-aot.json
src/Morphir/bin/Release/net10.0/morphir mck wasm-host report-check --kit conformance/suites/wasm-host \
  --report .dev/wasm-host-native-aot.json

Against the MoonBit native CLI, built from finos/morphir-moonbit as its worker README says:

src/Morphir/bin/Release/net10.0/morphir mck wasm-host run --kit conformance/suites/wasm-host \
  --host /path/to/moonbit/_build/native/release/build/finos/morphir/morphir.exe \
  --host-profile morphir-bytes-v1 --offering native-cli --platform win-x64 \
  --engine "moonrockz/extism 0.3.0 (WAMR)" --report .dev/wasm-host-moonbit.json
src/Morphir/bin/Release/net10.0/morphir mck wasm-host report-check --kit conformance/suites/wasm-host \
  --report .dev/wasm-host-moonbit.json

The two command lines differ in the host path, the three identity inputs (--offering, --platform, --engine) and --host-profile. Nothing else changes, and the runner sends both hosts the same worker arguments. Give --host-profile morphir-bytes-v1 only for a host that runs the byte profile. The .NET host runs extism-kernel-v1 only, so its ten byte-profile cases are not_applicable. Take the engine from extension runtimes --json of the .NET executable you drive, and the offering from the four names of add-deployment-and-acquisition (native-aot, self-contained, framework-dependent, dotnet-tool). A development build is recorded under the offering it would ship as. The platform is the host executable’s, so the x64 MoonBit build under emulation on Windows ARM64 is win-x64.

On a Windows ARM64 machine a run against a .NET executable takes 6 to 12 seconds. The Wasmtime fuel budget ends the spinning Extism guest after about 3 seconds. A run against the MoonBit CLI takes about 40 seconds, mostly waiting for the 10-second deadline of the byte profile and for its instruction budget to run out after about 25 seconds.

The generic guest kit’s model tier through the worker

The generic guest kit (tests/Fixtures/MepGuestKit/) has its own runner. Its model tier runs as a library, ModelTierRunner.RunAsync in src/Morphir.Conformance/GuestKit/ModelTier.fs, which asks its caller for a HostSession for each session. WorkerSessions (src/Morphir.Conformance/GuestKit/WorkerSessions.fs) starts a worker for each of those sessions with the corpus’s worker arguments, <host> extension worker --abi extism-kernel-v1 --module <wasm> --build-identity <id> --json. It reads the build identity from the host’s --help --json, gives the runner a HostSession over the worker’s stdio and keeps the worker’s stderr events. EndAsync waits for each worker to exit, stops the process tree of one that does not, and reports each worker’s exit status, events and problems.

This is internal and experimental, and there is no command for it. The end-to-end test ExecutableGuestKitModelTierTests runs it for the published Python 0.6.0 guest against the executable the E2E run selects. It covers the model tier only. The host does not speak MEP 0.1, so the kit’s legacy tier is not run, and the result is not a complete run of the generic guest kit. The approved finos/morphir#1154 plan now owns the full 21-case runner and dedicated development entry point. The historical 16-step evidence is not a full kit pass.

A worker that has ended cannot be written to. A later request, or the morphir.exit of its session, then fails with an IOException that ModelTierRunner does not catch. EndAsync still reports the worker.

Reading a result

  • A failure names the matcher of the corpus’s conventions that failed (exitCode, stdoutBytes, stdoutFrames, events, noEvents, guestOutput, guestOutputIncludes, noGuestOutput, withinSeconds) or a convention check (started.moduleSha256, started.bounds, fault.members).
  • A run with alternatives passes on one alternative that applies to the host; the report names it in satisfied, and a failure names its alternative.
  • Record each difference as a runner defect, a contract question or a host defect before you compare another host. The runs against the MoonBit native CLI are in openspec/changes/add-wasm-extension-host/evidence/1.5-moonbit-run.md, and the runs against the four .NET executables in evidence/4.3-worker-runs.md.
  • The report holds no qualification verdict until parent task 1.4 defines one. Do not read the exit status of run as a qualification.