{"id":49248637,"url":"https://github.com/pulseengine/witness","last_synced_at":"2026-05-13T06:05:34.037Z","repository":{"id":353660090,"uuid":"1220380475","full_name":"pulseengine/witness","owner":"pulseengine","description":"MC/DC-style branch coverage for WebAssembly components","archived":false,"fork":false,"pushed_at":"2026-04-24T21:41:18.000Z","size":91,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-24T23:12:32.011Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://pulseengine.eu","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pulseengine.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-24T20:55:47.000Z","updated_at":"2026-04-24T21:37:55.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pulseengine/witness","commit_stats":null,"previous_names":["pulseengine/witness"],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/pulseengine/witness","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pulseengine%2Fwitness","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pulseengine%2Fwitness/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pulseengine%2Fwitness/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pulseengine%2Fwitness/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pulseengine","download_url":"https://codeload.github.com/pulseengine/witness/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pulseengine%2Fwitness/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32335297,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"online","status_checked_at":"2026-04-27T02:00:06.769Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-24T23:04:58.073Z","updated_at":"2026-05-13T06:05:34.028Z","avatar_url":"https://github.com/pulseengine.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# witness\n\n**MC/DC-style branch coverage for WebAssembly components.**\n\nwitness instruments a Wasm module with branch counters, runs a test harness\nagainst it, and emits a coverage report you can read, compare, or feed into\n[rivet](https://github.com/pulseengine/rivet) as requirement-to-test evidence\nand into [sigil](https://github.com/pulseengine/sigil) as an in-toto coverage\npredicate.\n\n\u003e **New here?** [`docs/quickstart.md`](docs/quickstart.md) walks the install\n\u003e through first MC/DC truth table in 10 minutes. [`docs/concepts.md`](docs/concepts.md)\n\u003e defines every term used in this README — *MC/DC, masking, unique-cause,\n\u003e br_if, post-codegen, polarity inversion, DSSE envelope, in-toto* — with\n\u003e a worked leap-year example. New users should start there.\n\n### Is this for you?\n\nWitness is **for** you if any of these match: you ship a Wasm module\ninto a regulated context (avionics, medical, automotive); you want\nto know which match arms / branches your test corpus actually\nexercises in the form the runtime executes; you want a signed\ncoverage envelope an auditor can trust; you want an MCP-callable\ntool surface so AI agents can close gaps end to end.\n\nWitness is **probably not** for you if you want line/statement\ncoverage on idiomatic Rust code in a non-regulated context (use\n[cargo-llvm-cov](https://github.com/taiki-e/cargo-llvm-cov) or\n[tarpaulin](https://github.com/xd009642/tarpaulin) instead — both\ndo a great job and witness deliberately doesn't try to replace them).\n\nThe argument for why this tool exists lives in two blog posts:\n\n- [Spec-driven development is half the loop](https://pulseengine.eu/blog/spec-driven-development-is-half-the-loop/)\n- [MC/DC for AI-authored Rust is tractable — the variant-pruning argument](https://pulseengine.eu/blog/variant-pruning-rust-mcdc/)\n\nShort version: pattern-matching, `?` desugaring, and cfg expansion have all\nresolved by the time a Wasm module exists. Coverage measured at the Wasm\nlevel describes what actually ships, against an instruction set small enough\nand formally specified enough that the tool-qualification story moves in your\ndirection. And the *\"post-preprocessor C\"* precedent — MC/DC measured on\nexpanded C rather than pre-expansion source, accepted by DO-178C since 1992 —\nis structurally the same move as *\"post-rustc Wasm\"*.\n\n## Status\n\n**v0.10.x is the current release line.** v0.6.x ratcheted from\n\"consumer-side schema only\" up to a complete signed-evidence\npipeline. v0.7.x added the trace-buffer primitive that lifts the\nper-row-globals limit on loop-bearing programs. v0.8.0 added\nchain-direction analysis + three new real-application fixtures.\nv0.9.0 shipped the visualiser, the MCP server, and the agent\ncontract. **v0.10.0 closes the signed-evidence chain end to end**\n(the MC/DC truth tables themselves are signed, not just the branch\nsummary; release tarballs are cosign-signed via GitHub OIDC) and\nadds the `docs/concepts.md` glossary that names the polarity\nconvention and the post-codegen view explicitly.\n\n### What witness measures (and what it doesn't)\n\nWitness counts branches **after rustc + LLVM finish lowering**. That\nmatters: rustc may *fuse* multiple source-level conditions into a\nsingle `br_if` chain, eliminate dead arms, or constant-fold the\npredicate to bitwise arithmetic when the inputs let it. The truth\ntables you see are **the post-codegen reality**, not the source\nshape. The scaffolded leap-year fixture is the canonical example:\n`(year % 4 == 0 \u0026\u0026 year % 100 != 0) || (year % 400 == 0)` is three\nboolean conditions in source; rustc fuses the third into the same\nchain, and witness reports two. It's correct — just measured at the\nlayer where the runtime actually executes. See\n[docs/concepts.md](docs/concepts.md) §3-§4 for the worked example\nand the polarity convention (the truth-table column `c0=T` records\nthe wasm `br_if` value, not the source-level condition value).\n\nThis is the same move DO-178C made for \"post-preprocessor C\" in\n1992: measure what the compiler emits, not what the engineer typed.\n\n### Stability contract — v0.10.x\n\n| Surface | Stability |\n|---|---|\n| Schema URLs (`witness-mcdc/v1`, `witness-coverage/v1`, …) | **Stable from v0.10.** Breaking changes bump the version path. |\n| CLI flags + subcommands | **Stable from v0.10** unless the CHANGELOG calls out an explicit deprecation. |\n| `witness-mcdc-checker` crate (qualifiable kernel) | **Stable from v0.10** — kept deliberately tiny so it can be audited. |\n| `RunRecord` / `Manifest` JSON shape | **Stable from v0.10** with serde aliases for v0.9.x field names (e.g. `ambiguous_rows` → `trace_parser_active`). |\n| Rust public API of `witness-core` and `witness-viz` | **Use at your own risk** until v1.0. Major bumps may change types. |\n| MCP wire (`/mcp` JSON-RPC) | Stable from v0.10. rmcp adoption deferred to v0.11+ if a real spec-feature need surfaces. |\n\nv1.0 ships the **Check-It qualification artifact** — a small\nqualified checker (the `witness-mcdc-checker` crate today)\nvalidates witness output, audited under DO-330 instead of trying\nto qualify the whole pipeline. Until v1.0, witness is positioned as\n**supplementary evidence** in a qualification dossier, not primary.\n\nThe release cadence is high (witness shipped 16 tagged releases in\nits first month). The discipline is: every fix lands in a numbered\nrelease with green CI, signed binaries, and a CHANGELOG entry. If\nyou're tracking witness for production, **pin to a v0.10.x patch**\nand read the CHANGELOG before bumping.\n\n### The reviewer experience — v0.9.0\n\n```\n$ witness viz --reports-dir compliance/verdict-evidence/ --port 3037\nwitness-viz listening on http://127.0.0.1:3037\n```\n\nOpen the browser to land on a dashboard with the headline numbers,\nclick a verdict to drill down, click a decision to see the **truth\ntable** — every row, every condition, gap rows red-bordered,\nindependent-effect pairs cited inline. Same data over JSON at\n`/api/v1/*` and over MCP at `POST /mcp` with three agent-callable\ntools (`get_decision_truth_table`, `find_missing_witness`,\n`list_uncovered_conditions`). The MCP surface is a strict subset of\nthe HTTP surface — humans reviewing a PR see exactly what the agent\nsaw.\n\nThe differentiator: where LDRA, VectorCAST, RapiCover, Cantata,\nBullseye and gcov+gcovr ship percentages and gates, witness ships the\ntruth table — and an agent contract over the same surface. Every\ngap-closing test the agent proposes is verifiable: re-run witness, see\nthe row appear, see the pair turn from `gap` to `proved`.\n\n### Current verdict suite — v0.8.0\n\n```\nverdict              branches  decisions full      proved  gap   dead   rows\nleap_year            2         1         1/1       2       0     0      4\nrange_overlap        0         0         0/0       0       0     0      3   (optimised to bitwise)\ntriangle             2         1         1/1       2       0     0      4\nstate_guard          3         1         1/1       3       0     0      5\nmixed_or_and         0         0         0/0       0       0     0      5   (optimised to bitwise)\nsafety_envelope      4         1         1/1       3       0     0      6\nparser_dispatch      33        7         1/7       9       13    5      6\nhttparse             473       67        7/67      28      46    108    40\nnom_numbers          20        3         3/3       6       0     0      28\nstate_machine        14        5         4/5       11      1     0      27\njson_lite            165       29        2/29      26      31    33     28\nTOTAL                716       115       21/115    90      91    146\n```\n\n**90 conditions proved across 715 br_ifs in real Rust code**, 21 full\nMC/DC decisions, 146 dead conditions flagged with row-closure\nrecommendations the report emits inline. The four real-application\nfixtures (httparse, nom_numbers, state_machine, json_lite) account\nfor 672/716 branches and 104/115 decisions. The seven canonical\nfixtures (leap_year through parser_dispatch) provide hand-derivable\noracle truth tables for verifier confidence.\n\n### Version history\n\n| Version | What it added |\n|---|---|\n| **v0.11.0** | Audit-grade evidence: predicate `measurement.toolchain` (rust + wasmtime) + `test_cases` (row_id ↔ invocation map); `witness verify --check-content`; SECURITY.md rewritten for cosign chain; Action silent-no-op fixes |\n| **v0.10.4** | Round-3 evaluator bugs: SOURCE_DATE_EPOCH expression fixed; Action sha256-verifies the tarball; `@v0.10.4` pinned in docs; \"Is this for you?\" framing |\n| **v0.10.3** | DSSE error variants (no more \"wasm runtime error\" mistag); `seq_debug` stable string; compliance bundle de-nested; `witness rivet-evidence` + GH Action sections in quickstart |\n| **v0.10.2** | Tester caveats — post-codegen framing in README, harness mode lifted into `docs/quickstart.md` §7, prominent Gatekeeper note + cosign verify command, \"Stability contract\" table |\n| **v0.10.1** | Windows path-stripping fix + SOURCE_DATE_EPOCH test race fix |\n| **v0.10.0** | \"Signed evidence chain, end to end\": `witness predicate --kind mcdc` + sigstore-OIDC release signing + `interpretation_polarity` field + `docs/concepts.md` + `witness-mcdc-checker` crate + 4 published JSON schemas + `SOURCE_DATE_EPOCH` |\n| **v0.9.12** | `witness quickstart` embedded subcommand + `docs/proposals/v0.10.0.md` + 2026-05-05 blog draft staged in pulseengine.eu |\n| **v0.9.11** | scaffold→viz bridge (auto-emit `verdict-evidence/`) + typed-args default + MCP `initialize` handshake + chatty success + `docs/quickstart.md` |\n| **v0.9.10** | `witness new \u003cfixture\u003e` — template scaffold (Cargo.toml + lib.rs + build.sh + run.sh) eliminating the fiddly first-run setup |\n| **v0.9.9** | Composite GitHub Action (`pulseengine/witness/.github/actions/witness@v1`) + ISSUE_TEMPLATE + `witness-reporter-component` artefact rename |\n| **v0.9.8** | `WITNESS_TRACE_PAGES` env override at instrument time + `TraceHealth.bytes_used`/`pages_allocated` telemetry |\n| **v0.9.7** | Per-target `br_table` decision reconstruction — httparse +26, json_lite +10 conditions correctly counted as proved |\n| **v0.9.6** | `--invoke-with-args 'name:val,...'` — typed positional args (eliminates `core::hint::black_box` workaround) |\n| **v0.9.5** | `witness-harness-v2` — MC/DC-capable subprocess protocol (counters + brvals + brcnts + base64 trace memory) |\n| **v0.9.4** | Tester-review Tier 0: ship witness-viz in releases, component preflight, harness protocol docs, error-tag fixes, walrus warning silenced |\n| **v0.9.3** | Fix `json_lite` Linux CI build (`unused_mut` under `-D warnings`) |\n| **v0.9.2** | Stacked coverage bars on dashboard + 12th verdict (base64_decode) + visual TOTAL row |\n| **v0.9.1** | Gap drill-down view (tutorial-style explanation + copy-paste test stub) + real HTMX 2.0.4 bundle |\n| **v0.9.0** | witness-viz Axum visualiser + MCP server (`get_decision_truth_table`, `find_missing_witness`, `list_uncovered_conditions`) + Playwright suite |\n| **v0.8.0..2** | Chain-direction analysis + 3 real-app fixtures + scoreboard + suite-index.html |\n| **v0.7.5** | Expanded httparse rows (15→40); 6× full MC/DC, 2.3× proved |\n| **v0.7.4** | Per-function-call decision outcome capture (kind=2 trace records) |\n| **v0.7.3** | Trace-buffer parser → per-iteration `DecisionRow`s |\n| **v0.7.2** | Trace-buffer write side (1582 records on httparse) |\n| **v0.7.1** | Module-rollup report (`--format mcdc-rollup`) |\n| **v0.7.0** | Real Wasm Component (witness-component, ~400 KB) + httparse fixture |\n| **v0.6.7..9** | README quickstart, release-self-verify, SECURITY.md threat model |\n| **v0.6.4..6** | DSSE-signed verdict predicates (ephemeral keys), V-model traceability matrix, PR delta comments |\n| **v0.6.0..3** | MC/DC schema + reporter + verdict-suite scaffolding + populated compliance bundle |\n| v0.1.0–v0.5.0 | Branch coverage, DWARF reconstruction, rivet evidence, sigil predicate format, workspace split, LCOV |\n\nSee [DESIGN.md](DESIGN.md) for the roadmap and\n[`docs/roadmap.md`](docs/roadmap.md) for v0.9+.\n\nCounter values are exposed as exported mutable globals named\n`__witness_counter_\u003cid\u003e` (plus `__witness_brval_\u003cid\u003e` /\n`__witness_brcnt_\u003cid\u003e` from v0.6.1+), not via a dump function — any\nconformant Wasm runtime can read coverage with a two-line\n`instance.get_global` call. No cooperation protocol with the\nmodule-under-test is required.\n\n## Show me the proof — verify a release in 60 seconds\n\nEvery v0.6.4+ release ships a `compliance-evidence.tar.gz` archive\ncontaining the eleven verdict directories with end-to-end MC/DC\nreports, DSSE-signed in-toto Statements per verdict, an ephemeral\npublic key, and a V-model traceability matrix. Verify it:\n\n```sh\n# 1. Download the compliance archive for the latest release.\ngh release download v0.8.0 \\\n  --repo pulseengine/witness \\\n  --pattern '*compliance-evidence*'\n\n# 2. Extract.\ntar -xzf witness-v0.8.0-compliance-evidence.tar.gz\n\n# 3. See the per-verdict scoreboard with proved/gap/dead totals.\ncat compliance/verdict-evidence/SUMMARY.txt\n\n# 4. Verify a signed predicate against the verifying key.\nwitness verify \\\n  --envelope compliance/verdict-evidence/httparse/signed.dsse.json \\\n  --public-key compliance/verdict-evidence/verifying-key.pub\n```\n\nThe verify command prints `OK — DSSE envelope … verifies against …`\nand exits zero. Tampering with the envelope or the public key fails\nverification with a clear error and a non-zero exit. The `cosign\nverify-blob` command works equivalently — the envelope is\nstandards-compliant DSSE.\n\nThe `verdict-evidence/` directory contains:\n\n```\nverdict-evidence/\n├── SUMMARY.txt                 # one-line-per-verdict status\n├── verifying-key.pub           # Ed25519 public key (32 bytes)\n├── VERIFY.md                   # verification walkthrough\n├── traceability-matrix.json    # V-model matrix (v0.6.5+)\n├── traceability-matrix.html    # rendered for human review\n└── \u003cverdict-name\u003e/\n    ├── source.wasm             # pre-instrumentation\n    ├── instrumented.wasm       # post-instrumentation\n    ├── manifest.json           # branches + reconstructed decisions\n    ├── run.json                # per-row condition vectors (v3 schema)\n    ├── report.txt              # MC/DC truth tables (text)\n    ├── report.json             # MC/DC report (schema /witness-mcdc/v1)\n    ├── predicate.json          # in-toto Statement (unsigned)\n    ├── signed.dsse.json        # DSSE envelope (signed)\n    └── lcov.info, overview.txt # codecov-ingestible LCOV\n```\n\nThe eleven verdicts split into two groups:\n\n- **Seven canonical compound-decision shapes** with hand-derivable\n  truth tables (leap_year, range_overlap, triangle, state_guard,\n  mixed_or_and, safety_envelope, parser_dispatch). These are the\n  oracle: a reviewer can verify witness's MC/DC report by eye.\n- **Four real-application fixtures** at meaningful scale (httparse,\n  nom_numbers, state_machine, json_lite). 672 instrumented branches,\n  104 reconstructed decisions across real Rust crate code (RFC 7230\n  parser, parser-combinator integer parsing, TLS handshake state\n  machine, JSON parser). Their `TRUTH-TABLE.md` files document\n  source-author intent + the Wasm-level coverage-lifting story\n  (v0.2 paper).\n\nSee each verdict's [`TRUTH-TABLE.md`](verdicts/) and `V-MODEL.md`.\n\n## Usage\n\n```sh\n# Instrument a Wasm module with branch counters + per-row capture.\nwitness instrument app.wasm -o app.instrumented.wasm\n\n# Default: embedded wasmtime runner. Each --invoke is one row; witness\n# reads counter + per-row globals after each return.\nwitness run app.instrumented.wasm \\\n  --invoke run_row_0 --invoke run_row_1 --invoke run_row_2\n\n# Subprocess harness mode. The harness reads WITNESS_MODULE /\n# WITNESS_MANIFEST and writes a `witness-harness-v1` snapshot to\n# WITNESS_OUTPUT. See \"Harness-mode protocol\" below for the schema.\nwitness run app.instrumented.wasm --harness \"node tests/runner.mjs\"\n\n# Branch-coverage report (text / JSON).\nwitness report --input witness-run.json\nwitness report --input witness-run.json --format json\n\n# v0.6+ MC/DC report — truth tables, independent-effect citations,\n# gap-closure recommendations.\nwitness report --input witness-run.json --format mcdc\nwitness report --input witness-run.json --format mcdc-json\n\n# v0.6+ in-toto coverage predicate + DSSE signing + verification.\nwitness predicate --run witness-run.json --module app.instrumented.wasm \\\n  -o predicate.json\nwitness keygen --secret release.sk --public release.pub\nwitness attest --predicate predicate.json --secret-key release.sk \\\n  -o predicate.dsse.json\nwitness verify --envelope predicate.dsse.json --public-key release.pub\n\n# LCOV emission for codecov.\nwitness lcov --run witness-run.json --manifest app.instrumented.wasm.witness.json \\\n  -o lcov.info\n```\n\nFor a worked example end-to-end, see\n[`tests/fixtures/sample-rust-crate/`](tests/fixtures/sample-rust-crate/) —\na tiny `no_std` Rust crate that compiles to Wasm and exercises every\ninstrumentation pattern (`br_if`, `if/else`, `br_table`). Build it with\n`./tests/fixtures/sample-rust-crate/build.sh`, then run\n`cargo test --test integration_e2e` to see the round-trip\ninstrument→run→assert flow against compiler output (not just hand-written\nWAT). The fixture's `README.md` documents the entry-point conventions\nwitness uses for `--invoke`.\n\n## Harness-mode protocol (`witness-harness-v1`)\n\n`witness run --harness \u003ccmd\u003e` is the escape hatch for runtimes other\nthan embedded wasmtime — Node WASI, custom kiln deployments, hardware\nboards. witness spawns the harness via `sh -c` with three env vars set:\n\n```\nWITNESS_MODULE   — absolute path to the instrumented .wasm\nWITNESS_MANIFEST — absolute path to \u003cmodule\u003e.witness.json\nWITNESS_OUTPUT   — absolute path the harness must write to before exit\n```\n\nThe harness loads the module, exercises it however it wants, then\nwrites a JSON file to `WITNESS_OUTPUT` matching:\n\n```json\n{\n  \"schema\": \"witness-harness-v1\",\n  \"counters\": {\n    \"0\": 12,\n    \"1\": 7,\n    \"2\": 0,\n    \"3\": 12\n  }\n}\n```\n\n**Keys are the per-branch decimal IDs** as published in the manifest\n(`branches[].id`). **Values are u64 hit counts.** That's the entire\nv1 wire format. A 10-line Node WASI harness is enough.\n\n```js\n// harness.mjs — minimal witness-harness-v1 implementation\nimport fs from \"node:fs/promises\";\nimport { WASI } from \"node:wasi\";\n\nconst mod = await WebAssembly.compile(\n  await fs.readFile(process.env.WITNESS_MODULE),\n);\nconst wasi = new WASI({ version: \"preview1\" });\nconst inst = await WebAssembly.instantiate(mod, { wasi_snapshot_preview1: wasi.wasiImport });\ninst.exports.run_row_0(); inst.exports.run_row_1(); /* ... */\n\nconst counters = {};\nfor (const [name, val] of Object.entries(inst.exports)) {\n  if (name.startsWith(\"__witness_counter_\") \u0026\u0026 typeof val.value === \"bigint\") {\n    counters[name.replace(\"__witness_counter_\", \"\")] = Number(val.value);\n  }\n}\nawait fs.writeFile(\n  process.env.WITNESS_OUTPUT,\n  JSON.stringify({ schema: \"witness-harness-v1\", counters }),\n);\n```\n\n### v2 schema — full MC/DC from a subprocess (`witness-harness-v2`, v0.9.5+)\n\n`witness-harness-v1` carries counters only, so MC/DC reconstruction\ndegrades to branch coverage. **v0.9.5 ships v2** — the same wire\nformat extended with per-row `brvals` / `brcnts` / `trace_b64`,\nmirroring exactly what embedded wasmtime mode reads. A v2-aware\nharness produces full truth tables identical to embedded.\n\n```json\n{\n  \"schema\": \"witness-harness-v2\",\n  \"counters\": { \"0\": 7, \"1\": 3 },\n  \"rows\": [\n    {\n      \"name\": \"run_row_0\",\n      \"outcome\": 1,\n      \"brvals\": { \"0\": 1, \"1\": 0 },\n      \"brcnts\": { \"0\": 1, \"1\": 1 },\n      \"trace_b64\": \"AAAA...\"\n    },\n    {\n      \"name\": \"run_row_1\",\n      \"outcome\": 0,\n      \"brvals\": { \"0\": 0, \"1\": 0 },\n      \"brcnts\": { \"0\": 1, \"1\": 1 },\n      \"trace_b64\": \"AAAA...\"\n    }\n  ]\n}\n```\n\nA v2 harness must call `__witness_trace_reset` and\n`__witness_row_reset` between rows so each `HarnessRow` carries\nisolated post-invocation state. The trace bytes are the raw 64 KiB ×\nN pages of `__witness_trace` memory, base64 standard-encoded\n(including the 16-byte header).\n\n```js\n// harness.mjs — minimal witness-harness-v2 implementation\nimport fs from \"node:fs/promises\";\nimport { WASI } from \"node:wasi\";\n\nconst mod = await WebAssembly.compile(\n  await fs.readFile(process.env.WITNESS_MODULE),\n);\nconst wasi = new WASI({ version: \"preview1\" });\nconst inst = await WebAssembly.instantiate(mod, { wasi_snapshot_preview1: wasi.wasiImport });\nconst exp = inst.exports;\n\nconst traceMem = exp.__witness_trace;\nconst rows = [];\nconst rowNames = [\"run_row_0\", \"run_row_1\", \"run_row_2\"];\nfor (const name of rowNames) {\n  exp.__witness_trace_reset();\n  exp.__witness_row_reset();\n  const out = exp[name]();\n\n  const brvals = {}, brcnts = {};\n  for (const [k, v] of Object.entries(exp)) {\n    if (k.startsWith(\"__witness_brval_\")) brvals[k.replace(\"__witness_brval_\", \"\")] = Number(v.value);\n    else if (k.startsWith(\"__witness_brcnt_\")) brcnts[k.replace(\"__witness_brcnt_\", \"\")] = Number(v.value);\n  }\n  const trace_b64 = Buffer.from(traceMem.buffer).toString(\"base64\");\n  rows.push({ name, outcome: out, brvals, brcnts, trace_b64 });\n}\n\nconst counters = {};\nfor (const [k, v] of Object.entries(exp)) {\n  if (k.startsWith(\"__witness_counter_\")) counters[k.replace(\"__witness_counter_\", \"\")] = Number(v.value);\n}\nawait fs.writeFile(\n  process.env.WITNESS_OUTPUT,\n  JSON.stringify({ schema: \"witness-harness-v2\", counters, rows }),\n);\n```\n\n### v1 stays supported (counters-only fallback)\n\nExisting `witness-harness-v1` harnesses keep working unchanged in\nv0.9.5+. The schema-string dispatch picks v1's counters-only path,\nproducing branch coverage like before. Migrate when you need truth\ntables — v1 → v2 is a strict superset, no breaking changes to the v1\nfields.\n\n## Cross-language reach\n\nwitness operates at the wasm + DWARF layer, not at any specific\nsource language. In principle, any language that compiles to\nwasm with debug info is a candidate — see\n[docs/cross-language.md](docs/cross-language.md) for the honest\nmatrix: Rust is verified end-to-end; C is partially verified\n(instrumentation works, decision clustering needs a v0.19+\nextension to handle clang's `if/else` lowering); C++ / Zig /\nSwift / TinyGo / Kotlin/Wasm are documented as \"should work,\nuntested.\" Probes welcome.\n\nThis positions witness alongside but distinct from existing\nOSS MC/DC tools:\n\n- **[GCC 14 `-fcondition-coverage`](https://arxiv.org/html/2501.02133v1)**\n  (C/C++/D/Rust), **[Coveron](https://coveron.github.io/)**\n  (C/C++), and **[linux-mcdc](https://github.com/xlab-uiuc/linux-mcdc)**\n  (Linux kernel) all measure at the **source-level** (frontend).\n- **[GNATcoverage](https://github.com/AdaCore/gnatcoverage)** (Ada) measures at the\n  source level and object level.\n- **witness** measures at the **post-codegen wasm bytecode**\n  layer. The same DO-178C \"post-preprocessor C\" precedent\n  applies: measure what the compiler emits, not what the\n  engineer typed. Different chain layer → additive evidence.\n\n## Where it fits\n\nwitness is one piece of a composed pipeline. Each tool owns a narrow mechanical\ncheck; the composition is what the audit trail holds.\n\n| Tool | Role |\n|---|---|\n| [rivet](https://github.com/pulseengine/rivet) | Requirement ↔ test ↔ coverage traceability validator. Consumes witness reports. |\n| [sigil](https://github.com/pulseengine/sigil) | Signs Wasm + emits in-toto SLSA provenance; carries witness reports as coverage predicates as composition matures. |\n| [loom](https://github.com/pulseengine/loom) | Post-fusion Wasm optimization with Z3 translation validation — emits the optimized Wasm that witness measures. |\n| [meld](https://github.com/pulseengine/meld) | Component fusion — witness can measure coverage on fused modules or individual components. |\n| [kiln](https://github.com/pulseengine/kiln) | Wasm runtime — one of the execution options for the test harness. |\n| [spar](https://github.com/pulseengine/spar) | Architecture / MBSE layer — not directly involved in coverage, but selects the variant that determines what Wasm gets produced. |\n\n## Build\n\n```sh\ncargo build --release\ncargo test\n```\n\n## Contributing\n\nThis project uses [rivet](https://github.com/pulseengine/rivet) for\ntraceability. Before committing, run `rivet validate` to check artifact\nintegrity — the pre-commit hook installed by `rivet init --hooks` does this\nautomatically.\n\nCommit messages use [Conventional Commits](https://www.conventionalcommits.org/):\n`feat:`, `fix:`, `docs:`, `chore:`, `refactor:`, `test:`.\n\n## Related work\n\nwitness sits in a populated landscape. The closest precedents measure\ncoverage at non-source levels (JaCoCo on JVM bytecode, Clang and rustc on\nLLVM IR/MIR), and the Wasm ecosystem already has source-level coverage\ntools that project through Wasm. None of them measure MC/DC on Wasm\ndirectly, which is the gap witness occupies.\n\n| Tool | Measurement point | MC/DC? | Relationship |\n|---|---|---|---|\n| [JaCoCo](https://www.eclemma.org/jacoco/trunk/doc/counters.html) | JVM bytecode | No ([per maintainers](https://groups.google.com/g/jacoco/c/b8bAWaWPl6I/m/eMKixUpMCAAJ)) | Direct precedent. The JVM has had bytecode-level branch coverage for two decades and it ships in regulated contexts. witness does the same for Wasm in v0.1 and adds MC/DC reconstruction in v0.2. |\n| [Clang source-based MC/DC](https://discourse.llvm.org/t/rfc-source-based-mc-dc-code-coverage/59244) | LLVM IR (annotated from source AST at lowering) | Yes, capped at 6 conditions per decision | Source-level MC/DC. The 6-condition cap is a bitmap-encoding constraint. Different measurement point from witness; complementary. |\n| [rustc `-Zcoverage-options=mcdc`](https://github.com/rust-lang/rust/issues/124144) | HIR → MIR (lowered to LLVM coverage) | Yes, capped at 6 conditions per decision | Source-level MC/DC for Rust; inherits the LLVM cap. Covers what the human wrote; witness covers what survives rustc + LLVM into Wasm. Different blind spots. |\n| [wasmcov](https://hknio.github.io/wasmcov/) / [minicov](https://github.com/Amanieu/minicov) / [wasm-bindgen-test coverage](https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/coverage.html) | LLVM source-level coverage projected through Wasm execution | Inherits LLVM | Source-level coverage *via* Wasm runtime. Useful when source is available and you trust the LLVM-to-Wasm lowering. witness is Wasm-structural — useful when the source is not in scope, or when post-LLVM divergences matter. |\n| [Whamm](https://arxiv.org/html/2504.20192) | Wasm bytecode rewriting / engine monitoring | Not coverage-specific | General-purpose Wasm instrumentation DSL (April 2025). I think Whamm could be a future implementation backend for witness's rewrite phase if walrus's ergonomics stop scaling — no immediate action, worth tracking. |\n| [Wasabi](https://github.com/danleh/wasabi) | Wasm dynamic analysis | Not coverage-specific | Older Rust-based Wasm instrumentation framework. Precedent for the shape of the tool, no overlap in what it measures. |\n| Ferrous / DLR Rust MC/DC (under the [SCRC 2026 Project Goal](https://blog.rust-lang.org/2026/01/14/what-does-it-take-to-ship-rust-in-safety-critical/)) | Rust source / MIR | Yes (planned, DAL A target) | Same chain layer as rustc-mcdc, productised for safety-critical use. Explicitly complementary to witness: different measurement points, different blind spots, additive evidence. |\n\nWe adopt all of these. The overdo stance from\n[Overdoing the verification chain](https://pulseengine.eu/blog/overdoing-the-verification-chain/)\nis that techniques at the same chain layer with non-overlapping blind\nspots are paired, not picked between — the cost of running both is CI\nbudget; the cost of picking one is a certification campaign that stalls\non a missing technique. witness is the post-rustc Wasm measurement\npoint; rustc-mcdc and Ferrous/DLR are the pre-LLVM source-level\nmeasurement point. Resistance is futile.\n\n### Upstream — equivalence-class inference on legacy binaries\n\nDe Luca, De Angelis, Amalfitano, and Cimmino — *Inferring\nEquivalence Classes from Legacy Undocumented Embedded Binaries for\nISO 26262-Compliant Testing* ([arXiv:2604.22673](https://arxiv.org/abs/2604.22673)) —\naddresses the *input-side* problem that witness assumes solved:\nwhen the test corpus does not exist and the source has been lost,\nhow do you derive equivalence classes of inputs from a binary that\nstill has to be re-qualified under ISO 26262? Their work recovers\nthe input partitions; witness measures MC/DC on the executable\nthose inputs exercise. Read as a chain: arXiv:2604.22673 produces\nthe witness vectors a §6.4.2-style structural-coverage argument\nneeds, and witness measures whether those vectors actually\ndiscriminate the post-codegen decisions the runtime executes. The\ntwo halves are complementary — input-domain reconstruction\nupstream, structural-coverage measurement downstream — and form\none shape of evidence chain a 26262 or DO-178C dossier expects.\nThe citation here is bibliographic; we don't yet integrate with\ntheir pipeline.\n\n## License\n\nDual-licensed under Apache-2.0 OR MIT. See [LICENSE-APACHE](LICENSE-APACHE)\nand [LICENSE-MIT](LICENSE-MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpulseengine%2Fwitness","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpulseengine%2Fwitness","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpulseengine%2Fwitness/lists"}