Why
witness (MC/DC for Wasm) instruments core modules, adding exported mutable globals __witness_counter_<id>, and reads coverage back by snapshotting those globals after a run. It can do this for cores it runs under its embedded preview1 runtime, but not for Component-Model artifacts (wasm32-wasip2/p3): a component must run through a runtime's component API, and wasmtime's component::Instance gives no way to read the inner core's globals.
kiln is the opportunity here: we own it, it already does Component-Model decode/instantiation + WASI 0.2, it's no_std/deterministic/formally-verified (a certifiable coverage runtime is on-thesis), and it already reads globals internally (kiln-instructions … get_global, kiln-runtime/global.rs). Exposing that outward makes kiln a witness coverage backend — and lets us run the same instrumented artifact under kiln and wasmtime to differentially cross-check (kiln validated against wasmtime as oracle while it matures). Strategy tracked in pulseengine/witness#110.
The ask (concrete)
-
Post-run exported-global snapshot API. After running a module/component, read the values of exported globals by name (or a snapshot of all __witness_counter_*). The internal machinery exists (get_global by index + export info); this needs an outward, post-run accessor keyed by export name, reachable from a host driver. Return type: name → integer value.
-
Harness/CLI mode usable by witness. A kilnd invocation (or library entry) that witness's --harness can call: it receives WITNESS_MODULE / WITNESS_MANIFEST / WITNESS_OUTPUT env vars, instantiates + runs the module (invoking the specified export(s)), then writes the counter snapshot (step 1) to WITNESS_OUTPUT in witness's counter-snapshot JSON shape. (Happy to share that shape.)
-
Coverage-grade imports. For coverage we need imports satisfied only enough that branches execute — functionally-correct WASI is not required; stubbed/no-op wasi:* is acceptable. Flag which wasi:0.2 interfaces are real vs. need stubbing for a syscall-heavy fused core.
Scope / non-goals (for now)
- p3 async-lift / streams are out of scope here — that's gated on p3 execution maturity (kiln parses
stream<>/future<> types but doesn't execute async-lift tasks yet; nobody really does). Tracked separately as witness REQ-057 / witness#107(B).
- This is about synchronous Component-Model coverage first.
How witness uses it
meld fuse component.wasm -o core.wasm # leaf/syscall-heavy → single core
witness instrument core.wasm -o inst.wasm # adds __witness_counter_* globals
witness run inst.wasm --harness 'kilnd …' # kiln runs it, dumps counters
# witness reconstructs the MC/DC truth tables
Refs: pulseengine/witness#110 (strategy + dual-runtime cross-check), witness DEC-003 (counter mechanism), witness REQ-054/FEAT-034 (meld composition).
🤖 Drafted with Claude Code
Why
witness (MC/DC for Wasm) instruments core modules, adding exported mutable globals
__witness_counter_<id>, and reads coverage back by snapshotting those globals after a run. It can do this for cores it runs under its embedded preview1 runtime, but not for Component-Model artifacts (wasm32-wasip2/p3): a component must run through a runtime's component API, and wasmtime'scomponent::Instancegives no way to read the inner core's globals.kiln is the opportunity here: we own it, it already does Component-Model decode/instantiation + WASI 0.2, it's
no_std/deterministic/formally-verified (a certifiable coverage runtime is on-thesis), and it already reads globals internally (kiln-instructions … get_global,kiln-runtime/global.rs). Exposing that outward makes kiln a witness coverage backend — and lets us run the same instrumented artifact under kiln and wasmtime to differentially cross-check (kiln validated against wasmtime as oracle while it matures). Strategy tracked in pulseengine/witness#110.The ask (concrete)
Post-run exported-global snapshot API. After running a module/component, read the values of exported globals by name (or a snapshot of all
__witness_counter_*). The internal machinery exists (get_globalby index + export info); this needs an outward, post-run accessor keyed by export name, reachable from a host driver. Return type: name → integer value.Harness/CLI mode usable by witness. A
kilndinvocation (or library entry) that witness's--harnesscan call: it receivesWITNESS_MODULE/WITNESS_MANIFEST/WITNESS_OUTPUTenv vars, instantiates + runs the module (invoking the specified export(s)), then writes the counter snapshot (step 1) toWITNESS_OUTPUTin witness's counter-snapshot JSON shape. (Happy to share that shape.)Coverage-grade imports. For coverage we need imports satisfied only enough that branches execute — functionally-correct WASI is not required; stubbed/no-op
wasi:*is acceptable. Flag whichwasi:0.2interfaces are real vs. need stubbing for a syscall-heavy fused core.Scope / non-goals (for now)
stream<>/future<>types but doesn't execute async-lift tasks yet; nobody really does). Tracked separately as witness REQ-057 / witness#107(B).How witness uses it
Refs: pulseengine/witness#110 (strategy + dual-runtime cross-check), witness DEC-003 (counter mechanism), witness REQ-054/FEAT-034 (meld composition).
🤖 Drafted with Claude Code