Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Releases: jasisz/aver

Aver 0.24.1

06 Jun 21:05

Choose a tag to compare

Patch release on top of "Divide" — a correctness fix in the optimizer and broader aver check hints.

Fixed

  • Constant folding no longer drops a side effect or a non-terminating computation. When the optimizer collapsed a match / Result.withDefault / ? over a statically-known Result/Option, or an Int.div/Int.mod by a literal zero, it could discard a sub-expression that still had to run — silently dropping its effects (e.g. a Console.print), or turning a program that should loop forever into one that returns; one wildcard case could also crash the VM. Folded expressions now always run their effects, in source order.

Changed

  • aver check flags more redundant work — a loop invariant built directly inside a recursive call's arguments, and a pure function computed more than once with the same arguments in a single expression.

Aver 0.24.0 "Divide"

06 Jun 10:11

Choose a tag to compare

One middle-end under every backend; the last operator that could crash is now a function.

Breaking

  • Integer / is removed; use Int.div(a, b) : Result<Int, String>. It was the last partial operation posing as a total operator. a / b on two Ints is now a type error pointing at Int.div, which returns Result.Err on a zero divisor or the i64::MIN / -1 overflow. Int.div is Euclidean — the partner of Int.mod (Int.div(-7, 2) == -4). Float / stays total. Migration: match Int.div(a, b), or Result.withDefault(Int.div(a, b), <fallback>) when the divisor is known non-zero.

Added

  • aver compile --emit-ir-after=mir dumps the textual MirProgram — the executable middle-end the VM runs, after HIR → MIR lowering and the optimize pipeline.
  • aver compile --explain-mir-coverage reports how much of a program lowers to MIR (per function, with the dominant blocker); --target wasm-gc retargets the meter at the wasm-gc backend's reach.

Changed

  • Fn(...) is allowed only as a function-parameter type. Aver has no closures, so a function value can only be a named fn / builtin / constructor passed as an argument. Using Fn(...) as a return type, a field, a collection element, or a local binding is now a type error — keeping the concrete callee (and its effects) statically known at every call. To choose between functions dynamically, branch at the call site or model the choice as a sum type.

Fixed

  • wasm-gc: a tuple binding that follows _ is no longer dropped. match pair { (_, value) -> value } returned the field's zero default instead of the bound element; tuple fields are now paired with their binding by position regardless of where the wildcards sit.
  • aver run --self-host keeps pace with the language. The self-hosted interpreter now handles Int.div, float literals inside string interpolation, and Unit-returning main under record/replay — three places it had lagged the VM. (Higher-order functions and multi-module programs are still being brought across.)

Compiler internals

  • MIR is now the only runtime middle-end. The VM compiles exclusively through Core MIR (src/ir/mir/); the old ~2200-line HIR tree-walking compiler is gone — a function that can't lower to MIR is a hard error, not a silent second path. wasm-gc and wasip2 emit from MIR too (resolved-HIR fallback for shapes they don't yet cover) and run the shared ir::mir::optimize() passes, so one optimizer improves every backend at once.
  • Owned Vector/Map are mutated in place. The MIR last-use pass updates a uniquely-held collection without copying it first, turning build-and-fill loops from O(n²) into O(n) — roughly 20× faster on the VM and 26× on native (--target rust) versus 0.23; wasm-gc is unchanged (it leans on the engine's GC).
  • match on Int.div/Int.mod's Result lowers on every backend. The directly-consumed (boxed) form now builds the Result<Int, String> on wasm-gc and exports faithfully to Lean/Dafny (guarding the zero divisor so the Err arm is reachable), so the Ok/Err idiom is no longer VM-only.
  • A constant divisor compiles Int.div/Int.mod down to bare division. The MIR const-folder rewrites Int.div(a, k) for a literal k ∉ {0, -1} to Result.Ok(a div_euclid k), then folds the consumer (withDefault / match) over the now-literal constructor — so match Int.div(a, 10) { Ok / Err } and Result.withDefault(Int.div(a, 10), d) lower to a plain Euclidean division on every backend. The explicit Result-returning function disappears when it provably can't fail.
  • wasm-gc modules are byte-reproducible across builds. Carrier type slots are now registered in sorted order instead of HashMap-iteration order.

Aver 0.23.0 "Shape"

30 May 12:31

Choose a tag to compare

What aver shape sees, aver proof now uses.

Breaking

  • All commands now use the same module-root default: explicit --module-root wins, otherwise current working directory. Previously aver run, aver run --wasm-gc, and aver run --wasip2 walked the entry file's parent chain to find a directory where every depends [...] resolved, while aver verify / aver check / aver context / aver compile already used cwd. After this change, every command treats the same arguments the same way. Migration: if you relied on aver run projects/foo/main.av from the repo root finding projects/foo/depends/*, add --module-root projects/foo (or cd into that directory).

aver shape

  • MVP — architectural smell radar (CLI + LSP). Per-fn archetypes (14 labels), ModuleShape 5-dim vector with derived Kind (ServiceClient, Orchestration, SmartConstructor, DataModule, PureHelpers, Library, EffectfulLibrary, EffectfulShell), and a histogram-based Layer guess (Domain / Parse / Command / AiStrategy / RenderUi / Infra) with confidence and runners-up. aver shape <dir> walks corpora; --lint checks aver.toml's [[shape.expected]]. LSP surfaces the verdict via CodeLens, hover, and document symbol.
  • Module patterns are now first-class typed facts. Five shapes by name: RefinementSmartConstructor, WrapperOverRecursion, ResultPipelineChain, RendererFormatter, MatchDispatcherFold. Surfaced in a new Module patterns: section in the CLI output and as a patterns array in --json. Same data feeds the LSP and proof_lower.
  • Output is flat + colored — section headers at column 0, ANSI palette (bold headers, cyan Kind / Layer, yellow counts, magenta pattern variants). Auto-disables on non-TTY pipes; NO_COLOR respected.

Proof export

  • Three new ProofStrategy variants close universal laws on Lean and Dafny. Each consumes a ModulePattern:
    • WrapperOverRecursion — monoidal-accumulator wrapper (sum(xs) == sumDirect(xs)). Demo: examples/data/sum_acc.av.
    • ResultPipelineChain?-chain ≡ nested match Result.Err -> Err. Demo: examples/core/result_chain.av.
    • MatchDispatcherFold — two list folds equal by structural induction. Demo: examples/data/list_length_fold.av.

Tooling

  • aver proof --check runs the backend verifier and gates regressions. --error-budget=N (Dafny) and --sorry-budget=N (Lean) tolerate up to N residual failures so CI can pin a budget; --check-json emits {backend, errors|sorries, budget, passed} for external harnesses. Exit codes: 0 within budget, 1 over, 2 on harness failure.
  • Playground Audit panel gains a Shape section — Kind · Layer summary; expand to see module-shape vector, histogram, recognized patterns, and per-fn archetypes. Same payload as aver shape --json; no new toolbar button.
  • aver verify --hostile: opaque runtime handles can be fabricated inside verify-trace context for system-handle effects (today: Tcp.Connection), so Oracle stubs can feed a deterministic conn into the SUT without round-tripping through Tcp.connect. Opt-in per type — user-defined opaques (Refinement.Natural, …) stay protected. examples/services/redis.av exercises this via verify ping/set/get trace blocks.

Aver 0.22.1

29 May 08:54

Choose a tag to compare

Hardening

  • aver verify works on multi-module programs. 0.22.0 shipped with a regression: running aver verify <entry> on any program that declared depends [...] failed with "missing VM symbol for exposed function Foo.Bar.baz". Every multi-module example under examples/refinement/ (including the refinement-via-opaque flagship that Lift introduced), examples/apps/notepad/, and projects/payment_ops/ hit it; aver run, aver compile, and aver verify --wasm-gc were unaffected. The VM verify path now loads dep modules the same way the other paths already do (both the disk-loader CLI shape and the pre-loaded playground/LSP shape).

Aver 0.22.0 "Lift"

28 May 19:25

Choose a tag to compare

Aver source stays ordinary. The proof export lifts it to the backend's native mathematical shape — refinements become subtypes, mutual recursion becomes a structural block, every verify-law passes through one classifier, and Dafny closes more obligations on its own.

Refinement recovery

  • Aver recovers proof-language refinements from ordinary validated code. A single-field Int record with a validating smart constructor (e.g. Natural { v: Int } with v >= 0) now exports as a native subtype on Lean ({ v : Int // P v }) and subset type on Dafny (type X = v: int | P v witness W); the predicate travels with the type. A public function that guards n >= 0 before calling a private worker similarly refines that worker's domain in the export. Universal laws like add_commutative(a: Natural, b: Natural) close in the backend's natural proof shape, no per-law tactic plumbing. Conservative and source-compatible — no new Aver annotation, and when clauses stronger than the recovered invariant are kept as theorem premises rather than silently dropped. Recovered refinements survive module boundaries.

Mutual recursion in proof export

  • Mutual-recursion SCCs over List / Vector / String parameters export natively. Lean emits a single mutual ... termination_by ... end block keyed off the structural measure; Dafny emits a decreases <measure>, <rank> tuple per member. The fuel-bounded helper-and-wrapper indirection is gone for these groups. Bounded-∀ universal laws over the SCC verify as real proofs (BigInt's add_commutative moves from assume {:axiom} on trust to a real theorem). Sample assertions no longer exhaust Lean's synth budget on compound predicates.

Law strategy substrate

  • Both proof backends read every verify-law strategy from one classifier. Each verify <fn> law lowers to one of fourteen algebraic shapes (commutative / associative / identity / induction / library axiom / map update / linear arithmetic / four spec-equivalence flavours / linear recurrence) before Lean or Dafny emits anything. Visible via aver compile --emit-ir-after law_lower. Effectful impl-vs-spec laws classify on the canonical post-Oracle-Lift shape; fib(n) == fibSpec(n) closes as a real proof on both backends (Nat-helper bridge + worker-shift lemma) where prior releases emitted sorry / empty-body.

Dafny verifier improvements

  • dafny verify closes 25 more proofs across the flagship suite (160 errors → 135). Smarter infer_decreases picks the actually-moving recursion measure across self-call sites (catches repeat(char_, n - 1) and scanExpTail(s, pos + 1, start)). List-induction hints case-split |xs| == 0 and recurse on xs[1..], detecting recursive fns nested under Map.* / Option.* helpers. examples/data/map.av verifies clean; fibonacci, rle, quicksort, json, grok_s_language all improve without regression.
  • String.slice lowers via a clamp-to-empty helper instead of raw s[from..to]. Aver's runtime semantics — negative or out-of-range indices collapse to an empty slice — now travel into the Dafny export. examples/data/date.av verifies clean (the parseIntSlice(s, from, to) shape no longer leaves uncloseable range obligations on every caller); examples/data/json.av closes 24 more proofs at the same time (113 → 89 errors), entirely from the parser's slice-heavy lookahead. Source-compatible — no Aver-side change, the helper is emitted only when String.slice actually appears.
  • Float / lowers via a FloatDiv helper that mirrors Aver's IEEE-754 semantics. Aver float division never crashes — 1.0/0.0 is Infinity, 0.0/0.0 is NaN. Dafny's exact-rational real, in contrast, makes a / b impose b != 0 on every caller. The new helper returns a defined value (0.0) when the divisor is zero, so callers like goldenApprox(n) = Float.fromInt(fib(n + 1)) / Float.fromInt(fib(n)) no longer need to prove fib(n) ≥ 1 just to compute the ratio. examples/data/fibonacci.av verifies clean.
  • Negative-domain guard recognition broader. Recursive Int functions whose author writes match n <= 0 { true -> base; false -> recur(n - 1) } (or the equivalent match n < 1 { ... }) are now treated as self-guarded by infer_decreases, so the lowerer emits decreases if n >= 0 then n else 0 without a requires n >= 0 precondition the user never wrote. examples/data/rle.av's repeat(char_, n) shape closes (the expandRun(run) caller no longer has to prove run.count ≥ 0).

Tooling

  • tests/proof_spec gates dafny verify on the IR-clean examples and tracks per-example sorry and Dafny-error budgets across both backends. Lean: three examples carry honest sorry budgets (json.av 13 sampled-domain laws, rle.av 2, quicksort.av 2). Dafny: the flagship examples whose proofs Dafny still can't auto-discharge — fibonacci (1), rle (4), quicksort (5), date (2), json (113) — are now gated by an error-count budget instead of being silently unverified. Drift either way fails the test, so a new shape regressing or an old gap closing both surface in CI. aver compile --emit-ir-after={refinement_lower,contract_lower,law_lower} exposes the three new proof-lower stages — when a law falls through to sorry/empty-body, law_lower shows whether the lowerer pinned a strategy or fell back to BackendDispatch.

Backend foundation

  • Every backend (Rust, Lean, Dafny, wasm-gc) now consumes one typed view of the program. Pre-Lift each backend carried its own string-keyed fn-signature side-channel (ctx.fn_sigs) and re-derived types by parsing source-annotation strings on the fly during emit; identity decisions (which Shape does this constructor refer to? which dep module's Box is at this slot? is this callee pure?) lived in fragile bare-name lookups that could silently drift between backends. Lift consolidates the codegen substrate around one resolver-produced view of the post-name-resolution program — typed (name, Type) parameter lists, typed return types, opaque FnId / TypeId / CtorId identity, and a .ty() slot on every reachable body expression — and the bare-string fn-signature cache is gone. A guardrail test (tests/identity_guardrails.rs) blocks any new backend code from regressing to the old patterns. The most visible payoff: two dep modules can each declare record Box { value: Int } and compile cleanly on aver compile --target wasm-gc (pre-Lift the post-flatten type registry collided them under one slot and Left.Box(value = 5).value + Right.Box(value = 10).value failed wasm validation), and == / != on those records dispatches to the right per-type equality helper rather than silently picking the last writer. Single-declarer dep types stay on the legacy bare-key path with no behavior change; the next major IR step (MIR / Core IR for per-expression effects + ownership + cross-scope optimisation) lowers from this substrate, separate epic.
  • String.fromFloat on aver compile --target wasm-gc now matches VM and wasip2 to the last shortest-roundtrip digit. The wasm-gc WAT helper capped the fractional-digit loop at 15 to avoid an i64.trunc_f64_s trap on large-magnitude inputs, but the cap also chopped one digit off values in the [0.1, 10) range where the multiplication doesn't overflow — goldenApprox(n) = Float.fromInt(fib(n + 1)) / Float.fromInt(fib(n)) from examples/data/fibonacci.av printed 1.618181818181818 on wasm-gc vs 1.6181818181818182 on VM. The cap is now 17 (the IEEE 754 f64 maximum); the overflow guard above it still bails before the trap on large magnitudes, so behaviour for values outside [0.1, 10) is unchanged.
  • Console.print / Console.error / Console.warn on aver compile --target wasip2 now append the trailing newline that VM and --target wasm-gc already shipped. Pre-Lift wasip2's lowering wrote only the string bytes to the wasi output stream, so a sequence of Console.print("a"); Console.print("b") came out as ab on aver run --wasip2 and a\nb\n everywhere else — multi-line programs on wasip2 produced one jammed line. The println! semantic now lives in a dedicated __rt_println_to_lm bridge helper called by all three Console.* methods; other consumers of the bridge (Disk.writeText, Http.* marshalling) stay on the no-newline __rt_string_to_lm and are unaffected.

Examples

  • New examples/refinement/ collects the canonical refinement-via-opaque demos: Natural, Positive, IntRange, NonNegFloat, Email, BigInt. Each exercises a different point in the design space.

Hardening

  • Functions with wildcard _ parameters (fn f(_: Int)) compile and run cleanly on every backend. Pre-Lift the resolver short-circuited _ and never claimed a slot for it, so local_count stayed at zero while callsites still pushed one value per source-level param — aver verify on a program like fn ignore(_: Int) -> Int = 42 then ignore(7) => 42 deterministically panicked inside the VM dispatch loop. The resolver now allocates a slot for every param regardless of name; wildcard params still skip the scope map so the body cannot read them, but the frame layout matches what callsites push. Found by fuzz_verify_runner AFL nightly.

Aver 0.21.1

21 May 10:44

Choose a tag to compare

Verify

  • aver verify --hostile now exercises a third axis: execution order. Every verify <fn> law case whose fn contains an (a, b)! independent-product gets a twin run in which the branches execute right-to-left, with each result placed back into its source position. A pure law claims its independent products commute, so the twin's tuple must match the forward run; a mismatch surfaces as verify-hostile-mismatch with origin +reverse-eval. Catches the class of bug where the runtime, the stub map, or a compiler optimisation has snuck a hidden ordering dependency into code Aver was treating as order-invariant. The most common shape it catches: a law that pins a trace event by flat trace.event(k) (which indexes the global emission sequence, so reverse-eval moves it) instead of structural trace.group(g).branch(b).event(k) (which addresses by source position and stays order-invariant). Same --hostile flag — no new CLI surface, no source-language change. New worked example at examples/formal/hostile_order_axis.av; the diagnostic's repair message names the right rewrite when it fires.

Aver 0.21.0 "Iron"

20 May 13:29

Choose a tag to compare

Iron in the frame — the type checker stops lying to itself about negation, recursion, and identity. A new fuzz harness shakes the rest of the toolchain until things fall out.

Type checker

  • Duplicate fn names in one module are a type error. Pre-Iron the second definition silently replaced the first — a typo could swap a function's body with no signal.
  • Polymorphic recursion that would need T := F<...T...> is rejected. A shape like fn nest(v: A) -> Unit; nest([v]) now surfaces as a normal type-incompatibility error instead of silently typechecking with a circular binding that later confused backends.
  • Two same-named types from different modules don't conflate. A project importing A.Shape (Circle | Square) and B.Shape (Triangle | Hexagon) used to silently accept a B.Shape value where A.Shape was expected. Now rejected with expected A.Shape, got B.Shape; the legitimate same-name-same-type case still type-checks.
  • One source error stops at one diagnostic. let z = add(unknownFn(1), unknownFn(2)) reported the two real errors plus two follow-on expected Int, got Invalid cascades; the recovery sentinel is now treated as a wildcard so callers see only the originating diagnostic.
  • Unreachable match arms are rejected. A second _ -> ... (or a repeated 0 -> ...) used to compile silently — the second arm could never fire because Aver match is first-arm-wins, but nothing flagged it. The type checker now reports Unreachable match arm: pattern X is already covered by an earlier arm at line N, naming the covering arm so you can pick which one to delete. Catches duplicate wildcards, identical literal patterns, identical constructor patterns, and structurally-equivalent tuples.

Parser

  • Deeply-nested expressions return a parse error instead of crashing aver. Sources with 2500+ nested (...), Option.Some(...), {...}, chained -, chained ?, or deeply-nested match patterns used to abort the process with fatal runtime error: stack overflow. The parser now caps recursion at 64 levels and surfaces Expression too deeply nested as an ordinary parse-error.

Runtime

  • -0.0 keeps its sign bit on every backend. Pre-Iron -x desugared to 0 - x, and 0.0 - 0.0 collapses to +0.0 under IEEE 754. Unary minus is now a first-class AST node and every backend negates at the float bit level, so -0.0 round-trips through compile + eval + replay.
  • -x on tight numeric loops is faster. Typed unary-minus dispatch on the VM joins the typed-arith pipeline (so -x runs at the same shape as +/*// for known-Int / known-Float operands).
  • Hand-edited compiler inputs surface as compile errors, not panics. Sources that registered two meanings under one symbol (duplicate variant constructor with conflicting payload, constant rebound under a function name, empty namespace path) used to panic the VM compiler; they now surface as Compile error: VM symbol 'X' already exists as ... with a source span.

Replay

  • aver replay accepts every recording shape aver run --record writes. Three places where replay refused or panicked on legitimate input got fixed: recordings touching Vector values, single-string-key Map recordings (collided with the marker shape), and any JSON containing multi-byte UTF-8 in an unexpected position (the JSON keyword parser sliced &str and panicked on byte index N is not a char boundary).

wasm-gc backend

  • String.fromFloat(x) doesn't trap at 1e41e18 magnitudes. The shortest-roundtrip loop overflowed i64.trunc_f64_s and the module crashed with wasm trap: integer overflow. The loop now bails one iteration before overflow, accepting fewer fractional digits at very large magnitudes instead of a crash.
  • Result<Unit, X> and Tuple<Unit, X> compile. The carrier-equality codegen path had no Unit arm and rejected typecheck-clean programs that used these shapes (e.g. fn animate() -> Result<Unit, String>).
  • Console.print from in-process embedders is captured correctly. When the host called wasm-gc-compiled code inside a thread-local stdout capture, Console.print wrote directly to fd 1 and bypassed the buffer — programs looked silent from the host's view even though they did print. The wasm-gc Console import now routes through the same capture path the VM uses.

Verify

  • aver verify won't hang on a non-terminating case. A verify block whose function had no terminating recursive base case (an easy shape to write by accident, and the one the fuzz harness reproduces every nightly) used to spin in the VM forever. The runtime now caps per-case work at 10M VM opcodes (mirrored as a wasmtime fuel budget on aver verify --wasm-gc); over-budget cases surface as RuntimeError: VM step limit exceeded, not a hang.

Tooling

  • Iron ships a multi-target fuzz harness. Eight AFL++ targets run nightly against main: frontend (lex + parse + typecheck), codegen on three backends (wasm-gc / wasip2 / Rust), VM-vs-wasm-gc parity (in-process, ~300 execs/s), verify runner, replay record→replay roundtrip. Plus a custom AST-aware mutator that produces typecheck-clean inputs reaching the codegen + verify layers byte-havoc rarely touches. Every "the parser used to crash" / "the backend rejected a typecheck-clean program" bullet above was found by this harness during Iron development. PR-side cost is zero — fuzz only runs nightly + on manual workflow_dispatch.
  • aver verify runs on --wasm-gc. Cross-target check — catches divergence between VM and wasm-gc on equality. Pure value-givens and value-hostile cases supported; trace projections, classified-effect Oracle stubs, and BranchPath cases reject upfront with an actionable pointer back to the VM verify path.
  • Snapshot regression suites per backend. examples/ corpus walks under wasm-gc + wasip2 codegen on every PR (≈0.1s each), Rust codegen + Lean + Dafny build under nightly. A compiler refactor that breaks codegen on a vetted example fails a named test instead of an obscure end-to-end break.

Aver 0.20.0 "Pulse"

18 May 13:32

Choose a tag to compare

The same Aver source that opened HTTP both ways in 0.19 now opens raw TCP — connect, send, receive, ping. One pool, one handle shape, every backend the same.

Added

  • TCP client on --target wasip2. All six methods — Tcp.connect, Tcp.writeLine, Tcp.readLine, Tcp.send, Tcp.close, Tcp.ping — compile and run as components against wasi:sockets/*. Long-lived connect/writeLine/readLine/close share a 256-slot pool keyed by "tcp-N" (same id shape every other backend uses, so cross-backend code stays portable); send and ping are ephemeral (no pool slot), so a program holding 256 live connections can still issue either. Run with wasmtime run -W gc=y -W tail-call=y -S inherit-network=y -S allow-ip-name-lookup=y -S tcp=y, or via aver run --wasip2. See docs/wasip2.md for the per-method status table.

Aver 0.19.0 "Echo"

13 May 16:05

Choose a tag to compare

Aver speaks HTTP both ways now — same source can call out as a client and answer back as a server, the same .component.wasm runs on Cranelift and V8.

Added

  • HTTP client on --target wasip2. All six methods — Http.get, Http.head, Http.delete, Http.post, Http.put, Http.patch — now compile and run as components. Response headers surface as Map<String, List<String>>; multi-value headers (e.g. Set-Cookie) keep server emit order. Failure messages name the wasi:http error variant (http: connection-refused, http: DNS-timeout, …) instead of a generic string.
  • HttpServer.listen on --target wasip2 --world wasi:http/proxy. Write a handler fn(HttpRequest) -> HttpResponse, name it via --handler <fn> at compile time (same flag the wasm-gc + Cloudflare path uses), compile to a .component.wasm, and serve it with any wasi-http host — wasmtime serve --addr=:N, Spin, NGINX Unit, wasmCloud. Request method / path / query / body / headers surface inside the handler as ordinary Aver values; the response's status code, body bytes, and Map<String, List<String>> headers round-trip to the client. The same source keeps working under aver run because HttpServer.listen(port, handler) in main is still a real call under VM — it just lowers to a no-op when the wasip2 proxy codegen takes over (which reads the handler identity from the flag, not from main). HttpServer.listenWith (per-instance context handler) stays deferred for one more iteration.

Aver 0.18.0 "Span"

09 May 13:07

Choose a tag to compare

Cross the Component Model boundary the same way Aver crosses the source/wasm one — typed effects in, canonical-ABI imports out.

Added

  • --target wasip2. WASI 0.2 / Component Model output. Wraps a wasm-gc core module via wit-component, lowers Aver effects directly to canonical-ABI WASI imports (no preview-1 adapter, no --bridge shim). Emits .component.wasm + sibling .wit. Effect surface: Console.{print,error,warn,readLine}, Args.get, Env.get, Time.{unixMs,now,sleep}, Random.{int,float}, all 7 Disk.* methods (exists/readText/writeText/appendText/delete/deleteDir/makeDir/listDir). Env.set and Terminal.* reject at compile time as structurally absent from WASI 0.2; Http.* / Tcp.* / HttpServer.* deferred to 0.19+. See docs/wasip2.md.
  • aver run --wasip2 — embedded wasmtime + wasmtime-wasi runner. CWD preopened as . for filesystem effects (matches VM target's path resolution semantics).
  • tests/wasip2_stress.rs — six regression tests covering 100× write+read+delete (resource leak), 50KB write/read roundtrip (chunked-write boundary), 5KB Console.print (chunked-write on stdout side), 200-entry listDir, Random distribution sanity, Time.sleep precision.

Removed (breaking)

  • --target wasm deleted. The pre-2024 NaN-boxed wasm32 backend (src/codegen/wasm/, ~9.5 kLoC) is gone, plus the wasm-legacy Cargo feature, the --bridge {wasip1,fetch,none} flag, the aver wasm-runtime subcommand, and the legacy bundling code in src/main/commands.rs. Modern hosts run --target wasm-gc; standalone runtimes use --target wasip2. --target edge-wasm went too — it depended on the deleted codegen::wasm::emit_wasm_with_adapter.
  • BenchTarget::WasmLocal removed — bench targets are now vm / wasm-gc / wasm-gc-v8 / rust.

Internal

  • module.rs / builtins.rs / types.rs split. wasm_gc/module.rs 5984 → 4197 (extracted wasip2_helpers.rs); body/builtins.rs 2351 → 1740 (extracted body/builtins_wasip2.rs); wasm_gc/types.rs 2778 → 1934 (extracted types_discovery.rs for collect_* AST walkers). Pure code movement, no behaviour change.