v4.4.0
Released 2026-06-06 · GitHub release · CHANGELOG
Ships RFC-0043 — async systems gain an explicit @@[async] header and a
layered casing / machine codegen architecture with a single-driver gate —
plus RFC-0045 (relocating the state-name accessor) and an RFC-0044 kernel
context-stack fix. This release has breaking hard-cuts; both are mechanical to
fix and Frame is pre-public-beta, so no published program is affected. See the
migration guide.
Highlights
@@[async]system-header attribute (RFC-0043 — Async systems: layered codegen with a single-driver gate). A system declaring anyasyncmember now opts in explicitly with@@[async]on the line above@@system. It may also be used on a sync system that just wants the gate.- Layered casing / machine emission across 11 async backends. An async system
is emitted as a public casing (your declared name) wrapping a private
_<Name>Machinethat holds the existing dispatch core. Operations and persist save/load bypass the gate; internal self-calls go straight to the machine. Sync operations on an async system are no longer needlessly coroutinized. E703single-driver gate. The casing rejects a second external dispatch while one is in flight, surfaced as each language’s idiomatic — and recoverable — error (RustErr(FrameE703Error), Swiftthrows, JavafailedFuture, GDScriptpush_error+ typed-zero, etc.). Catches the classic “two tasks driving one machine” bug loudly instead of corrupting state.@@:system.state.nameis the state-name accessor (RFC-0045 — reserve@@:system.state). Bare@@:system.stateis reserved for a future meaning (a direct compartment reference); the current-state name now reads@@:system.state.name. Output is byte-identical to what@@:system.stateproduced before.- Kernel context-stack now unwinds on a handler exception (RFC-0044). A handler that raised mid-dispatch used to leak a stale context-stack entry per failed call; the dispatch wrapper now cleans up via each language’s try/finally idiom on 12 backends.
- Graphviz:
push$ -> $Xnow draws a forward edge — pushed-to states are no longer rendered unreachable.
Breaking changes
See the migration guide for worked before/after for each.
E720— async members require@@[async](RFC-0043, hard cut, no grace period). Fix: add the attribute, or runframec project add-async-attr <path>.E721— a sync system can’t compose an async system as a domain field (same-file). Fix: add@@[async]to the holder, or hold the async child from an async parent.E608— bare@@:system.stateis reserved (RFC-0045). Fix: mechanical@@:system.state→@@:system.state.name.- Rust casing methods return
Result<T, FrameE703Error>(D5). Fix:?-chain ormatchthe result. - Swift casing methods are
async throws(D2). Fix:try await sys.method()inside ado { … } catch { … }.
Action required
Most projects need nothing — sources that already carry @@[async] (or declare
no async members) and don’t use @@:system.state generate byte-identical output
to 4.3.x. If you do use either, the migration is a one-shot codemod plus a
find-and-replace:
framec project add-async-attr path/to/source-tree # inserts @@[async]
# and, across your tree:
# @@:system.state → @@:system.state.name