Skip to content

Doc/strictness cleanups from audit round 3 #190

Description

@lannbot

Small doc/contract/strictness cleanups collected while auditing. None is a
correctness emergency; each is either a doc claim that no longer matches the
source, a contract that omits behavior the code ships, or a strictness gap
that lets a wrong shape pass silently.

Found in structured audit round 3 (2026-08-21). Audit/testing regime: #175.

README/docs

Embedder API

  • Undocumented dual key spellings for world-level imports.
    runtime/src/embedder/instantiate.ts:499-514 resolves the kebab
    interface/resource id verbatim first, then falls back to camelCase;
    pick() accepts both pascalCase and raw kebab for resource classes
    (instantiate.ts:584, :646). contracts/embedder-api.md names only
    camelCase, and principle 3 permits liberal acceptance only where the TS
    type names a single canonical form. Consumers can (and will) couple to
    the unsanctioned kebab spelling. Either sanction both spellings in the
    contract or reject the non-canonical one.
  • EmbedderOptions.verifyHash opt-out is not in the contract.
    runtime/src/embedder/instantiate.ts:129-131 (default true) lets a
    caller skip the sha-256 check that contracts/embedder-api.md A4
    states unconditionally ("fails loudly"). Contract-document the escape
    hatch or remove it.
  • fromHost bool coerces instead of type-checking.
    runtime/src/embedder/values.ts:287-288 returns Boolean(v) for any
    input, while every sibling primitive lower rejects wrong-typed input
    with a site-named TypeError. "false" lowers as true; shape bugs
    pass silently.
  • A dropped-but-unbound Stream handle can still be transferred live.
    runtime/src/embedder/streams.ts:293-299: drop() on an unbound
    handle only sets #dropped (there is no #host to drop). A later
    lower calls takeValue (streams.ts:216-227), which runs
    bindElement and never consults #dropped, minting a live shared end
    for the guest. takeValue should refuse a dropped handle.

Contracts

  • contracts/plan-format.md exports[] schema is out of sync with the
    wire format.
    It omits the "instance" kind entirely — emitted by the
    shim since the resources corpus
    (crates/translator-shim/src/plan.rs:565), consumed at
    runtime/src/exec/executor.ts:851 and
    runtime/src/digest/digest.ts:209 — and documents the "type" kind
    with the wrong shape: the C2 amendment
    (contracts/plan-format.md:355-365) says
    {"kind":"type","resource":n}, while the wire shape is
    {kind:"type", name, type:{kind:"resource"|"value", …}}
    (runtime/src/plan/format.ts:259-278). The contract's own compat rule
    requires producer + consumer + contract in sync.
  • Cache-key features component is inert caller-supplied salt.
    runtime/src/cache/core.ts:122-139 takes features: string[] = []
    from the caller and never derives it from the translator, while
    crates/translator-shim/src/lib.rs:111-113 documents
    producer.features as "part of the artifact-cache key" (echoed by
    docs/architecture.md §10 and contracts/plan-format.md:30-36). Sound
    today only because translatorBuildHash subsumes it (features are
    compiled into the shim wasm) — it becomes a cache-poisoning bug the day
    translateRaw grows a runtime features parameter. Doc drift plus a
    dormant trap.
  • Digest name-flattening join is collision-capable and unspecified.
    runtime/src/digest/digest.ts:161-162 and :186 flatten with
    [...path, name].join("/"): import "a/b" with empty path collides
    with import "b" at path ["a"]. Not covered by
    contracts/digest.md's normalization rules. Preferred resolution is
    length-prefixing/nesting when the imports corpus lands — not blessing
    the join after the fact.
  • The envelope container format has no contracts/*.md authority.
    WireEnvelope (runtime/src/plan/format.ts:284-295) is defined only by
    crates/translator-shim/README.md, yet it is the production deploy
    artifact (tools/translate output, embedder-api A4) and the cache
    determinism unit. It carries no version field of its own, and
    truncation/extension behavior is unstated.

WASI

  • WASI WIT sets are not vendored in-repo. No wasi:io, wasi:cli,
    wasi:filesystem, wasi:random, or wasi:sockets WIT at @0.2
    exists anywhere in the tree (the only vendored wasi:* packages are
    clocks@0.3.0/0.3.1, http@0.3.1, sockets@0.3.0, and those sit
    under examples/guests/http-fetch/wit/deps/ as example-guest deps, not
    as an authority). Every 0.2-track semantic judgment therefore rests on
    unvendored authority, and source comments cite line numbers into WITs
    that cannot be checked in-tree (e.g. wasi/src/io.ts:69 cites
    "io.wit:23"). Same gap for wasi:cli/filesystem/random@0.3.
    Recommend vendoring the claimed sets — wasi/src/internal/fs_provider.ts:8
    claims WASI 0.2.12.
  • header-error.forbidden is never produced.
    wasi/src/http.ts:324-337 and :351-365 validate syntax only;
    forbidden names are accepted into fields and then silently owned by
    fetch at send time. This is the platform-managed-headers divergence
    already recorded at wasi/src/http.ts:57-59 — the divergence record
    should name forbidden explicitly.
  • Monotonic clock get-resolution claims 1 µs.
    wasi/src/clocks.ts:23-26 pins RESOLUTION_NS = 1_000n, optimistic
    where performance.now() is coarsened to 100 µs (non-cross-origin-
    isolated browser contexts).

Harness

  • Deno conformance lane pins no totals.
    harness/tests/conformance_test.ts:120-139 fails on unexpected
    failures, stale xfails, and zero commands, but nothing bounds skip
    counts or asserts the pass count — so pass→skip drift is invisible on
    the primary lane (the shell/browser lanes do pin totals). Corollary: an
    xfail entry whose command degrades from failed to skipped is
    neither stale nor failing on the Deno lane.
  • fileDeltas whole-file exemption has no stale detection.
    tools/browser/classify.ts:61,83-85: a fileDeltas entry turns ANY
    failure in the named file into an xfail, with no equivalent of the
    per-line stale check. Currently unused by all 9 overlay files, so this
    is latent machinery risk only; the first future use should get per-line
    deltas instead wherever feasible.
  • Node openAt passes O_CREAT|O_DIRECTORY to the OS.
    wasi/src/filesystem_node.ts:236-262: for a nonexistent path with
    create + directory set, the ENOTDIR pre-check is skipped
    (existing === undefined) and the combination reaches open(2), which
    Linux documents as unspecified — it can create a regular file and then
    fail ENOTDIR, leaving a stray file behind. wasmtime rejects
    create+directory up front as invalid.

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationp3Lowest priority: watchlists, doc-only adjudications, deletion candidates

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions