Aver Decisions - decisions/architecture.av
July 9, 2026 · View on GitHub
Generated by aver context --decisions-only
selection: mode: auto, budget: 20kb, included_depth: 0, selected: 15/15, truncated: false, used: 10529b
HostLanguage (2024-01-10)
Chosen: "Rust" Rejected: "Python", "Go", "Haskell" Impacts: "Lexer", "Parser", "Interpreter", "Checker"
Rust provides memory safety without a garbage collector, which matters for a language runtime. Its algebraic types (enum, Result, Option) map directly onto Aver concepts and kept the implementation honest. The rich ecosystem (clap, colored, thiserror) let us ship a polished CLI without boilerplate.
Author: Aver core team
SignificantIndentation (2024-01-20)
Chosen: "Indentation" Rejected: "Braces", "BeginEnd", "Keywords" Impacts: "Lexer", "Parser", "AllModules"
Braces are syntactic noise that adds no meaning and forces style debates. Indentation is already how humans read code, so making it structural removes a class of inconsistency. The lexer emits explicit INDENT and DEDENT tokens, keeping the parser context-free and easy to extend.
Author: Aver core team
ResultOverExceptions (2024-01-15)
Chosen: "Result" Rejected: "Exceptions", "Nullable", "Panic" Impacts: "Calculator", "Interpreter", "Checker"
Exceptions hide control flow: a function that throws is indistinguishable from one that does not at the call site. Result types make every error path visible in the signature, which is essential when an AI reads code without running it. The match expression and the ? operator together give ergonomic error handling without sacrificing explicitness.
Author: Aver core team
ColocatedVerify (2024-01-25)
Chosen: "ColocatedVerifyBlocks" Rejected: "SeparateTestFiles", "ExternalTestFramework" Impacts: "Checker", "AllFunctions"
Tests that live in a separate directory rot faster because nobody reads them when changing the function. A verify block immediately below its function is the minimum viable specification: it says what the function must do. Co-location also makes it trivial for an AI to regenerate or extend tests without searching the repository.
Author: Aver core team
DecisionBlocksAsLanguageFeature (2024-02-01)
Chosen: "FirstClassDecisions" Rejected: "MarkdownADR", "Comments", "ExternalWiki" Impacts: "Lexer", "Parser", "Checker", "AIDeveloperExperience"
Architecture Decision Records written in markdown files are invisible to the language tooling and decay silently. Making decisions a first-class parse node means they can be indexed, searched, and rendered by any tool that reads Aver. When an AI resumes work on a codebase, it can run aver context with --decisions-only to reconstruct the design rationale without reading prose.
Author: Aver core team
HybridExecutionModel (2026-03-01)
Chosen: "InterpreterPlusVMPlusTranspiler" Rejected: "InterpreterOnly", "VMOnly", "TranspilerOnly" Impacts: "Interpreter", "VM", "Transpiler", "CLI", "Performance"
Interpreter keeps feedback loop fast for language work (run/check/verify/context). Bytecode VM gives a real runtime path for run/verify/replay without leaving Aver source or its effect model. Transpilation gives a deployment path to native projects without shipping the Aver runtime. These three paths solve different problems, so they should coexist instead of replacing each other.
Author: Aver core team
RecordsAreDataMethodsInModules (2026-02-25)
Chosen: "DataOnlyRecords" Rejected: "RecordMethods", "ImplicitThis" Impacts: "TypeSystem", "RecordModel", "ModuleNamespaces"
Keeping records as data-only keeps the object model small and predictable. Module namespaces already provide a clear place for behavior via Module.fn(record, ...). This avoids introducing two parallel styles too early: methods on records and module functions.
Author: Aver core team
EffectContractsAreTransparentAcrossModules (2026-02-25)
Chosen: "TransparentAcrossModules" Rejected: "OpaqueModuleContracts", "ImplicitModuleCapabilities" Impacts: "ModuleSystem", "Effects", "TypeChecker", "RuntimeGate"
Current Aver model keeps module imports and capability effects as two explicit contracts.
depends [X]controls which module code is visible, while! [Effect]controls which side effects a function may use. Effect requirements propagate through cross-module calls, so callers must declare the effects required by imported functions.
Author: Aver core team
ConstructorContract (2026-02-26)
Chosen: "UpperCamelCalleeContract" Rejected: "TypeAscriptionBased", "KeywordConstructors", "UniformParens" Impacts: "Parser", "Interpreter", "TypeChecker", "AllModules"
Constructors need a single, unambiguous parsing rule that does not conflict with function calls or type annotations. UpperCamel callee distinguishes constructors from functions at parse time with one token of lookahead. Records use named args (=), variants use positional args. Zero-arg constructors are bare singletons without parens. This removes the need for TypeAscription and all cross-cutting heuristics in the parser.
Author: Aver core team
FunctionBodiesUseIndentationOnly (2026-03-06)
Chosen: "IndentedBodiesOnly" Rejected: "EqShorthand", "EqOnlyForPassthroughs", "DualBodyForms" Impacts: "Parser", "REPL", "Formatter", "Docs", "Examples", "AllFunctions"
Aver block bodies already return their last expression, so
= exprduplicated an existing body model without adding semantics. Using syntax to hint 'this is probably a trivial wrapper' blurred the boundary between surface syntax and checker heuristics. One body form is easier for humans to scan, easier for AI to emit consistently, and cheaper to maintain across parser, REPL, formatter, docs, and tests.
Author: Aver core team
ListBranchingStaysInMatch (2026-03-06)
Chosen: "MatchOwnsListBranching" Rejected: "FunctionClauses", "ListSpecificFunctionSyntax", "ErlangStyleHeads" Impacts: "Parser", "PatternMatching", "TypeChecker", "Docs", "AllListFunctions"
List cases are pattern matching, not a separate kind of function definition. Pulling list patterns into function clauses would create a second branching notation next to
match, weakening one of Aver's core simplifications. Keeping list branching insidematchpreserves one place for exhaustiveness, one place for pattern syntax, and one mental model for recursive case splits.
Author: Aver core team
Wasip2ComponentTarget (2026-05-08)
Chosen: "Wasip2ComponentTarget" Rejected: "KeepLegacyNanBoxedAsStandalonePath", "WaitForWasi03", "ShareRuntimeViaSidecar" Impacts: "Codegen", "EffectMatrix", "Docs", "CLI", "ReleasePath"
WASI 0.2 with the Component Model is the modern WebAssembly contract for hosts that are not browsers — wasmtime, Spin, NGINX Unit, wasmCloud, and Fermyon Cloud all consume
.component.wasmnatively, and the WIT-typed import surface beats the ad-hocaver/*bridge that legacy wasm exposed. GC values do not cross component boundaries today (canonical ABI for GC is still pre-proposal), which forces a clean architectural split: per-instantiation Map/List/Vector helpers stay inside the user core module, and the public ABI exposed to other components stays canonical-typed. Sync handlers behind incoming HTTP are bridged host-side by wasmtime fibers in 0.2 and that path is committed to keep working under 0.3 hosts, so Aver's source-side sync model needs no compromise.
Author: Aver core team
DropLegacyNanBoxedWasm (2026-05-08)
Chosen: "DropLegacyAfterGreenWasip2Matrix" Rejected: "KeepBoth", "DropLegacyFirst", "FeatureFlagOffByDefault" Impacts: "Codegen", "Docs", "CLI", "Tests", "ReleaseTrain"
After
--target wasip2covers the standalone-WASI cells in the effect matrix, the legacy--target wasmbackend has zero unique capability —--bridge fetchis replaced by wasm-gc plus--handler,--bridge wasip1is replaced by the component target, and--bridge nonewas an embedder hook nobody used. Carrying twelve thousand lines of NaN-boxed runtime, the wasm-merge toolchain dependency, and a four-cell--bridgematrix to support a backend with no remaining users is documentation overhead rather than engineering value. Audit confirms zero hard runtime coupling between wasm-gc and legacy: shared touch points are stale comments, a single feature-cfg gate, and a clap enum that lumps targets together for one UI helper, so the cut is mechanical rather than invasive.
Author: Aver core team
PlanFirstArtifactCertification (2026-07-08)
Chosen: "PlanFirstCanonicalLowering" Rejected: "TraceGuidedAcceptance", "AdHocWholeFunctionRecognizers", "TrustedCompilerMetadata" Impacts: "Certification", "WasmGC", "Lean", "Verifier", "Codegen"
Aver controls the wasm-gc codegen for certified exports, so the verifier should not recover intent from arbitrary stack-machine bytecode. The certificate sidecar should be an untrusted plan witness; the verifier typechecks that plan, canonical-lowers it in the actual module context, and compares the expected body with the byte-derived function body. Trace-guided replay remains useful only as temporary migration/debug scaffolding. Keeping it as a permanent acceptance fallback would preserve a second lifter-shaped TCB and weaken the canonical certified-island discipline.
Author: Aver core team
PlanEmittedCanonicalCodegen (2026-07-09)
Chosen: "UnconditionalCanonicalPlanEmission" Rejected: "CertifyGatedEmission", "SilentMirFallback" Impacts: "Codegen", "WasmGC", "Certification"
Plan-shaped functions lower through the canonical certification plan lowerer on every build, not only under
--certify, because the certificate must certify the exact bytes users ship — gating canonical emission on the certify flag would let the sidecar describe a body the production build never emits. Lowering failure stays a loud compile error: a plan that cannot be canonical-lowered aborts the build rather than silently diverging into a separately-emitted MIR body, so the certified path and the shipped path can never disagree. The canonical plan shape carries a carrier scratch local, so plan emission engages only in modules that declare the Int carrier type (theaint_struct_idxgate) — a documented, semantics-neutral byte-shape dependency that a future carrier-free locals profile would remove.
Author: Aver core team