Memory-model debt release. Every divergence found by the design-vs-implementation audit in MEMORY_MODEL.md is resolved, and three validation projects written in Zymbol (zy-GO, zy-Serpiente, zyKlingonGalaxy) contributed the rest: HLZ-001 … HLZ-011 are findings from writing real applications, not from unit tests. Full detail in CHANGELOG.md.
✨ Added
- Automatic destruction at last use (auto-free) — both engines, always on. A variable's memory is released right after the statement containing its last use instead of at scope end. Invisible by design: it never changes a correct program's behavior, it only lowers peak memory (measured: a script holding two sequential 30 MB strings peaks at ~64 MB instead of ~94 MB in the tree-walker). Backed by a new purely lexical, conservative last-use analysis (
zymbol_semantic::last_use); constants, hot names (x°/°x),_-prefixed names and module-level bindings are never auto-freed. - Zymbol Packages (
.zyp) — one file for a multi-file program.zymbol package DIR --script main.zy -o out.zypcomputes the transitive closure of module imports and</ file.zy />targets and zips it;zymbol run out.zyp [--script name] [--vm|--tw]extracts to an ephemeral temp dir and runs from there, neverchdir-ing — so the code is disposable but the data a script writes still lands in your real working directory. A.zypis an archive of source, unrelated tozymbol build. Deterministic writer (fixed timestamps and entry order): the same tree always produces a byte-identical archive. std/term— terminal display metrics.width/pad_left/pad_right/center/truncate, measured in terminal columns rather than grapheme count, so"手番"$#is2butterm::width("手番")is4.truncatenever splits a wide glyph. Replaces the hand-maintained East Asian width table zy-GO carried inside the game.- Match or-patterns —
p1 || p2alternatives in a??arm. Alternatives mix any pattern kind, not just literals:1..10 || 20..30,< 0 || > 100,["run", _] || ["build", _]. Tested left to right, first match wins. ##!on aCharyields its Unicode code point —##!'A'is65. The only direct Char→Int route, which makes characters classifiable by range.
🛠 Changed
- One shared module-path resolution rule —
ModulePath::resolve_from. The tree-walker, the semantic analyzer and the VM compiler each carried their own copy of "which file is this import?", and they diverged:compile_importignoredis_absoluteandhome_relative, so<# /abs/path => xresolved to a different file under--vmthan under the tree-walker — silently. All three now call one rule. zymbol checknow checks the whole program, not just the named file. It followed no imports, so a cleancheckmeant nothing for any project organised in modules. It now walks imports transitively (stdlib excluded, cycles cut) and reports each module's errors at its own line. The LSP gets the same coverage, plus amodule-has-errorsdiagnostic on the import line.
🐛 Fixed
- Memory model (MM-1 … MM-11). Module-state mutations made by intra-module calls were lost;
\ xinside a function poisoned the caller's same-named variable; root-scope constants vanished at call depth ≥ 2; modules loaded at run time skipped semantic analysis;x°/°xinside a function called from a@loop panicked the tree-walker. VM parity bugs found while verifying the fixes: each import alias got its own module-state copy (MM-10), and a leftover loop-iterator value diverged from the tree-walker (MM-11). - Numeral mode (
#d0d9#), audited end to end. It reached>>but not interpolation, juxtaposition,$++or>>~, and not collections or the round trip. It is now one rule across every string-building path, with ordering comparisons unified so no digit script is second-class. - Findings from the validation projects. HLZ-SRP-001 (a module function that wrote state and returned a value lost the write), HLZ-KL-001 (string interpolation rejected identifiers the lexer accepts), HLZ-008 (the VM silently dropped output parameters of module functions), HLZ-010 (the VM turned an interpolated constant into literal text), HLZ-009 (the VM could not slice a String inside a module function), plus HLZ-001 … HLZ-007 and HLZ-011.
- Static-tooling audit — what
checkand the LSP could not see: recursive check, stdlib visibility, re-export ordering, pattern-literal escapes. - Three bugs found by the new package gate itself:
>>?aborted in one engine and returned a size in the other when there was no terminal; the Linux packages declared a glibc floor they did not honour; a stdlib module the build does not include reported two different names.
📦 Packaging — releases now gate on a .deb that actually installs
Linux packages go through build → verify → publish: nothing reaches this page until the .deb has been installed and exercised in a pristine debian:12 container (packaging/verify/verify-deb.sh, 41 checks — dependency resolution, dpkg --audit, dynamic linkage, the declared glibc floor, CLI smoke tests, the full E2E parity suite, reinstall and removal). Reproduce it locally with bash packaging/verify/run-local.sh.
Availability of std/db: Windows binaries and source builds only (default db cargo feature). The prebuilt Linux/macOS binaries exclude it — the ODBC driver manager needs dlopen, impossible in a fully static binary.
✅ Validation
cargo test: 936 / 936 passvm_compare.sh(tree-walker vs VM parity): 536 / 536 PASS, 0 SKIP — the whole tracked corpus, verified against the installed.debas well as the development build- Golden suite: 523 / 525 — the two failures are stale hand-written
.expectedfixtures, not interpreter regressions (IMPL_V008.md § E.1) - Formatter property harness: 600 PASS / 0 FAIL, no regressions against the baseline
- Debian package gate: 41 / 41 checks in a pristine
debian:12container
Known debt: the browser interpreter is behind the Rust engines on seven cases — six v0.0.8 fixes with no counterpart there yet, plus one float-literal precision bug that predates this release (IMPL_V008.md § E.3).
Full Changelog: v0.0.7...v0.0.8