Status & Roadmap

Lumina is a working language, not a finished one. The core pipeline (source to AST, HIR, MIR, LLVM IR, then JIT or native binary) is green, and every sample on these pages was run with the current compiler. Much of the language already works: the first half of this page lists what you can build on today, the second half what remains.

Status legend

Every open point on the other pages carries one of these tags, linked to its entry below. Anything without a tag works as described.

What works today

You can build real programs on all of this.

Language core

Types and errors

See Errors & Types.

Memory and safety

See Memory.

Concurrency

See Async.

Tooling

See Tooling.

Interop and stdlib

See Interop & Stdlib and Zero-Copy & Servers.

Agents

See Built for Agents.

What remains

Each open point, with the concrete gap. The table is the summary; the entries below give the detail.

At a glance

PointStatusWhat remains
Stack safetymostly doneasync recursion on the host stack; advisory stack budget
Mutable referencesmostly donereassigning a whole struct parameter
Compile speedmostly donelarge programs not measured
String dialectmostly doneuser grammars, lazy templates
Coverage toolchainmostly doneneeds llvm-cov on PATH
Purityin progressbuilt-ins such as println bypass the check
Optional chainingin progressa field read on T? is not rejected
Contractsin progresscompound clauses, struct invariants
Function wrappersin progress@Logged, @Memoize, @Retry inert
Await elisionin progressbehind a flag, not the default
Preemptive schedulingin progresscooperative only
CPU-parallel tasksin progressopt-in thread count
Channel semanticsin progressstruct elements, closed-channel signals, rendezvous, send cases
Data-race checksin progresscaptured mut Integer silently copied
Move trackingin progressplain values usable after send
Frozen datain progressmutation via calls, collections, closures
Deep immutabilityin progressmutation through a mut alias
Sharing primitivesin progressno Lumina type yet
Zonesin progresssilent overflow, no budgets
Tuple GC layoutin progressmixed tuples across a GC cycle
GC compactionin progressbehind an environment flag
Bounded generics in lambdasin progressinterface call inside map crashes
Derive enginein progressderive() not wired
FFI gatingin progressW407 is a warning
Regexin progresscaptures, counted quantifiers, replace
Runtime modulesin progressambient built-ins escape the grant
Sandbox tiersin progressModule.load, namespace @Sandbox import
Agent harnessin progressrunAgentWith untested
Approval gatein progressopt-in; no human-facing handler
Scoped networkin progressredirects, wildcards, protocols, ambient bypass
Resource limitsin progressmemory-only limit can hang; no wall clock
Line numbers on every errorin progresssome codes lack a location
Help linesin progressgeneric per code
Quiet lintsin progressfalse positives; exit status
Runtime crash locationin progressno source location
Ambient I/O warningin progressbuilt-ins still work without a capability
Library template hintin progress"Next steps" names a missing file
Inferred borrow checkerdesignedescape placement in progress; checker is design
OS isolationdesignedLandlock, namespaces, hard stop
Fix interactivedesignedlumina fix -i not supported
Fuzz and Expectdesignedno generated input; @Expect inert
Panic boundariesdesignedno @PanicBoundary, no recover
Literal validationdesignedno compile-time literal checks
String templatedesignedno StringTemplate type
One effect modeldesignedseparate systems today
Copy-on-write and reference countingdesignedsuperseded, not planned
Zero-copy layersdesignedAF_XDP, sendfile, UDP, stream interfaces
Non-Linux event backendsdesignedkqueue, IOCP
HTTP frameworkdesignedrouter, middleware, TLS
Unsafe blocksdesigned@Trusted covers whole functions
Debug infodesigned-g keeps symbols only
MCP serverdesignedclient only
Type system extensionsdesigneddeferred by design
Self-hostingdesignedtools still in Rust

Mostly done

Stack safety

mostly done Guard pages and stack canaries turn a stack overflow in main or a spawned task into a clean runtime error that suggests @Runtime(stackSize: N). Recursion warnings (W601) and stack estimation exist. Still open: async code that recurses on the host call stack is not intercepted, and the stack budget check (E5110) reports without blocking the build.

Mutable references

mostly done mut-as-reference (ownership phase P1) is on by default: field writes, scalars, String and arrays passed to a mut parameter reach the caller. Still open: reassigning a whole struct parameter (p = Point { ... }) does not reach the caller.

Compile speed

mostly done On small and mid-sized programs turnaround is in Go's range. Large programs have not been measured yet. Indicative wall-clock numbers, taken with a debug build of the compiler on a busy multi-core aarch64 machine (so they lean pessimistic):

StepTime
lumina verify, small filetens of milliseconds (rustc --emit=metadata on the equivalent Rust file: about a fifth of a second)
lumina run (JIT), hello worldabout 0.1–0.2 s
lumina run (JIT), a mid-sized example programabout 1 s
Lumina warm incremental native build, small programabout 0.3–0.6 s
Go go build, same programsabout 0.2–0.4 s
rustc -O, no incremental cache, small programsabout 0.4–0.6 s
Cold Lumina build vs cold Go build (including its standard library)about 6 s each

On small programs Lumina is in the same range as Go, not measurably faster, and rustc is not slow there either.

String dialect

mostly done @StringDialect works: built-in dialects such as json are checked at compile time (E1112), and a @CompileTime validator defines your own. Still open: user-defined grammars and highlighting, and lazy templates, which need the StringTemplate type.

In progress

Purity

in progress Pure-by-default is the design. Full enforcement is an accepted transition (LANG-PURITY) still in progress: some built-in free functions, such as a bare println, still bypass the purity check.

Optional chaining

in progress ?. chaining is partial. Reading a field straight off a T? without unwrapping is not rejected yet: u = findUser(2) followed by u.name passes verify, and when u is None the program crashes with a segmentation fault.

Contracts

in progress @Contract.requires and @Contract.ensures work on parameters, functions and struct fields: a violation provable at compile time is E4011, the rest panic at run time. A compound clause such as a > 0 && a < 10 is not checked at run time, and a struct-level @Contract.invariant is accepted silently but never enforced.

Function wrappers

in progress @Cache works on free functions. @Logged, @Memoize and @Retry (outside tests) are accepted but have no observable effect. User annotations are an @Annotation interface plus an @AnnotationHandler, gated behind import from "lumina.meta". What remains is the transform dispatch: ctx.wrap and ctx.insertBefore rewriting is designed, and @! is validated but rewrites nothing. The old @Decorator marker is deprecated and its removal is tracked as part of this work. @Memoize under @Provide is a DI factory cache, not a function cache.

Await elision

in progress Explicit await is the default. Await elision (the colorless form) works behind lumina run --await-elision=on; making it the default needs deterministic ordering and async stack traces first. Overlapping un-awaited calls and spawning them automatically are further experimental switches.

Preemptive scheduling

in progress Tasks are cooperative, not preemptive. A loop that never awaits can starve its worker.

CPU-parallel tasks

in progress With @Runtime(threads: N) on main, spawned CPU-bound tasks run in parallel on a multi-threaded work-stealing runtime. Parallelism is opt-in: without a thread count the runtime stays on one thread unless tasks hand values to each other through channels, so CPU-bound tasks run one after another. The goal is a sensible default thread count so spawn uses all cores without an annotation. I/O-bound concurrency works either way.

Channel semantics

in progress for v in ch works for scalar elements but is broken for struct elements: a String field comes back as garbage. A receive on a closed channel returns -1, ambiguous with a real value (no v, ok form). A send on a closed channel returns -1 silently instead of failing loudly. Capacity-0 trySend buffers one item, so an unbuffered channel is not yet a true rendezvous. select has no send cases, and directional Sender<T>/Receiver<T> types are missing (ch.sender() is rejected with E6001).

Data-race checks

in progress Two tasks capturing the same mut array or struct are rejected (E5114, use after send), and consuming one through a take parameter is E5106, but a mut Integer captured by two task lambdas is silently copied, so their writes are lost without a diagnostic.

Move tracking

in progress Moves and use-after-move are checked for resources and take parameters; plain structs and arrays are copied, so using a value after sending it on a channel is accepted. Conditional (per-path) move tracking and move-on-mutable-capture are design.

Frozen data

in progress @Memory.frozen covers direct construction and frozen-typed parameters. Mutations through call results, collections and closures are not detected yet. Deep const is a conservative syntactic check.

Deep immutability

in progress Immutability is enforced on the binding. If two bindings refer to the same heap value, that value can still be mutated through a mut alias. Until deep immutability lands, use @Memory.frozen for data that must never change; ROADMAP.md tracks the remaining facets.

Sharing primitives

in progress Guarded<T>, Atomic<T> and Snapshot<T> are the designed way to share mutable state between tasks. Runtime support for guarded and snapshot values exists in runtime/src/, but no Lumina type exposes it yet.

Inferred borrow checker

designed Escape-driven stack-versus-reference placement (ownership phase P2) is in progress, and the inferred borrow and exclusivity checker (P3) is design.

Zones

in progress @Zone(N) works. When the region overflows, allocation falls back to the heap silently: there is no ZoneOutOfMemory, and the budget form @Zone(budget: .hard(5.mb)) does not parse. A zone struct passed as an argument only draws a warning (W302).

Tuple GC layout

in progress Tuples mixing references and values that survive a GC cycle are a known bug and need a per-slot-tagged layout.

GC compaction

in progress A handle table and sliding compaction exist, but only behind LUMINA_GC_HANDLES=1; the default path uses raw pointers.

Bounded generics in lambdas

in progress A lambda that calls an interface method on a bounded generic inside map (items.map(i => i.describe()) where items: Array<T>) fails at run time; a for loop works. A bounded generic function can also print a stray [codegen] debug: line to stderr during compilation; the program output is unaffected.

Derive engine

in progress .reflect metadata works on structs and enums. The implements-triggered derive() is not wired end to end.

FFI gating

in progress W407 (FFI without a capability) stays a warning in every mode. Making it an error for non-trusted code is a top roadmap item.

Regex

in progress Matching works. Capture groups, counted quantifiers and replace are missing.

Agents and sandboxing

Runtime modules

in progress Module.loadSource(src, caps:), module.call and Module.eval compile and run Lumina source at run time through the JIT. A module that names a capability it was not granted is refused at load time, but that check is a scan of the source text (src/runtime/capability_detection.rs), not the type system. Ambient built-ins such as read_file, write_file, exec, get_env_value, file_exists and the http_* functions still work inside a module granted nothing; closing this goes with the ambient I/O warning.

Sandbox tiers

in progress @Sandbox on a function works, and @Sandbox import { f } from "plugin" runs, without verified isolation. Module.load(path) does not exist (use Module.loadSource), and the namespace form @Sandbox import plugin from "plugin" fails. @Trusted (C bindings) and @FullAccess (view narrowing) are not sandbox tiers. Capability and limit gaps are under Runtime modules and Resource limits.

Agent harness

in progress stdlib/agent/ has runAgent, a tool registry, and runAgentSeamed, which replays scripted model replies without network access (examples/ai_effects/agent_e2e_flow.lumina). runAgentWith, which calls OpenAI or Anthropic with retry, budget and policy, has no tests yet and is limited by closure and capability gaps.

Approval gate

in progress The Approval effect and guardedAction in stdlib/ai/tool_effects.lumina work, and a missing handler is a compile error (E9003). The gate is opt-in: ambient built-ins bypass it, and no stdlib handler asks a human yet.

Scoped network

in progress ScopedNetwork.allow(hosts:, ports:) enforces its host and port allowlist, runs an SSRF check on every request, and is covered by tests/spec/34_sandbox/. Still missing: the protocols: argument is ignored, a wildcard host matches by substring, redirects are not re-checked against the allowlist, there is no rate limit, and ambient http_* built-ins bypass it.

Resource limits

in progress ResourceLimits runs each module call in a child process with memory and CPU rlimits; no root is needed. Hitting the CPU limit returns an unhelpful error, a memory-only limit can hang, and there is no wall-clock limit. The seccomp filter for the child exists on x86_64 only and is skipped silently elsewhere. cgroup v2 limits and timeouts (src/runtime/cgroup.rs, src/runtime/sandbox.rs) are used only by lumina test (@Timeout, --timeout), not by lumina run, the JIT or native binaries; without a delegated cgroup subtree they fall back silently.

OS isolation

designed Landlock, namespaces and selectable OS isolation levels for loaded modules, a wall-clock limit, and handle.kill() for a runaway call. Today a spawned module call can only be stopped cooperatively with handle.stop().

Toolchain and diagnostics

Issues hit while writing real tools in Lumina are also logged in docs/web/tools/FINDINGS.md.

Line numbers on every error

in progress Most errors carry a location. Some, including E9003, do not; in JSON their line is null. Others, such as E3009, put it only inside the message text.

Ambient I/O warning

in progress Calling an ambient I/O built-in (read_file, write_file, file_exists, list_directory, network_*, exec, getEnv, set_env_var, ...) draws warning W106 that names the capability call to use, for example read_file(path) becomes fs.read(path) with fs: FileSystem, and getEnv(key) becomes env.get(key) with env: Environment. It is a warning, not an error. Exempt: methods of structs that implement a covered capability, and functions marked @Allow("W106", "reason"), which silences W106 for that function only. @Trusted also silences it, but today a @Trusted marker on any function trusts the whole program, so every W106 in it goes quiet. Built-ins whose capability has no method yet draw no warning, and Clock, Random and print built-ins never do. Still open: the built-ins keep working without a capability, and their failure is a runtime error (E7014), not a type.

Help lines

in progress Codes, spans and lumina explain exist. The note: and help: lines are generic per code rather than specific to the mistake.

Quiet lints

in progress W120, W127 and W601 can fire on code that is fine, and lumina lint exits with status 1 even when every finding is only info.

Runtime crash location

in progress A runtime crash prints a long banner and a runtime stack trace instead of the program's own source location.

fix interactive

designed lumina fix -i (ask before each fix) is listed in --help but exits with "not supported yet". Preview with --diff and narrow with --only or -n instead.

Coverage toolchain

mostly done lumina test --coverage runs the tests, but the report needs an LLVM coverage toolchain (llvm-cov) on PATH; without one the report fails.

@Fuzz and @Expect

designed @Fuzz runs a test once, with no generated input. @Expect is accepted and has no effect. Relatedly, @Timeout limits CPU time, not wall-clock time.

Library template hint

in progress lumina new -t library creates src/lib.lumina, but its "Next steps" line says to run src/main.lumina, a file that template does not create.

Designed, not built

Panic boundaries

designed @PanicBoundary is rejected (E2001) and there is no recover: a panic always ends the program. Only the panicking function's @Defer blocks run; a caller's @Defer does not.

Literal validation

designed @Literal suffixes and prefixes work, but validating a literal at compile time does not: a @CompileTime fromLiteral that calls compileError fails (E6003, then E3002). Built-in literals such as 500ms and 2GB are plain integers (nanoseconds, bytes) and print as such.

String template

designed A StringTemplate type for lazy and callable templates. Backtick strings interpolate eagerly, and StringTemplate is an unknown type (E2007).

One effect model

designed Capabilities lowered to inferred effect rows, T!E as the Error effect, async as the Suspend effect, @Provide as the default handler. Today these are related but separate systems, and Language_Effects.md describes what is real.

Copy-on-write and reference counting

designed Superseded by the mut-as-reference ownership model, and not planned. Nothing is refcounted and no value is copied lazily. Pluggable, Lumina-authored GC selection is likewise design history; the GC is a fixed Rust runtime.

Zero-copy layers

designed AF_XDP kernel bypass, sendfile, UDP and Unix sockets, and PeekableStream/SpliceableStream interfaces with composable decorators. Today peek and spliceTo are methods on TcpSocket (see Zero-Copy & Servers).

Non-Linux event backends

designed kqueue (macOS) and IOCP (Windows). Networking runs on epoll and io_uring on Linux.

HTTP framework

designed A router, middleware and TLS in the standard library. Servers speak raw HTTP/1.1 today.

Unsafe blocks

designed unsafe blocks for local, auditable escapes. Today @Trusted marks a whole function.

Debug info

designed Source-level debug info (DWARF line tables) for Lumina code in native builds. -g currently keeps the symbol table only.

MCP server

designed An MCP server for the toolchain. An MCP client exists in stdlib/mcp/.

Type system extensions

designed Deferred by design: where clauses, variance, higher-kinded types, newtype, timeout expressions. where clauses already parse, but their constraints are not enforced; put bounds inline (<T implements X>).

Self-hosting

designed The formatter, explain and the warning system written in Lumina itself. Parser combinators and @StringDialect are already in place.

Where to get the real numbers

These pages deliberately carry no test counts or percentages, because those go stale with the next commit. The sources of truth live in the repository:

Where it is heading

The priority ladder in ROADMAP.md, roughly:

  1. Make "always async" true by default: stabilize await elision and add effect-inferred async.
  2. Harden stack safety so cooperative coroutines can compete with Go's scheduler.
  3. Close the FFI hole: W407 becomes an error for non-trusted code, and the stdlib's C bindings move behind capabilities.
  4. Developer experience: a quickstart path, better error text, more LSP code actions.
  5. Progressive disclosure: a T0 script tier with zero annotations, a T1 app tier built on five core annotations, and deeper power behind import walls you can grep for.

The guiding goal is TypeScript's ergonomics with Rust's correctness at native speed. Where today falls short of that, FEATURE_STATUS.md and ROADMAP.md say so plainly, and this page does the same.

Next: Overview