Roadmap
Open work
Open work only, shortest useful form. The reasoning behind every closed item lives in decisions.md (the numbered decision log) and tests/CONTRACT.md (what is verified, and how).
Platforms #
Linux is the primary, fully verified target. macOS and Windows builds exist, compile, and type-check against real platform headers in CI — see macos.md and windows.md for exactly what's supported on each. What remains open:
- Audio playback on a real Mac and a real Windows machine, and windowed mouse/keyboard/window behavior on a real Mac. Each stays behind an explicit opt-in env var (
FESTINA_ENABLE_MACOS_AUDIO/ _GRAPHICS,FESTINA_ENABLE_WINDOWS_AUDIO) until confirmed on real hardware. Windows windowing needs no hardware: the CI job opens a real Win32 window and drives its mouse, keyboard, resize and close handlers itself (see windows.md).
Compiling to wasm32-wasi is supported and
CI-verified — see wasm.md — and a
compiled .wasm runs in a browser tab on this
project's own WASI host (runtime/wasm/browser.html,
verified in headless Chromium on every push). Graphics/audio
are out of scope there permanently (WASI has no backend for
either), and so, it turns out, is sanitizer coverage:
clang --target=wasm32-wasi -fsanitize=address is
rejected by the compiler outright, and the wasm32
compiler-rt package ships only builtins — no
sanitizer runtime exists for the target. Nothing this project
can work around, and nothing it needs to: every allocation the
native sanitizer runs exercise is the same C source a wasm
build compiles, whose entire __wasi__ delta is
non-allocating stubs. Nothing open here.
Language & standard library #
- Media formats stay PNG/JPEG + WAV/MP3, deliberately: each new format is a new system dependency for every machine that compiles a media-using program. Revisit only with a concrete need.
- A raw byte-buffer type — a generalized, writable
blob, or a newbytestype, with[i] =assignment andtext.toBytes()/bytes.toText()conversions at the boundary. Open but unmotivated: the case usually made for it is skipping a shell-out to clang on textual LLVM IR, and that is already true without it —llvm_backend.pyparses the generated IR in-process via libLLVM's C API whenever it is available, withclang/cconly a fallback, and in-place string append makes building that IR text cheap as a plaintext. A mutable, indexable byte buffer could still earn its place on its own merits (binary protocol and data construction), but a full new primitive type costs surface area from the lexer through to the runtime, and nothing currently needs one.
Memory model #
Automatic reclamation is escape analysis plus reference
counting. Most managed types
(structarr[T]map[T]asciiimgaudregexblobhttpurlsocket/table
rows) carry a refcount header; text is the
exception — it has no header at all and is instead copied on
alias and freed outright — a live text pointer
can be a heap buffer, a bare .rodata literal, a
borrowed environ pointer or an X11 stack buffer,
and a header would have to be valid for all four. Reference
cycles are collected by trial deletion, with
free/delete as the manual override.
What remains open:
- Cycle trials are synchronous and per-release — every still-referenced release of a cycle-capable type walks the value's reachable subgraph. Fine for ordinary object graphs (20k dropped 21-node disjoint cycles in ~34 ms) — but the case that number never tested, shared structure under repeated release-while-live churn, measures a real, cleanly linear cost specifically tied to sharing rather than to total node count: a shared ring costs ~9-10x a disjoint one at the same total node/iteration count, scaling linearly in both ring size and iteration count. The classic deferred-root buffer is the known optimization, now motivated by measurement rather than assumption. Still deliberately not started: the real algorithm needs the free path of every cyclic release wrapper to become buffering-aware too (a still-buffered node hitting refcount zero can't be freed immediately without leaving a dangling pointer in the pending-roots buffer) — new correctness-critical surface in code every struct/arr/map-using Festina program runs through, and batching still trades lower amortized CPU for higher peak memory (collection is delayed). Earns its own dedicated round: a fresh plan, and ASan/LeakSanitizer-under-stress verification of the deferred-free "zombie" path specifically.
- Text globals are not freed at process exit — deliberate: they are reachable until exit, LeakSanitizer agrees, and freeing them would be exit-time busywork.
Deliberate behavior (documented, not planned work) #
- Array indexing is not bounds-checked — a performance choice, see api.md.
keyDownauto-repeats while held (that is how text entry works); a held key still fires exactly onekeyUp. Track held keys yourself for edge-triggered input.regex(pattern, flags)is memoized per call site — the runtime compares the actual pattern+flags against the site's last compilation, so a repeated pattern costs what a literal does (~24x cheaper than recompiling) and a changed one recompiles. One site alternating patterns still recompiles per change — see api.md.