Releases: tompassarelli/beagle
v0.18.0
Minor release. 142 commits since v0.17.1 — new surface/types + breaking removals (SQL target, ~"…" tilde-strings) + Apache-2.0 relicense. Highlights below.
Value-semantics JS runtime (emit-js)
- HAMT persistent map + set: value-keyed, tree-shakeable, value-dedup.
- Type-directed representation selection — provably-scalar → native, else → HAMT — with rep-aware/polymorphic reads (
$bc.get/keys/vals, kw-access). - Value-equality runtime:
=/==/not=/distinct/contains?/into/conjroute to value semantics. - lite/full runtime split (size leg) + configurable
$$bcimport specifier. - Cross-target conformance harness (babashka oracle) over a real semantic corpus.
- Fixes:
set!-mutated let →letnotconst, doseq IIFE in expr position, arrow-fn object-literal parens, unary(- x)lowering.
Code-as-claims / graph-native pipeline
emit-claimsbackend + reverse-path round-trip gate (code as canonical claims).--build-edn: compile straight from claim triples, byte-identical to text (#33).emit-edn-typed: typed-AST layer as derived claims (GLASS prep).- claims → clj DIRECT emit (graph-native, no
.bcljround-trip) + fail-closed claims-check-emit gate (claims → AST → type-check → emit). - Rented chartroom code-intelligence engine;
beagle-callgraphfrom the converged cross-module resolver. - Scope-correct rename/delete as gated authoring verbs (large adversarial gate sweep).
Type system (G-series)
- G1
defalias, G2 parametric(Atom T)(invariant), G3HVecheterogeneous tuple, G4 map-pattern destructuring (bind + narrow), G5defenummembership enforcement, G7for/doseq:-binders. - Typed JVM-class interop (receiver-typed class table),
^:dynamicvars + typedbinding, typed arrays, cross-module binding of an imported^:dynamicvar.
Surface / stewardship
- Collapse two readtables → ONE source of truth (#19).
- Remove
~"…"/~''…''tilde-strings —~= unquote, uniform across ALL targets (#25); interpolation is(s …)/(ms …). if-let/when-letaccept destructuring +:-typed binders (#22).- Prefix hygiene / hallucination-firewall stewardship: bare namespace = Clojure-only, prefix = target.
- Hallucination log +
beagle-hallucscanner (data-driven surface pruning). - Reject multi-arity anonymous fn and
(defn <combiner>)(were silent miscompiles); bracket fn-type return arity fix.
Build / chore
- Remove the SQL target (unused, undogfooded).
- Relicense under Apache-2.0 (+ fix
beagle-liblicense metadata, was stale MIT). - flake: pin racket + ship
.zoso bytecode skew can't recur;_beagle-racketresolves the pin inside worktrees.
v0.17.1
A patch release of JS-target hardening, driven entirely by authoring a real downstream app (the gjoa Firefox fork) in #lang beagle/js. Each item is a silent miscompile or footgun the port hit — now an emit fix or a loud compile-time guard. Active suite 1377/1377.
Fixed
- Async IIFEs are awaited in value/statement position (678bbd1): a
try/loop/doseqcontainingjs/awaitcompiles to an async IIFE; bound in aletwithout an enclosingjs/await, it was emitted withoutawait, so the binding held a pending Promise that downstream code then read synchronously.emit-jsnow awaits async IIFEs in value/statement position — tail position correctly left alone, and the exact(async () =>prefix match never double-awaits. - Macro-only
:refers are no longer emitted as runtime imports (69c718a): a refer that resolves to a macro is compile-time only and has no runtime export, but it was emitted inimport { … }— silently fine when a bundler tree-shakes the dead import, a load-time "does not provide an export named X" for any unbundled ESM consumer.emit-module-headernow drops macro refers and omits the import line entirely when a require's refers are all macros. - The purity check now covers exported functions (5e18635):
check-purity!descended only into list-shaped wrapper forms, sojs/export/js/export-default(which parse to structs) hid every exported defn — the public API — from the!-effect check. Now descends intojst-export/jst-export-default/with-meta.
Added
swallowed-bindingguard (E020) (004d291): aletbinding one paren short silently absorbs the followingname valuepair as body forms, emitting the swallowed name as a barename;statement → runtimeReferenceError, whilebeagle syntaxreports "ok" (parens net-balance). A non-final body statement that is a bare unbound symbol now errors with a pointed message; tail-position bare symbols (legitimate returns) are not flagged.%rejected as a call head —percent-not-modulo(2f7a441):(% a b)emitted_pct(a, b)(%is the#()anonymous-arg shorthand) → undefined call. Now a pointed parse error namingrem/modfor modulo;%inside#(…)lambdas is unaffected.- camelCase JS-export lint (7b5b6d5): kebab names mangle to
snake_caseand camelCase emits as-is, so a camelCase export referenced cross-module in kebab resolves to a different identifier (undefined in the bundle). A lint warns and names the kebab fix; it runs inlint-program!(fires atbuildandcheck) and is counted bycount-lint-warnings, so the--agentrepair loop surfaces it too.
v0.17.0
Where 0.16 locked the surface, 0.17 turns the compiler into something its own repair tooling can drive. Diagnostics now carry structured, machine-applicable data; beagle-doctor proves the repair loop works rather than merely runs; form dispatch unifies onto a single compile-time combiner registry; Odin joins as a live native target and the JS emitter returns to live. Five live targets: Clojure, ClojureScript, JavaScript, Nix, Odin.
Highlights
- Repair loop is real and proven end-to-end: diagnostics carry structured types and machine-consumable conversion data (
MessageData),beagle-repairapplies them, andbeagle-doctordemonstrates the loop functions, not just that the daemon is alive (d599fe1, 1cc1077, a0e6051). - Dispatch unified: one compile-time combiner registry resolves macros, builtins, and legacy forms; 21+ special forms plus the def/control/module/nix/js/sql families migrated onto it; the dead operative prototype was deleted (5d58d09 → b737821, 80c01a1).
- Odin is a live native target, replacing the now-parked zig backend; the JS emitter is promoted back from dormant to live (34fd382, e782375).
- Deterministic paren-balancing is auto-enforced via the PostToolUse hook, and hooks are distributed from tracked templates (bdaae9f, 8b13af3).
!-purity static pass (check-purity!) is on by default (c118f21, 0130145).
Added
- In-compiler error-explanation registry with machine-applicable suggestions (1743404).
- Structured types in diagnostics via
MessageData; structural fix-plans carry machine-consumable conversion data thatbeagle-repairconsumes (d599fe1, f36d18c, 1cc1077). - Exhaustive-match auto-fill: missing-case clause skeletons emitted as an applicable repair fix (cc30a6c).
- Auto-apply
replace-headsuggestions in the repair loop (822fa13). - Types-as-view:
beagle-explain-typeprojects inferred types through an extensible delaborator registry; numeric unions fold toNumber, with--writepromotion (4145ce4, 13847b3, f0ff58c). beagle-doctorproves the repair loop works, with a dynamic target inventory and a correctracoprobe (a0e6051, 2c5a56b).- Source positions carry origin/canonical with precise column propagation; macro expansions inherit the call-site source position (de155ba, 3a9af8f).
- Generated, example-verified capability cheatsheet that can't rot (10d5024).
- Odin backend:
#lang beagle/odin, numeric width types,.bodinbuild support,defenum, fixed arrays, range loops, pointer types, struct literals, keyword→enum variants, non-string map keys (map[K]V),stdlib-odinmath/casts, anddefmacroincl. the ECSdefcomponentpattern (34fd382 + series). - JS emitter live again:
@xderef sugar,js/import-meta,js/export-default, asyncloop/recurviajs/await, destructuring:or/:as, kebab-case property mangling, statement-position IIFE elimination (e782375 + series). !-purity static pass (check-purity!), shipped dark then enabled by default as an error (c118f21, 0130145).(:gen-class)innsfor clj AOT/native entry; batchdeclare-extern—(declare-extern [a b c] Type)(f82e6fa, 47f093c).- Multi-module type awareness for package targets (odin); qualified-call resolution for clj/cljs with fixed sibling imports (f2b8f2f, 8b92761).
stdlib-bbbabashka-runtime typed tranche (~130 entries) (da975a1).- Inline expected-diagnostic test harness with mechanical update (40da2b9).
Changed
- Form dispatch unified onto one compile-time combiner registry —
do/ifseeded first, then the when/if conditional family, def, control, module, nix, js, and sql forms; a single resolver now handles macros, builtins, and legacy forms (5d58d09 → b737821). - Odin replaces zig as the native target; zig is parked under
dormant/(34fd382). - Real mode-2 macro hygiene: definition-site free-variable resolution, across all live targets including odin (3fe36b7, 06bedfc).
- Numeric-preserving arithmetic with
Int→Floatwidening in the checker (63b62ca). - nil-narrowing extended to and/or composition and
not=, with soundness fixes and a deeper clj stdlib (d77855e). - clj emitter: lean release mode; dropped
^long/^doubleand unresolvable opaque-extern hints the JVM/AOT compiler rejects (b7ba4cc, 80233e0, a401115). - CLI consolidated onto
beagle <cmd>: 12 missing subcommands wired, 8 dead tools removed,beagle initunified onto the canonical scaffolder (5419551, 8b7ac68, adf8262). - Hooks distributed from tracked templates; pool mode is portable and scaffolded, and
--hooksidempotently merges into existing repos (8b13af3, c2319a9). - PostToolUse hook auto-enforces deterministic paren-balancing (bdaae9f).
- Version metadata bumped
0.15.3→0.17.0(info.rktwas never advanced for 0.16.0);pkg-desccorrected to the live target set.
Removed
- Dormant py / rkt / scheme / zig targets (SQL kept as a dormant emitter with live schema-typing) (4497259).
- Dead operative prototype deleted; the one-compiler ground truth is documented (80c01a1).
- Game/kernel extracted out of the language repo to
~/code/games(8377383).
Fixed
- Don't crash compiling nested macro calls (
datum->syntaxon a raw-datum srcloc) (8290e66). - Delaborator offset correctness across tabs/CRLF, with opt-in capture (45ab2a9).
- Repair-loop clause insertion: single-line matches and string-decoy anchors (afff6c4).
- Structural fix-plan blames the differing type argument (e6a6562).
- clj regex emission and a blame-path destructure crash (0389b8b).
- JS
:aswhole-map binding across all threeletpaths; record-ctor partial gated to real records (973dd9b). - Hardened
(ns ...)name extraction inbeagle-build(7437294). - Surface hardening: killed silent meaning-changers and closed LLM-prior gaps (2b38cad).
v0.16.0
The surface stopped accreting. v0.16 locks beagle's authoring layer to a three-statement spec — typed Clojure, load-bearing divergence or it dies, idiomatic per target — and converts the Clojure and ClojureScript emitters from dormant to live alongside Nix.
Highlights
- Surface lock: typed Clojure + inference; inline
:-annotations replaceclaimon def/defn/defonce/let (6fefc09). - Multi-target live loop: Nix, Clojure, ClojureScript all active; JS/Py/SQL/Rkt remain parked under
BEAGLE_ALL_TARGETS=1(ce51c1b). - Macros:
defmacro+ quasi-quote (` , ,@) shipped; legacydefine-macrohard-removed (96e9138). - Reader conditionals:
#?(:clj … :cljs … :nix … :default …)and#?@(…)splice across the live-target tier. - Sourcemap fidelity: diagnostics blame author position through every canonicalization —
sourcemap-fidelity.rktcorpus 5/11 → 11/11 (2025b33). - Typo suggestions against the real 16k NixOS schema: 96.9% Top-1 at 130 ms/query, +1.1% end-to-end overhead on the firn-validate corpus.
Added
- Inline
:-type annotations ondef,defn,defonce, andletbindings (parse.rkt:418, 1364). defmacrowith Scheme-style quasi-quote / unquote / unquote-splicing (parse.rkt:306, 2344).- Clojure threading family:
->,->>,as->,cond->,cond->>,some->,some->>(parse.rkt:2147). - Reader conditionals
#?(…)and splicing#?@(…)with:clj :cljs :nix :defaulttags (parse.rkt:450). - Quoted self-evaluating containers
'[…],'{…},'#{…}(2b2e258). - Keyword access canonicalization:
(:k target)and(get target :k)both lower to a singlekw-accessAST node (2eb7baa). - Conditional family completed:
when,when-not,if-not,unless,if-let,when-let,if-some,when-some,cond,condp. - Stdlib sugar:
inc,dec,not=typed instdlib-portable.rkt. - Per-target prefixes
nix/,js/,sql/for forms whose meaning diverges from Clojure (e.g.nix/assert,nix/with-cfg,js/await). - Structured diagnostic taxonomy:
cause-class?,surface-divergence,type-error,logic-errorexported fromdiagnostic-kind.rkt; consumed bybin/beagle-rejection-stats. bin/beagle-rejection-stats <dir|glob> [verify-script]aggregates failure causes by class.- Schema-typed NixOS option paths: 16k options loaded into the typed environment via
nixos-schema.rkt.
Changed
claimreplaced by inline:-annotations on binding forms; same checker, less syntax (6fefc09).- Keyword access is a single canonical AST node regardless of spelling — emitters and checkers see one shape (2eb7baa).
- Clj and Cljs emitters promoted to the active tier in
beagle-test/tiers.rktd; defaultbin/beagle-testrun now covers them. - Bare divergent forms now raise with a "use
(prefix/...)" hint instead of silently emitting (parse.rkt:1577). - README reframed around the typed authoring IR and the three-statement generative spec.
Removed
claimform (superseded by inline:-).- Pipe threading family (replaced by Clojure
-> ->> as-> cond-> some->) (1577987). define-macro(replaced bydefmacro+ quasi-quote) (96e9138).deftyperesidual surface; threading surface reconstruction completed (f24dcd4).- Bare aliases for prefix-divergent forms — must spell as
nix/.../js/.../sql/...(91a3abc).
Fixed
- Sourcemap drift through canonicalization passes: diagnostics now point at the author's original token across every rewrite (2025b33).
- Validator false positives resolved by quarantining the experimental operative checker behind
BEAGLE_EXPERIMENTAL_OPERATIVE=1. - Levenshtein typo suggester is now segment-aware against the real schema: 96.9% Top-1, latency cut 57% (306 ms → 130 ms/query).
Internal
- Phase 0 instrumentation + Phase 1 + Phase 2 batch migrations across the corpus (e273c35).
- Corpus migration tooling for the
:-adoption pass (140 files touched in 6fefc09). - CLAUDE.md formalizes ten standing rules and the three-statement spec — the surface is now spec-determined, not negotiated.
Known limitations
- Free-variable resolution at definition site: macros are datum-based, not syntax-object-based.
- Bidirectional inference Layer 2 deferred until a corpus has enough
defns to justify it. - Refinement types gated to a demo file behind a kill-switch.
- Operative checker quarantined behind
BEAGLE_EXPERIMENTAL_OPERATIVE=1; not shipping in the default tool surface. - JS / Py / SQL / Rkt emitters remain dormant; opt in with
BEAGLE_ALL_TARGETS=1for structural-only runs.
v0.15.3
read-beagle-syntax: target-aware readtable so build-all picks up ~''…''
bin/beagle-build loads each .bnix as a #lang module, so it always gets
the right reader from the lang directive. The build-all path
(used by bin/beagle-build-all and bin/firn-build) goes through
read-beagle-syntax in parse.rkt, which skipped the #lang line AND
hard-coded the base beagle-readtable. The base readtable doesn't know
about nix's ~"…" / ~''…'' reader macros, so any ~''…'' body
containing the first }, |, #, etc. failed to read with the
generic "unexpected }" / "unexpected dispatch sequence: #" errors.
v0.15.2's importer + normaliser both started emitting ~''…'', which
made this latent gap fatal for build-all consumers — including the
nixos-config firn-build pipeline.
Fix: read-beagle-syntax inspects the #lang target and, for nix files,
switches to beagle-nix-readtable (now exported from
beagle/nix/lang/reader-impl). Non-nix files keep the base readtable
so ~-as-interp stays scoped to the nix variant.
Regression test: tests/build-all-nix-reader.rkt feeds a ~''…'' body
through read-beagle-syntax with every char that previously crashed
the base reader.
v0.15.2
bnix multiline strings: importer emits ~''…''; lint hard-errors legacy cursed form
The pre-fix importer dumped indented Nix ''…'' literals as a single Racket
string with embedded \n escapes: (ms "#!/usr/bin/env bash\n\nset -e\n…").
Visually unreadable, and three latent bugs underneath:
- the legacy emit-nix-multiline-string and operative nix-ms both
concatenated (ms …) operands with no \n between them, so the
canonical multi-operand form (ms "line1" "line2") rendered as one
physical line in the output Nix. The cursed single-string-with-\n
only worked because it happened to be one operand. - operative nix-ms wrapped non-string operands with ${…} unconditionally,
so (ms (s "#!" pkgs.bash "/bin/bash") "rest") emitted as
${"#!${pkgs.bash}/bin/bash"}rest instead of inlined. - the ~''…'' reader's ''$ escape (literal $ in the body) was undone by
split-line-interp's second pass, which re-treated ${X} as interp
regardless of how the $ got there. ''${THEMES[@]} round-tripped to
${THEMES} — silent semantic regression.
Fixes:
- bin/beagle-import-nix: emit (ms …) using a new emit-ms-tilde helper
that produces ~''…'' with body indentation and per-line operand
splitting. Both str-lit-ind and str-interp-ind go through it. - beagle-lib/private/emit-nix-strings.rkt (legacy emit): join (ms …)
operands with \n before the re-split + indent pass. - beagle-lib/private/emit-operative.rkt (operative emit): same \n
join for nix-ms, and inline (s …) operands as raw ${EXPR} markers
in the body rather than wrapping the whole line in ${"…"}. - beagle-lib/nix/lang/reader-impl.rkt: read-multi-line-body emits a
U+0001 sentinel before literal $ from ''$; split-line-interp drops
the sentinel and passes the $ through as a chunk character without
interp parsing. ''${X} now survives end-to-end. - beagle-lib/private/lint.rkt: new lint-nix-ms pass — any (ms …) with
a string operand containing \n is a hard error pointing at ~''…''
and bin/beagle-normalize-ms. Bypassable via BEAGLE_NO_LINT=1 during
sweep. - bin/beagle-normalize-ms: one-shot rewriter for existing .bnix files.
Parses (ms …) forms (single- and multi-operand), reassembles the
body with ${EXPR} for interp operands, escapes for ~''…'', and
emits in place. --dry-run and --check supported. - bin/beagle-daemon: bump start timeout from 5s to 30s. Cold-start
require is ~8s without compiled bytecode (~0.5s with), so the
previous timeout killed the daemon mid-startup on a clean checkout.
Tests:
- beagle-test/tests/emit-nix.rkt: multi-operand (ms …) per-line
splitting; interp inlining for multiple (s …) operands. - beagle-test/tests/nix-roundtrip.rkt + fixtures/nix-tilde-ms.bnix:
~''…'' reader form with literal '', literal${X} via ''$ , and
real ${pkgs.bash} interp in the same heredoc. - beagle-test/tests/nix-import-roundtrip.rkt + fixtures/import-nix-
source/heredoc.nix: importer end-to-end — no cursed (ms STR-WITH-\n)
in output, real interp preserved, literal ${X}/${array[@]} survives
.nix → .bnix → .nix. - beagle-test/tests/nix-lints.rkt: hard-error on single- and multi-
operand cursed forms; canonical multi-operand passes. - beagle-test/tests/normalize-ms.rkt: normalizer rewrites both
cursed shapes, leaves canonical alone, is idempotent, --dry-run
preserves contents.
Also: beagle-lib/private/validate-nix.rkt picks up lib.mkDefault/mkForce/
mkMerge/mkOverride (dotted form, not just lib/mk*) as priority modifiers,
and coercedTo as a mergeable Nix-module type.
v0.15.1
Patch release — codegen bug fixes plus batch-build tooling.
Fixes
(module …)formals no longer emit..., .... The parser stripped no marker before falling through to the formal-name path, so the user-written...became a formal and themoduleform's hardcodedrest?flag then appended a second one. Hyphens and a literal…now read clean.(ms …)heredocs indent every physical line. Previously each list element was treated as one line regardless of embedded\n, so a single multi-newline string fragment broke the heredoc's leading indent. Parts are now flattened into one buffer, split on\n, and indented per physical line.
Tooling
beagle-build-all --in-place+.nixtarget. Writes<src>.nixnext to each<src>.bnixin a single Racket process. Wiringfirn-build(in the firnos config repo) through this path takes a full 216-file corpus regen from ~11min to ~9s.
Compatibility
No surface changes. Existing .bnix sources type-check and emit identically modulo the two bug fixes above. Regenerated .nix may differ cosmetically (empty heredoc lines no longer carry stray indent, trailing \n from source is now preserved).
v0.15.0
Highlights
- bnix converter rewrite on rnix-parser.
beagle-import-nixswaps
nix-instantiate --parsefor rnix-parser
via a small Rust binary (tools/nix-parse-json). The lossy-normalization
workarounds (float markers, path restoration, manual tokenizer) all go
away — net –600 lines of Racket. 100% semantic correctness on two real
corpora (Misterio77 nix-config + firnos nixos-config, 9/9 hosts). - Typed
flake-inputform ships,nix-identremoved. The last
escape-hatch-by-another-name is now a parse-time error with a
migration message. The "zero escape hatches" claim now matches code
reality. - dockerTools stdlib coverage.
buildLayeredImage,buildImage,
streamLayeredImage,pullImagetyped. - Tree prune.
lab/,notebook/,self-host/removed from this
repo (~35k-line net deletion). The Bun-based self-host proof-of-concept
is retired; production self-host direction is Cyclone Scheme.
Breaking
nix-identis now a parse-time error. Migrate to(flake-input :NAME :NAMESPACE :path ...).beagle-import-nixnow requirestools/nix-parse-jsonto be built:
cargo build --release --manifest-path tools/nix-parse-json/Cargo.toml.
Stats
- 1190 active-tier tests passing (+ oracle/differential/bun-parity)
- 116 commits since v0.14.0
Note (added post-release)
The Bun self-host bin scripts (beagle-bun, beagle-self-emit{,-clj,-rkt,-py,-nix}, beagle-ast) and the oracle-bun test are retired alongside the self-host/ deletion. If you need the working Bun PoC: git checkout v0.14.0. Cyclone Scheme is the production self-host destination.
v0.14.0
The Nix story landed.
Beagle is now a real typed authoring layer for NixOS configs (and still everything else). Schemas flow into the type checker, escape hatches are gone, the stdlib actually has teeth.
Highlights
Schema → type checker integration
config.services.openssh.enable now resolves to Bool from `.beagle-cache/schema.json`. Type errors surface at compile time:
```
(def msg : String config.services.openssh.enable)
;; ✗ def msg: expected String, got Bool
```
Also: `(let [cfg config.services.X] cfg.foo)` flows the alias through schema. Bare-symbol truthiness narrows nullable types (`(if config.X.optional ...)`).
Stdlib precision: 104 → 527 entries
Higher-order combinators get proper parametric types:
```
builtins/map [forall (A B) [[A → B] (List A) → (List B)]]
builtins/attrNames [forall (V) [(Map String V) → (List String)]]
lib/foldl [forall (A B) [[B A → B] B (List A) → B]]
```
Full lib.attrsets / lib.lists / lib.strings / lib.modules / lib.options / lib.types / lib.generators coverage.
Zero escape hatches
Deleted: `unsafe-nix`, `unsafe-js`, `unsafe-clj`, `unsafe-py`, `unsafe-rkt`, `unsafe`, `unsafe-expr`, `define-macro unsafe`, the `''...''` raw passthrough reader. All of them. When the stdlib doesn't cover something, add a one-line type signature. See `CLAUDE.md` for the design rule.
New nix surface forms
- `(derivation {:pname ... :src ...})` — typed shape validation, did-you-mean on typos
- `(flake {:description ... :inputs ... :outputs ...})` — required key check, outputs must be a function
- `(overlay [final prev] body)` — curried (was attrset-pattern, caused infinite recursion at nix-build time)
- `(with-cfg config.X BODY)` — AST-level cfg= let-binding (replaces the regex magic)
- `~"hi ${name}"` — single-line interp reader
- `~''multi\nline\n${name}''` — multi-line interp reader
Surface elegance — one canonical idiom per concept
```
inh → inherit rec-att → rec-attrs
inh-from → inherit-from assert-do → assert
with-do → with spath → search-path
impl → implies fn-set-rest → module
```
No aliases. Did-you-mean catches typos.
Tooling
- `bin/beagle-ci` — full gate (tests + property + nixos-config validate)
- `bin/beagle-nix-oracle` — emit + `nix-instantiate --parse` classifier
- `bin/beagle-test-nix` / `bin/beagle-test-tag` — file-glob and tag-based test runners
- LSP target-aware: completion suggests the right stdlib per file extension
- `beagle-validate` 1-line output by default; `-v / --verbose` for full chatter
contrib/
- `contrib/nvim-lspconfig/` — ready-to-PR stanza + standalone install instructions
- `tree-sitter-beagle` grammar (separate repo, pending push)
Status
- 1343 tests passing (was 1296)
- Schema integration verified end-to-end: `nix build .#nixosConfigurations.whiterabbit.config.system.build.toplevel` succeeds from beagle-generated flake.nix
- Dogfooded on a 220-file NixOS config (firnos)
- Still pre-1.0 by design — no v1.0 until others have used it in anger
Breaking changes
- `unsafe-*` parse-time error (was: target-specific verbatim emission)
- `with-do` → `with` (shape-disambiguated)
- `fn-set-rest` → `module`
- `fn-set@` removed (use let-binding + fn-set)
- `inh`/`inh-from`/`rec-att`/`assert-do`/`spath`/`impl` renamed to full words
- `''...''` reader removed (use `~''...''` for beagle-interp, or write a sibling `.nix` file for raw Nix)
- `define-macro unsafe` rejected (all macros are now `safe` and type-checked end-to-end)
- `.nisp-cache/` → `.beagle-cache/` (rename, schema and config files)
Migration helper
- For `firnos`-style configs: sed `with-do` → `with` and `fn-set-rest` → `module` across all `.bnix` files; the validator + check catches anything missed.
v0.13.1
Correctness & coverage
- Inf/NaN emission fixed across all 4 emitters: JS→
Infinity, CLJ→##Inf, Python→float('inf'), Nix→clear error - defenum keyword emission fix (emitted symbols instead of keywords in CLJ)
- defmethod/deftype/extend-type return type annotation leak fix (3 instances of same parser bug)
- CLJ behavioral test suite: 64 end-to-end tests (compile → Babashka → verify), including multi-module require round-trips and protocol/deftype edge cases
- Bun oracle CI: 23/30 oracle fixtures pass
raco make, 22/30 emission parity with Racket compiler
Macro expander
- Expansion provenance: error messages now include macro name, depth, and full expansion chain (both Racket and self-hosted expanders)
beagle-expand --trace: prints each expansion step to stderr (input/output per step, depth-indented for nested macros)
Security hardening
- Daemon port/pid files moved to
$XDG_RUNTIME_DIR(fallback/var/tmp) - Repair command restricted to paths within watched directory
0600permissions on port/pid files
Nix validation
- Flake-input HM programs (e.g.,
walker) no longer produce false-positive "unknown HM option" errors - 212 files, 0 false positives on real NixOS config
JS target
js-templatesplice sites now type-checked: collection types raise E016 diagnosticjs/quoteconfirmed complete (3 splice kinds:~expr,~@stmts,~%json)
Housekeeping
- 1278 → 1296 tests
- Cancelled 18 zero-value todo items with documented reasoning
- All 5 todo workstreams closed