v0.1.1
Added
- The documentation now carries a hand-authored SVG diagram set — thirty-three figures across the spec, the compiler README, and the top-level docs (
assets/diagrams/). The second wave adds the subtyping and effect-row lattices (§2.8, §7.8), module resolution with the transitive-visibility rule (§8.6), the async model and thevera serverequest lifecycle (§9.5), closure layout +call_indirectdispatch (§11.10), browser import introspection (§12.9.1), the WASI arena and server-world sequence (§13.3, §13.7), the FAQ's three verification layers, contract-driven-testing loop, and Dafny/Lean/Koka/F* comparison matrix, the CONTRIBUTING gate pipeline, the DE_BRUIJN binding-stack timeline, and the host-binding-families map (vera/README.md). The headline is the overall compiler architecture diagramvera/README.mdlacked entirely; alongside it: the spec §11.1 pipeline, the §12.5 memory layout and a new$alloc/mark-sweep GC flow, the §13.2 WASI component + dispatch table, the §6.8 three-tier verification decision flow, the §7.5.2 effect-handler suspend/resume sequence, the wasmtime embedding chain, the checker's three passes, Z3 proof-by-refutation, De Bruijn slot resolution (two figures, shared withDE_BRUIJN.md), theDiagnosticrecord, the CLI↔pipeline map (TOOLCHAIN.md), the three-layer/four-level testing model (TESTING.md), the LSP proof-delta session (LSP_SERVER.md), a by-the-numbers growth chart (HISTORY.md), and a workflow hero + the architecture inREADME.md. Every diagram that replaced an ASCII original keeps the text in a collapsed<details>block taggedtext— the docs feedllms-full.txtand agents reading files in a terminal, and images are invisible to both — and no diagram carries live counts (they drift outsidecheck_doc_counts.py's reach; the one exception, the growth chart, plots the historical release columns and says so). The architecture and pipeline figures draw the check → {verify | compile} fork truthfully —vera compiledoes not consume verify results and contract guards are always emitted — where the replaced ASCII's "runtime contract insertion for Tier 3" caption echoed the spec drift tracked in #958. The landing page gains its own figure in the site's bolder design language —docs/loop-web.svg, the write → prove → ship loop with the diagnostics return, embedded indocs/index.html§Why and mirrored into the generatedindex.md. The set's design system, conventions, and inventory are documented inassets/diagrams/README.md; theREADME.mdproject-structure tree's stale module counts (11 → 13 codegen, 9 → 19 wasm) ride along. - The canonical
E001diagnostic is now guarded against drift in all five of its documentation mirrors (#829).TestErrorDisplaySyncalready comparedREADME.md,docs/index.htmlandspec/00-introduction.mdagainst the diagnostic generated fromvera/errors.py, but two mirrors were unguarded —AGENTS.md's example--jsonblock, and the hardcoded example inscripts/build_site.pythat generatesdocs/index.md— and #826 had already drifted the ungated pair. Both are now compared.AGENTS.md'serror_code/spec_ref/fixare matched exactly (the extractorjson.loadsthe block, so the escaping is resolved and every field is directly comparable) and its ellipsis-truncateddescription/rationaleare prefix-compared;build_site.py's block is extracted by anchoring on the closing code fence rather than on thespec_reftext, so aspec_refdrift yields a precise Expected/Got diff instead of an opaque "block not found". Every guard is mutation-validated — drifting any mirror, or the canonical diagnostic, turns the corresponding test RED. The example is still hand-duplicated across the five mirrors; single-sourcing it so nothing can drift is tracked in #954. External contribution by @chethanuk.
Changed
- ROADMAP.md is reworked into a staged sprint plan. The tier/milestone mix is replaced by six themed stages continuing HISTORY.md's numbering — Stage 19 verification completeness, 20 single-source, 21 effect hardening, 22 the verified tool server, 23 agent experience, 24 browser — each with a rationale, an exit criterion, and an issue table, ordered from the design principles (verification truth first, then structural drift-proofing, then the flagship's capabilities, then the experience around them). The Stage 17 burndown set the model: a stage is a concentrated sprint over a coherent issue class, and it moves to HISTORY.md when its table empties. All 100 open issues are placed exactly once (staged, horizon-arc, ongoing, not-doing-now, or speculative; verified mechanically), pulling in the verification-limitation family that previously lived only in KNOWN_ISSUES.md; the browser sync-XHR fix (#355) moves from Http hardening to the browser sprint since every fix option shares the JSPI suspend machinery. Rides along: DESIGN.md's module row claimed "explicit re-exports", which don't exist (#127 is open) — it now says
public/privatevisibility and points at #127.
Fixed
-
A six-auditor documentation-consistency sweep reconciled every stale claim it could verify against the tree, the registry, and the tracker. The one behavioural-claim error:
vera/README.md§Runtime contracts still described the pre-#957 world — codegen "classifies contracts using the verifier's tier results" and omits Tier-1 guards — contradicting the architecture diagram above it; it now states the truth (compile never consults the verifier; guards are always emitted; the §11.8 aspiration stays tracked in #958). The rest is drift, each verified before fixing: SKILL.md's conformance count (103 → 143) and its spec table gaining the Chapter 13 row; the CLAUDE.md/AGENTS.md pipeline gaining the resolve stage; FAQ's feature bullets gainingIO.read_charand theExn<T>effect; README's "three-tier verification" delivered-claim scoped to the two implemented tiers; DESIGN.md's effect lists gainingHttpServer(andRandom/Divergerows) on a list that claims to mirrorvera effects --json, its Tier-1 coverage sentence aligned to spec §6.8, and the tiers + effect-row diagrams embedded; KNOWN_ISSUES gaining rows for the open #439 and #770 limitations; HISTORY's stage index gaining Stages 16–17 and the intro catching up to 94 development days;vera/README.md's module-map line counts regenerated from disk (worst drift:verifier.pylisted at 1,005 lines, actually 6,582); spec §12's Random prose no longer citing the closed #465 as a tracker and the heap-growth wording unified to "toward higher addresses"; andbuild_site.pynow rewrites image embeds to raw URLs (ablob/page is not image bytes), fixing the four diagrams inlined intollms-full.txt. Examples and conformance fixtures came back clean — no workaround shapes for fixed bugs survive there. -
Five stale bug-era annotations are retired from the test suite, each verified by running the affected tests before and after — found by the sweep's test auditors: the #869 table-forcing
array_foldis removed from the monomorphize fixture (the fixture itself is now the regression pin, sum unchanged); the #570-era "1,000 elements to stay under the bug threshold" GC graph is promoted to the true 5,000-elementArray<Box>wide graph its docstring always intended; the #516 module docstring no longer defers "Stage 2 (source mapping) and Stage 3 (Fix:paragraphs)" as future work — both shipped (v0.0.124/v0.0.125) and are exercised throughout the file;ch09_decimalnow exercisesdecimal_comparethrough a three-armOrderingmatch (the "not yet supported in codegen" note was disproven by running it); and the #773 scalar-only-Eqparenthetical speaks in the past tense. -
The verifier no longer proves false postconditions: a callee's
ensuresis now assumed only on the paths that establish it (#957)._translate_call_with_info(vera/smt.py) assumed each calleeensures— and each refined return's predicate — with a bareself.solver.add(...), which lands on the solver's base assertion stack.check_validfolds_path_conditionsaround the goal only, so a fact learned inside anifoutlived the branch and became an unconditional fact about the caller's own slots (_build_callee_envbinds the callee's parameters to the caller's terms). The escaped fact is circular:dec5 requires(@Nat.0 >= 5) ensures(@Nat.result == @Nat.0 - 5)injectsret == @Nat.0 - 5, which with@Nat's implicitret >= 0entails@Nat.0 >= 5— the very precondition the branch guard existed to establish. A caller's falseensures(@Nat.0 >= 5)then discharged, andvera verifyprintedOK/6 verified (Tier 1)with no diagnostic for a program thatvera runtraps on. Worse, two calls in mutually-exclusive arms inject contradictory facts, the base solver goes UNSAT, and every obligation discharges vacuously — silently deleting realE501diagnostics, including the ones the#776fix above adds. Each injected fact is now wrapped by_guard_factinz3.Implies(z3.And(*self._path_conditions), fact), matching the idiom_translate_matchalready used; the call translator was the only site that did not. Same soundness as discarding the facts, strictly more precision. Behaviour-neutral across all 143 conformance programs and 37 examples (tier11,464 → 1,464,tier3594 → 594, no diagnostic changes) and a legitimately branch-guarded call is not over-rejected. Note that the compiled program was never affected:vera compiledoes not consult the verifier, so the runtimecontract_failguard is emitted regardless of tier — the break was invera verify's claim. That mitigation is accidental rather than designed, andspec/11-compilation.md§11.8 (which promises Tier-1 contracts are omitted) is tracked as the drift it is in #958. Found by the adversarial review of #953. -
A call's precondition is now statically checked (
E501) even when the call sits inside an effect-operation argument (e.g.IO.print(need_pos(...))) (#776).translate_expr(vera/smt.py) had noQualifiedCallbranch, so an effect op (Module.op(args)) fell through toreturn Nonewithout recursing into its arguments — a precondition-bearing call passed as an argument was never visited, and noE501fired for a violatedrequires(...). AQualifiedCallbranch now walks each argument viatranslate_exprfor theE501precondition side effect (the#727span-keyed dedup keeps it duplicate-free) and returnsNone— the effect op itself is still not Z3-translated, since its result is chosen by the handler and translating an effect in a contract would be unsound (pinned bytest_effect_op_never_becomes_a_z3_term: returning an argument's term instead makes the verifier prove a postcondition only the handler could satisfy, reportingverifiedwhere it should reporttier3). Translating aFnCallargument does more than record itsE501: it also assumes the callee'sensures. That assumption is scoped to the branch it was learned in by the new_guard_fact(see the next entry) — without it, routing effect-op arguments into the call translator would have converted programsmaincorrectly rejected into programs it silently accepted. Mirrors the#730statement-position (ExprStmt) handling, whose walk reaches the same unguarded assumption and is tracked separately. -
The diagnostic-fields gate's plumbing-skip no longer swallows a stray
Diagnostic(#827).scripts/check_diagnostic_fields.pyexempted everyDiagnostic(...)lexically inside any function named_error/_warning, on the premise that such a construction is the helper's own plumbing. The name alone is not enough: a second, under-tagged ctor in the same helper (say, in anelsebranch), or a module-level function coincidentally named_error, escaped all three of the field-presence,spec_ref-validity anderror_code-registration passes — #826 had propagated the same name-based skip into the latter two. Latent rather than live (all five real helpers invera/construct exactly oneDiagnosticeach, so nothing escaped in practice), but a blind spot in the gate that exists to close exactly this class of silent under-reporting. A ctor is now skipped only when its enclosing function is a genuine helper method — a class member with aselfreceiver, so a module-level or@staticmethodlook-alike is inspected, not exempted — and it is that method's sole own-scope construction, counted without descending into nesteddef/lambda/classscopes. A helper holding two constructions is ambiguous: neither is skipped, and both are inspected. Counting every own-scope construction (rather than only the one structurallyreturned or.append(...)-ed) is what makes the rule sound — a helper that binds its real diagnostic to a local first (d = Diagnostic(...), thenself.errors.append(d)) would otherwise leave a stray direct ctor as the only recognised construction, which would then be elected as the helper's own and skipped. It also means an ordinary hoist-to-a-local refactor no longer trips the gate onspec_ref is not a string literal. "Own scope" is the helper's body: aDiagnostic(...)written in a decorator argument, a parameter default, or a return annotation is evaluated in the enclosing scope, is never the helper's plumbing, and is always inspected — counting those would let one be elected as the helper's sole construction and skipped, which is the escape this gate exists to prevent. Every exemption therefore lies inside a function span the previous rule already exempted, so the exempt set can only shrink and the change cannot open an escape; onvera/it resolves to the same five plumbing ctors as before. External contribution by @chethanuk. -
The landing page's loop figure is rebalanced and its claims audited against the tree.
docs/loop-web.svgcarried 70 canvas-units of dead space above the drawing and 166 to its right (vs 30 left / 18 below), so the embed rendered top-heavy and visually left-shifted; the viewBox is recropped symmetric (12 units each side, browser-measured) and the embed's margins equalised — 48px of air above and below. The audit that rode along, every claim verified against the registry or a live run: the effect enumeration gainsDivergeand its count corrects to nine (vera effects --json; the same one-word gap inREADME.mdrides along); theInferenceprovider list gains Mistral (vera/runtime/inference.pydetects four keys, both here and in the generatedindex.md); the three-tier feature card is scoped to what shipped, mirroring README's phrasing (the Z3-guided middle tier is specified, not implemented); the WASI showcase block now showsvera compile --target wasi-p2 --world server's actual output line and a consistentexamples/http_server.wasmartifact path (previous text was a paraphrase; thewasmtime servebanner was verified against a real run); and thesafe_divide/classify_sentimentsamples link theirexamples/counterparts the way the fizzbuzz sample always did — with the fizzbuzz link itself backfilled into the generatedindex.md, which had never carried it.
Security
- The
wasmtimeruntime floor is raised to>=46.0.1, off a release affected by GHSA-4ch3-9j33-3pmj (medium; #949). The advisory is awasmtime-wasiFilePermsbypass on hard-link and rename destinations; its affected range includes45.0.0, whichuv.lockhad pinned. The upstream core backport is45.0.3, but thewasmtime-pybinding never tagged a45.0.xpatch —46.0.1is the only patched binding release, so the floor moves up a major rather than sideways. Vera's own WASI host (vera/runtime/wasi_host.py) is not exploitable: it preopens with the defaultREAD_WRITEFilePerms, so there is no restricted permission for a link/rename to bypass. CI already resolved the unbounded floor to46.0.1; this pins the lockfile and the declared floor off the affected release for minimum-version resolvers (uv sync, distro packagers, pre-existing venvs).