-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathllms.txt
More file actions
227 lines (185 loc) · 64 KB
/
Copy pathllms.txt
File metadata and controls
227 lines (185 loc) · 64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# Almide
> The programming language LLMs can write most accurately. Pure-Rust
> compiler, `.almd` source files, dual-target codegen (Rust, WASM).
> Every design decision serves one metric: **modification survival rate**
> on the [almide-dojo](https://github.com/almide/almide-dojo) MSR benchmark.
If you are an LLM writing Almide code, read this file first. It is
concise on purpose. Follow the link map for depth.
## Fast facts
- Extension: `.almd`. Stdlib modules `string`, `int`, `float`, `list`,
`map`, `set`, `option`, `result`, `value`, `env`, `error`, `math`,
`datetime` are auto-imported.
Sized-numeric conversion modules (`int8`, `int16`, `int32`, `uint8`,
`uint16`, `uint32`, `uint64`, `float32`) are also auto-imported for
UFCS — e.g. `x.to_int64()` on `x: Int32` resolves to `int32.to_int64(x)`.
`json`, `fs`, `http`, `regex`, `datetime`, `bytes`, `matrix`, `process`,
`testing`, `random`, `io` need explicit `import`.
- Null is spelled `Option[T]`. Errors are spelled `Result[T, E]`. No
`null`, no `throw`, no exceptions.
- Effectful functions are written `effect fn f() -> T`. Pure functions
cannot call effect functions (checked, emits E006).
- Generics use `[T]` (not `<T>`). Variants use leading `|`.
- `match` is exhaustive; missing arms emit a warning.
- Multiple lets chain by newline (no `in`). No `let rec`, no `head :: tail`
cons, no `[a, ...rest]` spread in list patterns.
## Link map
| Topic | File |
|---|---|
| Syntax + stdlib idioms cheatsheet | [docs/CHEATSHEET.md](docs/CHEATSHEET.md) |
| Design philosophy (ambiguity removal) | [docs/DESIGN.md](docs/DESIGN.md) |
| Full language spec | [docs/SPEC.md](docs/SPEC.md) |
| Grammar | [docs/GRAMMAR.md](docs/GRAMMAR.md) |
| Stdlib spec per module | [docs/specs/](docs/specs/) |
| Module system (import, sub-modules, diamonds) | [docs/specs/module-system.md](docs/specs/module-system.md) |
| Package system (deps, MVS, versions) | [docs/specs/package-system.md](docs/specs/package-system.md) |
| Error-code reference | [docs/diagnostics/](docs/diagnostics/) (run `almide explain E001`) |
| Patterns that are rejected on purpose | [docs/REJECTED_PATTERNS.md](docs/REJECTED_PATTERNS.md) |
| Recent changes | [CHANGELOG.md](CHANGELOG.md) |
## CLI
```
almide check <file> # type-check only
almide run <file> # compile + execute
almide build <file> -o app # build binary (default: Rust target)
almide build <file> --target wasm
almide test [pattern] # run tests (*.almd with `test { }` blocks)
almide fmt <file> # format
almide compile [module] # emit .almdi module interface
almide explain E001 # expand a diagnostic code
almide ide outline <file> # one-line-per-decl summary
almide ide outline @stdlib/string # stdlib API listing
almide ide doc string.to_upper # signature + doc for a symbol
almide ide stdlib-snapshot [--json] # concatenated stdlib outlines for
# SYSTEM_PROMPT injection (~3.5K tokens)
```
## Core idioms
### Prefer match over if-chains
```almide
// ✗
if kind == "int" then "i64"
else if kind == "float" then "f64"
else "unknown"
// ✓
match kind {
"int" => "i64",
"float" => "f64",
_ => "unknown",
}
```
### Prefer list combinators over `var + for`
```almide
// ✗
var result: List[String] = []
for item in items { result = result + [transform(item)] }
result
// ✓
items |> list.map(transform)
```
### Recursion for stateful loops (no head :: tail)
```almide
fn skip_ws(s: String, p: Int) -> Int =
if p < string.len(s) and is_ws(peek(s, p)) then skip_ws(s, p + 1)
else p
```
For head/tail on lists, use `list.first` / `list.drop(xs, 1)`:
```almide
match xs {
[] => base,
_ => {
let h = list.first(xs)!
let t = list.drop(xs, 1)
// ...
},
}
```
### Effect fn with `guard else err(...)`
```almide
effect fn open_file(path: String) -> Result[String, String] = {
guard fs.is_file(path) else err("not a file: ${path}")
fs.read_text(path)!
}
```
### `??` / `?` / `!`
```almide
option.get(v, key) ?? default // Option fallback
result.get(v, key)? // Result → Option (inside a fn returning Option)
parse_int(s)! // unwrap a Result, propagate err (effect fn only)
```
### Operators, not comparison functions
Almide uses operators for comparison and equality. There is no
`int.gt`, `int.lt`, `int.eq`, etc. Use `>`, `<`, `==`, `!=`, `>=`, `<=`.
Equality (`==` / `!=`) works on any type.
### sqrt, abs, etc.
`int.sqrt` does not exist. Convert, sqrt, optionally convert back:
```almide
let root_f = float.sqrt(int.to_float(n))
let root_i = float.to_int(root_f) // truncating
```
`int.abs`, `float.abs` do exist.
## Diagnostic codes LLMs hit most (retry anchors)
| Code | Meaning | Usual fix (from `try:` snippet) |
|---|---|---|
| E001 | Type mismatch | Often Unit-leak: add a trailing expression, or rebind via `let new_x = if cond then … else …` |
| E002 | Undefined function | Often a cross-language idiom (`length` → `len`, `push` → `+ [x]`). Check `almide ide outline @stdlib/<m>` |
| E003 | Undefined variable | Missing explicit `import json` / `import fs` / etc |
| E004 | Arg count mismatch | Signature placeholder in try: shows `<name: Type>` |
| E005 | Arg type mismatch | Often needs explicit conversion (`int.to_string(n)` etc) |
| E006 | Pure fn calls effect fn | Mark the caller `effect fn` |
| E007/E008 | Fan block misuse | See `docs/specs/fan.md` |
| E009 | Reassign immutable | `let` → `var` |
| E010 | Non-exhaustive match | Add the missing arm(s) or `_ => ...` |
| E011 | Mutable var mutated inside closure in pure fn | Refactor with `list.fold` / `list.count` / etc, or mark fn `effect fn` |
| E012 | Duplicate definition (fn / test) | Rename one side |
| E013 | Field access on non-record / missing field | Check spelling; non-records don't have method syntax (see E002) |
| E014 | Unreachable match arm | Drop the dead arm, or tighten the earlier arm so this one is reachable |
| E015 | Possible stdlib reimplementation (warning) | Delegate via the `try:` shim, or rename if intentionally different |
| E016 | Function in a `Set` element or `Map` key | Keep closures in a `List`, or use a comparable key (closures are fine as `Map` values) |
| E017 | Record construction on an enum type name | Construct a specific record-bearing case (`Named { ... }`), not the enum type |
| E018 | Empty collection with an uninferable element type | Annotate the element type (`let xs: List[Int] = []`), or inline (`list.len([]: List[Int])`) — never silently defaulted (Rust/Swift rule) |
| E019 | Ambiguous variant constructor (same ctor name in two or more types) | Rename the constructor in one of the types so the name is unique (a qualified `Type.Ctor` is not yet supported) |
| E020 | Two distinct types share one name with different structures | Rename one of the colliding types |
| E021 | Invalid record construction/pattern shape (positional args on a record, unknown/duplicate/missing field, paren pattern on a record case) | Records take named fields: `Cfg(name: x)` or `Cfg { name: x }`; match record cases as `Case { .. }` |
| E022 | `!` (unwrap-propagate) outside an effect fn body or test block | Mark the enclosing fn `effect fn`, or use `??` for a fallback value (`!` inside a lambda can't propagate) |
| E023 | Derived `Codec`/`Ord`/`Hash` not satisfied by a field type | Add `: P` to the offending field type (every field of a `: P` type must itself be `P`) — or hand-write `Type.encode`/`decode` for Codec |
| E024 | Integer literal out of range for its type (would silently fold to 0) | Use a literal within the type's range; for large magnitudes use `UInt64` (or `Float`, lossy); `-9223372036854775808` (i64::MIN) is valid |
| E025 | Cannot infer a concrete type — an undecidable type slot (e.g. the error type of a value that is always `ok(...)`) was never pinned | Annotate the binding with its full type (`let r: Result[Int, String] = ...`); an unconstrained slot is an error even if never constructed, never silently defaulted |
| E026 | `[]` indexing on a String (a UTF-8 codepoint sequence, not a byte/char array) | Use `string.get(s, i)` (returns `Option[String]`) or `string.char_at(s, i)`; range slicing `s[a..b]` is fine |
| E420 | Function visibility violation (callee is private) | Make the callee public, or add a public shim |
A rustc 4-digit code (`error[E0XXX]`) leaking through always indicates
an Almide codegen bug, not a user error — report at the issues URL.
## Project layout
```
mypkg/
almide.toml # [package] name = "mypkg"
src/
main.almd # entry: `import self.<sibling>`
classifier.almd # sibling module
```
Sibling import is `import self.classifier` — no file paths.
## What Almide is NOT
- Not Haskell/ML — no `let rec`, no `let … in`, no type classes (protocols exist but have explicit `impl`), no HKTs.
- Not Rust — no lifetimes, no borrow checker surface syntax, no `unsafe`, no traits-as-data-attributes.
- Not JS/Python — no dynamic typing, no `null`, no exceptions, no method-call syntax (`x.foo()` on non-UDTs).
Cross-language idioms that compile in other languages and break here:
`let x = … in body` (OCaml), `head :: tail` (Haskell), `[a, ...rest]`
(JS/Rust), `int.gt(a, b)` (Go-ish), `x.toUpperCase()` (JS method chain).
Diagnostics for each carry a `try:` snippet that shows the Almide form.
## For agents and harnesses
- Start by calling `almide ide stdlib-snapshot` once and embedding the
result in your SYSTEM_PROMPT. It is the authoritative inventory
(~3.5K tokens text, ~14K JSON).
- On compile error, read the `hint:` and `try:` sections of the
diagnostic — they are designed to be copy-pasteable.
- Do not write `test "…" { }` blocks in harness-submitted code unless
the harness explicitly asks for them; this is the top parser cascade
cause.
## Meta
- License: MIT or Apache-2.0 (see LICENSE files).
- Repo: <https://github.com/almide/almide>
- Dojo (MSR benchmark): <https://github.com/almide/almide-dojo>
- Current stable: v0.28.6 (prev v0.28.5). v0.28.6 crushes the two fatal wasm-parity bugs. (1) #696 — a loop rebuilding a list via `list.push` (dropped or swap-carried each round) trapped with a raw wasm OOB at n≳8000 while native merge-sorted 152k fine, and every such loop silently paid O(n²) bytes where it did not trap. Root cause was compound: LICM hoisted the loop-local `[]` allocation into a shared `__licm` temp (aliasing every iteration to ONE buffer, so per-iteration value semantics survived only via the COW guard), and the swap edge `cur = merged` marked the buffer although `merged` is DEAD after the swap — so the unconditional `__cow_check` re-cloned the whole container on EVERY push. Fixed statically, zero runtime change (contract C-131 / ALS-C5): LICM never hoists a heap-typed result out of a loop, and AliasCowPass elides a DIRECT whole-var alias edge whose source has no read or mutation after it (same-loop sources are re-declared before any use; a LIVE alias — source read after the copy — still clones, `alias_cow` / `bytes_set_value_semantics` byte-identical). Merge sort now byte-matches native at 152k in ~3s. Fixture spec/wasm_cross/loop_buffer_churn.almd. An earlier rc-gated `__cow_check_rc` attempt was REVERTED as unsound: `var b = a` carries no rc_inc for any type, so a refcount is not a sharing witness at bind granularity. (2) #705 — a `mut` parameter of a REALLOCATING container (List/String/Bytes) silently lost its writeback on wasm except for Unit-returning callees in statement position: `let i = push9(v, 7)` pushed into a discarded copy (wasm len=1 vs native len=3; an MLP printed loss 0.0). MutParamLoweringPass now lowers EVERY call position via a bottom-up IrMutVisitor — the call becomes `{ let (__mp_res, __mp_buf) = call; <writeback>; __mp_res }` (value callees return an (orig, buffer) tuple, destructured on the proven BindDestructure path; a hand-built TupleIndex read left the buffer dangling when the tuple temp dropped), writing back to bare vars (Assign) and record fields (`f(b.items, …)` → FieldAssign); bare-name collisions are excluded wholesale (the #692 class — rewriting callee but not caller is an invalid module). Contract C-132 / ALS-M13, fixture spec/wasm_cross/mut_heap_param.almd. Full bar: almide test 280/280 (267 wasm / 13 fallback baseline), wasm_cross release gate green, workspace green, frees-churn green, 132 contracts / 242 fixtures bidirectional. Found & filed #753 (pre-existing debug-only ANF postcondition trap). Previously v0.28.5 (prev v0.28.4).
- Previously v0.28.5 (prev v0.28.4). v0.28.5 closes fuzz triage #727: ALL 8 divergence clusters from nightly run #723 (478 native↔wasm findings, seed 1782027195514436784) are dead — 13/13 campaign representatives replay CLEAN. Two ownership fixes in the v0 wasm emitter did it, both the same class (a combinator handing back a BARE aliased handle the caller then releases): (1) `option.unwrap_or_else`'s Some path now acquires (+1) a HEAP payload before the Option temp's recursive drop releases it — a some(Map) payload was read after free (a 2-entry map printed as hundreds of `false`; String literals masked the bug because data-section statics are never freed); (2) `map.fold` now acquires a heap init when storing the accumulator and releases the previous accumulator after every closure call — the zero-iteration fold returned the init var's handle bare, double-freeing it against the var's own release (rc sentinel trap; with a `map.get_or` subject the allocator reuse printed the init as `[:]`). Killed clusters include the string-garbage/linear-memory dumps, multibyte-concat tail drops, corrupt map keys, ok→err Result flips, one-element list truncation, control-char rendering, float-in-list garbage, and the wasm hang. Contract C-130 (ALS-I1, the share-+1 pass-through discipline) + 2 fixtures — one of which the reference interpreter evaluates, making it a real 3-way vote. Also hardened this session's verification method: full stdout+stderr+exit comparison (a head-1 diff had hidden trailing rc-sentinel traps). Full bar: almide test 280/280, wasm_cross green, interp 3-way green, frees-churn gate green. Previously: v0.28.4 (prev v0.28.3).
- Previously v0.28.4 (prev v0.28.3). v0.28.4 completes the spec↔contract↔fixture traceability program (#565, #563 closed) and fixes the cross-target divergence its first run caught. `list.chunk(xs, 0)` / `list.windows(xs, 0)` are now DOMAIN ERRORS aborting in the ALS-T6 form (`Error: chunk size must be positive` / `Error: window size must be positive`, one stderr line + exit 1, byte-identical native⇄wasm) — previously native leaked Rust's raw `chunks(0)`/`windows(0)` panic (exit 101) while wasm trapped on chunk and **silently returned len+1 empty windows** for `windows(xs, 0)` (a value-vs-abort divergence). A NEGATIVE size keeps the historical behavior promoted to the norm (chunk → whole-as-one-chunk, windows → empty; ALS-T4 rewritten to say exactly that). New contract C-129 + 3 fixtures. The traceability layer itself: `scripts/check-contracts.sh` now enforces (a) a REQUIRED `spec` key on every contract, (b) key resolution to a real `## ALS-xx` heading, and (c) **reverse coverage — every normative ALS section must be cited by ≥1 contract** — all mutation-tested; C-087 re-keyed to ALS-T3 (json.parse adjudications). The ALS stands at 62 normative sections / 129 contracts / 238 bidirectionally-linked fixtures. The reverse check's FIRST run surfacing a live divergence behind an uncited section is the mechanism working as designed: an uncited normative claim is exactly where spec↔implementation drift hides. Previously: v0.28.3 (prev v0.28.2). v0.28.3 fixes #692: when the main program and an imported module of the same package each define a fn with the SAME bare name (different arity/result), an intra-module TAIL call bound the main program's fn and emitted an invalid `return_call` (structural validation failure: "current function requires result type [i64] but callee returns [f64]") while `almide check` stayed green. Root cause: `emit_tail_call`'s Named arm looked up `func_map` by bare name only, while the non-tail path correctly tried the current module's qualified `{module}.{fn}` first. Fix: one shared resolver (current-module qualified → bare → any-module qualified) used by BOTH `emit_call` and `emit_tail_call`, so the two can never resolve the same name to different functions. New CI gate `tests/wasm_same_name_crossmod_test.rs` builds the two-file package and invokes the export via wasmtime. Also in this release cycle: #737 (v0 wasm value.merge) verified already fixed in 0.28.0 and closed; #696 root-cause narrowed to the loop-carried `cur = merged` buffer-swap shape (double-release-with-reuse poisoning the allocator free list — findings on the issue) and #705 (bare `mut List` param writeback, an ABI/repr design decision) remain open. Full bar: almide test 280/280, wasm_cross_target_spec green, develop CI green. Previously: v0.28.2 (prev v0.28.1). v0.28.2 fixes three reported issues. (1) #739 — EVERY native `almide build` of a matrix program had failed since the 0.28.0 flat-matrix runtime landed (`almide check` green, then 6 rustc errors on the generated crate; matrix programs are excluded from the rlib fast path, so all took the broken self-contained cargo path). Root cause was a two-part drift: the generated crate embedded almide-kernel's standalone `bridge` module (a stale `Vec<Vec<f64>>` adapter nothing references), and the burn matrix splicer's marker (`pub type AlmideMatrix = Vec<Vec<f64>>`) — gone from the flat runtime — now matched ONLY inside that bridge, splicing the burn enum into it. Fix: the kernel embed ships compute modules only, and the rotten splicer + its burn/BLAS Cargo template are retired — the flat AlmideMatrix + embedded almide-kernel SIMD (matmul/transpose/softmax/gelu) are pure Rust with zero extra deps, so matrix builds now use the base template (much faster cargo builds). A new e2e gate (tests/native_build_matrix_test.rs) builds AND runs a matrix program natively in CI, closing the \"check green but build broken\" blind spot. `runtime/rs/burn/` is now unreferenced; restoring a BLAS-backed large-matrix path is a tracked follow-up (needs a flat-runtime anchor + 4 fns burn lacks: div/neg/pow/mha_core). (2) #736 — the LAST silent leg of the v1 heap-element list-op class: `list.update` over List[Value]/List[String] trapped on the v1 spine with an indirect-call type mismatch (closure params are i64-widened but RESULTS are not, so a (Value)->Value lambda's i32 handle result mismatched the generic f: (Int)->Int call type). New `list_update_value`/`list_update_str` self-host variants declare the closure at its real element type, share every non-target slot rc_inc-co-owned, and store the closure's owned return with an explicit slot acquisition (a raw store64 is invisible to the ownership cert, whose scope-end auto-release otherwise freed the stored element). Verified on the REAL v1 path (ALMIDE_VERIFIED_DEBUG): byte-identical across native/v0/v1 incl. OOB, identity closure, nested-Object replace, and a 5000x churn; contract C-101 extended + fixture spec/wasm_cross/list_update_heap.almd. insert/remove_at/swap/filter from the same issue were verified already fixed; the remaining nested-list gaps wall loudly and fall back to v0 (honest-wall). (3) #740 — an Int-only math builtin (`math.abs`/`pow`/`max`/`min`) given a Float now hints the Float-PRESERVING sibling (`float.abs`/`math.fpow`/`fmax`/`fmin`) instead of recommending `float.to_int`, which silently truncates (in NN code nearly every abs/pow is on Floats, so the old hint was an actively wrong suggestion); the misleading auto-fix snippet is suppressed. Full bar: almide test 280/280, wasm_cross_target_spec green, crossmod matrix gate green, mir 575 + checker 169, contract ledger 235/235 bidirectional. Previously: v0.28.1 (prev v0.28.0). v0.28.1 fixes two reported bugs, both under the byte-identical native⇄wasm gate. (1) #746 — string literals now decode numeric/Unicode escapes `\xNN` (two hex digits, codepoint 0x00..0xFF) and `\u{…}` (1–6 hex digits, any Unicode scalar); previously they passed through literally so an ANSI ESC (`\x1b`) could not be written as a literal. A malformed escape stays literal, and the escape resolves at lex time so both targets emit identical bytes. (2) #747 — `datetime.format(ts, pattern)` now substitutes the strftime specifiers `%Y %m %d %H %M %S` (the documented `%Y-%m-%d` form) IDENTICALLY on all three backends via the same sequential `string.replace`: native, the default v0 wasm emit (previously a STUB that returned `int.to_string(ts)` and ignored the pattern), and the v1 self-hosted module (which, with native, had substituted a different `YYYY/MM/DD` token set the docs never matched). `%` is special only before a recognized specifier — there is no `%%` escape — and the wasm civil-date decomposition is now one shared helper (contract C-128 / ALS-T17, fixture spec/wasm_cross/datetime_format.almd; SCOPE year 0..9999, as to_iso). Full almide test 280/280, wasm_cross_target_spec green. Previously: v0.28.0 (prev v0.27.6). v0.28.0 ships the v1 MIR trust-spine as an OPT-IN verified wasm codegen and reconciles the v1 line with every substantive fix from the release line. `almide build/run --target wasm --verified` tries the PCC-verified v1 pipeline (byte-identical to the v0 oracle when a program lowers entirely within the trusted subset) and falls back to the v0 codegen otherwise — honest-wall, zero miscompile risk; without `--verified` the default path is unchanged. The reconciliation RE-APPLIED the release line's correctness work onto the v1 tree (which had diverged with its own emit_wasm restructure) rather than merging: `list.push` into a `mut` record field now persists cross-target (the wasm writeback stores the realloc'd pointer back into the record slot — #703/#705), wasm `bytes.from_string`/`to_string_lossy` COPY instead of aliasing (#690), `error.context`'s ok payload is copied so it never aliases the arg on wasm (#591 — the double-free is gone), an in-place mutator on a loop-outer var counts as an iteration-scope heap escape, LICM no longer hoists an in-place mutator's receiver out of a loop (#712), a nested boxed constructor pattern matches on native (#610), String `[]` indexing is rejected at check with `E026` (#558), a local type may shadow a dependency's same-name type (#433), the cdylib build wires `native/*.rs` shims + `[native-deps]` and copies native asset subdirs (#719/#683), an http binary client + percent-decoded query params land, and the dead npm/JS build target is removed. wasm-encoder/wasmparser bumped to 0.252. All under the full bar: parity 210 byte-identical native⇄wasm (MISMATCH 0), PCC ownership/caps/caps-transitive ACCEPT, mir 575 + spec 279 + workspace cargo suite green, 233 contract fixtures linked. Previously: v0.27.6 (prev v0.27.4). v0.27.6 is the ROUND 5–7 HOLE-HUNT + PERF BURN. Round 7 (this release): the wasm `process.args`/`stdin_lines` `List[String]` and its inner Strings were framed with a 4-byte header but read with the canonical 8-byte `[len][cap][data]`, so every element/byte mis-read — now 8-byte, byte-identical content (#645 content; the round-5/6 fix only resolved the emit ICE); cross-module derived `Codec` methods dispatch on wasm instead of an emit ICE, via a unique `func_map` suffix match + the codec helper routed before the dotted-name split (#609, contract C-098); generic `+` on a type parameter re-dispatches to ConcatStr/ConcatList at monomorphization, so `fn dup[T](x: T) = x + x` concatenates for String/List instead of panicking the IR-verify gate (#558, contract C-097); the #433 class on a linked module's record flowing through a `list.fold` lambda is repaired at codegen entry — unambiguous bare type refs completed + the link mangle extended to lambda-param/type-arg/cast Ty positions, so `nn/qwen_loader` builds again (#681); and a `Codec` derive over a `Map`/`Set` field is rejected at check time with `E023` (#655, the derive had no Map/Set arm so it silently emitted a `Value`-as-String fallback). Three bugs are documented OPEN in `docs/roadmap/active/` rather than rushed: #643 (the wasm `?? ""` / `list.get(xs,i) ?? d` RC double-free is allocator-reuse-timing-dependent — a heisenbug that observation via an allocating `__println` perturbs; the #668 share-`+1` fixed a DIFFERENT pass-through-combinator site, not this one), #591 (`error.context`'s OK path leaks a Result into main's return on wasm), and #610 (native-only: a nested ctor pattern at a Box'd recursive-variant field emits invalid Rust; wasm is correct). Rounds 5+6 cleared the rest of the user-reported set (#644/#646/#647/#648) and two rounds of fresh-lens detection. The headline is PERF (#647): user-fn params and closure captures of records/lists used to DEEP-CLONE per call — a 600k-string dictionary workload measured 21x end-to-end (71s → 3.4s), i.e. code an LLM writes naturally silently fell into the slow world. Borrow inference now passes a read-only record param as `&T` (the structural-record rule generalized to user `Ty::Named` record types, populated once per program from the type decls), and forwarding a heap var into a callee slot that itself borrows no longer counts as a consume; auto-derived convention fns (encode/decode/eq/…) are EXCLUDED via a structural `@derived` attribute the derive generator stamps at the single point it produces them — never a method-name guess, which a user method could collide with. Other user issues: the pass-through RC combinators (filter/or_else/flatten/map_err/map/flat_map) now share-`+1` the returned input cell so they no longer double-free (the related `list.get(xs,i) ?? d` loop case is the still-open #643 noted above); emit ICE'd on native-only intrinsics inside UNREACHABLE imported code — a reachability prune now runs before wasm emit so an import graph no longer forces module splitting (#644); `process.args` had no wasm dispatch (emit ICE) — implemented via the WASI `args_sizes_get`/`args_get` imports, byte-identical arg COUNT to native (#645); `[native-deps]` collided with the generated Cargo template + build/test manifests disagreed (#646); `io.print` buffered without flush so streaming output was invisible until exit (#648). Round-6 hole-hunt (#650–#667, #671–#674, #677): codec decode error strings match native variant names + `missing field`, Float decode widens JSON Int, `i64::MIN` literal folding, JSON `\uXXXX`+surrogate pairs and correctly-rounded float tokens (−0.0 sign + exponent ULP), Rust-keyword fn names escaped at CALL sites, param-referencing default args inlined, `bytes.from_list` slice coercion, nested let-destructure + generic record field sizing on wasm, mutually-recursive variant boxing by cycle reachability (#671), ordering operators on compound types rejected in the checker (#672), a real recursive wasm `json.stringify_pretty` mirroring the native depth-indented oracle (#661), inferred lambda-param type pinning for direct HOF calls (#674), and `E025` for an undecidable type slot instead of an internal-error ICE (#677). Round-5 hole-hunt (#620–#634): new diagnostics `E022` (bang-unwrap outside an error-propagating context), `E023` (deriving Codec/Ord/Hash on a non-deriving field), `E024` (out-of-range integer literal, type-aware post-solve); generics resolved through inferred lambda params (fresh-instantiate unbound generics), anonymous-record variant payload registration, phantom generic-param stripping from Rust record structs, empty `map.from_list`/`set.from_list` element type resolved from the result, and nine cross-target/codegen fixes. Every observable cross-target change ships a `spec/wasm_cross/*.almd` fixture + a named contract (ledger now at C-098) and every new wasm runtime routine cites its native oracle in the rt-oracle registry; each landed under the full bar (corpus both targets, byte gate, 3-way interp oracle, churn family, cargo suite). Previously: v0.27.4 (prev v0.27.3). v0.27.4 is the HOLE-HUNT ROUND 2+3 HARVEST: rotating fresh detection lenses (pass-ordering, checker-vs-lowering, interp third-judge, concurrency, numeric, string/Unicode, repr) found and killed another ~14 cross-target bug classes, including TWO silent-wrong-on-wasm corruptions. Silent-wrong killed: (a) the wasm `xs[i]` index syntax had no write-path bounds check — an out-of-bounds `xs[i] = v` was a silent heap write into adjacent allocator memory, and a read at an index >= 2^32 wrapped before the check and returned the wrong element; both now abort with a unified `Error: index out of bounds` on BOTH targets (native via `almide_index!`/`almide_index_set!` macros, wasm via a full-i64 bound guard). (b) An unhashable Float Map key bypassed the hashability checker via the stdlib builder API (map.new/set/from_list) — `almide check` passed, then on wasm two distinct float keys silently collapsed (len 2 -> 1); the checker now sweeps the solved type map so every Map[K,V] gets the rejection regardless of construction syntax. Effect-fn tail recursion regained TCO (the frontend auto-? Try-wrap had hidden the tail self-call from TailCallOpt, so idiomatic recursive effect fns crashed at ~1e5 depth on both targets). Construction-position auto-? became target-directed (a Result-typed record/list/tuple/map slot keeps its Result; native built invalid Rust and wasm ran the wrong value before). The interp third judge's own gaps closed: Map/Set equality is order-independent, NaN comparison returns IEEE false instead of aborting, the None-unwrap message matches the backends, and huge ranges iterate lazily (fuel-bounded) instead of materializing and OOM-aborting the fuzzer. Checker accepts now match lowering: named arguments validate by parameter name (not appended position), calling a non-function value is a clean error, the auto-? first-arg exemption derives from callee signatures (error.message/context, user fns taking a Result first), and UFCS calls fill default arguments. fan race/any/settle accept a var-bound thunk list (not only inline literals). Wide-int Map keys (Int64/UInt64) compile on wasm. string.lines is a true str::lines byte scan (trailing-newline suppression + CRLF stripping) instead of split-on-newline. The pass-ordering belt gained target-conditional before-deps and load-bearing edges; two dead passes (856 lines) were deleted. sort_by calls its key fn once per element on both targets. Each landed under the full bar (corpus both targets explicit, byte gate, 3-way interp oracle, churn family, cargo suite) per change. Previously: v0.27.3 (prev v0.27.2). v0.27.3 is the HOLE-HUNT HARVEST: a systematic audit hunting three bug CLASSES (multi-place truths that drift, silent fallbacks that fake success, hand-built IR that bypasses pass rules) found and KILLED ten of them at the class level. Corruption fixed: the Perceus Assign arm bypassed the alias rule (`var c = fresh; c = list.get_or(xs, 0, d)` double-freed the shared element — verified sentinel trap), with a principled MOVE exception for ownership-donating TCO temps; the return terminal's expression now goes through the FULL Perceus rule set (the dominant `fn f() { stmts; match … }` shape previously received ZERO rc processing); rc ops targeting MODULE GLOBALS were dropped on BOTH sides (balanced only by mutual omission) and now emit through the global. Silent-wrong killed: every mutation statement (index/map/field assigns, the var-get helper, lambda captures) resolves globals through ONE shared lookup with an ICE on miss — the old bare-name-first probes could bind a same-named WRONG global or silently drop the whole statement; ctor field-layout lookups distinguish 'no registration' (now an ICE) from 'registered empty'; the repr-dispatch fabrication and the mutator write-back silent-skip are ICEs; bytes typed-append routes through the shared write-back (it lost realloc'd buffers for module-global Bytes and overwrote shared-cell pointers). Determinism: the var-rescue tie-break no longer depends on HashMap iteration order (emitted bytes were host-dependent); tuple-destructure offset advances are unconditional (one missing bind no longer corrupts every subsequent element's load). Made unrepresentable: compiled wasm bodies carry their REGISTERED function index and `add_compiled` asserts the landing position — the 'compile order mirrors registration order' convention (held by hand across ~157 runtime routines, where a same-signature neighbor swap binds the wrong body to a name and validates cleanly) is now a build-failure invariant, with 121 sites migrated; top-let global emission positions are asserted; `next_global` derives from the index constants. Deleted: PerceusOptPass (138 lines) — probed at ZERO firings across the corpus; its pattern targeted a removed convention and a match would have removed a protective inc (latent use-after-free). Leak killed: TCO identity carries (`loop(xs, n-1)` keeping xs) no longer emit a temp whose alias-inc had no balancing dec (+1 rc per iteration, rc creep toward wrap). Two new C-066 fixtures pin the assign-alias rule and the container-return dual-authority pair. All landed under the full bar (corpus both targets explicit, byte gate, churn family, cargo suite) per change. Previously: v0.27.2 (prev v0.27.1). v0.27.2 completes Stage C of true Perceus: TCO SELF-CALL LOOPS RECLAIM HEAP PER ITERATION — a 2M-deep constant-size tail recursion went from 4.27 GB peak to a flat ~7 MB (the wasmtime floor; new gate fixture spec/churn/tco_deep_recursion_churn.almd). Three ownership refinements landed together: self-call ARGUMENTS that are calls (not just literals) are now adoption-safe, so their params are managed (user fns return OWNED since the mechanism-#6 dup, and alias-shaped temps receive the bind-level Inc once the TCO rewrite turns args into VDecls — the literal-only test left `spin(n-1, string.take(acc + "x", 8))` unmanaged = unbounded growth); the bind-level alias-Inc on a block-valued binding now fires only when the tail temp's own value ALIASES (an unconditional +1 on fresh tails was a measured 16 B/iteration leak); and removing those masking over-counts exposed TWO real under-counts, both fixed at the root: `?? default` reaching Perceus in its Call{option.unwrap_or} spelling was classified FRESH (the alias classifier only knew the RuntimeCall spelling) — a map-owned payload double-freed at teardown; and the Perceus return-lift hand-builds its `__perceus_ret` binding BYPASSING the alias rule, so a return expression aliasing a to-be-Dec'd local (`if filled then base + "…" else base` with `Dec base` below) returned a FREED pointer — latent for years behind the chain-temp over-incs, surfaced as a resurrection trap the moment they were removed. Also in this release, completeness-by-construction §4 stages 1-2: the unified TopLetStorage attribute (ONE total decision table for module-global storage — classify_storage, the single static-name format site, the single Copy predicate, total alias resolution that REFUSES the build on an unresolvable module-global reference) computed once by a pass and consumed by every walker reference site, the declaration emit, the eager-force order, and the wasm global lookup (VarId-keyed alias map first, name reconstruction demoted to fallback); a walker-side agreement verifier asserts every legacy predicate matches the attribute on every build, so the next #486/#500-class drift is a build failure, not a silent miscompile. Each consumer flip was proven semantics-preserving by a generated-Rust byte-diff across all 342 single-file specs — which itself exposed and fixed that the generated Rust source was NONDETERMINISTIC run-to-run (anonymous-record declarations emitted in raw HashMap order; now sorted). Documented limit (not a bug): a monotonically growing accumulator still peaks at the sum of intermediates — every freed block is one unit smaller than the next request, so first-fit can never reuse it; recorded as an allocator-policy frontier. Previously: v0.27.1 (prev v0.27.0). v0.27.1 fixes the three cross-module top-let storage bugs the new (mutability × module-origin) matrix cells caught the day they were added (#500, #501, #502 — the completeness-by-construction §4 census): a cross-module `var` read miscompiled on BOTH targets (#500 — native rustc E0308/E0277 from rendering the raw `thread_local!`; wasm SILENTLY WRONG: the global lookup still expected old-style prefixed var names after the clean-name+module_origin rework, so every key missed and the read fell to a typed-zero fallback — printed 0/empty and lost mutations; there is now ONE shared `lookup_global` (id → module_origin-prefixed key → bare name) used by the value-read arm and the closure-capture materializer, and a module-origin var that misses every key is a loud ICE instead of a silent zero); `items = items + [n]` inside a module fn silently lost the write on native (#501 — NOT cross-module-specific: the push rewrite turned the Assign into a Method call that pushed onto a DISCARDED CLONE of the ModuleRc read accessor; mutable top-lets now join the keep-as-Assign exemption, and new tests pin append-through-fn plus the self-referential `items = items + [list.len(items)]`); and a cross-module spread base stuck at `SpreadRecord ty=Unknown`, refused by the AllTypesConcrete gate (#502 — three cooperating holes: the unannotated named-ctor top-let seed had no named-record arm so main read a stale Unknown, the repair belt had no SpreadRecord rule, and the Phase-1b name bridge missed the SCREAMING_CASE spelling of the synthetic use-site var; all three fixed, with spread-from-top-let / rebound-local / fn-return covered on both targets). Also fixed in the same release: direct cross-module assignment `m.x = v` to a `pub var` (#505 — the FieldAssign lowering fell back to VarId(0) for a module-alias target and rendered garbage lvalues like `NUMS.nums = …`, rustc E0425; the lvalue now resolves through the SAME shared module-top-let rule the read path uses, on both targets — accepting the spelling rather than rejecting it, since reads compose and an asymmetric rejection would be an LLM trap), plus a doctest fence fix in the matrix-shape pass docs (the root `cargo test` skips sub-crate doctests, so it never surfaced in CI). The matrix gate grew 9 cells and is 29/29 green. Previously: v0.27.0 (prev v0.26.21). v0.27.0 is TRUE PERCEUS: the wasm target FREES BY DEFAULT. Since the first wasm backend, rc_inc/rc_dec were deliberate no-ops — a bump-allocate-and-leak model (sound, but heap grew linearly with every loop iteration; ~431 MB over 4M iterations). The reference-count bodies are now REAL and ON by default (contract C-066): rc_dec frees to a reuse list, construct-and-drop churn runs in O(1) memory (2M-iteration record churn: flat ~13 MB RSS, byte-identical output to native), and loop iterations whose heap values do not escape reclaim wholesale (the per-iteration region reset now also clears the free list, so allocator state never straddles a rolled-back frontier — the unsoundness that class of reset had under real frees). `ALMIDE_WASM_FREES=0` opts back out to the leak model (env-conditional emission is declared behavior under the host-determinism gates). The activation drained 14 cross-target divergences the corpus had never been able to observe under no-op rc, each a missing SHARE-vs-MOVE ownership judgement, including: the record drop glue computed field offsets in NAME order while construction/member/spread use declaration order (mis-offset reads assembled fake pointers — trap or silent leak); a binding's alias-Inc ran AFTER its block value's temp Decs (the temp's typed dec freed the payload, then the late Inc resurrected a freed block — silent corruption; alias-Incs now hoist inside the block, before any Dec); functions returning a borrowed alias (`fn ostr(o) = match o { some(s) => s, … }`) now return OWNED (+1 before return); `string.join` on a 1-element list returned the element pointer itself un-owned; `list.repeat` stored the same pointer N times with one reference; list/map/json runtime families gained per-site element dups (sort/filter post-build walks, map keys/values/recap/remove/update, json get/set/remove_path no-op alias returns and share copies). A tripwire belt makes the remaining failure modes LOUD instead of silent or wall-clock hangs: freed blocks are stamped rc=0 and a second dec traps (double-free sentinel), rc_inc of a freed block traps (resurrection — previously silent value corruption), and the free-list walk has an absolute 1M-step cap so a corrupted cycle traps in milliseconds instead of spinning the host CPU for hours. Known bounded exceptions (tracked in docs/roadmap/active/wasm-frees-ownership-discipline.md): TCO self-call loops still leak per iteration (the per-iteration reclaim re-land is a redesign gated on the new `spec/churn/` fixtures — the cherry-pick of the old design corrupted the free list and was rejected by exactly that gate), and a stored-field over-count leaks one reference per construct-from-temp outside reclaimed regions. New durable gates: `spec/churn/` + `scripts/check-frees-churn.sh` (2M-iteration reclamation fixtures, wall-clock-killed wasmtime, native-output comparison — the corpus alone passed builds with live double-frees twice; these shapes are the gate the corpus cannot be) and `spec/wasm_cross/rc_reclaim_churn.almd` in the byte-identical gate. Previously: v0.26.21 (prev v0.26.20). v0.26.21 installs the first wave of completeness gates (docs/roadmap/active/completeness-by-construction.md) and ships the two compiler fixes they caught on day one. Gates: a NameResolutionTotal verifier at codegen entry refuses any bare type name whose only declaration is module-qualified (the #433/#484/E0425 class, machine-checked across every Ty position); a generated cross-module shape-matrix CI gate (20 multi-file cells — variant payloads, every top-let class, constructions, closures, effect calls) where a rustc error on generated code fails CI and the wasm target must match byte-for-byte under wasmtime; a fmt semantic-preservation gate (formatted output of all 341 single-file specs must re-typecheck); and six emit_wasm lookup-miss sites that buried failures as runtime `unreachable` traps now fail the build with a structured ICE. Day-one catches, all fixed: derived signatures registered their VALUE types bare (`Pigment.decode`'s `Result[Pigment, …]` leaked an unresolvable name into callers); cross-module paren-named construction `m.Cfg(name: x)` was E002 (the v0.26.20 normalization only intercepted bare TypeName callees, not the Member spelling); five formatter meaning-changers (imports used only via a match-subject call deleted — unused-import decisions now consult a token-scan superset that cannot under-count; `mut` parameters dropped; module-level `var` rewritten to `let`; test `where` clauses deleted entirely; a local variable sharing a stdlib module's name got a spurious import injected — auto-add now verifies the module defines a referenced function). And one real miscompilation: a Unit-typed tail variable emitted `local.get`, pushing a value the type system says does not exist — a structurally INVALID module that **wasm-opt had been silently repairing** on every machine that has binaryen; environments without it got modules wasmtime rejects ("values remaining on stack at end of block"). The emitter now honors the `ty_to_valtype(Unit) = None` contract (raw-module sweep: 106/106 wasm_cross fixtures validate without wasm-opt), and post-emit `wasmparser::validate` is **always-on and fatal** (was debug-only, print-only), so a shipped artifact can never again depend on an optional external sanitizer. v0.26.20 closes every open issue in one batch. Cross-module type identity is finished: a variant payload type from another module (`type Tag = EmotionTag(m.Emotion)`) no longer emits an unqualified enum payload (`#484`, regression since v0.26.16 — `lower_variant_case` was the one type-decl position the #433 qualification missed); a cross-module non-Copy top-let passed by value clones out of its `LazyLock` static instead of moving (`#486` — the emit-time-prefixing rework had left CloneInsertionPass's name-prefix predicate dead; classification now keys on `module_origin`); and the checker's record-construction inference — the LAST producer of bare cross-module type names — now returns the canonical qualified `mod.Type` (an un-annotated module record top-let used cross-module previously hit native `E0425` AND a wasm runtime trap from one shared root; the qualification predicate now lives in exactly one function shared with annotation resolution). The effect-fn `Result` auto-unwrap rule is uniform and type-directed across every binding position (`#485`, contract C-064): plain reassignment `x = effectCall()` unwraps like `let`; a Result-typed target (explicit annotation or Result-typed var) keeps the Result; index/map/field assignment follow the same rule (each previously passed the checker and exploded at rustc); and lambda bodies now disable auto-unwrap entirely (`#489` — a closure may escape, so `?` cannot propagate out; the checker and the lowering, which never inserted `?` in lambdas, now agree at check time). The string position API speaks ONE unit — codepoints (`#419`, contract C-065): `len`/`index_of`/`last_index_of`/`get`/`slice` are codepoint-indexed on BOTH targets (docs always said characters; `len` was bytes, `index_of` bytes, `slice`/`get` bytes-with-boundary-snap), so `string.take(s, string.index_of(s, sub) ?? 0)` finally composes on multibyte input; wasm gains a registry-verified `__str_cp_of_byte` conversion routine. Record construction is validated (`#488`, new diagnostic `E021`): the paren-NAMED spelling is accepted and normalized into the brace pipeline (`Cfg(name: x)` ≡ `Cfg { name: x }`, defaults included — previously the checker validated NOTHING and the named args were silently dropped, native ICE-banner / wasm trap or garbage payload); positional args on a record, named args on a tuple constructor, brace construction of a tuple case, unknown/duplicate/missing-without-default fields, and paren patterns on record-payload cases (`SetEmotion(_)` — a native/wasm acceptance divergence) are all rejected at check time; an unknown TypeName in call position is now `E003` instead of a silent fallthrough. A record/variant brace literal in match-subject position is parenthesized in the generated Rust (`#490` — rustc rejects bare struct literals there; wasm ran it). Formatter semantic-preservation fixes: `almide fmt` no longer deletes an import whose only uses are in TYPE position (the unused-import scan walked expressions only), and record field DEFAULTS (`age: Int = 7`) and serialization aliases (`name as "key": T`) survive formatting (both were silently dropped). New roadmap `docs/roadmap/active/completeness-by-construction.md` maps each of these to the missing mechanical guarantee and sequences the next belt (cross-module shape-matrix gate, no-silent-trap emission, fmt re-typecheck gate, NameResolutionTotal verifier). v0.26.19 verifies `#470` resolved and locks the pattern: a large real-world VRM/glTF `json.get_array` whose `list.len` was right but whose element access trapped (`memory access out of bounds`) on `--target wasm` — filed against 0.26.10 — no longer reproduces on 0.26.18+. It was chased against the actual `VRM1_Constraint_Twist_Sample.vrm` (10.7 MB; 240 KB JSON chunk, 717 accessors / 898 bufferViews / 3 meshes / 171 nodes) through nine faithful variants, including the reporter's exact host path (a JS host allocating wasm memory via the exported `__alloc`, writing the full file, calling an exported `probe(ptr, len)` that does `bytes.from_raw_ptr` → GLB-chunk slice → `json.parse` → `get_array` → `list.get_or`) and a heavy walk touching all 717 accessors' nested float arrays — all correct, no trap; the associated `[perceus-belt]` `no RcDec` leak does not reproduce either. The host-buffer / RawPtr / linear-memory bridge that path relies on was reworked in v0.26.15 (`#440`), the most likely fix window. New `spec/wasm_cross/json_gltf_walk.almd` adds a heterogeneous-nested glTF parse + `get_array`/`list.get_or` walk (meshes, every accessor's `min`/`max`, bufferViews) to the byte-identical native⇄wasm gate so the pattern stays locked. v0.26.18 fixes wasm dead-code elimination dropping an exported `pub fn` (`#457`): on `--target wasm`, a `pub fn` exported for the host to call but unreachable from `main`/`_start` had its body eliminated to `unreachable`, so it trapped (`RuntimeError: unreachable`) on the first host call despite still appearing in the export section — breaking the standard host-driven pattern where JS calls exported callbacks (`render_frame(time)`, `on_pointer_*`, any exported `pub fn`) that `main` never invokes. Root cause: WASM DCE ran BEFORE the export set was collected, seeding roots from `main`/`_start` (+ a few runtime helpers) only. Exported `pub fn`s (and `@export(wasm, "symbol")` exports) are now collected before DCE and seeded as roots, so their bodies survive; genuinely-dead PRIVATE functions are still eliminated. Locked by `tests/wasm_export_dce_root_test.rs` (builds the export, invokes it via `wasmtime --invoke`, asserts the value not a trap). v0.26.17 completes per-package namespacing on the CONSTRUCTOR side (`#413`): two packages each declaring a variant with the SAME constructor name (e.g. each with an `Active` case) now resolve correctly — a module's own `Active` is its own enum's case, not the other package's. Root cause: the Rust codegen built a single `constructor → enum` map keyed by the bare ctor name, collapsing a shared name to the last-registered enum, so `package_a.start() = Active` mis-emitted `package_b`'s `Phase::Active` (caught as `E0308`). Following the codegen design principle (the type checker is the source of truth — codegen uses the IR's `.ty`), the walker now derives a constructor's enum from the construction's resolved type (`.ty`) and a match arm's enum from the subject's type, falling back to the collapsing map only for non-variant positions (newtypes, structs); the checker disambiguates the construction by preferring the CURRENT module's candidate (the constructor table now records each candidate's owning module). byte-identical native ⇄ wasm; fixture `spec/integration/modules/same_name_ctor_test.almd`. With v0.26.16 (`#433`, types) this makes per-package namespacing complete — same-name types AND constructors coexist across packages. v0.26.16 lands per-package type namespacing (`#433`): two distinct packages (or sub-modules) declaring a type with the SAME name but a different structure now coexist. Previously the second was silently dropped at link time and a function using it referenced the wrong struct (a cryptic generated-Rust `E0560`/`E0609`), which the v0.26.8 `E020` diagnostic could only flag (forcing a rename). A user module's types are now identified by their qualified canonical name `mod.Type` end-to-end and mangled to a flat, valid identifier `almide_rt_<mod>_<Type>` at codegen — the type-side analogue of the existing `almide_rt_<mod>_<fn>` function mangling — threaded through resolution, registration, lowering, link, and both codegens, byte-identical native ⇄ wasm. Stdlib types stay bare (they back the bare-named Rust runtime); `E020` now only fires for a true same-module duplicate. Covers same-name records and variants, used directly, via dependency functions, in `let` annotations, and as cross-module derived-`Codec` field types (the codec method-name construction reconciles with the new naming). Fixture `spec/integration/modules/same_name_collision_test.almd`. Known limit: a same-CONSTRUCTOR-name across packages (two packages each declaring, say, an `Active` variant case) is still ambiguous and resolves by first-match (tracked in `#413`). v0.26.15 implements the wasm RawPtr / linear-memory bridge (`#440`): `bytes.as_ptr` / `as_mut_ptr` / `from_raw_ptr` / `copy_to_ptr` now work on `--target wasm` (they previously ICE'd in WASM emit; v0.26.12 turned that into a clean build error, and this release implements the bridge itself). A RawPtr is an i32 linear-memory offset on wasm (a real `*mut u8` on native): `as_ptr`/`as_mut_ptr` return the offset of the Bytes data region (`b + DATA_OFF`), `from_raw_ptr(ptr, len)` copies `len` bytes from a raw offset into a fresh Bytes, and `copy_to_ptr(b, ptr, cap)` copies `min(len(b), cap)` bytes through the pointer via `memory.copy` and returns the count. The pointer VALUE differs per target by nature and is never observed across the byte-identical gate — only the bytes it moves are, and those match byte-for-byte (contract `C-062`, fixture `spec/wasm_cross/bytes_rawptr.almd`). `as_mut_ptr` is now `@mutating`, so its Bytes argument is a mutable borrow on native too (the buffer must be `RefMut` to hand out a writable pointer — the same borrow class as the v0.26.9 `mut Map` fix); read/write/clamp are covered on both targets by `spec/stdlib/bytes_rawptr_bridge_test.almd`. The v0.26.12 native-only-ptr emit gate is removed now that all four ops are implemented on wasm. (Passing a RawPtr across the JS boundary via `@export(wasm)` remains a separate follow-up — the four ops and in-language round-trips are complete.) v0.26.14 makes `http.serve` (and any tail call to an `@intrinsic effect fn`) compile (`#434`): such a call lowers to a `RuntimeCall` whose runtime fn returns `Result<T, String>`, but the Result-propagation tail-wrap treated it as a plain value and `Ok(...)`-wrapped it, double-wrapping the Result (`E0308`, "expected `()`, found `Result<(), String>`") — so `effect fn main() = { http.serve(port, handler) }` failed to build even after the v0.26.12 closure-repr fix. Intrinsic effect-fn runtime symbols are now recognized and their tail call is left un-wrapped (it IS the `Result` the effect fn returns), scoped to effect-fn bodies. v0.26.13 smooths an effect-fn `Result` auto-unwrap rough edge (`#438`): inside an effect fn, an `if` whose one branch auto-unwraps an effect-fn call (e.g. via `match`, yielding `T`) and whose other branch is an explicit `ok(...)` (`Result[T, E]`) used to fail `E001` ("type mismatch in if branches"). The auto-unwrap now applies per-branch before the cross-branch comparison, mirroring the existing match-arm rule, so the two reconcile at the `T` level. The if-expression's value type is unchanged, so `if .. then ok(n) else err(..)` still types and runs as a `Result`; the rule is scoped to effect-fn bodies, leaving pure/test `if/else` strict. v0.26.12 turns the wasm RawPtr ICE into a clean build error (`#440`): `bytes.as_ptr` / `as_mut_ptr` / `from_raw_ptr` / `copy_to_ptr` are native-cdylib-only and used to ICE in WASM emit (`[ICE] … no WASM runtime fn`); a pre-emit gate now refuses them on `--target wasm` with a span-tagged `error: RawPtr / linear-memory bridge is not supported on the wasm target` (the linear-memory bridge itself stays tracked in `#440`). It also aligns the `http.serve` handler to the uniform `Rc<dyn Fn>` closure repr, resolving the closure-repr type-check failure (`#434`, the SSE-callback fix's sibling) — note an effect-fn tail call to `http.serve` still hits a separate effect-propagation bug (intrinsic effect calls aren't lifted to `Result`), tracked under `#438`. v0.26.11 completes the soft-keyword-field-name fix (`#418`, begun in v0.26.0): `ok`/`err`/`some`/`none`/`todo` are now valid field and member names in *every* position, not just record literals and type declarations — a named record whose first field is a soft keyword (`Status { ok: true }`), a record-pattern destructure (`match s { Status { ok: o, .. } => … }`), a spread field (`{ ...base, ok: false }`), and a field assignment (`obj.ok = v`). They stay value constructors in *binding* position, so a `let { ok } = r` shorthand remains an error (a bound `ok` could never be read — in value position the lexeme is the constructor again). Tooling fix: `almide test` on the WASM path no longer reports a false PASS for a file with a parse error — it now fails like the rust target / native fallback (a recovered partial AST was previously compiled and run as if green). (Internals: a dead `Map`/`Set` borrow-inference branch was removed, and CI now gates the generated runtime registry against drift from its committed sources.) v0.26.10 lets an inline element-type annotation be parenthesized — `(expr: Type)`, e.g. `([]: List[String])` — so an otherwise un-inferable empty collection can be annotated as a record-field value (`{ tags: ([]: List[String]) }`) or a `let` initializer, not just as a bare call argument (`f([]: List[T])`). Previously the `:` after the inner expression inside parens was a parse error ("Expected ')'"). The parser is shared, so both targets accept it identically. v0.26.9 fixes a `mut Map`/`mut Set` function parameter mutated in place (contract C-061): `fn put(mut m: Map[K,V], …) = map.insert(m, …)` used to fail native `E0596` ("cannot borrow `m` as mutable") because `Map`/`Set` weren't recognized as heap types in borrow inference, so the param was forced to an immutable owned binding and the in-place mutator's `&mut m` had no `mut` binding to borrow — while the structurally-identical `mut List` + `list.push` compiled. Now the param is promoted to a mutable borrow (RefMut) like a list, so it builds on both targets and the mutation persists to the caller, byte-identically wasm (a read-then-write through the same `mut` param works too). v0.26.8 adds `E020`: two distinct types that share the same bare name with different structures (a local type colliding with a dependency's, or a duplicate within one file) are now reported at the source instead of silently shadowing — the second declaration used to win the bare-name slot through link + codegen, surfacing later as a cryptic generated-Rust `E0560`/`E0609` ("no field …") in a function that used the shadowed type. A structurally-identical re-declaration (the diamond case) is not flagged. This is an interim guard while per-package type namespacing (the full fix for `#433`) is pending; rename one type to disambiguate. v0.26.7 makes a `Value` (the dynamic JSON-like type) repr as its JSON text byte-identically native ⇄ wasm (contract C-060): a `Value` field in a `Repr`-deriving record/variant used to fail to compile natively (`Value` impl'd neither `AlmideRepr` nor `Display` — `E0599`/`E0277`) while wasm emitted the placeholder `Value { }`; both now route through the same JSON serializer (`almide_rt_value_stringify` / `__value_stringify`), bare (`"${v}"`) and as a field (`.repr()` and `{}` paths). The wasm float branch was also corrected to the native `format!`/`__float_display` Display form (it used the round-trip `float_to_string`, so an integer float diverged `3` vs `3.0`), which also makes `value.stringify`/`json.stringify` of a float agree. v0.26.6 makes the shared build-scratch lock (`BuildDirLock`) cross-platform (`flock` on unix, `LockFileEx` on Windows, via `fs2`) instead of a unix-only no-op — so concurrent `almide run`/`build` subprocesses no longer race the shared scratch dir on Windows, and CI runs the test suite in parallel on every OS (no `--test-threads=1` Windows carve-out). v0.26.5 lands two contributor fixes: a `result.*`/`option.*` function no longer auto-`?`'s its FIRST argument (it consumes that as the `Result`/`Option`, so `result.unwrap_or(int.parse(s), d)` keeps `int.parse(s)` intact instead of unwrapping it — `E0308`); `build -o build/app` creates the output's parent directory; and SSE streaming callbacks take `Rc<dyn Fn(String)>` to match the codegen's closure repr, so a capturing closure passed to `http.openai_streaming_call(...)` compiles. v0.26.4 completes cross-module derived `Codec`: a derived codec referencing another module's `Codec` type used to fail to link (`E0425` — the cross-module `T.encode`/`T.decode` call dropped its module prefix); a `mod.Type.method()` call on a cross-module type was rejected (`E003`); and an `Option[CustomCodecType]` field failed to derive on both targets. All three now work, byte-identical native ⇄ wasm. (Internals: the "raw-`impl Fn` element-codec argument" set is now DERIVED from the runtime signatures by build.rs instead of a hand-maintained allow-list.) v0.26.3 supports cross-module record-variant construction and pattern matching (`mod.Ctor { … }` both as an expression and a pattern; previously the construction was mis-typed as a standalone `mod.Ctor` type — `E001` — and the pattern left its bound fields untyped). v0.26.2 implemented derived `Codec` decode for tuple- and record-variant payloads (previously only unit variants decoded; non-unit cases returned `Err("… payload decode not yet implemented")`). v0.26.1 patched a WASM Perceus RC-verification false positive (a `let`-bound list whose only use is being consumed by a `for`-in loop was wrongly flagged as a memory leak and the build was refused). v0.26.0 was a minor release — it carried a BREAKING stdlib rename — fixing Fizz-reported codegen bugs (`ok`/`err`/`some`/`none`/`todo` usable as record field and member names, WASM scratch-local overflow on complex functions, ownership of a `??`-fallback operand and of borrow args inside computed index/key/value assignments, a `value.keys` accessor, and a clear `E019` for an ambiguous variant constructor declared in two or more types) plus the BREAKING http `Response`/`Request` → `HttpResponse`/`HttpRequest` rename (the emitted Rust type alias collided with a user/dependency `enum Response`, `E0428`). v0.25.0 was the cross-target VERIFICATION milestone: a behavior-contract traceability ledger (`docs/contracts/`, 58 contracts C-001..C-058) makes every observable native==wasm promise traceable to executable evidence (a `spec/wasm_cross/` fixture, a differential fuzz, an emit-time Σ-probe, or a Lean theorem) under a bidirectional CI gate; a reference interpreter (`almide-interp`) tree-walks the typed IR as a third independent judge in a three-way differential harness (interp==native==wasm); a generative differential fuzzer runs a nightly campaign; the oracle-pairing registry is drained to ZERO grandfathered routines (every wasm runtime routine cites a native-oracle differential test). Correctness hardening (C-036, C-047..C-058): integer div/mod and `list.product` WRAP on overflow (no debug/release split), scalar `float.min`/`max` are NaN-ignoring with a fixed ±0 tie (no LLVM `maxsd` second-operand drift), vendored musl libm makes trig/exp/log/pow byte-identical, the wasm regex engine is a faithful port of the native engine, list/string Int counts and indices clamp on the full i64 BEFORE narrowing (no 2^32 truncation, no OOB), `List[Float]` ordering uses IEEE-754 totalOrder, and aliased mutable collections enforce value semantics (copy-on-write). New behavior: `almide run --target wasm` builds and runs on wasmtime (the flag was previously swallowed into program args and ran native); an undecidable empty collection (`[]` / `set.new()` / `for _ in []` with no element-type context) is a compile error `E018`, following Rust/Swift instead of silently defaulting the element type; assigning a Unit-returning in-place mutator's result is rejected `E001`. Both targets reject identically where they used to diverge (one compiled, the other didn't).
- v0.24.0 (prev v0.23.14). The cross-target completeness milestone: integer / and % are TOTAL (zero divisor -> `Error: division by zero`, signed MIN/-1 -> `Error: integer overflow`, exit 1, byte-identical native==wasm); fan.race/fan.any deterministic by list-order (fan.timeout = the one documented wall-clock exemption, compile warning on wasm); compound string interpolation renders Almide-literal repr on both targets (lists, maps `["a": 1]`, sets, tuples, some/ok, records `P { x: 1 }`, variants, anonymous records, recursive ADTs); string predicates (is_alpha/is_alphanumeric/is_upper/is_lower) full-Unicode on wasm via emit-time tables derived from Rust std; abortable top-level lets evaluate eagerly at startup on both targets. Verification: oracle-pairing registry CI gate (every wasm runtime routine must cite a differential test vs the native oracle), Lean proofs kernel-checked in CI (lake build), cross-target gate corpus 65+ files with zero grandfathered exceptions.
- v0.23.14 (prev v0.23.13). Determinism Belt L3: a `Canonical<IrProgram>` type-state + a terminal canonicalization pass make WASM emit order a pure function of content — emit accepts only `Canonical` (and `emit` is `pub(crate)`), so bytes are unreachable without the certificate, the order-determinism analogue of how `Verified` gates emit on RC balance. Also fixes egg fused-lambda param names drifting across compiles in a long-lived process (now VarId-derived), names anonymous records by content hash, sorts default struct-fields, and confines the compiler's clock reads to a wasm-safe shim behind a CI purity gate. 240/240 Rust tests.
- Baseline → 0.14.6 MSR: llama-3.3-70b 17/30 → 23/30 (+20pt);
Sonnet 4.6 30/30 (100%) validates design.