Skip to content

Expose post-run exported-global snapshot + a witness-harness mode (coverage backend for witness MC/DC) #340

Description

@avrabe

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)

  1. 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.

  2. 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.)

  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions