Wado

Research: Language Survey — Almide

Survey against the rubric.

almide/almide — 7827 commits / first 2026-03-07 / surveyed at b900c57ed 2026-08-31

Arbiter: "Almide optimizes for minimal thinking tokens: the less an LLM has to branch over syntax, semantics, repair strategies, or missing abstractions, the faster, cheaper, and more reliable code generation becomes." — opens docs/design/DESIGN.md.

A statically-typed language compiling to native (via Rust) and to wasm, holding byte-identical observable output across the two as a hard invariant. Its stated metric is modification survival rate: how often code still compiles and passes its tests after a series of AI-driven edits.

Examined: README.md, llms.txt, docs/CHEATSHEET.md, the five files under docs/design/, docs/adr/README.md, docs/specs/edit-locality.md, docs/STABILITY.md, docs/TRUST-SPINE.md, docs/contracts/proven-vs-trusted.md, the header and schema of docs/contracts/contracts.toml, docs/project/BENCHMARKS.md, demo/make-verify/README.md, research/grammar-lab/REPORT.md, and the stdlib/, runtime/ and crates/ trees by listing, count and sampled reading.

Not examined: the 1,607-line SPEC.md and the rest of docs/specs/; the 327 contract entries themselves, as against the schema they follow; the ADR bodies beyond their section structure and two sampled falsifiers; what the Lean belts prove, as against the grep showing they prove it without sorry.

A. The language

Axis Claim Reality Holds in self-application Unimplemented / Rejected
A1 Canonicity "One name per operation, no aliases" (DESIGN.md); the cheatsheet lists string.length as wrong Prose convention only; nothing checks it No — stdlib/string_len.almd defines string.length as an alias of string.len, and list.len/list.length both map to almide_rt_list_len —
A2 Type vocabulary Invented; primitives cut to Int, Float, String, Bool, Unit, Path Sized integers exist as conversion modules (int8…uint64) reached by UFCS, not as surface types. There is no external ABI the vocabulary has to mirror — —
A3 Effects effect fn marks I/O; a pure fn cannot call one (E006) One bit. No effect names, no polymorphism, no handlers Yes Rejected — REJECTED_PATTERNS.md refuses algebraic effects, citing Gleam
A4 Errors Result[T, E] only; T! is Result[T, String], T!E is Result[T, E], T? is Option[T] Propagation is ! and always explicit (ADR-0008 abolished auto-?); ! never converts E, a mismatch stays a type error (ADR-0003) Yes Rejected: exceptions, null, implicit conversion at propagation
A5 Concurrency fan, structured, no async/await Deterministic by construction: fan.race picks the winner by least compute spent, ties by source order, same answer on every target and machine. Compute and Duration are separate types; a bare Int is not a time and there are no literal suffixes Yes Rejected: async/await (function colouring), goroutines, exposed Future[T]
A6 Boundary mechanisms No macros, no reflection No generation a user can invoke. The TOML and build.rs that REJECTED_PATTERNS.md names as the alternative to macros are the compiler's own build — codegen/templates/rust.toml is the Rust backend's syntax table, "pure formatting, no semantic logic". Serialization does exist: Codec, derived by writing : Codec, produces Value, with JSON the only format on the far side — Rejected: macros, reflection, monkey patching, and executable build scripts — almide.toml is declarative TOML only, because setup.py-style executable configuration destroys reproducibility
A7 Hidden operations Zig's "no hidden control flow" cited, then deliberately departed from docs/design/HIDDEN_OPERATIONS.md enumerates five, each with trigger condition, implementing file, and user impact: clone insertion, runtime embedding, Perceus RC insertion, fan threading, and one entry recording that auto-? was removed — —

A4 comes with a stated doctrine that the other axes do not have. Errors have two jobs, and the choice of E is which job you are doing.

E = String is the reporting channel, and the default. A variant E is the branching channel, and it only earns its cost inside a closed domain — a module or package that handles that error itself. Leaving that domain demotes the variant back to String, and the demotion is always visible as a map_err, because there is no conversion hook that could do it quietly.

Two lints hold the doctrine up. E035 warns when code branches on an error's message text. E036 warns when a map_err takes a named parameter and then drops ${e} from the result.

The self-application cross-check

There are two implementations of the semantics: a Rust runtime for native, and self-hosted .almd for wasm.

Almost everything under B6, and most of C4, is the bill for that. The 327-contract ledger, the 610 cross-target fixtures, the three-way oracle against the interpreter, the differential fuzz — all of it exists to hold two implementations to one answer. The byte-identity claim is what that buys.

The hardest program written in Almide is its own standard library. The compiler itself is Rust, 836 files across 24 crates, so the strongest form of this check — a compiler written in the language — is not available here. The stdlib has to carry it alone.

232 of the 309 stdlib files, or 75%, call prim. The module's own header calls it "the PRIMITIVE FLOOR: raw memory access … UNSAFE: addresses are unchecked". In practice that means hand-written address arithmetic:

let x = prim.load64(sh + 12 + i * 8)
prim.store_str(dh + 12 + i * 8, y)

The public surface is 971 functions across 43 modules, and they are declared generically with empty bodies — the .almd files carry 808 @intrinsic annotations. The real bodies are in two other places: runtime/rs (42 Rust files, 12,941 lines) for native, and the self-hosted .almd files for wasm.

Generics reach the declaration but not the implementation. List[A] is a single generic signature sitting on top of one hand-written file per type pairing: list_map_s2h (scalar source, heap result), list_fold_hsca, list_to_string_ll, list_filter_str, map_skv, option_to_string_oi. The compiler picks between them.

list_map_s2h's own comment says what happens when it picks wrong: list.map_str "would misread the i64 scalar slot as a String handle and corrupt it". In total, 3,955 function definitions stand behind 971 public ones.

So three of the surface claims — no raw pointers, generics, no aliases — do not survive contact with the language's own standard library.

B. The practice

Axis Present Scope
B1 Arbiter Yes One sentence, opening the philosophy document; every other section derives from it
B2 Accept/reject criteria Yes, numeric Rejected if median MSR drops ≥ 3 points on any model, or if it introduces a second canonical form, or if it trades generation predictability for human aesthetics. Accepted if MSR measurably rises, or if it removes an error class that retry-with-diagnostic cannot repair
B3 The "why" axis Yes docs/adr/, 12 records. Its README states the problem directly: roadmap and spec both fail to preserve why, so the same argument is replayed and a rejected proposal returns with its rationale lost
B4 Falsifier Yes, mandatory, and obeyed Template field, with the rule "a decision whose retraction condition cannot be written is a preference, not a decision". All twelve ADRs carry it, and carry Alternatives too. The ones sampled hold a threshold, a named fallback position and, in ADR-0001, the experiment that will produce the number: if the calibration gate measures a spread beyond the declared band, roughly 5×, the millisecond label comes off and the units go back to dimensionless
B5 Rejection record Yes docs/design/REJECTED_PATTERNS.md, 154 lines, with an operating rule: add on rejection with the reason, remove only on an edition-level change of direction, cite it when a PR proposes the feature. Carries a reversal in place — ?? was rejected, then reinstated by ADR-0005, and the rejection is kept as history
B6 Sync gate Yes, partial check-readme-numbers.sh refuses a bare number in the README, gen-claims.sh generates the public claims block from the contract ledger, check-contracts.sh fails on drift. Scope is numbers and contracts. Prose is unguarded, which is how A1 leaked
B7 Self-reported violations Yes docs/specs/edit-locality.md §3 lists eight standing violations of its own L1 invariant with mechanism and file:line, triaged into language bugs, backend obligations, and declared side conditions
B8 Who decides No rule The ADR rules constrain a decision's quality, not its author

One invariant is worth naming, because it is what all the surface rules serve. They call it L1, the "edit frame":

Take a well-typed program and edit one definition's body without changing its signature. Every execution that never enters that definition must produce identical observable output.

edit-locality.md §2 maps each language rule to the role it plays in enforcing L1: mandatory return types, no overloading, no glob imports, module-isolated inference, no dynamically scoped handlers. §5 turns it into a gate — every change has to answer "does this preserve L1?" before it can land.

Two of the project's rules are unchecked prose, and only one of them leaked. Nothing enforces the mandatory falsifier, and all twelve ADRs have one. Nothing enforces "no synonyms", and the standard library has synonyms.

The difference is where you have to remember the rule. A falsifier is a heading in a template the author already has open, so an empty one is visible while the decision is being written. Not adding a synonym has to occur to you months later, at an unrelated keystroke, with nothing on screen to prompt it.

So: a rule attached to the thing it governs holds without a gate. A rule you have to remember somewhere else needs one.

The boundary map, and what a certificate does not buy

docs/contracts/proven-vs-trusted.md opens with the question an auditor asks first about a compiler that ships proofs: which part is actually proven? It answers before it sells anything.

An accepted certificate proves the function is memory-safe … It proves nothing about whether the lowering picked the right semantics: a certified- sound function can still print the wrong string.

Under that is a table with one row per pipeline stage. Each row is marked proven, trusted, or unqualified tool, and says what backs it. The lexer and parser are trusted, on differential fuzz. The type checker kernel is proven in Coq. The MIR ownership witness is "proven to be re-checkable": an untrusted producer emits a witness and the kernel-proven checker re-verifies it. wasmtime is out of scope by construction.

The document also names which row is the gap that matters and the issue tracking it. And it scopes itself to one of the two wasm legs, warning the reader that nothing in it describes bytes the other leg produced.

TRUST-SPINE.md states the architecture in one move. Do not prove the compiler. Prove a tiny checker, and have the compiler emit a certificate on every build that the checker re-verifies.

That works because building is hard and checking is cheap. The compiler is allowed to have bugs: if it produces a wrong artifact, the attached certificate fails to check. The only theorem needed is "if the checker accepts, the artifact has the property", and that theorem never mentions the compiler's internals. The trusted base drops from the whole compiler to 1,348 lines of OCaml extracted from the proofs — a figure regenerated into the document between markers, so it cannot drift.

What makes it worth reading is that this advocacy piece states its own limits. There is no mechanized evaluation relation for Almide source, and no theorem of the shape ⟦s⟧ ≈ ⟦compile(s)⟧. The translation checker does a structural realization check, not a semantic refinement proof. Stack balance and termination are proven in the Rocq spine but are not extracted into the checker and not witnessed per build. Verified extraction is called a future ratchet, not a present fact.

Byte-for-byte agreement between the two targets is the project's headline claim, and the document says in the same paragraph that it "is established empirically". CI is deliberately placed outside the trusted base, as a courtesy pre-run, so that trusting the artifact never requires trusting their infrastructure.

The contract ledger runs the same traceability the other way. Each of its 327 entries is a named promise, carrying both the specification section it certifies and the fixture that executes it. The gate makes that link mandatory in both directions: a specification heading no contract cites fails, and so does a contract citing no heading. The stated purpose is that "no claimed behaviour rests on prose alone".

C. What outlives it

Kind What Externality Contender Wins
C1 Artifact None. Largest non-stdlib, non-test program is tools/almide-gates (~2,000 lines), the project's own CI gate. Then a stdlib crawler, a Lisp interpreter, a raytracer — — —
C2 Method research/grammar-lab/ — deciding syntax by measured LLM A/B rather than by taste High A language designer's intuition Yes
C2 Method The contract ledger: named C-NNN promises, bidirectionally linked to fixtures, CI-enforced, with a ratcheted counter that may only fall High Prose release notes Yes
C2 Method ADR with a mandatory falsifier, plus a rejection record with an operating rule High Git history as the record of why Yes
C3 Proof Three Lean 4 belts: almide-edit-belt for the edit frame and modular typing over the kernel calculus, almide-perceus-belt for the RC discipline, almide-race-belt. No sorry in the 18 .lean files, grepped rather than built High — theorems about a calculus, not about adopting Almide — —
C3 Proof The v1 trust spine: emit a certificate per build and re-verify it with an extracted checker (~1,400 lines of OCaml), collapsing the trusted base from the ~100k-line compiler High Testing the compiler Claimed, not yet complete
C4 Corpus 610 cross-target fixtures, 327 contracts, 426 .almd spec test files Medium — —

C is thick in method and proof and empty in artifacts. That follows from B1: a project that picked a measurable arbiter went and built measuring and verifying equipment. It is not an effect of being young — the Lean belts and the syntax A/B harness are in a repository whose first commit is 2026-03-07.

A6 limits C1 from the other side. A wire format does have a route in, through Codec. A foreign grammar or IDL does not: there is no generation mechanism, so nothing can turn one into Almide source at build time, and the only way in is by hand. That means the programs you can write are its own parts plus whatever speaks JSON. No amount of effort would have produced a Gale here, because a Gale starts by consuming a grammar file.

research/grammar-lab/REPORT.md is the clearest example of the method. The short lambda (x) => expr was tested against the then-current fn(x) => expr by transpiling model output, across 10 tasks × 3 trials. The result was 86% against 86%, p = 1.0 on Haiku, and 100% against 100% on Sonnet. The fn lambda form was removed on the strength of that.

Where the thesis stands

demo/make-verify/README.md records the headline metric failing, and the framing being rebuilt around the failure.

Measured directly, modification survival came out at roughly 100% in every language, for a strong model on moderate tasks. That is a ceiling effect, so as they put it, "that is the wrong thing to measure."

The replacement injects eight realistic modification mistakes and asks whether the language makes each one visible at author or CI time. Almide caught 6 of 8 at compile time; Python's py_compile caught 0. The MSR paper and its ablation study are on hold in docs/roadmap/on-hold/.

All of this evidence is self-referential: the project's own benchmark, its own metric, its own injected faults. Nothing is measured against an outside yardstick.

For Wado

Learned:

Take:

Refuse:

Hold: