Development
Conformance Strategy
Last updated 2026-04-13
Conformance Strategy
mustard currently uses two active conformance buckets:
- Node-parity programs
These are guest programs that should evaluate the same way in
mustardand Node for the documented subset. We exercise them with direct differential tests, generated property tests, and the curatedtest262pass manifest. - Validator-rejected programs
These are syntactic or statically recognizable forms that should never reach
runtime. We exercise them with generated validation-negative cases and the
curated
test262unsupported manifest.
The contract format still reserves a third known_divergence bucket so new
audited mismatches can be tracked explicitly if they are discovered, but there
are no current documented divergence entries in the machine-readable contract.
Generated Coverage
The machine-readable source of truth for these outcomes lives in
tests/node/conformance-contract.js.
Unsupported contract entries now record both the expected phase
(constructor/validation or runtime) and a diagnostic category, so the
negative suites can assert fail-closed behavior without depending on full
message equality.
The property-based conformance generator is split intentionally:
SUPPORTED_PARITY_FAMILIESsplits Node-parity generation into independent semantic families such as control flow, exceptions, objects/arrays, keyed collections, async promises, and capability traces. Each family runs as its own property test with isolated shrinking.REJECTION_FAMILIESdoes the same for fail-closed coverage, so unsupported syntax, ambient globals, binding errors, operator rejects, runtime surface rejects, and missing global built-ins all shrink independently.supportedProgramArbitraryremains the mixed Node-parity source used by the broader mixed conformance property. Those programs must compile and differentially match Node.unsupportedValidationCaseArbitraryonly emits programs that should fail during constructor-time validation with explicit diagnostics.conformanceCaseArbitrarymixes both domains and asserts that each generated case has only two legal outcomes: Node-equivalent execution or validation failure.ast-conformance.jsadds a second generated layer that works on a small typed AST, supports bounded exhaustive enumeration, renders trace-sensitive programs, and feeds metamorphic rewrites from the same source AST.
When a family property fails, the test output prints the fast-check seed and shrink path plus the minimized guest program and a canonical outcome or trace diff instead of a raw object mismatch.
This is more useful than a naive source fuzzer because the generated programs stay inside deliberate semantic buckets and produce canonicalizable outputs.
The contract also carries a curated rejection-regression slice for phase- and
category-sensitive cases such as ambient globals, unsupported operators, and
deferred object-model built-ins like Object.create, Object.freeze, and
Object.seal.
Separate from parity and rejection generators, the Node test layer now carries
a lifecycle property suite for run() / start() / Progress.load() /
resume() / resumeError() / cancel() flows. It drives short stateful
sequences through direct progress reuse and explicit-policy restores, prints
minimized action histories when a lifecycle invariant breaks, and keeps longer
replay-sequence exploration in an opt-in out-of-band lane.
The async surface also now has a deterministic schedule matrix in
tests/node/async-schedule.test.js:
- guest-only promise chains and two-input combinator schedules are enumerated exhaustively in small bounded matrices and compared directly against Node
- addon
run()uses a deferred-capability harness so host settlement, microtask checkpoints, and cancellation races are exercised without wall-clock sleeps - the same bounded host schedules are replayed through
start()/Progress.load()/resume()to prove that queued async host work survives suspend, dump/load, and later resume steps with matching canonical traces
Failures in that matrix render canonical host traces plus guest continuation traces, so async ordering regressions show up as reproducible diffs rather than timing flakes.
Fixture Coverage
The curated test262 subset complements the generated layer:
passfixtures are stable regression cases inside the Node-parity subset.passfixtures live undertests/test262/cases/pass/**; when a fixture pins a specific machine-readable contract entry, the manifest records that link withcontractId.unsupportedfixtures are stable regression cases for explicit validator exclusions.
When adding coverage, prefer one of these paths:
- add a new generated family when the behavior is a broad semantic class
- add a new curated fixture when the behavior is a stable regression or an exact parser diagnostic category
- add an audit test when the behavior is especially regression-prone or relies on observable ordering, rendering, or trace details